Compare commits

...

162 Commits

Author SHA1 Message Date
Asger F
27e1a8bd7a Ruby: cache predicates related to getConst 2022-11-07 11:18:29 +01:00
Asger F
d4b018f242 Ruby: typo: found up -> looked up 2022-11-07 09:58:00 +01:00
Asger F
25f0382fce Ruby: replace asMethod with asCallableAstNode 2022-11-07 09:38:48 +01:00
Asger F
af5a378572 Ruby: fix typo in qldoc 2022-11-07 09:20:35 +01:00
Asger F
9a38e31baa Ruby: add explicit 'this' 2022-11-07 09:20:28 +01:00
Asger F
ff20908bbd Ruby: Assignment -> AssignExpr 2022-11-07 09:20:16 +01:00
Asger F
25dd8db423 Ruby: Refactor out getAnElementWriteCall 2022-11-07 09:18:18 +01:00
Asger F
5fa49b3319 Ruby: asExpr() -> getExprNode() 2022-11-07 09:18:00 +01:00
Asger F
8b85744d3e Ruby: use lambdaCreation and handle "proc" in there 2022-11-07 09:14:55 +01:00
Asger F
4ae90e35d5 Ruby: inline transitive class-hierarchy getters 2022-11-04 08:50:33 +01:00
Asger F
472a10fd54 Ruby: direct -> immediate 2022-11-04 08:49:01 +01:00
Asger F
0f1b3486de Ruby: Use another join order for nested constant lookup 2022-11-03 10:47:39 +01:00
Asger F
a195ea942e Ruby: only drop to CFG layer for getConstantValue() 2022-11-03 10:18:31 +01:00
Asger F
cf4a3e0bbe Ruby: 'a' -> 'an' in a qldoc 2022-11-03 10:13:39 +01:00
Asger F
fe8945b5c9 Ruby: Rename getCanonicalEnclosing/Nested module
getCanonicalEnclosingModule -> getParentModule
getCanonicalNestedModule -> getNestedModule
2022-11-03 10:10:47 +01:00
Asger F
bd2a065562 Ruby: rename ConstantValue::getX -> fromX 2022-11-03 10:03:40 +01:00
Asger F
2619f3f667 Ruby: include overridden methods in getAnInstanceSelf 2022-11-01 08:32:55 +01:00
Asger F
ab4e341e65 Ruby: fix handling of namespaces with no 'self' 2022-10-31 14:05:11 +01:00
Asger F
9da5ec79c5 Ruby: Drive-by fix a QL4QL alert 2022-10-31 14:05:11 +01:00
Asger F
e549f15b1c Ruby: fix implicit 'this' 2022-10-31 14:05:11 +01:00
Asger F
056b1e8d63 Ruby: add some basic tests 2022-10-31 14:05:11 +01:00
Asger F
9be2512050 Ruby: rename one of the PostsController2 classes
These had the same name and ended up being unified
2022-10-31 13:33:41 +01:00
Asger F
b4b34cc994 Ruby: port part of ActionController model 2022-10-31 13:33:41 +01:00
Asger F
12ce46e4b1 Ruby: port part of Railties model 2022-10-31 13:33:41 +01:00
Asger F
38955d1761 Ruby: port part of the Rails model 2022-10-31 13:33:41 +01:00
Asger F
9f59b6b439 Update type-tracking test 2022-10-31 13:33:41 +01:00
Asger F
0a8f39fe96 Ruby: recover some incomplete capture flow 2022-10-31 13:33:41 +01:00
Asger F
ff02ba5965 Ruby: include SSA param input step for flowsTo 2022-10-31 13:33:41 +01:00
Asger F
017157820a Ruby: make ParameterNode extend LocalSourceNode 2022-10-31 13:33:41 +01:00
Asger F
b29ac5249e Ruby: add type-tracking inline test in global flow test 2022-10-31 13:33:41 +01:00
Asger F
4ed61c13f8 Ruby: add some captured-variable flow tests 2022-10-31 13:33:41 +01:00
Asger F
b632e21ba0 Ruby: add ConstRef 2022-10-31 13:33:41 +01:00
Asger F
06ec03de74 Ruby: add convenience-accessors for ConstantValue 2022-10-28 15:16:14 +02:00
Asger F
046e669c78 Ruby: add getAncestorExpr 2022-10-28 15:16:14 +02:00
Asger F
77d1788619 Ruby: add data flow versions of ArrayLiteral, HashLiteral, Pair 2022-10-28 15:16:14 +02:00
Asger F
2546d09fe2 Ruby: add SetterCallNode 2022-10-28 15:16:14 +02:00
Asger F
515b8366d2 Ruby: add getAnAncestor, getADescendent 2022-10-28 15:16:14 +02:00
Asger F
c8f7519cee Ruby: add Module.getNamespaceOrTopLevel 2022-10-28 15:16:14 +02:00
Asger F
1f644a9c1d Ruby: add getEnclosingToplevel 2022-10-28 15:16:14 +02:00
Asger F
436cc60138 Ruby: update some uses of getConstantValue() 2022-10-28 15:16:14 +02:00
Asger F
156964bfc9 Ruby: add getEnclosingModule and getNestedModule 2022-10-28 15:16:14 +02:00
Asger F
67772bbc43 Ruby: Accessors for attributes and elements 2022-10-28 15:16:14 +02:00
Asger F
8976ba5583 Ruby: Add CallableNode, MethodNode, and accessors 2022-10-28 15:16:13 +02:00
Asger F
ac4cac889f Ruby: add DataFlow::ModuleNode
sdf
2022-10-24 15:35:17 +02:00
Asger F
65add15416 Ruby: add getALocalUse()
This is the inverse of getALocalSource()
2022-10-24 15:35:17 +02:00
Asger F
aab1e1f5b4 Ruby: add some helpers at the AST level 2022-10-24 15:35:17 +02:00
Tony Torralba
2148e8be4d Merge pull request #10892 from atorralba/atorralba/swift/customurlschemes
Swift: Add a new Custom URL Scheme source
2022-10-24 15:33:27 +02:00
Tony Torralba
30f5fb6d83 Update expectations after merge 2022-10-24 14:24:13 +02:00
Erik Krogh Kristensen
5ff98cd80e Merge pull request #10888 from erik-krogh/glob
Ruby: add model for Dir.glob and other Dir methods
2022-10-24 14:17:37 +02:00
Asger F
bcfe4ece6f Merge pull request #10918 from asgerf/rb/constant-compound-assignment
Ruby: handle compound constant-assignment
2022-10-24 14:07:28 +02:00
Asger F
cac2e2e2e4 Merge pull request #10928 from asgerf/rb/assumed-global-const
Ruby: assume some global constants are defined
2022-10-24 14:06:34 +02:00
Paolo Tranquilli
22adf21dd3 Merge pull request #10912 from jketema/templ-func-prototype
C++: Update test result after extractor changes
2022-10-24 13:44:02 +02:00
Tamás Vajk
1d2087b92a Merge pull request #10949 from tamasvajk/kotlin-underscore-var
Kotlin: exclude Kotlin files from `java/underscore-identifier`
2022-10-24 13:32:49 +02:00
Asger F
0ffb0f6d4d Ruby: constant lookup is unaffected by blocks 2022-10-24 13:07:21 +02:00
Chris Smowton
86e99c497d Merge pull request #10930 from smowton/smowton/fix/external-property-overloads
Kotlin: give external extension properties with matching name and file distinct trap filenames
2022-10-24 11:32:37 +01:00
erik-krogh
07d90b34df use instanceof in DirPathAccess 2022-10-24 12:05:26 +02:00
Erik Krogh Kristensen
669b0c35fe fix qldoc
Co-authored-by: Nick Rolfe <nickrolfe@github.com>
2022-10-24 12:05:26 +02:00
erik-krogh
85cd7f9121 add model for Dir.glob and other Dir methods 2022-10-24 12:05:26 +02:00
Tony Torralba
f523fbc9d0 Merge branch 'main' into atorralba/swift/customurlschemes 2022-10-24 11:41:50 +02:00
Tony Torralba
3973e1ce04 Update swift/ql/test/library-tests/dataflow/dataflow/FlowConfig.qll
Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
2022-10-24 11:37:51 +02:00
Chris Smowton
88c4a2f6e2 Merge pull request #10936 from smowton/smowton/fix/internal-constructor-called-from-java
Kotlin: make internal constructors' trap labels consistent with the Java extractor
2022-10-24 09:37:00 +01:00
Paolo Tranquilli
90d4861b70 Merge pull request #10875 from github/redsun82/swift-codegen-doc
Swift: add infrastructure for documenting generated code
2022-10-24 10:14:57 +02:00
Tony Torralba
80f7d58fae Add missing tests for not-quite-working flow steps 2022-10-24 09:37:22 +02:00
Paolo Tranquilli
fd226c51c1 Merge pull request #10924 from github/redsun82/swift-fix-qltest-failure-reporting
Swift: add qltest tests and fix its failure reporting
2022-10-24 09:34:12 +02:00
Paolo Tranquilli
15e5faf5b6 Merge branch 'main' into templ-func-prototype 2022-10-24 09:19:46 +02:00
Tamas Vajk
d585839b7e Kotlin: exclude Kotlin files from java/underscore-identifier 2022-10-24 09:05:28 +02:00
Tamas Vajk
0192ae8331 Kotlin: Add test case for variables named underscore 2022-10-24 09:04:54 +02:00
Arthur Baars
b3855b089a Ruby: some more tests 2022-10-22 14:15:29 +02:00
Arthur Baars
ccaa12998d Ruby: desugar compound constant-assignments 2022-10-22 01:11:35 +02:00
Nick Rolfe
e5663574fe Merge pull request #10935 from github/nickrolfe/taint-step 2022-10-21 19:28:23 +01:00
Chris Smowton
00800017fd Kotlin: make internal constructors' trap labels consistent with the Java extractor
Previously we accidentally named these something like <init>$main, which is a name-mangling the Kotlin compiler applies to internal methods but not to constructors, which look to Java just like regular public constructors.
2022-10-21 16:48:37 +01:00
Nick Rolfe
9fb436e22b Ruby: add change note for localTaintStep fix 2022-10-21 16:33:29 +01:00
Nick Rolfe
269c27757d Ruby: include value-preserving flow in localTaintStep 2022-10-21 16:17:11 +01:00
Nick Rolfe
5319216c18 Ruby: add test of TaintTracking::localFlowStep 2022-10-21 16:04:04 +01:00
Jean Helie
88c6453fa6 Merge pull request #10934 from github/jhelie/add-hello-world-workflow-atm-queries
ATM: add hello world version of workflow checking queries run
2022-10-21 16:58:44 +02:00
Jean Helie
c0593c945b ATM: add hello world version of workflow checking queries run 2022-10-21 16:37:43 +02:00
Paolo Tranquilli
6bd09b1858 Merge branch 'main' into redsun82/swift-codegen-doc 2022-10-21 15:31:52 +02:00
Chris Smowton
42d6968c20 Kotlin: give external extension properties with matching name and file distinct trap filenames 2022-10-21 14:28:53 +01:00
Paolo Tranquilli
408968a417 Swift: fix swift compilation in QL tests 2022-10-21 15:20:38 +02:00
Asger F
84ae17dcbb Ruby: ensure Object is a transitive superclass 2022-10-21 15:18:59 +02:00
Chris Smowton
5e28e5a170 Merge pull request #10909 from smowton/smowton/fix/kotlin-varargs-dataflow
Kotlin: Fix varargs dataflow, and varargs default handling
2022-10-21 13:32:34 +01:00
Paolo Tranquilli
bd62f2be0e Merge branch 'main' into redsun82/swift-fix-qltest-failure-reporting 2022-10-21 14:07:19 +02:00
Paolo Tranquilli
04f6debb88 Swift: fix bazel packaging 2022-10-21 13:42:24 +02:00
Paolo Tranquilli
dbdf6ea489 Swift: fix qltest failure reporting
`qltest.sh` was not exiting with a failure when the extractor was
failing.
2022-10-21 12:54:09 +02:00
Paolo Tranquilli
cf7a5f877b Swift: add qltest.sh tests 2022-10-21 12:54:09 +02:00
Jonas Jensen
7a8c9e7644 Merge pull request #10919 from kaspersv/kaspersv/document-equiv-rel-module
QL language spec: Document built-in equivalence relation module
2022-10-21 12:47:29 +02:00
Ian Lynagh
2e6d6e1538 Merge pull request #10894 from igfoo/igfoo/psi
Kotlin: Refactor PSI handling
2022-10-21 11:43:49 +01:00
Chris Smowton
1fe9e8457f Kotlin: Fix varargs dataflow, and varargs default handling
Dataflow requires accounting for the fact that the varargs parameter isn't necessarily last in the parameter list in a couple more places. Default handling just requires that if the only null parameter is the varargs argument, and it has no default value, then no $default method is required-- the caller is expected to simply pass nothing (at QL
/ source level) or an empty array (at JVM level).
2022-10-21 11:14:41 +01:00
Kasper Svendsen
b29ed3b85a Address reviewer comments from @jbj 2022-10-21 12:08:51 +02:00
Arthur Baars
a56ed88db2 Merge pull request #10920 from github/post-release-prep/codeql-cli-2.11.2
Post-release preparation for codeql-cli-2.11.2
2022-10-21 11:58:12 +02:00
Asger F
3fd2b9ad7b Ruby: add a comment
This would have saved me some time
2022-10-21 11:44:12 +02:00
Asger F
ee7970afcb Ruby: treat String as a builtin 2022-10-21 11:44:11 +02:00
Asger F
db58e3357b Ruby: allow speculative container qname resolution 2022-10-21 11:44:11 +02:00
Kasper Svendsen
925fd2eb45 Accept reviewer reformulation
Co-authored-by: Anders Schack-Mulligen <aschackmull@users.noreply.github.com>
2022-10-21 11:34:48 +02:00
Kasper Svendsen
6fe0de8a9e Accept auto-format suggestion
Co-authored-by: Anders Schack-Mulligen <aschackmull@users.noreply.github.com>
2022-10-21 11:32:37 +02:00
Kasper Svendsen
7faea53c18 QL language spec: Document built-in equivalence relation module 2022-10-21 11:03:01 +02:00
Rasmus Wriedt Larsen
8e8fb3d34f Merge pull request #10911 from RasmusWL/location-debug
Python: add `debug based on location` snippet
2022-10-21 10:59:51 +02:00
Tony Torralba
7a43bdbf05 Apply suggestions from code review
Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
2022-10-21 10:20:01 +02:00
Tamás Vajk
352c20b0c8 Merge pull request #10885 from tamasvajk/kotlin-const-loop-cond-fp
Kotlin: Add test case for false positive with modified captured variable
2022-10-21 10:08:02 +02:00
github-actions[bot]
be7693283b Post-release preparation for codeql-cli-2.11.2 2022-10-21 08:07:17 +00:00
Tom Hvitved
cf35299d08 Merge pull request #10910 from hvitved/ruby/call-graph-refactor
Ruby: Refactor call graph logic for singleton methods
2022-10-21 09:36:13 +02:00
Asger F
d26b0892cf Ruby: also add an AST test 2022-10-21 09:23:21 +02:00
Asger F
038bdecad7 Ruby: add test with compound assignment to a constant 2022-10-21 09:20:03 +02:00
Tamas Vajk
9d1af76c02 Add more test cases 2022-10-21 08:51:11 +02:00
Tamas Vajk
7559d3095f Revert "Kotlin: Exclude captured variables from constant loop condition check"
This reverts commit 3e476f96bd.
2022-10-21 08:38:30 +02:00
Tom Hvitved
db699ae314 Ruby: Refactor call graph logic for singleton methods 2022-10-21 07:27:41 +02:00
Chris Smowton
ac013f9d19 Merge pull request #10889 from smowton/smowton/fix/enum-entry-class-warning
Kotlin: Don't warn on extracting an enum-entry class
2022-10-20 22:08:29 +01:00
Jeroen Ketema
4b5674af32 C++: Update test result after extractor changes 2022-10-20 22:18:32 +02:00
Rasmus Wriedt Larsen
ad915e2698 Python: add debug based on location snippet 2022-10-20 21:20:24 +02:00
Geoffrey White
138643519c Merge pull request #10757 from geoffw0/sqlinject
Swift: Query for SQL injection
2022-10-20 18:55:38 +01:00
Geoffrey White
661106c1a0 Apply suggestions from code review
Co-authored-by: Ben Ahmady <32935794+subatoi@users.noreply.github.com>
2022-10-20 17:54:40 +01:00
Chuan-kai Lin
2e9c8c759c Merge pull request #10907 from cklin/document-assume-small-delta
QL language spec: pragma[assume_small_delta]
2022-10-20 09:00:45 -07:00
Chris Smowton
f2749a8878 Don't warn on extracting an enum-entry class 2022-10-20 16:09:45 +01:00
Chuan-kai Lin
9df725901b QL language spec: pragma[assume_small_delta] 2022-10-20 07:30:02 -07:00
Asger F
8c2c28dd56 Ruby: add test showing missing superclass edge 2022-10-20 15:56:58 +02:00
Ian Lynagh
9bc0c98b8e Kotlin: Update logs test 2022-10-20 14:18:31 +01:00
Ian Lynagh
291330c7e1 Kotlin: Small code simplification 2022-10-20 12:29:48 +01:00
Paolo Tranquilli
7144383505 Swift: fix british spelling of behaviour 2022-10-20 11:43:46 +02:00
Paolo Tranquilli
f9df8a645f Swift: fix potential name conflict on schema class fields 2022-10-20 11:30:05 +02:00
Paolo Tranquilli
8813aea893 Swift: allow default class doc name to be set for properties 2022-10-20 11:23:13 +02:00
Paolo Tranquilli
c22a7e1c81 Swift: rename _DocnameModifier to _DocModifier 2022-10-20 11:05:55 +02:00
Paolo Tranquilli
37b405f134 Swift: add generated docs for predicates 2022-10-20 11:05:01 +02:00
Paolo Tranquilli
22bd10132f Swift: insert blank line between doc and desc 2022-10-20 10:49:26 +02:00
Paolo Tranquilli
b65f49bd50 Swift: document introducer_int 2022-10-20 10:46:12 +02:00
Paolo Tranquilli
7b181a2de0 Swift: change doc of Immediate property getters 2022-10-20 10:39:37 +02:00
Paolo Tranquilli
6830c2f355 Swift: enhance property docs 2022-10-20 10:35:47 +02:00
Paolo Tranquilli
9abaa5c0b3 Swift: rename doc_name with doc in properties 2022-10-20 08:59:08 +02:00
Paolo Tranquilli
492d5aec78 Swift: rename doc to description in properties 2022-10-20 08:57:41 +02:00
Geoffrey White
5b1e138300 Swift: Another qhelp edit. 2022-10-19 20:49:26 +01:00
Geoffrey White
495f744cd3 Swift: Attempt to address qhelp suggestions. 2022-10-19 20:44:27 +01:00
Geoffrey White
05d9c7b892 Swift: More 'an SQL' -> 'a SQL'. 2022-10-19 19:44:59 +01:00
Geoffrey White
83dc6d1564 Apply suggestions from code review
Co-authored-by: Ben Ahmady <32935794+subatoi@users.noreply.github.com>
2022-10-19 19:42:35 +01:00
Ian Lynagh
74a4061508 Kotlin: Refactor PSI handling
We were giving warnings about comments, when we were actually trying to
populate numlines.
2022-10-19 18:02:24 +01:00
Tony Torralba
c2a2d6b379 Fix LaunchOptionsUrlVarDecl
Update test expectations
2022-10-19 17:42:28 +02:00
Tony Torralba
e2c9240973 Add a new Custom URL Scheme source
Also adds a couple of data flow steps to model flow through `?` expressions.
2022-10-19 16:55:14 +02:00
Tamas Vajk
3e476f96bd Kotlin: Exclude captured variables from constant loop condition check 2022-10-19 15:01:17 +02:00
Tamas Vajk
0bc57410a0 Kotlin: Add FP test case for constant loop condition 2022-10-19 14:19:49 +02:00
Paolo Tranquilli
861377f650 Swift: property doc tweaks 2022-10-19 11:40:05 +02:00
Geoffrey White
027b71381a Swift: annotate all cases. 2022-10-18 16:38:02 +01:00
Paolo Tranquilli
65fd9cbf9c Swift: docname and desc examples 2022-10-18 17:05:19 +02:00
Paolo Tranquilli
35c1d311c5 Swift: add doc name override 2022-10-18 17:04:51 +02:00
Paolo Tranquilli
8de7df9c21 Swift: add auto-generated docs for getters 2022-10-18 17:04:51 +02:00
Paolo Tranquilli
4d87abed0e Swift: generate docname in qlgen 2022-10-18 17:04:51 +02:00
Paolo Tranquilli
5f7fa6f915 Swift: generate class docs
Python docstrings in `schema.py` are now added to the generated classes.

As an example, a docstring is added to `Expr`.
2022-10-18 17:04:51 +02:00
Paolo Tranquilli
f41fd81965 Swift: add docstring parsing 2022-10-18 16:54:26 +02:00
Geoffrey White
9767064310 Swift: Fix bug for sqlite3_prepare_v3. 2022-10-17 13:40:35 +01:00
Geoffrey White
1221cbaee7 Swift: Updated results after merge with main. 2022-10-17 13:35:46 +01:00
Geoffrey White
13018150ed Merge branch 'main' into sqlinject 2022-10-17 13:30:14 +01:00
Geoffrey White
8eccae1cdd Swift: Fix the qhelp. 2022-10-14 15:31:53 +01:00
Geoffrey White
f96e4eb87e Swift: One more go at getting the query message how ql-for-ql wants it. 2022-10-14 15:28:14 +01:00
Geoffrey White
227b10adf6 Swift: Qhelp. 2022-10-14 15:18:47 +01:00
Geoffrey White
24c6bb4c52 Swift: More modern (?) phrasing. 2022-10-14 14:41:02 +01:00
Geoffrey White
3da3a278ab Swift: Query metadata. 2022-10-14 14:31:38 +01:00
Geoffrey White
76ff593cc5 Swift: Bring it all together into a query. 2022-10-13 16:06:44 +01:00
Geoffrey White
7d78df25bf Swift: Define SQL sinks. 2022-10-13 15:50:57 +01:00
Geoffrey White
ce5631e7cb Swift: Complete the rename. 2022-10-13 15:22:36 +01:00
Geoffrey White
12cb099376 Swift: Rename to match other languages (except Java). 2022-10-13 15:21:39 +01:00
Geoffrey White
398b2a392f Swift: Add more test variants. 2022-10-13 15:13:29 +01:00
Geoffrey White
5496b11153 Swift: Update tests based on feedback. 2022-10-12 14:52:14 +01:00
Geoffrey White
4258147edf Swift: Test SQL injection via the SQLite.swift library. 2022-10-10 17:40:22 +01:00
Geoffrey White
964c92418c Swift: Test SQL injection via the C API. 2022-10-10 17:40:22 +01:00
Geoffrey White
bcab9d8e7c Swift: Add framework for SQL Injection query. 2022-10-10 17:25:08 +01:00
310 changed files with 7317 additions and 656 deletions

View File

@@ -0,0 +1,13 @@
name: ATM Check Queries Run
# This check is required, therefore we must run it on all PRs, even if only Markdown has changed.
on:
workflow_dispatch:
jobs:
hello-world:
runs-on: ubuntu-latest
steps:
- name: foo
run: echo "Hello world"

View File

@@ -23,12 +23,23 @@ jobs:
- uses: ./.github/actions/fetch-codeql
- name: Check QL formatting
run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only
qltest-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: bazelbuild/setup-bazelisk@v2
- uses: actions/setup-python@v4
with:
python-version-file: 'swift/.python-version'
- name: Test qltest.sh
run: |
bazel test //swift/tools/test/qltest
qltest:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os : [ubuntu-20.04, macos-latest]
os: [ ubuntu-20.04, macos-latest ]
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all
version: 0.4.2
version: 0.4.3-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 0.4.2
version: 0.4.3-dev
groups:
- cpp
- queries

View File

@@ -13,6 +13,7 @@
| test.cpp:4:26:4:26 | c<<expression>> |
| test.cpp:4:26:4:26 | c<<unnamed>> |
| test.cpp:5:29:5:29 | e |
| test.cpp:6:24:6:24 | f |
| test.cpp:6:26:6:26 | (unnamed parameter 0) |
| test.cpp:6:29:6:31 | (unnamed parameter 1) |
| test.cpp:7:20:7:20 | f |

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-all
version: 1.3.2
version: 1.3.3-dev
groups:
- csharp
- solorigate

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-queries
version: 1.3.2
version: 1.3.3-dev
groups:
- csharp
- solorigate

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-all
version: 0.4.2
version: 0.4.3-dev
groups: csharp
dbscheme: semmlecode.csharp.dbscheme
extractor: csharp

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-queries
version: 0.4.2
version: 0.4.3-dev
groups:
- csharp
- queries

View File

@@ -273,4 +273,71 @@ The ``<module_expression>`` itself can be a module name, a selection, or a quali
reference. For more information, see ":ref:`name-resolution`."
For information about how import statements are looked up, see "`Module resolution <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#module-resolution>`__"
in the QL language specification.
in the QL language specification.
Built-in modules
****************
QL defines a ``QlBuiltins`` module that is always in scope.
Currently, it defines a single parameterized sub-module
``EquivalenceRelation``, that provides an efficient abstraction for working with
(partial) equivalence relations in QL.
Equivalence relations
=====================
The built-in ``EquivalenceRelation`` module is parameterized by a type ``T`` and a
binary base relation ``base`` on ``T``. The symmetric and transitive closure of ``base``
induces a partial equivalence relation on ``T``. If every value of ``T`` appears in
``base``, then the induced relation is an equivalence relation on ``T``.
The ``EquivalenceRelation`` module exports a ``getEquivalenceClass`` predicate that
gets the equivalence class, if any, associated with a given ``T`` element by the
(partial) equivalence relation induced by ``base``.
The following example illustrates an application of the ``EquivalenceRelation``
module to generate a custom equivalence relation:
.. code-block:: ql
class Node extends int {
Node() { this in [1 .. 6] }
}
predicate base(Node x, Node y) {
x = 1 and y = 2
or
x = 3 and y = 4
}
module Equiv = QlBuiltins::EquivalenceRelation<Node, base/2>;
from int x, int y
where Equiv::getEquivalenceClass(x) = Equiv::getEquivalenceClass(y)
select x, y
Since ``base`` does not relate ``5`` or ``6`` to any nodes, the induced
relation is a partial equivalence relation on ``Node`` and does not relate ``5``
or ``6`` to any nodes either.
The above select clause returns the following partial equivalence relation:
+---+---+
| x | y |
+===+===+
| 1 | 1 |
+---+---+
| 1 | 2 |
+---+---+
| 2 | 1 |
+---+---+
| 2 | 2 |
+---+---+
| 3 | 3 |
+---+---+
| 3 | 4 |
+---+---+
| 4 | 3 |
+---+---+
| 4 | 4 |
+---+---+

View File

@@ -639,7 +639,7 @@ Various kinds of syntax can have *annotations* applied to them. Annotations are
| "override"
| "query"
argsAnnotation ::= "pragma" "[" ("inline" | "noinline" | "nomagic" | "noopt") "]"
argsAnnotation ::= "pragma" "[" ("inline" | "noinline" | "nomagic" | "noopt" | "assume_small_delta") "]"
| "language" "[" "monotonicAggregates" "]"
| "bindingset" "[" (variable ( "," variable)*)? "]"
@@ -687,17 +687,19 @@ Parameterized annotations take some additional arguments.
The parameterized annotation ``pragma`` supplies compiler pragmas, and may be applied in various contexts depending on the pragma in question.
+--------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
| Pragma | Classes | Characters | Member predicates | Non-member predicates | Imports | Fields | Modules | Aliases |
+==============+=========+============+===================+=======================+=========+========+=========+=========+
| ``inline`` | | yes | yes | yes | | | | |
+--------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
| ``noinline`` | | yes | yes | yes | | | | |
+--------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
| ``nomagic`` | | yes | yes | yes | | | | |
+--------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
| ``noopt`` | | yes | yes | yes | | | | |
+--------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
+---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
| Pragma | Classes | Characters | Member predicates | Non-member predicates | Imports | Fields | Modules | Aliases |
+===========================+=========+============+===================+=======================+=========+========+=========+=========+
| ``inline`` | | yes | yes | yes | | | | |
+---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
| ``noinline`` | | yes | yes | yes | | | | |
+---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
| ``nomagic`` | | yes | yes | yes | | | | |
+---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
| ``noopt`` | | yes | yes | yes | | | | |
+---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
| ``assume_small_delta`` | | yes | yes | yes | | | | |
+---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
The parameterized annotation ``language`` supplies language pragmas which change the behavior of the language. Language pragmas apply at the scope level, and are inherited by nested scopes.
@@ -2048,7 +2050,7 @@ The complete grammar for QL is as follows:
| "override"
| "query"
argsAnnotation ::= "pragma" "[" ("noinline" | "nomagic" | "noopt") "]"
argsAnnotation ::= "pragma" "[" ("inline" | "noinline" | "nomagic" | "noopt" | "assume_small_delta") "]"
| "language" "[" "monotonicAggregates" "]"
| "bindingset" "[" (variable ( "," variable)*)? "]"

View File

@@ -1,5 +1,5 @@
name: codeql/go-all
version: 0.3.2
version: 0.3.3-dev
groups: go
dbscheme: go.dbscheme
extractor: go

View File

@@ -1,5 +1,5 @@
name: codeql/go-queries
version: 0.3.2
version: 0.3.3-dev
groups:
- go
- queries

View File

@@ -35,8 +35,6 @@ class ExternalDeclExtractor(val logger: FileLogger, val invocationTrapFile: Stri
if (ret) externalDeclWorkList.add(Pair(d, signature))
return ret
}
fun extractLater(p: IrProperty) = extractLater(p, propertySignature)
fun extractLater(f: IrField) = extractLater(f, fieldSignature)
fun extractLater(c: IrClass) = extractLater(c, "")
fun extractExternalClasses() {

View File

@@ -2,6 +2,7 @@ package com.github.codeql
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.IrElement
@@ -139,6 +140,8 @@ class KotlinExtractorExtension(
logger.flush()
logger.info("Extraction for invocation TRAP file $invocationTrapFile")
logger.flush()
logger.info("Kotlin version ${KotlinCompilerVersion.getVersion()}")
logger.flush()
logPeakMemoryUsage(logger, "before extractor")
if (System.getenv("CODEQL_EXTRACTOR_JAVA_KOTLIN_DUMP") == "true") {
logger.info("moduleFragment:\n" + moduleFragment.dump())

View File

@@ -305,7 +305,7 @@ open class KotlinFileExtractor(
val kind = c.kind
if (kind == ClassKind.ENUM_CLASS) {
tw.writeIsEnumType(classId)
} else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT) {
} else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) {
logger.errorElement("Unrecognised class kind $kind", c)
}
}
@@ -452,7 +452,7 @@ open class KotlinFileExtractor(
val kind = c.kind
if (kind == ClassKind.ENUM_CLASS) {
tw.writeIsEnumType(classId)
} else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT) {
} else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) {
logger.warnElement("Unrecognised class kind $kind", c)
}
@@ -1884,7 +1884,7 @@ open class KotlinFileExtractor(
IrConstImpl.defaultValueForType(0, 0, getDefaultsMethodLastArgType(callTarget))
)
extractCallValueArguments(id, valueArgsWithDummies + extraArgs, enclosingStmt, enclosingCallable, nextIdx)
extractCallValueArguments(id, valueArgsWithDummies + extraArgs, enclosingStmt, enclosingCallable, nextIdx, extractVarargAsArray = true)
}
private fun getFunctionInvokeMethod(typeArgs: List<IrTypeArgument>): IrFunction? {
@@ -1961,8 +1961,12 @@ open class KotlinFileExtractor(
superQualifierSymbol: IrClassSymbol? = null) {
val locId = tw.getLocation(locElement)
val varargParam = syntacticCallTarget.valueParameters.withIndex().find { it.value.isVararg }
// If the vararg param is the only one not specified, and it has no default value, then we don't need to call a $default method,
// as omitting it already implies passing an empty vararg array.
val nullAllowedIdx = if (varargParam != null && varargParam.value.defaultValue == null) varargParam.index else -1
if (valueArguments.any { it == null }) {
if (valueArguments.withIndex().any { (index, it) -> it == null && index != nullAllowedIdx }) {
extractsDefaultsCall(
syntacticCallTarget,
locId,
@@ -2082,11 +2086,11 @@ open class KotlinFileExtractor(
private fun extractCallValueArguments(callId: Label<out DbExprparent>, call: IrFunctionAccessExpression, enclosingStmt: Label<out DbStmt>, enclosingCallable: Label<out DbCallable>, idxOffset: Int) =
extractCallValueArguments(callId, (0 until call.valueArgumentsCount).map { call.getValueArgument(it) }, enclosingStmt, enclosingCallable, idxOffset)
private fun extractCallValueArguments(callId: Label<out DbExprparent>, valueArguments: List<IrExpression?>, enclosingStmt: Label<out DbStmt>, enclosingCallable: Label<out DbCallable>, idxOffset: Int) {
private fun extractCallValueArguments(callId: Label<out DbExprparent>, valueArguments: List<IrExpression?>, enclosingStmt: Label<out DbStmt>, enclosingCallable: Label<out DbCallable>, idxOffset: Int, extractVarargAsArray: Boolean = false) {
var i = 0
valueArguments.forEach { arg ->
if(arg != null) {
if (arg is IrVararg) {
if (arg is IrVararg && !extractVarargAsArray) {
arg.elements.forEachIndexed { varargNo, vararg -> extractVarargElement(vararg, enclosingCallable, callId, i + idxOffset + varargNo, enclosingStmt) }
i += arg.elements.size
} else {

View File

@@ -253,19 +253,24 @@ open class KotlinUsesExtractor(
}
}
private fun propertySignature(p: IrProperty) =
((p.getter ?: p.setter)?.extensionReceiverParameter?.let { useType(erase(it.type)).javaResult.signature } ?: "")
private fun extractPropertyLaterIfExternalFileMember(p: IrProperty) {
if (isExternalFileClassMember(p)) {
extractExternalClassLater(p.parentAsClass)
dependencyCollector?.addDependency(p, externalClassExtractor.propertySignature)
externalClassExtractor.extractLater(p)
val signature = propertySignature(p) + externalClassExtractor.propertySignature
dependencyCollector?.addDependency(p, signature)
externalClassExtractor.extractLater(p, signature)
}
}
private fun extractFieldLaterIfExternalFileMember(f: IrField) {
if (isExternalFileClassMember(f)) {
extractExternalClassLater(f.parentAsClass)
dependencyCollector?.addDependency(f, externalClassExtractor.fieldSignature)
externalClassExtractor.extractLater(f)
val signature = (f.correspondingPropertySymbol?.let { propertySignature(it.owner) } ?: "") + externalClassExtractor.fieldSignature
dependencyCollector?.addDependency(f, signature)
externalClassExtractor.extractLater(f, signature)
}
}
@@ -813,7 +818,7 @@ open class KotlinUsesExtractor(
OperatorNameConventions.INVOKE.asString())
fun getSuffixIfInternal() =
if (f.visibility == DescriptorVisibilities.INTERNAL) {
if (f.visibility == DescriptorVisibilities.INTERNAL && f !is IrConstructor) {
"\$" + getJvmModuleName(f)
} else {
""

View File

@@ -1,9 +1,10 @@
package com.github.codeql
import com.github.codeql.utils.versions.Psi2Ir
import com.github.codeql.utils.versions.getPsi2Ir
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.kdoc.psi.api.KDocElement
@@ -15,9 +16,16 @@ class LinesOfCode(
val tw: FileTrapWriter,
val file: IrFile
) {
val psi2Ir = Psi2Ir(logger)
val psi2Ir = getPsi2Ir(logger).also {
if (it == null) {
logger.warn("Lines of code will not be populated as Kotlin version is too old (${KotlinCompilerVersion.getVersion()})")
}
}
fun linesOfCodeInFile(id: Label<DbFile>) {
if (psi2Ir == null) {
return
}
val ktFile = psi2Ir.getKtFile(file)
if (ktFile == null) {
return
@@ -26,6 +34,9 @@ class LinesOfCode(
}
fun linesOfCodeInDeclaration(d: IrDeclaration, id: Label<out DbSourceline>) {
if (psi2Ir == null) {
return
}
val p = psi2Ir.findPsiElement(d, file)
if (p == null) {
return

View File

@@ -3,9 +3,11 @@ package com.github.codeql.comments
import com.github.codeql.*
import com.github.codeql.utils.IrVisitorLookup
import com.github.codeql.utils.isLocalFunction
import com.github.codeql.utils.versions.Psi2Ir
import com.github.codeql.utils.versions.getPsi2Ir
import com.github.codeql.utils.versions.Psi2IrFacade
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBody
@@ -21,18 +23,23 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
class CommentExtractor(private val fileExtractor: KotlinFileExtractor, private val file: IrFile, private val fileLabel: Label<out DbFile>) {
private val tw = fileExtractor.tw
private val logger = fileExtractor.logger
private val psi2Ir = Psi2Ir(logger)
private val ktFile = psi2Ir.getKtFile(file)
fun extract() {
val psi2Ir = getPsi2Ir(logger)
if (psi2Ir == null) {
logger.warn("Comments will not be extracted as Kotlin version is too old (${KotlinCompilerVersion.getVersion()})")
return
}
val ktFile = psi2Ir.getKtFile(file)
if (ktFile == null) {
logger.warn("Comments are not being processed in ${file.path}.")
} else {
ktFile.accept(commentVisitor)
return
}
val commentVisitor = mkCommentVisitor(psi2Ir)
ktFile.accept(commentVisitor)
}
private val commentVisitor =
private fun mkCommentVisitor(psi2Ir: Psi2IrFacade): KtVisitor<Unit, Unit> =
object : KtVisitor<Unit, Unit>() {
override fun visitElement(element: PsiElement) {
element.acceptChildren(this)

View File

@@ -1,6 +1,6 @@
package com.github.codeql.utils
import com.github.codeql.utils.versions.Psi2Ir
import com.github.codeql.utils.versions.Psi2IrFacade
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
@@ -8,7 +8,7 @@ import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.util.isFakeOverride
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
class IrVisitorLookup(private val psi2Ir: Psi2Ir, private val psi: PsiElement, private val file: IrFile) :
class IrVisitorLookup(private val psi2Ir: Psi2IrFacade, private val psi: PsiElement, private val file: IrFile) :
IrElementVisitor<Unit, MutableCollection<IrElement>> {
private val location = psi.getLocation()

View File

@@ -1,19 +1,5 @@
package com.github.codeql.utils.versions
import com.github.codeql.FileLogger
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.psi.KtFile
class Psi2Ir(private val logger: FileLogger) : Psi2IrFacade {
override fun getKtFile(irFile: IrFile): KtFile? {
logger.warn("Comment extraction is not supported for Kotlin < 1.5.20")
return null
}
override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? {
logger.error("Attempted comment extraction for Kotlin < 1.5.20")
return null
}
}
fun getPsi2Ir(@Suppress("UNUSED_PARAMETER") logger: FileLogger): Psi2IrFacade? = null

View File

@@ -8,7 +8,9 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.psi.KtFile
class Psi2Ir(private val logger: FileLogger): Psi2IrFacade {
fun getPsi2Ir(logger: FileLogger): Psi2IrFacade? = Psi2Ir(logger)
private class Psi2Ir(private val logger: FileLogger): Psi2IrFacade {
override fun getKtFile(irFile: IrFile): KtFile? {
return irFile.getKtFile()
}

View File

@@ -0,0 +1,2 @@
| user.kt:3:22:3:22 | getF(...) | lib/lib/TestKt.class:0:0:0:0 | getF |
| user.kt:3:28:3:28 | getF(...) | lib/lib/TestKt.class:0:0:0:0 | getF |

View File

@@ -0,0 +1,7 @@
package lib
val String.f
get() = 1
val Int.f
get() = 2

View File

@@ -0,0 +1,4 @@
from create_database_utils import *
runSuccessfully(["kotlinc", "test.kt", "-d", "lib"])
run_codeql_database_create(["kotlinc user.kt -cp lib"], lang="java")

View File

@@ -0,0 +1,4 @@
import java
from MethodAccess ma
select ma, ma.getCallee()

View File

@@ -0,0 +1,3 @@
import lib.f
fun test() = "hello".f + 1.f

View File

@@ -32,6 +32,7 @@ with open('logs.csv', 'w', newline='') as f_out:
j = json.loads(line)
msg = j['message']
msg = re.sub('(?<=Extraction for invocation TRAP file ).*/kt-db/trap/java/invocations/kotlin\..*\.trap', '<FILENAME>', msg)
msg = re.sub('(?<=Kotlin version )[0-9.]+', '<VERSION>', msg)
if msg.startswith('Peak memory: '):
# Peak memory information varies from run to run, so just ignore it
continue

View File

@@ -1,5 +1,6 @@
| 1 | 1 | Test script | Log file | 1 |
| 1 | 2 | CodeQL Kotlin extractor | INFO | Extraction started |
| 1 | 3 | CodeQL Kotlin extractor | INFO | Extraction for invocation TRAP file <FILENAME> |
| 1 | 4 | CodeQL Kotlin extractor | INFO | Extracting file test.kt |
| 1 | 5 | CodeQL Kotlin extractor | INFO | Extraction completed |
| 1 | 4 | CodeQL Kotlin extractor | INFO | Kotlin version <VERSION> |
| 1 | 5 | CodeQL Kotlin extractor | INFO | Extracting file test.kt |
| 1 | 6 | CodeQL Kotlin extractor | INFO | Extraction completed |

View File

@@ -1,5 +1,5 @@
name: codeql/java-all
version: 0.4.2
version: 0.4.3-dev
groups: java
dbscheme: config/semmlecode.dbscheme
extractor: java

View File

@@ -2367,14 +2367,33 @@ class Argument extends Expr {
*/
predicate isNthVararg(int arrayindex) {
not this.isExplicitVarargsArray() and
exists(Callable tgt, int varargsParamPos |
exists(Callable tgt |
call.getCallee() = tgt and
tgt.getParameter(varargsParamPos).isVarargs() and
arrayindex = pos - varargsParamPos and
arrayindex = pos - tgt.getVaragsParameterIndex() and
arrayindex >= 0 and
arrayindex <= call.getNumArgument() - tgt.getNumberOfParameters()
)
}
/**
* Gets the parameter position that will receive this argument.
*
* For all vararg arguments, this is the position of the vararg array parameter.
*/
int getParameterPos() {
exists(Callable c | c = call.getCallee() |
if c.isVarargs()
then
if pos < c.getVaragsParameterIndex()
then result = pos // Vararg method argument, before the vararg parameter
else (
if this.isVararg()
then result = c.getVaragsParameterIndex() // Part of the implicit vararg array
else result = pos - (call.getNumArgument() - c.getNumberOfParameters()) // Vararg method argument, after the vararg parameter (offset could be -1 in the zero-vararg case)
)
else result = pos // Not a vararg method
)
}
}
/**

View File

@@ -283,6 +283,9 @@ class Callable extends StmtParent, Member, @callable {
/** Holds if the last parameter of this callable is a varargs (variable arity) parameter. */
predicate isVarargs() { this.getAParameter().isVarargs() }
/** Gets the index of this callable's varargs parameter, if any exists. */
int getVaragsParameterIndex() { this.getParameter(result).isVarargs() }
/**
* Gets the signature of this callable, where all types in the signature have a fully-qualified name.
* The parameter types are only separated by a comma (without space). If this callable has

View File

@@ -378,11 +378,11 @@ module Private {
*/
predicate argumentOf(DataFlowCall call, int pos) {
exists(Argument arg | this.asExpr() = arg |
call.asCall() = arg.getCall() and pos = arg.getPosition()
call.asCall() = arg.getCall() and pos = arg.getParameterPos()
)
or
call.asCall() = this.(ImplicitVarargsArray).getCall() and
pos = call.asCall().getCallee().getNumberOfParameters() - 1
pos = call.asCall().getCallee().getVaragsParameterIndex()
or
pos = -1 and this = getInstanceArgument(call.asCall())
or

View File

@@ -282,13 +282,8 @@ private predicate constructorStep(Expr tracked, ConstructorCall sink) {
* Converts an argument index to a formal parameter index.
* This is relevant for varadic methods.
*/
private int argToParam(Call call, int arg) {
exists(call.getArgument(arg)) and
exists(Callable c | c = call.getCallee() |
if c.isVarargs() and arg >= c.getNumberOfParameters()
then result = c.getNumberOfParameters() - 1
else result = arg
)
private int argToParam(Call call, int argIdx) {
result = call.getArgument(argIdx).(Argument).getParameterPos()
}
/** Access to a method that passes taint from qualifier to argument. */

View File

@@ -22,7 +22,7 @@ class IdentifierElement extends Element {
from IdentifierElement e, string msg
where
e.fromSource() and
e.getCompilationUnit().isJavaSourceFile() and
not e.(Constructor).isDefaultConstructor() and
(
e.getName() = "_" and

View File

@@ -1,5 +1,5 @@
name: codeql/java-queries
version: 0.4.2
version: 0.4.3-dev
groups:
- java
- queries

View File

@@ -0,0 +1,5 @@
public class User {
public static void test() { new Test(1, 2); }
}

View File

@@ -0,0 +1 @@
| User.java:3:31:3:44 | new Test(...) | test.kt:3:3:3:51 | { ... } |

View File

@@ -0,0 +1,5 @@
public class Test() {
internal constructor(x: Int, y: Int) : this() { }
}

View File

@@ -0,0 +1,4 @@
import java
from ClassInstanceExpr ce
select ce, ce.getConstructor().getBody()

View File

@@ -0,0 +1,4 @@
import java
import semmle.code.java.Diagnostics
select any(Diagnostic d | not d.toString().matches("Not rewriting trap file for%"))

View File

@@ -7,6 +7,394 @@ test.kt:
# 1| 0: [Parameter] a
# 1| 0: [TypeAccess] Object
# 1| 5: [BlockStmt] { ... }
# 184| 2: [Method] varargsTest
# 184| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 184| 0: [Parameter] x
# 184| 0: [TypeAccess] String
# 184| 1: [Parameter] y
# 184| 0: [TypeAccess] String[]
# 184| 0: [WildcardTypeAccess] ? ...
# 184| 0: [TypeAccess] String
# 184| 2: [Parameter] z
# 184| 0: [TypeAccess] String
# 184| 5: [BlockStmt] { ... }
# 185| 0: [ExprStmt] <Expr>;
# 185| 0: [MethodAccess] sink(...)
# 185| -1: [TypeAccess] TestKt
# 185| 0: [VarAccess] x
# 186| 1: [ExprStmt] <Expr>;
# 186| 0: [MethodAccess] sink(...)
# 186| -1: [TypeAccess] TestKt
# 186| 0: [ArrayAccess] ...[...]
# 186| 0: [VarAccess] y
# 186| 1: [IntegerLiteral] 0
# 187| 2: [ExprStmt] <Expr>;
# 187| 0: [MethodAccess] sink(...)
# 187| -1: [TypeAccess] TestKt
# 187| 0: [VarAccess] z
# 184| 3: [Method] varargsTest$default
# 184| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 184| 0: [Parameter] p0
# 184| 0: [TypeAccess] String
# 184| 1: [Parameter] p1
# 184| 0: [TypeAccess] String[]
# 184| 2: [Parameter] p2
# 184| 0: [TypeAccess] String
# 184| 3: [Parameter] p3
# 184| 0: [TypeAccess] int
# 184| 4: [Parameter] p4
# 184| 0: [TypeAccess] Object
# 184| 5: [BlockStmt] { ... }
# 184| 0: [IfStmt] if (...)
# 184| 0: [EQExpr] ... == ...
# 184| 0: [AndBitwiseExpr] ... & ...
# 184| 0: [IntegerLiteral] 1
# 184| 1: [VarAccess] p3
# 184| 1: [IntegerLiteral] 0
# 184| 1: [ExprStmt] <Expr>;
# 184| 0: [AssignExpr] ...=...
# 184| 0: [VarAccess] p0
# 184| 1: [StringLiteral] before-vararg-default sunk
# 184| 1: [IfStmt] if (...)
# 184| 0: [EQExpr] ... == ...
# 184| 0: [AndBitwiseExpr] ... & ...
# 184| 0: [IntegerLiteral] 2
# 184| 1: [VarAccess] p3
# 184| 1: [IntegerLiteral] 0
# 184| 1: [ExprStmt] <Expr>;
# 184| 0: [AssignExpr] ...=...
# 184| 0: [VarAccess] p1
# 184| 1: [ArrayCreationExpr] new String[]
# 184| -2: [ArrayInit] {...}
# 184| 0: [StringLiteral] first-vararg-default sunk
# 184| 1: [StringLiteral] second-vararg-default sunk
# 184| -1: [TypeAccess] String
# 184| 0: [IntegerLiteral] 2
# 184| 2: [IfStmt] if (...)
# 184| 0: [EQExpr] ... == ...
# 184| 0: [AndBitwiseExpr] ... & ...
# 184| 0: [IntegerLiteral] 4
# 184| 1: [VarAccess] p3
# 184| 1: [IntegerLiteral] 0
# 184| 1: [ExprStmt] <Expr>;
# 184| 0: [AssignExpr] ...=...
# 184| 0: [VarAccess] p2
# 184| 1: [StringLiteral] after-vararg-default sunk
# 184| 3: [ReturnStmt] return ...
# 184| 0: [MethodAccess] varargsTest(...)
# 184| -1: [TypeAccess] TestKt
# 184| 0: [VarAccess] p0
# 184| 1: [VarAccess] p1
# 184| 2: [VarAccess] p2
# 190| 4: [Method] varargsUser
# 190| 3: [TypeAccess] Unit
# 190| 5: [BlockStmt] { ... }
# 191| 0: [ExprStmt] <Expr>;
# 191| 0: [MethodAccess] varargsTest$default(...)
# 191| -1: [TypeAccess] TestKt
# 1| 0: [NullLiteral] null
# 1| 1: [NullLiteral] null
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 0
# 1| 4: [NullLiteral] null
# 192| 1: [ExprStmt] <Expr>;
# 192| 0: [MethodAccess] varargsTest$default(...)
# 192| -1: [TypeAccess] TestKt
# 192| 0: [StringLiteral] no-varargs-before, no-z-parameter sunk
# 1| 1: [NullLiteral] null
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 1
# 1| 4: [NullLiteral] null
# 193| 2: [ExprStmt] <Expr>;
# 193| 0: [MethodAccess] varargsTest$default(...)
# 193| -1: [TypeAccess] TestKt
# 193| 0: [StringLiteral] no-varargs-before sunk
# 1| 1: [NullLiteral] null
# 193| 2: [StringLiteral] no-varargs-after sunk
# 1| 3: [IntegerLiteral] 5
# 1| 4: [NullLiteral] null
# 194| 3: [ExprStmt] <Expr>;
# 194| 0: [MethodAccess] varargsTest(...)
# 194| -1: [TypeAccess] TestKt
# 194| 0: [StringLiteral] one-vararg-before sunk
# 194| 1: [StringLiteral] one-vararg sunk
# 194| 2: [StringLiteral] one-vararg-after sunk
# 195| 4: [ExprStmt] <Expr>;
# 195| 0: [MethodAccess] varargsTest(...)
# 195| -1: [TypeAccess] TestKt
# 195| 0: [StringLiteral] two-varargs-before sunk
# 195| 1: [StringLiteral] two-vararg-first sunk
# 195| 2: [StringLiteral] two-vararg-second sunk
# 195| 3: [StringLiteral] two-varargs-after sunk
# 196| 5: [ExprStmt] <Expr>;
# 196| 0: [MethodAccess] varargsTest$default(...)
# 196| -1: [TypeAccess] TestKt
# 196| 0: [StringLiteral] no-z-parmeter sunk
# 196| 1: [ArrayCreationExpr] new String[]
# 196| -2: [ArrayInit] {...}
# 196| 0: [StringLiteral] no-z-parameter first vararg sunk
# 196| 1: [StringLiteral] no-z-parameter second vararg sunk
# 196| -1: [TypeAccess] String
# 196| 0: [IntegerLiteral] 2
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 3
# 1| 4: [NullLiteral] null
# 199| 5: [Method] varargsTestOnlySinkVarargs
# 199| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 199| 0: [Parameter] x
# 199| 0: [TypeAccess] String
# 199| 1: [Parameter] y
# 199| 0: [TypeAccess] String[]
# 199| 0: [WildcardTypeAccess] ? ...
# 199| 0: [TypeAccess] String
# 199| 2: [Parameter] z
# 199| 0: [TypeAccess] String
# 199| 5: [BlockStmt] { ... }
# 200| 0: [ExprStmt] <Expr>;
# 200| 0: [MethodAccess] sink(...)
# 200| -1: [TypeAccess] TestKt
# 200| 0: [ArrayAccess] ...[...]
# 200| 0: [VarAccess] y
# 200| 1: [IntegerLiteral] 0
# 199| 6: [Method] varargsTestOnlySinkVarargs$default
# 199| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 199| 0: [Parameter] p0
# 199| 0: [TypeAccess] String
# 199| 1: [Parameter] p1
# 199| 0: [TypeAccess] String[]
# 199| 2: [Parameter] p2
# 199| 0: [TypeAccess] String
# 199| 3: [Parameter] p3
# 199| 0: [TypeAccess] int
# 199| 4: [Parameter] p4
# 199| 0: [TypeAccess] Object
# 199| 5: [BlockStmt] { ... }
# 199| 0: [IfStmt] if (...)
# 199| 0: [EQExpr] ... == ...
# 199| 0: [AndBitwiseExpr] ... & ...
# 199| 0: [IntegerLiteral] 1
# 199| 1: [VarAccess] p3
# 199| 1: [IntegerLiteral] 0
# 199| 1: [ExprStmt] <Expr>;
# 199| 0: [AssignExpr] ...=...
# 199| 0: [VarAccess] p0
# 199| 1: [StringLiteral] before-vararg-default not sunk 2
# 199| 1: [IfStmt] if (...)
# 199| 0: [EQExpr] ... == ...
# 199| 0: [AndBitwiseExpr] ... & ...
# 199| 0: [IntegerLiteral] 2
# 199| 1: [VarAccess] p3
# 199| 1: [IntegerLiteral] 0
# 199| 1: [ExprStmt] <Expr>;
# 199| 0: [AssignExpr] ...=...
# 199| 0: [VarAccess] p1
# 199| 1: [ArrayCreationExpr] new String[]
# 199| -2: [ArrayInit] {...}
# 199| 0: [StringLiteral] first-vararg-default sunk 2
# 199| 1: [StringLiteral] second-vararg-default sunk 2
# 199| -1: [TypeAccess] String
# 199| 0: [IntegerLiteral] 2
# 199| 2: [IfStmt] if (...)
# 199| 0: [EQExpr] ... == ...
# 199| 0: [AndBitwiseExpr] ... & ...
# 199| 0: [IntegerLiteral] 4
# 199| 1: [VarAccess] p3
# 199| 1: [IntegerLiteral] 0
# 199| 1: [ExprStmt] <Expr>;
# 199| 0: [AssignExpr] ...=...
# 199| 0: [VarAccess] p2
# 199| 1: [StringLiteral] after-vararg-default not sunk 2
# 199| 3: [ReturnStmt] return ...
# 199| 0: [MethodAccess] varargsTestOnlySinkVarargs(...)
# 199| -1: [TypeAccess] TestKt
# 199| 0: [VarAccess] p0
# 199| 1: [VarAccess] p1
# 199| 2: [VarAccess] p2
# 203| 7: [Method] varargsUserOnlySinkVarargs
# 203| 3: [TypeAccess] Unit
# 203| 5: [BlockStmt] { ... }
# 204| 0: [ExprStmt] <Expr>;
# 204| 0: [MethodAccess] varargsTestOnlySinkVarargs$default(...)
# 204| -1: [TypeAccess] TestKt
# 1| 0: [NullLiteral] null
# 1| 1: [NullLiteral] null
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 0
# 1| 4: [NullLiteral] null
# 205| 1: [ExprStmt] <Expr>;
# 205| 0: [MethodAccess] varargsTestOnlySinkVarargs$default(...)
# 205| -1: [TypeAccess] TestKt
# 205| 0: [StringLiteral] no-varargs-before, no-z-parameter not sunk 2
# 1| 1: [NullLiteral] null
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 1
# 1| 4: [NullLiteral] null
# 206| 2: [ExprStmt] <Expr>;
# 206| 0: [MethodAccess] varargsTestOnlySinkVarargs$default(...)
# 206| -1: [TypeAccess] TestKt
# 206| 0: [StringLiteral] no-varargs-before not sunk 2
# 1| 1: [NullLiteral] null
# 206| 2: [StringLiteral] no-varargs-after not sunk 2
# 1| 3: [IntegerLiteral] 5
# 1| 4: [NullLiteral] null
# 207| 3: [ExprStmt] <Expr>;
# 207| 0: [MethodAccess] varargsTestOnlySinkVarargs(...)
# 207| -1: [TypeAccess] TestKt
# 207| 0: [StringLiteral] one-vararg-before not sunk 2
# 207| 1: [StringLiteral] one-vararg sunk 2
# 207| 2: [StringLiteral] one-vararg-after not sunk 2
# 208| 4: [ExprStmt] <Expr>;
# 208| 0: [MethodAccess] varargsTestOnlySinkVarargs(...)
# 208| -1: [TypeAccess] TestKt
# 208| 0: [StringLiteral] two-varargs-before not sunk 2
# 208| 1: [StringLiteral] two-vararg-first sunk 2
# 208| 2: [StringLiteral] two-vararg-second sunk 2
# 208| 3: [StringLiteral] two-varargs-after not sunk 2
# 209| 5: [ExprStmt] <Expr>;
# 209| 0: [MethodAccess] varargsTestOnlySinkVarargs$default(...)
# 209| -1: [TypeAccess] TestKt
# 209| 0: [StringLiteral] no-z-parmeter not sunk 2
# 209| 1: [ArrayCreationExpr] new String[]
# 209| -2: [ArrayInit] {...}
# 209| 0: [StringLiteral] no-z-parameter first vararg sunk 2
# 209| 1: [StringLiteral] no-z-parameter second vararg sunk 2
# 209| -1: [TypeAccess] String
# 209| 0: [IntegerLiteral] 2
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 3
# 1| 4: [NullLiteral] null
# 212| 8: [Method] varargsTestOnlySinkRegularArgs
# 212| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 212| 0: [Parameter] x
# 212| 0: [TypeAccess] String
# 212| 1: [Parameter] y
# 212| 0: [TypeAccess] String[]
# 212| 0: [WildcardTypeAccess] ? ...
# 212| 0: [TypeAccess] String
# 212| 2: [Parameter] z
# 212| 0: [TypeAccess] String
# 212| 5: [BlockStmt] { ... }
# 213| 0: [ExprStmt] <Expr>;
# 213| 0: [MethodAccess] sink(...)
# 213| -1: [TypeAccess] TestKt
# 213| 0: [VarAccess] x
# 214| 1: [ExprStmt] <Expr>;
# 214| 0: [MethodAccess] sink(...)
# 214| -1: [TypeAccess] TestKt
# 214| 0: [VarAccess] z
# 212| 9: [Method] varargsTestOnlySinkRegularArgs$default
# 212| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 212| 0: [Parameter] p0
# 212| 0: [TypeAccess] String
# 212| 1: [Parameter] p1
# 212| 0: [TypeAccess] String[]
# 212| 2: [Parameter] p2
# 212| 0: [TypeAccess] String
# 212| 3: [Parameter] p3
# 212| 0: [TypeAccess] int
# 212| 4: [Parameter] p4
# 212| 0: [TypeAccess] Object
# 212| 5: [BlockStmt] { ... }
# 212| 0: [IfStmt] if (...)
# 212| 0: [EQExpr] ... == ...
# 212| 0: [AndBitwiseExpr] ... & ...
# 212| 0: [IntegerLiteral] 1
# 212| 1: [VarAccess] p3
# 212| 1: [IntegerLiteral] 0
# 212| 1: [ExprStmt] <Expr>;
# 212| 0: [AssignExpr] ...=...
# 212| 0: [VarAccess] p0
# 212| 1: [StringLiteral] before-vararg-default sunk 3
# 212| 1: [IfStmt] if (...)
# 212| 0: [EQExpr] ... == ...
# 212| 0: [AndBitwiseExpr] ... & ...
# 212| 0: [IntegerLiteral] 2
# 212| 1: [VarAccess] p3
# 212| 1: [IntegerLiteral] 0
# 212| 1: [ExprStmt] <Expr>;
# 212| 0: [AssignExpr] ...=...
# 212| 0: [VarAccess] p1
# 212| 1: [ArrayCreationExpr] new String[]
# 212| -2: [ArrayInit] {...}
# 212| 0: [StringLiteral] first-vararg-default not sunk 3
# 212| 1: [StringLiteral] second-vararg-default not sunk 3
# 212| -1: [TypeAccess] String
# 212| 0: [IntegerLiteral] 2
# 212| 2: [IfStmt] if (...)
# 212| 0: [EQExpr] ... == ...
# 212| 0: [AndBitwiseExpr] ... & ...
# 212| 0: [IntegerLiteral] 4
# 212| 1: [VarAccess] p3
# 212| 1: [IntegerLiteral] 0
# 212| 1: [ExprStmt] <Expr>;
# 212| 0: [AssignExpr] ...=...
# 212| 0: [VarAccess] p2
# 212| 1: [StringLiteral] after-vararg-default sunk 3
# 212| 3: [ReturnStmt] return ...
# 212| 0: [MethodAccess] varargsTestOnlySinkRegularArgs(...)
# 212| -1: [TypeAccess] TestKt
# 212| 0: [VarAccess] p0
# 212| 1: [VarAccess] p1
# 212| 2: [VarAccess] p2
# 217| 10: [Method] varargsUserOnlySinkRegularArgs
# 217| 3: [TypeAccess] Unit
# 217| 5: [BlockStmt] { ... }
# 218| 0: [ExprStmt] <Expr>;
# 218| 0: [MethodAccess] varargsTestOnlySinkRegularArgs$default(...)
# 218| -1: [TypeAccess] TestKt
# 1| 0: [NullLiteral] null
# 1| 1: [NullLiteral] null
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 0
# 1| 4: [NullLiteral] null
# 219| 1: [ExprStmt] <Expr>;
# 219| 0: [MethodAccess] varargsTestOnlySinkRegularArgs$default(...)
# 219| -1: [TypeAccess] TestKt
# 219| 0: [StringLiteral] no-varargs-before, no-z-parameter sunk 3
# 1| 1: [NullLiteral] null
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 1
# 1| 4: [NullLiteral] null
# 220| 2: [ExprStmt] <Expr>;
# 220| 0: [MethodAccess] varargsTestOnlySinkRegularArgs$default(...)
# 220| -1: [TypeAccess] TestKt
# 220| 0: [StringLiteral] no-varargs-before sunk 3
# 1| 1: [NullLiteral] null
# 220| 2: [StringLiteral] no-varargs-after sunk 3
# 1| 3: [IntegerLiteral] 5
# 1| 4: [NullLiteral] null
# 221| 3: [ExprStmt] <Expr>;
# 221| 0: [MethodAccess] varargsTestOnlySinkRegularArgs(...)
# 221| -1: [TypeAccess] TestKt
# 221| 0: [StringLiteral] one-vararg-before sunk 3
# 221| 1: [StringLiteral] one-vararg not sunk 3
# 221| 2: [StringLiteral] one-vararg-after sunk 3
# 222| 4: [ExprStmt] <Expr>;
# 222| 0: [MethodAccess] varargsTestOnlySinkRegularArgs(...)
# 222| -1: [TypeAccess] TestKt
# 222| 0: [StringLiteral] two-varargs-before sunk 3
# 222| 1: [StringLiteral] two-vararg-first not sunk 3
# 222| 2: [StringLiteral] two-vararg-second not sunk 3
# 222| 3: [StringLiteral] two-varargs-after sunk 3
# 223| 5: [ExprStmt] <Expr>;
# 223| 0: [MethodAccess] varargsTestOnlySinkRegularArgs$default(...)
# 223| -1: [TypeAccess] TestKt
# 223| 0: [StringLiteral] no-z-parmeter sunk 3
# 223| 1: [ArrayCreationExpr] new String[]
# 223| -2: [ArrayInit] {...}
# 223| 0: [StringLiteral] no-z-parameter first vararg not sunk 3
# 223| 1: [StringLiteral] no-z-parameter second vararg not sunk 3
# 223| -1: [TypeAccess] String
# 223| 0: [IntegerLiteral] 2
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 3
# 1| 4: [NullLiteral] null
# 3| 2: [Class] TestMember
# 3| 1: [Constructor] TestMember
# 3| 5: [BlockStmt] { ... }

View File

@@ -13,3 +13,6 @@
| test.kt:171:3:171:97 | f | test.kt:171:3:171:97 | f$default |
| test.kt:179:3:179:46 | f | test.kt:179:3:179:46 | f$default |
| test.kt:180:3:180:34 | f | test.kt:180:3:180:34 | f$default |
| test.kt:184:1:188:1 | varargsTest | test.kt:184:1:188:1 | varargsTest$default |
| test.kt:199:1:201:1 | varargsTestOnlySinkVarargs | test.kt:199:1:201:1 | varargsTestOnlySinkVarargs$default |
| test.kt:212:1:215:1 | varargsTestOnlySinkRegularArgs | test.kt:212:1:215:1 | varargsTestOnlySinkRegularArgs$default |

View File

@@ -180,3 +180,45 @@ class TestOverloadsWithDefaults {
fun f(z: String, w: Int = 0) { }
}
fun varargsTest(x: String = "before-vararg-default sunk", vararg y: String = arrayOf("first-vararg-default sunk", "second-vararg-default sunk"), z: String = "after-vararg-default sunk") {
sink(x)
sink(y[0])
sink(z)
}
fun varargsUser() {
varargsTest()
varargsTest(x = "no-varargs-before, no-z-parameter sunk")
varargsTest(x = "no-varargs-before sunk", z = "no-varargs-after sunk")
varargsTest(x = "one-vararg-before sunk", "one-vararg sunk", z = "one-vararg-after sunk")
varargsTest(x = "two-varargs-before sunk", "two-vararg-first sunk", "two-vararg-second sunk", z = "two-varargs-after sunk")
varargsTest("no-z-parmeter sunk", "no-z-parameter first vararg sunk", "no-z-parameter second vararg sunk")
}
fun varargsTestOnlySinkVarargs(x: String = "before-vararg-default not sunk 2", vararg y: String = arrayOf("first-vararg-default sunk 2", "second-vararg-default sunk 2"), z: String = "after-vararg-default not sunk 2") {
sink(y[0])
}
fun varargsUserOnlySinkVarargs() {
varargsTestOnlySinkVarargs()
varargsTestOnlySinkVarargs(x = "no-varargs-before, no-z-parameter not sunk 2")
varargsTestOnlySinkVarargs(x = "no-varargs-before not sunk 2", z = "no-varargs-after not sunk 2")
varargsTestOnlySinkVarargs(x = "one-vararg-before not sunk 2", "one-vararg sunk 2", z = "one-vararg-after not sunk 2")
varargsTestOnlySinkVarargs(x = "two-varargs-before not sunk 2", "two-vararg-first sunk 2", "two-vararg-second sunk 2", z = "two-varargs-after not sunk 2")
varargsTestOnlySinkVarargs("no-z-parmeter not sunk 2", "no-z-parameter first vararg sunk 2", "no-z-parameter second vararg sunk 2")
}
fun varargsTestOnlySinkRegularArgs(x: String = "before-vararg-default sunk 3", vararg y: String = arrayOf("first-vararg-default not sunk 3", "second-vararg-default not sunk 3"), z: String = "after-vararg-default sunk 3") {
sink(x)
sink(z)
}
fun varargsUserOnlySinkRegularArgs() {
varargsTestOnlySinkRegularArgs()
varargsTestOnlySinkRegularArgs(x = "no-varargs-before, no-z-parameter sunk 3")
varargsTestOnlySinkRegularArgs(x = "no-varargs-before sunk 3", z = "no-varargs-after sunk 3")
varargsTestOnlySinkRegularArgs(x = "one-vararg-before sunk 3", "one-vararg not sunk 3", z = "one-vararg-after sunk 3")
varargsTestOnlySinkRegularArgs(x = "two-varargs-before sunk 3", "two-vararg-first not sunk 3", "two-vararg-second not sunk 3", z = "two-varargs-after sunk 3")
varargsTestOnlySinkRegularArgs("no-z-parmeter sunk 3", "no-z-parameter first vararg not sunk 3", "no-z-parameter second vararg not sunk 3")
}

View File

@@ -16,6 +16,9 @@
| test.kt:36:40:36:41 | 30 | test.kt:25:10:25:18 | ...[...] |
| test.kt:36:44:36:45 | 31 | test.kt:25:10:25:18 | ...[...] |
| test.kt:36:48:36:49 | 32 | test.kt:25:10:25:18 | ...[...] |
| test.kt:37:33:37:34 | 41 | test.kt:29:10:29:18 | ...[...] |
| test.kt:37:37:37:38 | 42 | test.kt:29:10:29:18 | ...[...] |
| test.kt:37:41:37:42 | 43 | test.kt:29:10:29:18 | ...[...] |
| test.kt:41:26:41:27 | 51 | test.kt:9:10:9:18 | ...[...] |
| test.kt:41:30:41:31 | 52 | test.kt:9:10:9:18 | ...[...] |
| test.kt:41:34:41:35 | 53 | test.kt:9:10:9:18 | ...[...] |

View File

@@ -0,0 +1,24 @@
fun fn0(f: Function0<Unit>) = f()
fun fn1() {
var c = true
while (c) { // TODO: false positive
fn0 {
c = false
}
}
var d = true
while (d) {
fn0 {
println(d)
}
}
val e = true
while (e) {
fn0 {
println(e)
}
}
}

View File

@@ -0,0 +1,3 @@
| A.kt:5:12:5:12 | c | $@ might not terminate, as this loop condition is constant within the loop. | A.kt:5:5:9:5 | while (...) | Loop |
| A.kt:12:12:12:12 | d | $@ might not terminate, as this loop condition is constant within the loop. | A.kt:12:5:16:5 | while (...) | Loop |
| A.kt:19:12:19:12 | e | $@ might not terminate, as this loop condition is constant within the loop. | A.kt:19:5:23:5 | while (...) | Loop |

View File

@@ -0,0 +1 @@
Likely Bugs/Termination/ConstantLoopCondition.ql

View File

@@ -0,0 +1,11 @@
fun fn() {
try {
val l = listOf(1, 2, 3)
l.forEachIndexed { index, _ -> println(index) }
val p = Pair(1, 2)
val (first, _) = p
} catch (_: Exception) {
// expected
}
}

View File

@@ -0,0 +1 @@
Compatibility/JDK9/UnderscoreIdentifier.ql

View File

@@ -0,0 +1,7 @@
| Test.kt:3:9:3:31 | List<Integer> l |
| Test.kt:4:28:4:32 | index |
| Test.kt:4:35:4:35 | p1 |
| Test.kt:6:9:6:26 | Pair<Integer,Integer> p |
| Test.kt:7:14:7:18 | int first |
| Test.kt:7:26:7:26 | Pair<Integer,Integer> tmp0_container |
| Test.kt:8:14:8:25 | Exception _ |

View File

@@ -0,0 +1,3 @@
import java
query predicate variables(Variable v) { v.fromSource() }

View File

@@ -1,5 +1,5 @@
name: codeql/javascript-all
version: 0.3.2
version: 0.3.3-dev
groups: javascript
dbscheme: semmlecode.javascript.dbscheme
extractor: javascript

View File

@@ -1,5 +1,5 @@
name: codeql/javascript-queries
version: 0.4.2
version: 0.4.3-dev
groups:
- javascript
- queries

View File

@@ -1,3 +1,3 @@
name: codeql/suite-helpers
version: 0.3.2
version: 0.3.3-dev
groups: shared

View File

@@ -25,6 +25,17 @@
"description": "has relative path",
},
"debug based on location": {
"scope": "ql",
"prefix": "debug based on location",
"body": [
"$1.getLocation().getFile().getShortName() = \"$2\" and",
"$1.getLocation().getStartLine() = $3 and",
],
"description": "debug based on location",
},
"Exists": {
"scope": "ql",
"prefix": "exists",

View File

@@ -1,5 +1,5 @@
name: codeql/python-all
version: 0.6.2
version: 0.6.3-dev
groups: python
dbscheme: semmlecode.python.dbscheme
extractor: python

View File

@@ -1,5 +1,5 @@
name: codeql/python-queries
version: 0.5.2
version: 0.5.3-dev
groups:
- python
- queries

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* There was a bug in `TaintTracking::localTaint` and `TaintTracking::localTaintStep` such that they only tracked non-value-preserving flow steps. They have been fixed and now also include value-preserving steps.

View File

@@ -36,6 +36,13 @@ private module Cached {
not s instanceof ModuleBase and
result = getEnclosingMethod(s.getOuterScope())
}
cached
Toplevel getEnclosingToplevel(Scope s) {
result = s
or
result = getEnclosingToplevel(s.getOuterScope())
}
}
private import Cached
@@ -66,6 +73,9 @@ class AstNode extends TAstNode {
/** Gets the enclosing method, if any. */
final MethodBase getEnclosingMethod() { result = getEnclosingMethod(scopeOfInclSynth(this)) }
/** Gets the enclosing top-level. */
final Toplevel getEnclosingToplevel() { result = getEnclosingToplevel(scopeOfInclSynth(this)) }
/** Gets a textual representation of this node. */
cached
string toString() { none() }

View File

@@ -271,10 +271,7 @@ module Http {
/** Gets the URL pattern for this route, if it can be statically determined. */
string getUrlPattern() {
exists(CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode |
this.getUrlPatternArg().getALocalSource() = DataFlow::exprNode(strNode) and
result = strNode.getExpr().getConstantValue().getStringlikeValue()
)
result = this.getUrlPatternArg().getALocalSource().getConstantValue().getStringlikeValue()
}
/**
@@ -538,10 +535,12 @@ module Http {
/** Gets the mimetype of this HTTP response, if it can be statically determined. */
string getMimetype() {
exists(CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode |
this.getMimetypeOrContentTypeArg().getALocalSource() = DataFlow::exprNode(strNode) and
result = strNode.getExpr().getConstantValue().getStringlikeValue().splitAt(";", 0)
)
result =
this.getMimetypeOrContentTypeArg()
.getALocalSource()
.getConstantValue()
.getStringlikeValue()
.splitAt(";", 0)
or
not exists(this.getMimetypeOrContentTypeArg()) and
result = this.getMimetypeDefault()

View File

@@ -170,6 +170,24 @@ module ConstantValue {
/** A constant `nil` value. */
class ConstantNilValue extends ConstantValue, TNil { }
/** Gets the integer constant `x`. */
ConstantValue fromInt(int x) { result.getInt() = x }
/** Gets the float constant `x`. */
ConstantValue fromFloat(float x) { result.getFloat() = x }
/** Gets the string constant `x`. */
ConstantValue fromString(string x) { result.getString() = x }
/** Gets the symbol constant `x`. */
ConstantValue fromSymbol(string x) { result.getSymbol() = x }
/** Gets the regexp constant `x`. */
ConstantValue fromRegExp(string x) { result.getRegExp() = x }
/** Gets the string, symbol, or regexp constant `x`. */
ConstantValue fromStringlikeValue(string x) { result.getStringlikeValue() = x }
}
/** An access to a constant. */
@@ -252,6 +270,20 @@ private class ConstantReadAccessSynth extends ConstantAccess, TConstantReadAcces
final override predicate hasGlobalScope() { value.matches("::%") }
}
private class ConstantWriteAccessSynth extends ConstantAccess, TConstantWriteAccessSynth {
private string value;
ConstantWriteAccessSynth() { this = TConstantWriteAccessSynth(_, _, value) }
final override string getName() {
if this.hasGlobalScope() then result = value.suffix(2) else result = value
}
final override Expr getScopeExpr() { synthChild(this, 0, result) }
final override predicate hasGlobalScope() { value.matches("::%") }
}
/**
* A use (read) of a constant.
*
@@ -323,7 +355,9 @@ class ConstantReadAccess extends ConstantAccess {
*/
class ConstantWriteAccess extends ConstantAccess {
ConstantWriteAccess() {
explicitAssignmentNode(toGenerated(this), _) or this instanceof TNamespace
explicitAssignmentNode(toGenerated(this), _) or
this instanceof TNamespace or
this instanceof TConstantWriteAccessSynth
}
override string getAPrimaryQlClass() { result = "ConstantWriteAccess" }

View File

@@ -61,7 +61,7 @@ class ArgumentList extends Expr, TArgumentList {
private class LhsExpr_ =
TVariableAccess or TTokenConstantAccess or TScopeResolutionConstantAccess or TMethodCall or
TDestructuredLhsExpr;
TDestructuredLhsExpr or TConstantWriteAccessSynth;
/**
* A "left-hand-side" (LHS) expression. An `LhsExpr` can occur on the left-hand side of

View File

@@ -3,6 +3,7 @@ private import codeql.ruby.CFG
private import internal.AST
private import internal.Module
private import internal.TreeSitter
private import internal.Scope
/**
* A representation of a run-time `module` or `class` value.
@@ -23,9 +24,30 @@ class Module extends TModule {
/** Gets an `include`d module. */
Module getAnIncludedModule() { result = getAnIncludedModule(this) }
/** Gets the super class or an included or prepended module. */
Module getAnImmediateAncestor() {
result = [this.getSuperClass(), this.getAPrependedModule(), this.getAnIncludedModule()]
}
/** Gets a direct subclass or module including or prepending this one. */
Module getAnImmediateDescendent() { this = result.getAnImmediateAncestor() }
/** Gets a module that is transitively subclassed, included, or prepended by this module. */
pragma[inline]
Module getAnAncestor() { result = this.getAnImmediateAncestor*() }
/** Gets a module that transitively subclasses, includes, or prepends this module. */
pragma[inline]
Module getADescendent() { result = this.getAnImmediateDescendent*() }
/** Holds if this module is a class. */
pragma[noinline]
predicate isClass() { this.getADeclaration() instanceof ClassDeclaration }
predicate isClass() {
this.getADeclaration() instanceof ClassDeclaration
or
// If another class extends this, but we can't see the class declaration, assume it's a class
getSuperClass(_) = this
}
/** Gets a textual representation of this module. */
string toString() {
@@ -58,6 +80,99 @@ class Module extends TModule {
loc.getStartColumn()
)
}
/** Gets a constant or `self` access that refers to this module. */
private Expr getAnImmediateReferenceBase() {
resolveConstantReadAccess(result) = this
or
result.(SelfVariableAccess).getVariable() = this.getADeclaration().getModuleSelfVariable()
}
/** Gets a singleton class that augments this module object. */
SingletonClass getASingletonClass() { result.getValue() = this.getAnImmediateReferenceBase() }
/**
* Gets a singleton method on this module, either declared as a singleton method
* or an instance method on a singleton class.
*
* Does not take inheritance into account.
*/
MethodBase getAnOwnSingletonMethod() {
result.(SingletonMethod).getObject() = this.getAnImmediateReferenceBase()
or
result = this.getASingletonClass().getAMethod().(Method)
}
/**
* Gets an instance method named `name` declared in this module.
*
* Does not take inheritance into account.
*/
Method getOwnInstanceMethod(string name) { result = this.getADeclaration().getMethod(name) }
/**
* Gets an instance method declared in this module.
*
* Does not take inheritance into account.
*/
Method getAnOwnInstanceMethod() { result = this.getADeclaration().getMethod(_) }
/**
* Gets the instance method named `name` available in this module, including methods inherited
* from ancestors.
*/
Method getInstanceMethod(string name) { result = lookupMethod(this, name) }
/**
* Gets an instance method available in this module, including methods inherited
* from ancestors.
*/
Method getAnInstanceMethod() { result = lookupMethod(this, _) }
/** Gets a constant or `self` access that refers to this module. */
Expr getAnImmediateReference() {
result = this.getAnImmediateReferenceBase()
or
result.(SelfVariableAccess).getVariable().getDeclaringScope() = this.getAnOwnSingletonMethod()
}
pragma[nomagic]
private string getEnclosingModuleName() {
exists(string qname |
qname = this.getQualifiedName() and
result = qname.regexpReplaceAll("::[^:]*$", "") and
qname != result
)
}
pragma[nomagic]
private string getOwnModuleName() {
result = this.getQualifiedName().regexpReplaceAll("^.*::", "")
}
/**
* Gets the enclosing module, as it appears in the qualified name of this module.
*
* For example, the parent module of `A::B` is `A`, and `A` itself has no parent module.
*/
pragma[nomagic]
Module getParentModule() { result.getQualifiedName() = this.getEnclosingModuleName() }
/**
* Gets a module named `name` declared inside this one (not aliased), provided
* that such a module is defined or reopened in the current codebase.
*
* For example, for `A::B` the nested module named `C` would be `A::B::C`.
*
* Note that this is not the same as constant lookup. If `A::B::C` would resolve to a
* module whose qualified name is not `A::B::C`, then it will not be found by
* this predicate.
*/
pragma[nomagic]
Module getNestedModule(string name) {
result.getParentModule() = this and
result.getOwnModuleName() = name
}
}
/**
@@ -136,6 +251,46 @@ class ModuleBase extends BodyStmt, Scope, TModuleBase {
/** Gets the representation of the run-time value of this module or class. */
Module getModule() { none() }
/**
* Gets the `self` variable in the module-level scope.
*
* Does not include the `self` variable from any of the methods in the module.
*/
SelfVariable getModuleSelfVariable() { result.getDeclaringScope() = this }
/** Gets the nearest enclosing `Namespace` or `Toplevel`, possibly this module itself. */
Namespace getNamespaceOrToplevel() {
result = this
or
not this instanceof Namespace and
result = this.getEnclosingModule().getNamespaceOrToplevel()
}
/**
* Gets an expression denoting the super class or an included or prepended module.
*
* For example, `C` is an ancestor expression of `M` in each of the following examples:
* ```rb
* class M < C
* end
*
* module M
* include C
* prepend C
* end
* ```
*/
Expr getAnAncestorExpr() {
exists(MethodCall call |
call.getReceiver().(SelfVariableAccess).getVariable() = this.getModuleSelfVariable() and
call.getMethodName() = ["include", "prepend"] and
result = call.getArgument(0) and
scopeOfInclSynth(call) = this // only permit calls directly in the module scope, not in a block
)
or
result = this.(ClassDeclaration).getSuperclassExpr()
}
}
/**

View File

@@ -116,6 +116,9 @@ private module Cached {
TConstantReadAccessSynth(Ast::AstNode parent, int i, string value) {
mkSynthChild(ConstantReadAccessKind(value), parent, i)
} or
TConstantWriteAccessSynth(Ast::AstNode parent, int i, string value) {
mkSynthChild(ConstantWriteAccessKind(value), parent, i)
} or
TDefinedExpr(Ruby::Unary g) { g instanceof @ruby_unary_definedquestion } or
TDelimitedSymbolLiteral(Ruby::DelimitedSymbol g) or
TDestructuredLeftAssignment(Ruby::DestructuredLeftAssignment g) {
@@ -373,12 +376,13 @@ private module Cached {
class TAstNodeSynth =
TAddExprSynth or TAssignExprSynth or TBitwiseAndExprSynth or TBitwiseOrExprSynth or
TBitwiseXorExprSynth or TBraceBlockSynth or TClassVariableAccessSynth or
TConstantReadAccessSynth or TDivExprSynth or TExponentExprSynth or
TGlobalVariableAccessSynth or TIfSynth or TInstanceVariableAccessSynth or
TIntegerLiteralSynth or TLShiftExprSynth or TLocalVariableAccessSynth or
TLogicalAndExprSynth or TLogicalOrExprSynth or TMethodCallSynth or TModuloExprSynth or
TMulExprSynth or TNilLiteralSynth or TRShiftExprSynth or TRangeLiteralSynth or TSelfSynth or
TSimpleParameterSynth or TSplatExprSynth or TStmtSequenceSynth or TSubExprSynth;
TConstantReadAccessSynth or TConstantWriteAccessSynth or TDivExprSynth or
TExponentExprSynth or TGlobalVariableAccessSynth or TIfSynth or
TInstanceVariableAccessSynth or TIntegerLiteralSynth or TLShiftExprSynth or
TLocalVariableAccessSynth or TLogicalAndExprSynth or TLogicalOrExprSynth or
TMethodCallSynth or TModuloExprSynth or TMulExprSynth or TNilLiteralSynth or
TRShiftExprSynth or TRangeLiteralSynth or TSelfSynth or TSimpleParameterSynth or
TSplatExprSynth or TStmtSequenceSynth or TSubExprSynth;
/**
* Gets the underlying TreeSitter entity for a given AST node. This does not
@@ -565,6 +569,8 @@ private module Cached {
or
result = TConstantReadAccessSynth(parent, i, _)
or
result = TConstantWriteAccessSynth(parent, i, _)
or
result = TDivExprSynth(parent, i)
or
result = TExponentExprSynth(parent, i)
@@ -672,7 +678,8 @@ class TMethodCall =
class TSuperCall = TTokenSuperCall or TRegularSuperCall;
class TConstantAccess =
TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace or TConstantReadAccessSynth;
TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace or
TConstantReadAccessSynth or TConstantWriteAccessSynth;
class TControlExpr = TConditionalExpr or TCaseExpr or TCaseMatch or TLoop;

View File

@@ -1,11 +1,13 @@
private import codeql.ruby.AST
private import Scope as Scope
// Names of built-in modules and classes
private string builtin() {
result =
[
"Object", "Kernel", "BasicObject", "Class", "Module", "NilClass", "FalseClass", "TrueClass",
"Numeric", "Integer", "Float", "Rational", "Complex", "Array", "Hash", "Symbol", "Proc"
"Numeric", "Integer", "Float", "Rational", "Complex", "Array", "Hash", "String", "Symbol",
"Proc",
]
}
@@ -16,6 +18,8 @@ private module Cached {
TResolved(string qName) {
qName = builtin()
or
qName = getAnAssumedGlobalConst()
or
qName = namespaceDeclaration(_)
} or
TUnresolved(Namespace n) { not exists(namespaceDeclaration(n)) }
@@ -38,7 +42,10 @@ private module Cached {
Module getSuperClass(Module cls) {
cls = TResolved("Object") and result = TResolved("BasicObject")
or
cls = TResolved(["Module", "Numeric", "Array", "Hash", "FalseClass", "TrueClass", "NilClass"]) and
cls =
TResolved([
"Module", "Numeric", "Array", "Hash", "FalseClass", "TrueClass", "NilClass", "String"
]) and
result = TResolved("Object")
or
cls = TResolved(["Integer", "Float", "Rational", "Complex"]) and
@@ -58,6 +65,12 @@ private module Cached {
forex(ClassDeclaration d | d = cls.getADeclaration() |
not exists(resolveConstantReadAccess(d.getSuperclassExpr()))
)
or
// If a module is used as a base class of another class, but we don't see its class declaration
// treat it as a class extending Object, so its subclasses transitively extend Object.
result = TResolved("Object") and
not cls.getADeclaration() instanceof ClassDeclaration and
cls = resolveConstantReadAccess(any(ClassDeclaration d).getSuperclassExpr())
)
}
@@ -65,7 +78,7 @@ private module Cached {
(
m = resolveConstantReadAccess(c.getReceiver())
or
m = enclosingModule(c).getModule() and
m = enclosingModuleNoBlock(c).getModule() and
c.getReceiver() instanceof SelfVariableAccess
) and
result = resolveConstantReadAccess(c.getAnArgument())
@@ -388,11 +401,23 @@ private module ResolveImpl {
result = resolveConstantWriteAccessRec(c, _, _)
}
/**
* Gets the name of a constant `C` that we assume to be defined in the top-level because
* it is referenced in a way that can only resolve to a top-level constant.
*/
string getAnAssumedGlobalConst() {
exists(ConstantAccess access |
not exists(access.getScopeExpr()) and
result = access.getName() and
isToplevel(access)
)
}
pragma[nomagic]
private string isDefinedConstantNonRec(string container, string name) {
result = resolveConstantWriteAccessNonRec(_, container, name)
or
result = builtin() and
result = [builtin(), getAnAssumedGlobalConst()] and
name = result and
container = "Object"
}
@@ -447,7 +472,7 @@ private module ResolveImpl {
result = resolveConstantReadAccess(this.getReceiver(), _)
or
exists(ModuleBase encl |
encl = enclosingModule(this) and
encl = enclosingModuleNoBlock(this) and
result = [qualifiedModuleNameNonRec(encl, _, _), qualifiedModuleNameRec(encl, _, _)]
|
this.getReceiver() instanceof SelfVariableAccess
@@ -495,7 +520,20 @@ private module ResolveImpl {
private import ResolveImpl
/**
* A variant of AstNode::getEnclosingModule that excludes
* Gets an enclosing scope of `scope`, stopping at the first module or block.
*
* Includes `scope` itself and the final module/block.
*/
private Scope enclosingScopesNoBlock(Scope scope) {
result = scope
or
not scope instanceof ModuleBase and
not scope instanceof Block and
result = enclosingScopesNoBlock(scope.getOuterScope())
}
/**
* A variant of `AstNode::getEnclosingModule` that excludes
* results that are enclosed in a block. This is a bit wrong because
* it could lead to false negatives. However, `include` statements in
* blocks are very rare in normal code. The majority of cases are in calls
@@ -503,15 +541,10 @@ private import ResolveImpl
* methods evaluate the block in the context of some other module/class instead of
* the enclosing one.
*/
private ModuleBase enclosingModule(AstNode node) {
result = node.getParent()
or
exists(AstNode mid |
result = enclosingModule(mid) and
mid = node.getParent() and
not mid instanceof ModuleBase and
not mid instanceof Block
)
private ModuleBase enclosingModuleNoBlock(Stmt node) {
// Note: don't rely on AstNode.getParent() here.
// Instead use Scope.getOuterScope() to correctly handle the scoping of things like Namespace.getScopeExpr().
result = enclosingScopesNoBlock(Scope::scopeOfInclSynth(node))
}
private Module getAncestors(Module m) {

View File

@@ -42,7 +42,8 @@ newtype SynthKind =
StmtSequenceKind() or
SelfKind(SelfVariable v) or
SubExprKind() or
ConstantReadAccessKind(string value) { any(Synthesis s).constantReadAccess(value) }
ConstantReadAccessKind(string value) { any(Synthesis s).constantReadAccess(value) } or
ConstantWriteAccessKind(string value) { any(Synthesis s).constantWriteAccess(value) }
/**
* An AST child.
@@ -107,6 +108,11 @@ class Synthesis extends TSynthesis {
*/
predicate constantReadAccess(string name) { none() }
/**
* Holds if a constant write access of `name` is needed.
*/
predicate constantWriteAccess(string name) { none() }
/**
* Holds if `n` should be excluded from `ControlFlowTree` in the CFG construction.
*/
@@ -493,6 +499,231 @@ private module AssignOperationDesugar {
}
}
/**
* An assignment operation where the left-hand side is a constant
* without scope expression, such as`FOO` or `::Foo`.
*/
private class ConstantAssignOperation extends AssignOperation {
string name;
pragma[nomagic]
ConstantAssignOperation() {
name =
any(Ruby::Constant constant | TTokenConstantAccess(constant) = this.getLeftOperand())
.getValue()
or
name =
"::" +
any(Ruby::Constant constant |
TScopeResolutionConstantAccess(any(Ruby::ScopeResolution g | not exists(g.getScope())),
constant) = this.getLeftOperand()
).getValue()
}
final string getName() { result = name }
}
pragma[nomagic]
private predicate constantAssignOperationSynthesis(AstNode parent, int i, Child child) {
exists(ConstantAssignOperation cao |
parent = cao and
i = -1 and
child = SynthChild(AssignExprKind())
or
exists(AstNode assign | assign = TAssignExprSynth(cao, -1) |
parent = assign and
i = 0 and
child = childRef(cao.getLeftOperand())
or
parent = assign and
i = 1 and
child = SynthChild(getKind(cao))
or
parent = getSynthChild(assign, 1) and
(
i = 0 and
child = SynthChild(ConstantReadAccessKind(cao.getName()))
or
i = 1 and
child = childRef(cao.getRightOperand())
)
)
)
}
/**
* ```rb
* FOO += y
* ```
*
* desugars to
*
* ```rb
* FOO = FOO + y
* ```
*/
private class ConstantAssignOperationSynthesis extends Synthesis {
final override predicate child(AstNode parent, int i, Child child) {
constantAssignOperationSynthesis(parent, i, child)
}
final override predicate constantReadAccess(string name) {
name = any(ConstantAssignOperation o).getName()
}
final override predicate location(AstNode n, Location l) {
exists(ConstantAssignOperation cao, BinaryOperation bo |
bo = cao.getDesugared().(AssignExpr).getRightOperand()
|
n = bo and
l = getAssignOperationLocation(cao)
or
n = bo.getLeftOperand() and
hasLocation(cao.getLeftOperand(), l)
)
}
}
/**
* An assignment operation where the left-hand side is a constant
* with scope expression, such as `expr::FOO`.
*/
private class ScopeResolutionAssignOperation extends AssignOperation {
string name;
Expr scope;
pragma[nomagic]
ScopeResolutionAssignOperation() {
exists(Ruby::Constant constant, Ruby::ScopeResolution g |
TScopeResolutionConstantAccess(g, constant) = this.getLeftOperand() and
name = constant.getValue() and
toGenerated(scope) = g.getScope()
)
}
final string getName() { result = name }
final Expr getScopeExpr() { result = scope }
}
pragma[nomagic]
private predicate scopeResolutionAssignOperationSynthesis(AstNode parent, int i, Child child) {
exists(ScopeResolutionAssignOperation cao |
parent = cao and
i = -1 and
child = SynthChild(StmtSequenceKind())
or
exists(AstNode stmts | stmts = TStmtSequenceSynth(cao, -1) |
parent = stmts and
i = 0 and
child = SynthChild(AssignExprKind())
or
exists(AstNode assign | assign = TAssignExprSynth(stmts, 0) |
parent = assign and
i = 0 and
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(cao, 0)))
or
parent = assign and
i = 1 and
child = childRef(cao.getScopeExpr())
)
or
parent = stmts and
i = 1 and
child = SynthChild(AssignExprKind())
or
exists(AstNode assign | assign = TAssignExprSynth(stmts, 1) |
parent = assign and
i = 0 and
child = SynthChild(ConstantWriteAccessKind(cao.getName()))
or
exists(AstNode cwa | cwa = TConstantWriteAccessSynth(assign, 0, cao.getName()) |
parent = cwa and
i = 0 and
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(cao, 0)))
)
or
parent = assign and
i = 1 and
child = SynthChild(getKind(cao))
or
exists(AstNode op | op = getSynthChild(assign, 1) |
parent = op and
i = 0 and
child = SynthChild(ConstantReadAccessKind(cao.getName()))
or
exists(AstNode cra | cra = TConstantReadAccessSynth(op, 0, cao.getName()) |
parent = cra and
i = 0 and
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(cao, 0)))
)
or
parent = op and
i = 1 and
child = childRef(cao.getRightOperand())
)
)
)
)
}
/**
* ```rb
* expr::FOO += y
* ```
*
* desugars to
*
* ```rb
* __synth__0 = expr
* __synth__0::FOO = _synth__0::FOO + y
* ```
*/
private class ScopeResolutionAssignOperationSynthesis extends Synthesis {
final override predicate child(AstNode parent, int i, Child child) {
scopeResolutionAssignOperationSynthesis(parent, i, child)
}
final override predicate constantReadAccess(string name) {
name = any(ScopeResolutionAssignOperation o).getName()
}
final override predicate localVariable(AstNode n, int i) {
n instanceof ScopeResolutionAssignOperation and
i = 0
}
final override predicate constantWriteAccess(string name) { this.constantReadAccess(name) }
final override predicate location(AstNode n, Location l) {
exists(ScopeResolutionAssignOperation cao, StmtSequence stmts | stmts = cao.getDesugared() |
n = stmts.getStmt(0) and
hasLocation(cao.getScopeExpr(), l)
or
exists(AssignExpr assign | assign = stmts.getStmt(1) |
n = assign and hasLocation(cao, l)
or
n = assign.getLeftOperand() and
hasLocation(cao.getLeftOperand(), l)
or
n = assign.getLeftOperand().(ConstantAccess).getScopeExpr() and
hasLocation(cao.getScopeExpr(), l)
or
exists(BinaryOperation bo | bo = assign.getRightOperand() |
n = bo and
l = getAssignOperationLocation(cao)
or
n = bo.getLeftOperand() and
hasLocation(cao.getLeftOperand(), l)
or
n = bo.getLeftOperand().(ConstantAccess).getScopeExpr() and
hasLocation(cao.getScopeExpr(), l)
)
)
)
}
}
/** An assignment operation where the left-hand side is a method call. */
private class SetterAssignOperation extends AssignOperation {
private MethodCall mc;

View File

@@ -7,7 +7,14 @@ private import internal.ControlFlowGraphImpl
private import internal.Splitting
private import internal.Completion
/** An AST node with an associated control-flow graph. */
/**
* An AST node with an associated control-flow graph.
*
* Top-levels, methods, blocks, and lambdas are all CFG scopes.
*
* Note that module declarations are not themselves CFG scopes, as they are part of
* the CFG of the enclosing top-level or callable.
*/
class CfgScope extends Scope instanceof CfgScopeImpl {
/** Gets the CFG scope that this scope is nested under, if any. */
final CfgScope getOuterCfgScope() {

View File

@@ -890,7 +890,12 @@ module Trees {
private class ConstantAccessTree extends PostOrderTree, ConstantAccess {
ConstantAccessTree() {
not this instanceof ClassDeclaration and
not this instanceof ModuleDeclaration
not this instanceof ModuleDeclaration and
// constant accesses with scope expression in compound assignments are desugared
not (
this = any(AssignOperation op).getLeftOperand() and
exists(this.getScopeExpr())
)
}
final override predicate propagatesAbnormal(AstNode child) { child = this.getScopeExpr() }

View File

@@ -289,7 +289,7 @@ module Ssa {
)
}
final override string toString() { result = "<captured>" }
final override string toString() { result = "<captured> " + this.getSourceVariable() }
override Location getLocation() { result = this.getBasicBlock().getLocation() }
}

View File

@@ -375,17 +375,10 @@ private module Cached {
private predicate selfInSingletonMethodFlowsToMethodCallReceiver(
RelevantCall call, Module m, string method
) {
exists(SsaSelfDefinitionNode self, Module target, MethodBase caller |
exists(SsaSelfDefinitionNode self, MethodBase caller |
flowsToMethodCallReceiver(call, self, method) and
target = m.getSuperClass*() and
selfInMethod(self.getVariable(), caller, target) and
singletonMethod(caller, _, _) and
// Singleton methods declared in a block in the top-level may spuriously end up being seen as singleton
// methods on Object, if the block is actually evaluated in the context of another class.
// The 'self' inside such a singleton method could then be any class, leading to self-calls
// being resolved to arbitrary singleton methods.
// To remedy this, we do not allow following super-classes all the way to Object.
not (m != target and target = TResolved("Object"))
selfInMethod(self.getVariable(), caller, m) and
singletonMethod(caller, _, _)
)
}
@@ -441,12 +434,13 @@ private module Cached {
// M.extend(M)
// M.instance # <- call
// ```
exists(Module m | result = lookupSingletonMethod(m, method) |
exists(Module m, boolean exact | result = lookupSingletonMethod(m, method, exact) |
// ```rb
// def C.singleton; end # <- result
// C.singleton # <- call
// ```
moduleFlowsToMethodCallReceiver(call, m, method)
moduleFlowsToMethodCallReceiver(call, m, method) and
exact = true
or
// ```rb
// class C
@@ -454,7 +448,8 @@ private module Cached {
// self.singleton # <- call
// end
// ```
selfInModuleFlowsToMethodCallReceiver(call, m, method)
selfInModuleFlowsToMethodCallReceiver(call, m, method) and
exact = true
or
// ```rb
// class C
@@ -464,7 +459,8 @@ private module Cached {
// end
// end
// ```
selfInSingletonMethodFlowsToMethodCallReceiver(call, m, method)
selfInSingletonMethodFlowsToMethodCallReceiver(call, m, method) and
exact = false
)
)
or
@@ -796,26 +792,54 @@ private predicate singletonMethodOnModule(MethodBase method, string name, Module
)
}
pragma[nomagic]
private MethodBase lookupSingletonMethodDirect(Module m, string name) {
singletonMethodOnModule(result, name, m)
or
exists(DataFlow::LocalSourceNode sourceNode |
sourceNode = trackModuleAccess(m) and
not m = resolveConstantReadAccess(sourceNode.asExpr().getExpr()) and
flowsToSingletonMethodObject(sourceNode, result, name)
)
}
/**
* Holds if `method` is a singleton method named `name`, defined on module
* `m`, or any transitive base class of `m`.
*/
pragma[nomagic]
private MethodBase lookupSingletonMethod(Module m, string name) {
singletonMethodOnModule(result, name, m)
or
// cannot be part of `singletonMethodOnModule` because it would introduce
// negative recursion below
exists(DataFlow::LocalSourceNode sourceNode |
sourceNode = trackModuleAccess(m) and
not m = resolveConstantReadAccess(sourceNode.asExpr().getExpr()) and
flowsToSingletonMethodObject(sourceNode, result, name)
)
result = lookupSingletonMethodDirect(m, name)
or
// cannot use `lookupSingletonMethodDirect` because it would introduce
// negative recursion
not singletonMethodOnModule(_, name, m) and
result = lookupSingletonMethod(m.getSuperClass(), name)
}
pragma[nomagic]
private MethodBase lookupSingletonMethodInSubClasses(Module m, string name) {
// Singleton methods declared in a block in the top-level may spuriously end up being seen as singleton
// methods on Object, if the block is actually evaluated in the context of another class.
// The 'self' inside such a singleton method could then be any class, leading to self-calls
// being resolved to arbitrary singleton methods.
// To remedy this, we do not allow following super-classes all the way to Object.
not m = TResolved("Object") and
exists(Module sub | sub.getSuperClass() = m |
result = lookupSingletonMethodDirect(sub, name) or
result = lookupSingletonMethodInSubClasses(sub, name)
)
}
pragma[nomagic]
private MethodBase lookupSingletonMethod(Module m, string name, boolean exact) {
result = lookupSingletonMethod(m, name) and
exact in [false, true]
or
result = lookupSingletonMethodInSubClasses(m, name) and
exact = false
}
/**
* Holds if `method` is a singleton method named `name`, defined on expression
* `object`, where `object` is not likely to resolve to a module:

View File

@@ -109,11 +109,27 @@ module LocalFlow {
* Holds if `nodeFrom` is a parameter node, and `nodeTo` is a corresponding SSA node.
*/
predicate localFlowSsaParamInput(Node nodeFrom, Node nodeTo) {
nodeTo = getParameterDefNode(nodeFrom.(ParameterNode).getParameter())
nodeTo = getParameterDefNode(nodeFrom.(ParameterNodeImpl).getParameter())
or
nodeTo = getSelfParameterDefNode(nodeFrom.(SelfParameterNode).getMethod())
}
/**
* Holds if `nodeFrom -> nodeTo` is a step from a parameter to a capture entry node for
* that parameter.
*
* This is intended to recover from flow not currently recognised by ordinary capture flow.
*/
predicate localFlowSsaParamCaptureInput(Node nodeFrom, Node nodeTo) {
exists(Ssa::CapturedEntryDefinition def |
nodeFrom.asParameter().(NamedParameter).getVariable() = def.getSourceVariable()
or
nodeFrom.(SelfParameterNode).getSelfVariable() = def.getSourceVariable()
|
nodeTo.(SsaDefinitionNode).getDefinition() = def
)
}
/**
* Holds if there is a local use-use flow step from `nodeFrom` to `nodeTo`
* involving SSA definition `def`.
@@ -246,6 +262,9 @@ private module Cached {
)
} or
TSsaDefinitionNode(Ssa::Definition def) or
TRawNamespaceSelf(Namespace ns) {
not exists(Ssa::SelfDefinition def | def.getSourceVariable() = ns.getModuleSelfVariable())
} or
TNormalParameterNode(Parameter p) {
p instanceof SimpleParameter or
p instanceof OptionalParameter or
@@ -299,7 +318,7 @@ private module Cached {
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
or
defaultValueFlow(nodeTo.(ParameterNode).getParameter(), nodeFrom)
defaultValueFlow(nodeTo.(ParameterNodeImpl).getParameter(), nodeFrom)
or
LocalFlow::localFlowSsaParamInput(nodeFrom, nodeTo)
or
@@ -316,7 +335,7 @@ private module Cached {
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
or
defaultValueFlow(nodeTo.(ParameterNode).getParameter(), nodeFrom)
defaultValueFlow(nodeTo.(ParameterNodeImpl).getParameter(), nodeFrom)
or
LocalFlow::localFlowSsaParamInput(nodeFrom, nodeTo)
or
@@ -327,7 +346,12 @@ private module Cached {
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(nodeFrom, nodeTo, _)
}
/** This is the local flow predicate that is used in type tracking. */
/**
* This is the local flow predicate that is used in type tracking.
*
* This needs to exclude `localFlowSsaParamInput` due to a performance trick
* in type tracking, where such steps are treated as call steps.
*/
cached
predicate localFlowStepTypeTracker(Node nodeFrom, Node nodeTo) {
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
@@ -366,7 +390,7 @@ private module Cached {
cached
predicate isLocalSourceNode(Node n) {
n instanceof ParameterNode
n instanceof TParameterNode
or
// Expressions that can't be reached from another entry definition or expression
n instanceof ExprNode and
@@ -381,6 +405,8 @@ private module Cached {
or
// Needed for stores in type tracking
TypeTrackerSpecific::storeStepIntoSourceNode(_, n, _)
or
n instanceof TRawNamespaceSelf
}
cached
@@ -502,6 +528,38 @@ class SsaSelfDefinitionNode extends LocalSourceNode, SsaDefinitionNode {
Scope getSelfScope() { result = self.getDeclaringScope() }
}
/**
* A representative for the created or re-opened class/module in a `Namespace` that does
* not have an SSA definition for `self`.
*/
class RawNamespaceSelf extends NodeImpl, TRawNamespaceSelf {
private Namespace namespace;
RawNamespaceSelf() { this = TRawNamespaceSelf(namespace) }
/** Gets the namespace for which this represents the created or re-opened class/module. */
Namespace getNamespace() { result = namespace }
override CfgScope getCfgScope() { result = namespace.getCfgScope() }
override Location getLocationImpl() { result = namespace.getLocation() }
override string toStringImpl() { result = namespace.toString() }
}
/**
* Gets a representative for the created or re-opened class/module in a `Namespace`.
*
* This is usually the SSA definition for `self`, but if this node does not exist due to lack of liveness,
* it is the `RawNamespaceSelf` node.
*/
LocalSourceNode getNamespaceSelf(Namespace namespace) {
result.(SsaDefinitionNode).getDefinition().(Ssa::SelfDefinition).getSourceVariable() =
namespace.getModuleSelfVariable()
or
result = TRawNamespaceSelf(namespace)
}
/**
* A value returning statement, viewed as a node in a data flow graph.
*
@@ -1242,7 +1300,7 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
creation.asExpr() =
any(CfgNodes::ExprNodes::MethodCallCfgNode mc |
c.asCallable() = mc.getBlock().getExpr() and
mc.getExpr().getMethodName() = "lambda"
mc.getExpr().getMethodName() = ["lambda", "proc"]
)
)
}

View File

@@ -54,6 +54,30 @@ class Node extends TNode {
* Gets a data flow node to which data may flow from this node in one local step.
*/
Node getASuccessor() { localFlowStep(this, result) }
/** Gets the constant value of this expression, if any. */
ConstantValue getConstantValue() { result = this.asExpr().getConstantValue() }
/**
* Gets the callable corresponding to this block, lambda expression, or call to `proc` or `lambda`.
*
* For example, gets the callable in each of the following cases:
* ```rb
* { |x| x } # block expression
* ->(x) { x } # lambda expression
* proc { |x| x } # call to 'proc'
* lambda { |x| x } # call to 'lambda'
* ```
*/
pragma[noinline]
CallableNode asCallable() {
result = this
or
exists(DataFlowCallable c |
lambdaCreation(this, _, c) and
result.asCallableAstNode() = c.asCallable()
)
}
}
/** A data-flow node corresponding to a call in the control-flow graph. */
@@ -124,6 +148,31 @@ class CallNode extends LocalSourceNode, ExprNode {
}
}
/**
* A call to a setter method.
*
* For example,
* ```rb
* self.foo = 10
* a[0] = 10
* ```
*/
class SetterCallNode extends CallNode {
SetterCallNode() { this.getExprNode().getExpr() instanceof SetterMethodCall }
/**
* Gets the name of the method being called without the trailing `=`. For example, in the following
* two statements the target name is `value`:
* ```rb
* foo.value=(1)
* foo.value = 1
* ```
*/
final string getTargetName() {
result = this.getExprNode().getExpr().(SetterMethodCall).getTargetName()
}
}
/**
* An expression, viewed as a node in a data flow graph.
*
@@ -144,7 +193,7 @@ class ExprNode extends Node, TExprNode {
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParameterNode extends Node, TParameterNode instanceof ParameterNodeImpl {
class ParameterNode extends LocalSourceNode, TParameterNode instanceof ParameterNodeImpl {
/** Gets the parameter corresponding to this node, if any. */
final Parameter getParameter() { result = super.getParameter() }
}
@@ -174,6 +223,80 @@ class LocalSourceNode extends Node {
*/
pragma[inline]
LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t2 = t.step(result, this) }
/**
* Gets a node to which data may flow from this node in zero or
* more local data-flow steps.
*/
pragma[inline]
Node getALocalUse() { hasLocalSource(result, this) }
/** Gets a method call where this node flows to the receiver. */
CallNode getAMethodCall() { Cached::hasMethodCall(this, result, _) }
/** Gets a call to a method named `name`, where this node flows to the receiver. */
CallNode getAMethodCall(string name) { Cached::hasMethodCall(this, result, name) }
/** Gets a call `obj.name` with no arguments, where this node flows to `obj`. */
CallNode getAnAttributeRead(string name) {
result = this.getAMethodCall(name) and
result.getNumberOfArguments() = 0
}
/**
* Gets a value assigned to `name` on this object, such as the `x` in `obj.name = x`.
*
* Concretely, this gets the argument of any call to `name=` where this node flows to the receiver.
*/
Node getAnAttributeWriteValue(string name) {
result = this.getAMethodCall(name + "=").getArgument(0)
}
/**
* Gets an access to an element on this node, such as `obj[key]`.
*
* Concretely this gets a call to `[]` with 1 argument, where this node flows to the receiver.
*/
CallNode getAnElementRead() {
result = this.getAMethodCall("[]") and result.getNumberOfArguments() = 1
}
/**
* Gets an access to the element `key` on this node, such as `obj[:key]`.
*
* Concretely this gets a call to `[]` where this node flows to the receiver
* and the first and only argument has the constant value `key`.
*/
CallNode getAnElementRead(ConstantValue key) {
result = this.getAnElementRead() and
key = result.getArgument(0).getConstantValue()
}
private CallNode getAnElementWriteCall() {
result = this.getAMethodCall("[]=") and
result.getNumberOfArguments() = 2
}
/**
* Gets a value stored as an element on this node, such as the `x` in `obj[key] = x`.
*
* Concretely, this gets the second argument from any call to `[]=` where this node flows to the receiver.
*/
Node getAnElementWriteValue() { result = this.getAnElementWriteCall().getArgument(1) }
/**
* Gets the `x` in `obj[key] = x`, where this node flows to `obj`.
*
* Concretely, this gets the second argument from any call to `[]=` where this node flows to the receiver
* and the first argument has constant value `key`.
*/
Node getAnElementWriteValue(ConstantValue key) {
exists(CallNode call |
call = this.getAnElementWriteCall() and
call.getArgument(0).getConstantValue() = key and
result = call.getArgument(1)
)
}
}
/**
@@ -193,18 +316,74 @@ class PostUpdateNode extends Node instanceof PostUpdateNodeImpl {
}
cached
private predicate hasLocalSource(Node sink, Node source) {
// Declaring `source` to be a `SourceNode` currently causes a redundant check in the
// recursive case, so instead we check it explicitly here.
source = sink and
source instanceof LocalSourceNode
or
exists(Node mid |
hasLocalSource(mid, source) and
localFlowStepTypeTracker(mid, sink)
)
private module Cached {
cached
predicate hasLocalSource(Node sink, Node source) {
// Declaring `source` to be a `SourceNode` currently causes a redundant check in the
// recursive case, so instead we check it explicitly here.
source = sink and
source instanceof LocalSourceNode
or
exists(Node mid | hasLocalSource(mid, source) |
localFlowStepTypeTracker(mid, sink)
or
// Explicitly include the SSA param input step as type-tracking omits this step.
LocalFlow::localFlowSsaParamInput(mid, sink)
or
LocalFlow::localFlowSsaParamCaptureInput(mid, sink)
)
}
cached
predicate hasMethodCall(LocalSourceNode source, CallNode call, string name) {
source.flowsTo(call.getReceiver()) and
call.getMethodName() = name
}
cached
predicate hasYieldCall(BlockParameterNode block, CallNode yield) {
exists(MethodBase method, YieldCall call |
block.getMethod() = method and
call.getEnclosingMethod() = method and
yield.asExpr().getExpr() = call
)
}
/**
* A place in which a named constant can be looked up during constant lookup.
*/
cached
newtype TConstLookupScope =
/** Look up in a qualified constant name `base::`. */
MkQualifiedLookup(ConstantAccess base) or
/** Look up in the ancestors of `mod`. */
MkAncestorLookup(Module mod) or
/** Look up in a module syntactically nested in a declaration of `mod`. */
MkNestedLookup(Module mod) or
/** Pseudo-scope for accesses that are known to resolve to `mod`. */
MkExactLookup(Module mod)
/**
* Gets a `LocalSourceNode` to represent the constant read or written by `access`.
*/
cached
LocalSourceNode getConstantAccessNode(ConstantAccess access) {
// Namespaces don't evaluate to the constant being accessed, they return the value of their last statement.
// Use the definition of 'self' in the namespace as the representative in this case.
if access instanceof Namespace
then result = getNamespaceSelf(access)
else result.asExpr().getExpr() = access
}
cached
predicate forceCachingInSameStage() { any() }
cached
predicate forceCachingBackref() { exists(any(ConstRef const).getConst(_)) }
}
private import Cached
/** Gets a node corresponding to expression `e`. */
ExprNode exprNode(CfgNodes::ExprCfgNode e) { result.getExprNode() = e }
@@ -559,3 +738,591 @@ abstract deprecated class BarrierGuard extends CfgNodes::ExprCfgNode {
result.asExpr() = this.getAMaybeGuardedCapturedDef().getARead()
}
}
/**
* A representation of a run-time module or class.
*
* This is equivalent to the type `Ast::Module` but provides data-flow specific methods.
*/
class ModuleNode instanceof Module {
/** Gets a declaration of this module, if any. */
final ModuleBase getADeclaration() { result = super.getADeclaration() }
/** Gets the super class of this module, if any. */
final ModuleNode getSuperClass() { result = super.getSuperClass() }
/** Gets an immediate sub class of this module, if any. */
final ModuleNode getASubClass() { result = super.getASubClass() }
/** Gets a `prepend`ed module. */
final ModuleNode getAPrependedModule() { result = super.getAPrependedModule() }
/** Gets an `include`d module. */
final ModuleNode getAnIncludedModule() { result = super.getAnIncludedModule() }
/** Gets the super class or an included or prepended module. */
final ModuleNode getAnImmediateAncestor() { result = super.getAnImmediateAncestor() }
/** Gets a direct subclass or module including or prepending this one. */
final ModuleNode getAnImmediateDescendent() { result = super.getAnImmediateDescendent() }
/** Gets a module that is transitively subclassed, included, or prepended by this module. */
pragma[inline]
final ModuleNode getAnAncestor() { result = super.getAnAncestor() }
/** Gets a module that transitively subclasses, includes, or prepends this module. */
pragma[inline]
final ModuleNode getADescendent() { result = super.getADescendent() }
/**
* Gets the expression node denoting the super class or an included or prepended module.
*
* For example, `C` is an ancestor expression of `M` in each of the following examples:
* ```rb
* class M < C
* end
*
* module M
* include C
* prepend C
* end
* ```
*/
final ExprNode getAnAncestorExpr() {
result.asExpr().getExpr() = super.getADeclaration().getAnAncestorExpr()
}
/** Holds if this module is a class. */
predicate isClass() { super.isClass() }
/** Gets a textual representation of this module. */
final string toString() { result = super.toString() }
/**
* Gets the qualified name of this module, if any.
*
* Only modules that can be resolved will have a qualified name.
*/
final string getQualifiedName() { result = super.getQualifiedName() }
/** Gets the location of this module. */
final Location getLocation() { result = super.getLocation() }
/**
* Gets `self` in a declaration of this module.
*
* This only gets `self` at the module level, not inside any (singleton) method.
*/
LocalSourceNode getModuleLevelSelf() {
result.(SsaDefinitionNode).getVariable() = super.getADeclaration().getModuleSelfVariable()
}
/**
* Gets `self` in the module declaration or in one of its singleton methods.
*
* Does not take inheritance into account.
*/
LocalSourceNode getAnOwnModuleSelf() {
result = this.getModuleLevelSelf()
or
result = this.getAnOwnSingletonMethod().getSelfParameter()
}
/**
* Gets a call to method `name` on `self` in the module-level scope of this module.
*
* For example,
* ```rb
* module M
* include A # getAModuleLevelCall("include")
* foo :bar # getAModuleLevelCall("foo")
* end
* ```
*/
CallNode getAModuleLevelCall(string name) {
result = this.getModuleLevelSelf().getAMethodCall(name)
}
/** Gets a constant or `self` variable that refers to this module. */
LocalSourceNode getAnImmediateReference() {
result.asExpr().getExpr() = super.getAnImmediateReference()
}
/**
* Gets a singleton method declared in this module (or in a singleton class
* augmenting this module).
*
* Does not take inheritance into account.
*/
MethodNode getAnOwnSingletonMethod() {
result.asCallableAstNode() = super.getAnOwnSingletonMethod()
}
/**
* Gets the singleton method named `name` declared in this module (or in a singleton class
* augmenting this module).
*
* Does not take inheritance into account.
*/
MethodNode getOwnSingletonMethod(string name) {
result = this.getAnOwnSingletonMethod() and
result.getMethodName() = name
}
/**
* Gets an instance method declared in this module.
*
* Does not take inheritance into account.
*/
MethodNode getAnOwnInstanceMethod() {
result.asCallableAstNode() = this.getADeclaration().getAMethod().(Method)
}
/**
* Gets an instance method named `name` declared in this module.
*
* Does not take inheritance into account.
*/
MethodNode getOwnInstanceMethod(string name) {
result = this.getAnOwnInstanceMethod() and
result.getMethodName() = name
}
/**
* Gets the `self` parameter of an instance method declared in this module.
*
* Does not take inheritance into account.
*/
ParameterNode getAnOwnInstanceSelf() {
result = TSelfParameterNode(this.getAnOwnInstanceMethod().asCallableAstNode())
}
/**
* Gets the `self` parameter of an instance method available in this module,
* including those inherited from ancestors.
*/
ParameterNode getAnInstanceSelf() {
// Make sure to include the 'self' in overridden instance methods
result = this.getAnAncestor().getAnOwnInstanceSelf()
}
private InstanceVariableAccess getAnOwnInstanceVariableAccess(string name) {
exists(InstanceVariable v |
v.getDeclaringScope() = this.getADeclaration() and
v.getName() = name and
result.getVariable() = v
)
}
/**
* Gets an access to the instance variable `name` in this module.
*
* Does not take inheritance into account.
*/
LocalSourceNode getAnOwnInstanceVariableRead(string name) {
result.asExpr().getExpr() =
this.getAnOwnInstanceVariableAccess(name).(InstanceVariableReadAccess)
}
/**
* Gets the right-hand side of an assignment to the instance variable `name` in this module.
*
* Does not take inheritance into account.
*/
Node getAnOwnInstanceVariableWriteValue(string name) {
exists(AssignExpr assignment |
assignment.getLeftOperand() = this.getAnOwnInstanceVariableAccess(name) and
result.asExpr().getExpr() = assignment.getRightOperand()
)
}
/**
* Gets the instance method named `name` available in this module, including methods inherited
* from ancestors.
*
* Overridden methods are not included.
*/
MethodNode getInstanceMethod(string name) {
result.asCallableAstNode() = super.getInstanceMethod(name)
}
/**
* Gets an instance method available in this module, including methods inherited
* from ancestors.
*
* Overridden methods are not included.
*/
MethodNode getAnInstanceMethod() { result = this.getInstanceMethod(_) }
/**
* Gets the enclosing module, as it appears in the qualified name of this module.
*
* For example, the canonical enclosing module of `A::B` is `A`, and `A` itself has no canonical enclosing module.
*/
ModuleNode getCanonicalEnclosingModule() { result = super.getParentModule() }
/**
* Gets a module named `name` declared inside this one (not aliased), provided
* that such a module is defined or reopened in the current codebase.
*
* For example, for `A::B` the canonical nested module named `C` would be `A::B::C`.
*
* Note that this is not the same as constant lookup. If `A::B::C` would resolve to a
* module whose qualified name is not `A::B::C`, then it will not be found by
* this predicate.
*/
ModuleNode getCanonicalNestedModule(string name) { result = super.getNestedModule(name) }
}
/**
* A representation of a run-time class.
*/
class ClassNode extends ModuleNode {
ClassNode() { this.isClass() }
}
/**
* A data flow node corresponding to a method, block, or lambda expression.
*/
class CallableNode extends ExprNode {
private Callable callable;
CallableNode() { this.asExpr().getExpr() = callable }
/** Gets the underlying AST node as a `Callable`. */
Callable asCallableAstNode() { result = callable }
private ParameterPosition getParameterPosition(ParameterNodeImpl node) {
node.isSourceParameterOf(callable, result)
}
/** Gets the `n`th positional parameter. */
ParameterNode getParameter(int n) { this.getParameterPosition(result).isPositional(n) }
/** Gets the keyword parameter of the given name. */
ParameterNode getKeywordParameter(string name) {
this.getParameterPosition(result).isKeyword(name)
}
/** Gets the `self` parameter of this callable, if any. */
ParameterNode getSelfParameter() { this.getParameterPosition(result).isSelf() }
/**
* Gets the `hash-splat` parameter. This is a synthetic parameter holding
* a hash object with entries for each keyword argument passed to the function.
*/
ParameterNode getHashSplatParameter() { this.getParameterPosition(result).isHashSplat() }
/**
* Gets the block parameter of this method, if any.
*/
ParameterNode getBlockParameter() { this.getParameterPosition(result).isBlock() }
/**
* Gets a `yield` in this method or `.call` on the block parameter.
*/
CallNode getABlockCall() {
hasYieldCall(this.getBlockParameter(), result)
or
result = this.getBlockParameter().getAMethodCall("call")
}
/**
* Gets the canonical return node from this callable.
*
* Each callable has exactly one such node, and its location may not correspond
* to any particular return site - consider using `getAReturningNode` to get nodes
* whose locations correspond to return sites.
*/
Node getReturn() { result.(SynthReturnNode).getCfgScope() = callable }
/**
* Gets a data flow node whose value is about to be returned by this callable.
*/
Node getAReturningNode() { result = this.getReturn().(SynthReturnNode).getAnInput() }
}
/**
* A data flow node corresponding to a method (possibly a singleton method).
*/
class MethodNode extends CallableNode {
MethodNode() { super.asCallableAstNode() instanceof MethodBase }
/** Gets the underlying AST node for this method. */
override MethodBase asCallableAstNode() { result = super.asCallableAstNode() }
/** Gets the name of this method. */
string getMethodName() { result = this.asCallableAstNode().getName() }
}
/**
* A data flow node corresponding to a block argument.
*/
class BlockNode extends CallableNode {
BlockNode() { super.asCallableAstNode() instanceof Block }
/** Gets the underlying AST node for this block. */
override Block asCallableAstNode() { result = super.asCallableAstNode() }
}
/**
* A representation of a pair such as `K => V` or `K: V`.
*
* Unlike most expressions, pairs do not evaluate to actual objects at runtime and their nodes
* cannot generally be expected to have meaningful data flow edges.
* This node simply provides convenient access to the key and value as data flow nodes.
*/
class PairNode extends ExprNode {
PairNode() { this.getExprNode() instanceof CfgNodes::ExprNodes::PairCfgNode }
/**
* Holds if this pair is of form `key => value` or `key: value`.
*/
predicate hasKeyAndValue(Node key, Node value) {
exists(CfgNodes::ExprNodes::PairCfgNode n |
this.getExprNode() = n and
key = TExprNode(n.getKey()) and
value = TExprNode(n.getValue())
)
}
/** Gets the key expression of this pair, such as the `K` in `K => V` or `K: V`. */
Node getKey() { this.hasKeyAndValue(result, _) }
/** Gets the value expression of this pair, such as the `V` in `K => V` or `K: V`. */
Node getValue() { this.hasKeyAndValue(_, result) }
}
/**
* A data-flow node that corresponds to a hash literal. Hash literals are desugared
* into calls to `Hash.[]`, so this includes both desugared calls as well as
* explicit calls.
*/
class HashLiteralNode extends LocalSourceNode, ExprNode {
HashLiteralNode() { super.getExprNode() instanceof CfgNodes::ExprNodes::HashLiteralCfgNode }
/** Gets a pair in this hash literal. */
PairNode getAKeyValuePair() {
result.getExprNode() =
super.getExprNode().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair()
}
/** Gets the value associated with the constant `key`, if known. */
Node getElementFromKey(ConstantValue key) {
exists(ExprNode keyNode |
this.getAKeyValuePair().hasKeyAndValue(keyNode, result) and
keyNode.getConstantValue() = key
)
}
}
/**
* A data-flow node corresponding to an array literal. Array literals are desugared
* into calls to `Array.[]`, so this includes both desugared calls as well as
* explicit calls.
*/
class ArrayLiteralNode extends LocalSourceNode, ExprNode {
ArrayLiteralNode() { super.getExprNode() instanceof CfgNodes::ExprNodes::ArrayLiteralCfgNode }
/**
* Gets an element of the array.
*/
Node getAnElement() { result = this.(CallNode).getPositionalArgument(_) }
}
/**
* An access to a constant, such as `C`, `C::D`, or a class or module declaration.
*
* See `DataFlow::getConst` for usage example.
*/
class ConstRef extends LocalSourceNode {
private ConstantAccess access;
ConstRef() { this = getConstantAccessNode(access) }
/** Gets the underlying constant access AST node. */
ConstantAccess asConstantAccess() { result = access }
/** Gets the underlying module declaration, if any. */
Namespace asNamespaceDeclaration() { result = access }
/** Gets the module defined or re-opened by this constant access, if any. */
ModuleNode asModule() { result.getADeclaration() = access }
/**
* Gets the simple name of the constant being referenced, such as
* the `B` in `A::B`.
*/
string getName() { result = access.getName() }
/**
* Holds if this might refer to a top-level constant.
*/
predicate isPossiblyGlobal() {
exists(Module mod |
not exists(mod.getParentModule()) and
mod.getAnImmediateReference() = access
)
or
not exists(Module mod | mod.getAnImmediateReference() = access) and
not exists(access.getScopeExpr())
}
/**
* Gets a module for which this constant is the reference to an ancestor module.
*
* For example, `M` is the ancestry target of `C` in the following examples:
* ```rb
* class M < C {}
*
* module M
* include C
* end
*
* module M
* prepend C
* end
* ```
*/
private ModuleNode getAncestryTarget() { result.getAnAncestorExpr() = this }
/**
* Gets the known target module.
*
* We resolve these differently to prune out infeasible constant lookups.
*/
private Module getExactTarget() { result.getAnImmediateReference() = access }
/**
* Gets a scope in which a constant lookup may access the contents of the module referenced by this constant.
*/
cached
private TConstLookupScope getATargetScope() {
forceCachingInSameStage() and
result = MkAncestorLookup(this.getAncestryTarget().getAnImmediateDescendent*())
or
access = any(ConstantAccess ac).getScopeExpr() and
result = MkQualifiedLookup(access)
or
result = MkNestedLookup(this.getAncestryTarget())
or
result = MkExactLookup(access.(Namespace).getModule())
}
/**
* Gets the scope expression, or the immediately enclosing `Namespace` (skipping over singleton classes).
*
* Top-levels are not included, since this is only needed for nested constant lookup, and unqualified constants
* at the top-level are handled by `DataFlow::getConst`, never `ConstRef.getConst`.
*/
private TConstLookupScope getLookupScope() {
result = MkQualifiedLookup(access.getScopeExpr())
or
not exists(this.getExactTarget()) and
not exists(access.getScopeExpr()) and
not access.hasGlobalScope() and
(
result = MkAncestorLookup(access.getEnclosingModule().getNamespaceOrToplevel().getModule())
or
result = MkNestedLookup(access.getEnclosingModule().getEnclosingModule*().getModule())
)
}
/**
* Holds if this can reference a constant named `name` from `scope` using a lookup of `kind`.
*/
cached
private predicate accesses(TConstLookupScope scope, string name) {
forceCachingInSameStage() and
scope = this.getLookupScope() and
name = this.getName()
or
exists(Module mod |
this.getExactTarget() = mod.getNestedModule(name) and
scope = MkExactLookup(mod)
)
}
/**
* Gets a constant reference that may resolve to a member of this node.
*
* For example `DataFlow::getConst("A").getConst("B")` finds the following:
* ```rb
* A::B # simple reference
*
* module A
* B # in scope
* module X
* B # in nested scope
* end
* end
*
* module X
* include A
* B # via inclusion
* end
*
* class X < A
* B # via subclassing
* end
* ```
*/
pragma[inline]
ConstRef getConst(string name) {
exists(TConstLookupScope scope |
pragma[only_bind_into](scope) = pragma[only_bind_out](this).getATargetScope() and
result.accesses(pragma[only_bind_out](scope), name)
)
}
/**
* Gets a module that transitively subclasses, includes, or prepends the module referred to by
* this constant.
*
* For example, `DataFlow::getConst("A").getADescendentModule()` finds `B`, `C`, and `E`:
* ```rb
* class B < A
* end
*
* class C < B
* end
*
* module E
* include C
* end
* ```
*/
ModuleNode getADescendentModule() { MkAncestorLookup(result) = this.getATargetScope() }
}
/**
* Gets a constant reference that may resolve to the top-level constant `name`.
*
* To get nested constants, call `getConst()` one or more times on the result.
*
* For example `DataFlow::getConst("A").getConst("B")` finds the following:
* ```rb
* A::B # simple reference
*
* module A
* B # in scope
* module X
* B # in nested scope
* end
* end
*
* module X
* include A
* B # via inclusion
* end
*
* class X < A
* B # via subclassing
* end
* ```
*/
pragma[nomagic]
ConstRef getConst(string name) {
result.getName() = name and
result.isPossiblyGlobal()
}

View File

@@ -115,8 +115,8 @@ private module Cached {
*/
cached
predicate localTaintStepCached(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
defaultAdditionalTaintStep(nodeFrom, nodeTo)
or
DataFlow::localFlowStep(nodeFrom, nodeTo) or
defaultAdditionalTaintStep(nodeFrom, nodeTo) or
// Simple flow through library code is included in the exposed local
// step relation, even though flow is technically inter-procedural
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _)

View File

@@ -24,7 +24,7 @@ deprecated class ParamsCall = Rails::ParamsCall;
deprecated class CookiesCall = Rails::CookiesCall;
/**
* A `ClassDeclaration` for a class that extends `ActionController::Base`.
* A class that extends `ActionController::Base`.
* For example,
*
* ```rb
@@ -36,26 +36,61 @@ deprecated class CookiesCall = Rails::CookiesCall;
* end
* ```
*/
class ActionControllerControllerClass extends ClassDeclaration {
ActionControllerControllerClass() {
this.getSuperclassExpr() =
class ActionControllerClass extends DataFlow::ClassNode {
ActionControllerClass() {
this =
[
API::getTopLevelMember("ActionController").getMember("Base"),
DataFlow::getConst("ActionController").getConst("Base"),
// In Rails applications `ApplicationController` typically extends `ActionController::Base`, but we
// treat it separately in case the `ApplicationController` definition is not in the database.
API::getTopLevelMember("ApplicationController"),
DataFlow::getConst("ApplicationController"),
// ActionController::Metal technically doesn't contain all of the
// methods available in Base, such as those for rendering views.
// However we prefer to be over-sensitive in this case in order to find
// more results.
API::getTopLevelMember("ActionController").getMember("Metal")
].getASubclass().getAValueReachableFromSource().asExpr().getExpr()
DataFlow::getConst("ActionController").getConst("Metal")
].getADescendentModule()
}
/**
* Gets an `ActionControllerActionMethod` defined in this class.
*/
ActionControllerActionMethod getAnAction() {
result = this.getAnInstanceMethod().asCallableAstNode()
}
/**
* Gets a `self` that possibly refers to an instance of this class.
*/
DataFlow::LocalSourceNode getSelf() {
result = this.getAnInstanceSelf()
or
// Include the module-level `self` to recover some cases where a block at the module level
// is invoked with an instance as the `self`, which we currently can't model directly.
// Concretely this happens in the block passed to `rescue_from`.
// TODO: revisit when we have better infrastructure for handling self in a block
result = this.getModuleLevelSelf()
}
}
private DataFlow::LocalSourceNode actionControllerInstance() {
result = any(ActionControllerClass cls).getSelf()
}
/**
* DEPRECATED. Use `ActionControllerClass` instead.
*
* A `ClassDeclaration` corresponding to an `ActionControllerClass`.
*/
deprecated class ActionControllerControllerClass extends ClassDeclaration {
ActionControllerControllerClass() { this = any(ActionControllerClass cls).getADeclaration() }
/**
* Gets a `ActionControllerActionMethod` defined in this class.
*/
ActionControllerActionMethod getAnAction() { result = this.getAMethod() }
ActionControllerActionMethod getAnAction() {
result = this.getAMethod().(Method) and result.isPrivate()
}
}
/**
@@ -63,9 +98,11 @@ class ActionControllerControllerClass extends ClassDeclaration {
* This may be the target of a route handler, if such a route is defined.
*/
class ActionControllerActionMethod extends Method, Http::Server::RequestHandler::Range {
private ActionControllerControllerClass controllerClass;
private ActionControllerClass controllerClass;
ActionControllerActionMethod() { this = controllerClass.getAMethod() and not this.isPrivate() }
ActionControllerActionMethod() {
this = controllerClass.getAnInstanceMethod().asCallableAstNode() and not this.isPrivate()
}
/**
* Establishes a mapping between a method within the file
@@ -91,7 +128,7 @@ class ActionControllerActionMethod extends Method, Http::Server::RequestHandler:
/**
* Gets the controller class containing this method.
*/
ActionControllerControllerClass getControllerClass() {
ActionControllerClass getControllerClass() {
// TODO: model the implicit render call when a path through the method does
// not end at an explicit render or redirect
result = controllerClass
@@ -102,37 +139,23 @@ class ActionControllerActionMethod extends Method, Http::Server::RequestHandler:
* May return multiple results.
*/
ActionDispatch::Routing::Route getARoute() {
exists(string name |
exists(string name, DataFlow::MethodNode m |
isRoute(result, name, controllerClass) and
isActionControllerMethod(this, name, controllerClass)
m = controllerClass.getInstanceMethod(name) and
this = m.asCallableAstNode()
)
}
}
pragma[nomagic]
private predicate isRoute(
ActionDispatch::Routing::Route route, string name, ActionControllerControllerClass controllerClass
ActionDispatch::Routing::Route route, string name, ActionControllerClass controllerClass
) {
route.getController() + "_controller" =
ActionDispatch::Routing::underscore(controllerClass.getAQualifiedName()) and
ActionDispatch::Routing::underscore(controllerClass.getQualifiedName()) and
name = route.getAction()
}
// A method call with a `self` receiver from within a controller class
private class ActionControllerContextCall extends MethodCall {
private ActionControllerControllerClass controllerClass;
ActionControllerContextCall() {
this.getReceiver() instanceof SelfVariableAccess and
this.getEnclosingModule() = controllerClass
}
/**
* Gets the controller class containing this method.
*/
ActionControllerControllerClass getControllerClass() { result = controllerClass }
}
/**
* A `RemoteFlowSource::Range` to represent accessing the
* ActionController parameters available via the `params` method.
@@ -158,13 +181,17 @@ class CookiesSource extends Http::Server::RequestInputAccess::Range {
}
/** A call to `cookies` from within a controller. */
private class ActionControllerCookiesCall extends ActionControllerContextCall, CookiesCallImpl {
ActionControllerCookiesCall() { this.getMethodName() = "cookies" }
private class ActionControllerCookiesCall extends CookiesCallImpl {
ActionControllerCookiesCall() {
this = actionControllerInstance().getAMethodCall("cookies").asExpr().getExpr()
}
}
/** A call to `params` from within a controller. */
private class ActionControllerParamsCall extends ActionControllerContextCall, ParamsCallImpl {
ActionControllerParamsCall() { this.getMethodName() = "params" }
private class ActionControllerParamsCall extends ParamsCallImpl {
ActionControllerParamsCall() {
this = actionControllerInstance().getAMethodCall("params").asExpr().getExpr()
}
}
/** Modeling for `ActionDispatch::Request`. */
@@ -174,10 +201,7 @@ private module Request {
* `ActionDispatch::Request`.
*/
private class RequestNode extends DataFlow::CallNode {
RequestNode() {
this.asExpr().getExpr() instanceof ActionControllerContextCall and
this.getMethodName() = "request"
}
RequestNode() { this = actionControllerInstance().getAMethodCall("request") }
}
/**
@@ -234,7 +258,7 @@ private module Request {
// Request headers are prefixed with `HTTP_` to distinguish them from
// "headers" supplied by Rack middleware.
this.getMethodName() = ["get_header", "fetch_header"] and
this.getArgument(0).asExpr().getExpr().getConstantValue().getString().regexpMatch("^HTTP_.+")
this.getArgument(0).getConstantValue().getString().regexpMatch("^HTTP_.+")
}
override Http::Server::RequestInputKind getKind() { result = Http::Server::headerInputKind() }
@@ -290,9 +314,8 @@ private module Request {
*/
private class EnvHttpAccess extends DataFlow::CallNode, Http::Server::RequestInputAccess::Range {
EnvHttpAccess() {
any(EnvCall c).(DataFlow::LocalSourceNode).flowsTo(this.getReceiver()) and
this.getMethodName() = "[]" and
this.getArgument(0).asExpr().getExpr().getConstantValue().getString().regexpMatch("^HTTP_.+")
this = any(EnvCall c).getAMethodCall("[]") and
this.getArgument(0).getConstantValue().getString().regexpMatch("^HTTP_.+")
}
override Http::Server::RequestInputKind getKind() { result = Http::Server::headerInputKind() }
@@ -302,21 +325,32 @@ private module Request {
}
/** A call to `render` from within a controller. */
private class ActionControllerRenderCall extends ActionControllerContextCall, RenderCallImpl {
ActionControllerRenderCall() { this.getMethodName() = "render" }
private class ActionControllerRenderCall extends RenderCallImpl {
ActionControllerRenderCall() {
this = actionControllerInstance().getAMethodCall("render").asExpr().getExpr()
}
}
/** A call to `render_to` from within a controller. */
private class ActionControllerRenderToCall extends ActionControllerContextCall, RenderToCallImpl {
ActionControllerRenderToCall() { this.getMethodName() = ["render_to_body", "render_to_string"] }
private class ActionControllerRenderToCall extends RenderToCallImpl {
ActionControllerRenderToCall() {
this =
actionControllerInstance()
.getAMethodCall(["render_to_body", "render_to_string"])
.asExpr()
.getExpr()
}
}
/** A call to `html_escape` from within a controller. */
private class ActionControllerHtmlEscapeCall extends HtmlEscapeCallImpl {
ActionControllerHtmlEscapeCall() {
// "h" is aliased to "html_escape" in ActiveSupport
this.getMethodName() = ["html_escape", "html_escape_once", "h", "sanitize"] and
this.getEnclosingModule() instanceof ActionControllerControllerClass
this =
actionControllerInstance()
.getAMethodCall(["html_escape", "html_escape_once", "h", "sanitize"])
.asExpr()
.getExpr()
}
}
@@ -324,9 +358,16 @@ private class ActionControllerHtmlEscapeCall extends HtmlEscapeCallImpl {
* A call to the `redirect_to` method, used in an action to redirect to a
* specific URL/path or to a different action in this controller.
*/
class RedirectToCall extends ActionControllerContextCall {
class RedirectToCall extends MethodCall {
private ActionControllerClass controller;
RedirectToCall() {
this.getMethodName() = ["redirect_to", "redirect_back", "redirect_back_or_to"]
this =
controller
.getSelf()
.getAMethodCall(["redirect_to", "redirect_back", "redirect_back_or_to"])
.asExpr()
.getExpr()
}
/** Gets the `Expr` representing the URL to redirect to, if any */
@@ -338,8 +379,10 @@ class RedirectToCall extends ActionControllerContextCall {
/** Gets the `ActionControllerActionMethod` to redirect to, if any */
ActionControllerActionMethod getRedirectActionMethod() {
this.getKeywordArgument("action").getConstantValue().isStringlikeValue(result.getName()) and
result.getEnclosingModule() = this.getControllerClass()
exists(string name |
this.getKeywordArgument("action").getConstantValue().isStringlikeValue(name) and
result = controller.getInstanceMethod(name).asCallableAstNode()
)
}
/**
@@ -373,18 +416,8 @@ class ActionControllerRedirectResponse extends Http::Server::HttpRedirectRespons
}
pragma[nomagic]
private predicate isActionControllerMethod(Method m, string name, ActionControllerControllerClass c) {
m.getName() = name and
m.getEnclosingModule() = c
}
pragma[nomagic]
private predicate actionControllerHasHelperMethodCall(ActionControllerControllerClass c, string name) {
exists(MethodCall mc |
mc.getMethodName() = "helper_method" and
mc.getAnArgument().getConstantValue().isStringlikeValue(name) and
mc.getEnclosingModule() = c
)
private predicate actionControllerHasHelperMethodCall(ActionControllerClass c, string name) {
c.getAModuleLevelCall("helper_method").getArgument(_).getConstantValue().isStringlikeValue(name)
}
/**
@@ -404,27 +437,28 @@ private predicate actionControllerHasHelperMethodCall(ActionControllerController
* See also https://api.rubyonrails.org/classes/AbstractController/Helpers/ClassMethods.html#method-i-helper_method
*/
class ActionControllerHelperMethod extends Method {
private ActionControllerControllerClass controllerClass;
private ActionControllerClass controllerClass;
ActionControllerHelperMethod() {
exists(string name |
isActionControllerMethod(this, name, controllerClass) and
actionControllerHasHelperMethodCall(controllerClass, name)
exists(DataFlow::MethodNode m, string name |
m = controllerClass.getInstanceMethod(name) and
actionControllerHasHelperMethodCall(controllerClass, name) and
this = m.asCallableAstNode()
)
}
/** Gets the class containing this helper method. */
ActionControllerControllerClass getControllerClass() { result = controllerClass }
ActionControllerClass getControllerClass() { result = controllerClass }
}
/**
* Gets an `ActionControllerControllerClass` associated with the given `ErbFile`
* Gets an `ActionControllerClass` associated with the given `ErbFile`
* according to Rails path conventions.
* For instance, a template file at `app/views/foo/bar/baz.html.erb` will be
* mapped to a controller class in `app/controllers/foo/bar/baz_controller.rb`,
* if such a controller class exists.
*/
ActionControllerControllerClass getAssociatedControllerClass(ErbFile f) {
ActionControllerClass getAssociatedControllerClass(ErbFile f) {
// There is a direct mapping from template file to controller class
controllerTemplateFile(result, f)
or
@@ -442,13 +476,13 @@ ActionControllerControllerClass getAssociatedControllerClass(ErbFile f) {
// TODO: improve layout support, e.g. for `layout` method
// https://guides.rubyonrails.org/layouts_and_rendering.html
/**
* Holds if `templatesFile` is a viable file "belonging" to the given
* Holds if `templateFile` is a viable file "belonging" to the given
* `ActionControllerControllerClass`, according to Rails conventions.
*
* This handles mappings between controllers in `app/controllers/`, and
* templates in `app/views/` and `app/views/layouts/`.
*/
predicate controllerTemplateFile(ActionControllerControllerClass cls, ErbFile templateFile) {
predicate controllerTemplateFile(ActionControllerClass cls, ErbFile templateFile) {
exists(string templatesPath, string sourcePrefix, string subPath, string controllerPath |
controllerPath = cls.getLocation().getFile().getRelativePath() and
templatesPath = templateFile.getParentContainer().getRelativePath() and
@@ -484,16 +518,14 @@ class ActionControllerSkipForgeryProtectionCall extends CsrfProtectionSetting::R
/**
* A call to `protect_from_forgery`.
*/
private class ActionControllerProtectFromForgeryCall extends CsrfProtectionSetting::Range {
private ActionControllerContextCall callExpr;
private class ActionControllerProtectFromForgeryCall extends CsrfProtectionSetting::Range,
DataFlow::CallNode {
ActionControllerProtectFromForgeryCall() {
callExpr = this.asExpr().getExpr() and
callExpr.getMethodName() = "protect_from_forgery"
this = actionControllerInstance().getAMethodCall("protect_from_forgery")
}
private string getWithValueText() {
result = callExpr.getKeywordArgument("with").getConstantValue().getSymbol()
result = this.getKeywordArgument("with").getConstantValue().getSymbol()
}
// Calls without `with: :exception` can allow for bypassing CSRF protection
@@ -508,11 +540,7 @@ private class ActionControllerProtectFromForgeryCall extends CsrfProtectionSetti
*/
private class SendFile extends FileSystemAccess::Range, DataFlow::CallNode {
SendFile() {
this.getMethodName() = "send_file" and
(
this.asExpr().getExpr() instanceof ActionControllerContextCall or
this.getReceiver().asExpr().getExpr() instanceof Response::ResponseCall
)
this = [actionControllerInstance(), Response::response()].getAMethodCall("send_file")
}
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
@@ -522,21 +550,13 @@ private module ParamsSummaries {
private import codeql.ruby.dataflow.FlowSummary
/**
* An instance of `ActionController::Parameters`, including those returned
* Gets an instance of `ActionController::Parameters`, including those returned
* from method calls on other instances.
*/
private class ParamsInstance extends DataFlow::Node {
ParamsInstance() {
this.asExpr().getExpr() instanceof Rails::ParamsCall
or
this =
any(DataFlow::CallNode call |
call.getReceiver() instanceof ParamsInstance and
call.getMethodName() = paramsMethodReturningParamsInstance()
)
or
exists(ParamsInstance prev | prev.(DataFlow::LocalSourceNode).flowsTo(this))
}
private DataFlow::LocalSourceNode paramsInstance() {
result.asExpr().getExpr() instanceof Rails::ParamsCall
or
result = paramsInstance().getAMethodCall(paramsMethodReturningParamsInstance())
}
/**
@@ -578,8 +598,7 @@ private module ParamsSummaries {
MethodsReturningParamsInstanceSummary() { this = "ActionController::Parameters#<various>" }
override MethodCall getACall() {
any(ParamsInstance i).asExpr().getExpr() = result.getReceiver() and
result.getMethodName() = methodReturnsTaintFromSelf()
result = paramsInstance().getAMethodCall(methodReturnsTaintFromSelf()).asExpr().getExpr()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
@@ -601,9 +620,8 @@ private module ParamsSummaries {
override MethodCall getACall() {
result.getMethodName() = ["merge", "reverse_merge", "with_defaults"] and
exists(ParamsInstance i |
i.asExpr().getExpr() = [result.getReceiver(), result.getArgument(0)]
)
paramsInstance().getALocalUse().asExpr().getExpr() =
[result.getReceiver(), result.getArgument(0)]
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
@@ -625,9 +643,8 @@ private module ParamsSummaries {
override MethodCall getACall() {
result.getMethodName() = ["merge!", "reverse_merge!", "with_defaults!"] and
exists(ParamsInstance i |
i.asExpr().getExpr() = [result.getReceiver(), result.getArgument(0)]
)
paramsInstance().getALocalUse().asExpr().getExpr() =
[result.getReceiver(), result.getArgument(0)]
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
@@ -643,15 +660,12 @@ private module ParamsSummaries {
* response.
*/
private module Response {
class ResponseCall extends ActionControllerContextCall {
ResponseCall() { this.getMethodName() = "response" }
DataFlow::LocalSourceNode response() {
result = actionControllerInstance().getAMethodCall("response")
}
class BodyWrite extends DataFlow::CallNode, Http::Server::HttpResponse::Range {
BodyWrite() {
this.getReceiver().asExpr().getExpr() instanceof ResponseCall and
this.getMethodName() = "body="
}
BodyWrite() { this = response().getAMethodCall("body=") }
override DataFlow::Node getBody() { result = this.getArgument(0) }
@@ -661,10 +675,7 @@ private module Response {
}
class SendFileCall extends DataFlow::CallNode, Http::Server::HttpResponse::Range {
SendFileCall() {
this.getReceiver().asExpr().getExpr() instanceof ResponseCall and
this.getMethodName() = "send_file"
}
SendFileCall() { this = response().getAMethodCall("send_file") }
override DataFlow::Node getBody() { result = this.getArgument(0) }
@@ -677,19 +688,12 @@ private module Response {
HeaderWrite() {
// response.header[key] = val
// response.headers[key] = val
exists(MethodCall headerCall |
headerCall.getMethodName() = ["header", "headers"] and
headerCall.getReceiver() instanceof ResponseCall
|
this.getReceiver().asExpr().getExpr() = headerCall and
this.getMethodName() = "[]="
)
this = response().getAMethodCall(["header", "headers"]).getAMethodCall("[]=")
or
// response.set_header(key) = val
// response[header] = val
// response.add_header(key, val)
this.getReceiver().asExpr().getExpr() instanceof ResponseCall and
this.getMethodName() = ["set_header", "[]=", "add_header"]
this = response().getAMethodCall(["set_header", "[]=", "add_header"])
}
override string getName() {
@@ -702,12 +706,12 @@ private module Response {
class SpecificHeaderWrite extends DataFlow::CallNode, Http::Server::HeaderWriteAccess::Range {
SpecificHeaderWrite() {
// response.<method> = val
this.getReceiver().asExpr().getExpr() instanceof ResponseCall and
this.getMethodName() =
[
"location=", "cache_control=", "_cache_control=", "etag=", "charset=", "content_type=",
"date=", "last_modified=", "weak_etag=", "strong_etag="
]
this =
response()
.getAMethodCall([
"location=", "cache_control=", "_cache_control=", "etag=", "charset=",
"content_type=", "date=", "last_modified=", "weak_etag=", "strong_etag="
])
}
override string getName() {

View File

@@ -571,9 +571,7 @@ class ActiveRecordAssociation extends DataFlow::CallNode {
* For example, in `has_many :posts`, this is `post`.
*/
string getTargetModelName() {
exists(string s |
s = this.getArgument(0).asExpr().getExpr().getConstantValue().getStringlikeValue()
|
exists(string s | s = this.getArgument(0).getConstantValue().getStringlikeValue() |
// has_one :profile
// belongs_to :user
this.isSingular() and

View File

@@ -182,3 +182,39 @@ module FileUtils {
override DataFlow::Node getAPermissionNode() { result = permissionArg }
}
}
/**
* Classes and predicates for modeling the core `Dir` module.
*/
module Dir {
/**
* A call to a method on `Dir` that operates on a path as its first argument, and produces file-names.
* Considered as a `FileNameSource` and a `FileSystemAccess`.
*/
class DirGlob extends FileSystemAccess::Range, FileNameSource instanceof DataFlow::CallNode {
DirGlob() {
this =
API::getTopLevelMember("Dir")
.getAMethodCall(["glob", "[]", "children", "each_child", "entries", "foreach"])
}
override DataFlow::Node getAPathArgument() { result = super.getArgument(0) }
}
/**
* A call to a method on `Dir` that operates on a path as its first argument, considered as a `FileSystemAccess`.
*/
class DirPathAccess extends FileSystemAccess::Range instanceof DataFlow::CallNode {
DirPathAccess() {
this =
API::getTopLevelMember("Dir")
.getAMethodCall([
"chdir", "chroot", "delete", "empty?", "exist?", "exists?", "mkdir", "new", "open",
"rmdir", "unlink"
])
}
override DataFlow::Node getAPathArgument() { result = super.getArgument(0) }
}
// TODO: Model that `(Dir.new "foo").each { |f| ... }` yields a filename (and some other public methods)
}

View File

@@ -92,102 +92,63 @@ module Rails {
}
/**
* A reference to either `Rails::Railtie`, `Rails::Engine`, or `Rails::Application`.
* Gets a reference to the `Rails` constant.
*/
private DataFlow::ConstRef rails() { result = DataFlow::getConst("Rails") }
/**
* Gets a reference to either `Rails::Railtie`, `Rails::Engine`, or `Rails::Application`.
* `Engine` and `Application` extend `Railtie`, but may not have definitions present in the database.
*/
private class RailtieClassAccess extends ConstantReadAccess {
RailtieClassAccess() {
this.getScopeExpr().(ConstantAccess).getName() = "Rails" and
this.getName() = ["Railtie", "Engine", "Application"]
}
private DataFlow::ConstRef railtie() {
result = rails().getConst(["Railtie", "Engine", "Application"])
}
// A `ClassDeclaration` that (transitively) extends `Rails::Railtie`
private class RailtieClass extends ClassDeclaration {
RailtieClass() {
this.getSuperclassExpr() instanceof RailtieClassAccess or
exists(RailtieClass other |
other.getModule() = this.getSuperclassExpr().(ConstantReadAccess).getModule()
)
}
}
/** Gets a class that transitively extends `Rails::Railtie` */
private DataFlow::ClassNode railtieClass() { result = railtie().getADescendentModule() }
private DataFlow::CallNode getAConfigureCallNode() {
// `Rails.application.configure`
result = API::getTopLevelMember("Rails").getReturn("application").getAMethodCall("configure")
/**
* Gets a reference to `Rails::Application` or `Rails.application`.
*/
private DataFlow::LocalSourceNode railsApp() {
result = rails().getAMethodCall("application")
or
// `Rails::Application.configure`
exists(ConstantReadAccess read, RailtieClass cls |
read = result.getReceiver().asExpr().getExpr() and
read.getModule() = cls.getModule() and
result.asExpr().getExpr().(MethodCall).getMethodName() = "configure"
)
result = rails().getConst("Application")
}
/**
* Classes representing accesses to the Rails config object.
*/
private module Config {
/**
* An access to a Rails config object.
*/
private class SourceNode extends DataFlow::LocalSourceNode {
SourceNode() {
// `Foo < Rails::Application ... config ...`
exists(MethodCall configCall | this.asExpr().getExpr() = configCall |
configCall.getMethodName() = "config" and
configCall.getEnclosingModule() instanceof RailtieClass
)
or
// `Rails.application.config`
this = API::getTopLevelMember("Rails").getReturn("application").getReturn("config").asSource()
or
// `Rails.application.configure { ... config ... }`
// `Rails::Application.configure { ... config ... }`
exists(DataFlow::CallNode configureCallNode, Block block, MethodCall configCall |
configCall = this.asExpr().getExpr()
|
configureCallNode = getAConfigureCallNode() and
block = configureCallNode.asExpr().getExpr().(MethodCall).getBlock() and
configCall.getParent+() = block and
configCall.getMethodName() = "config"
)
}
DataFlow::LocalSourceNode configSource() {
// `Foo < Rails::Application ... config ...`
result = railtieClass().getAnOwnModuleSelf().getAMethodCall("config")
or
// `Rails.application.config`
result = railsApp().getAMethodCall("config")
or
// TODO: move away from getParent+() when have better infrastructure for overridden 'self' in blocks
// `Rails.application.configure { ... config ... }`
// `Rails::Application.configure { ... config ... }`
exists(Block block, MethodCall configCall | configCall = result.asExpr().getExpr() |
block = railsApp().getAMethodCall("configure").getBlock().asExpr().getExpr() and
configCall.getParent+() = block and
configCall.getMethodName() = "config"
)
}
/**
* A reference to the Rails config object.
* Gets a reference to the ActionController config object.
*/
class Node extends DataFlow::Node {
Node() { exists(SourceNode src | src.flowsTo(this)) }
DataFlow::LocalSourceNode actionController() {
result = configSource().getAMethodCall("action_controller")
}
/**
* A reference to the ActionController config object.
* Gets a reference to the ActionDispatch config object.
*/
class ActionControllerNode extends DataFlow::Node {
ActionControllerNode() {
exists(DataFlow::CallNode source |
source.getReceiver() instanceof Node and
source.getMethodName() = "action_controller"
|
source.flowsTo(this)
)
}
}
/**
* A reference to the ActionDispatch config object.
*/
class ActionDispatchNode extends DataFlow::Node {
ActionDispatchNode() {
exists(DataFlow::CallNode source |
source.getReceiver() instanceof Node and
source.getMethodName() = "action_dispatch"
|
source.flowsTo(this)
)
}
DataFlow::LocalSourceNode actionDispatch() {
result = configSource().getAMethodCall("action_dispatch")
}
}
@@ -200,24 +161,18 @@ private module Settings {
loc.getFile().getStem() = "test"
}
private class Setting extends DataFlow::CallNode {
private class Setting extends DataFlow::SetterCallNode {
Setting() {
// exclude some test configuration
not isInTestConfiguration(this.getLocation()) and
this.getReceiver+() instanceof Config::Node and
this.asExpr().getExpr() instanceof SetterMethodCall
this = Config::configSource().getAMethodCall+()
}
}
private class LiteralSetting extends Setting {
ConstantValue value;
LiteralSetting() {
exists(DataFlow::LocalSourceNode lsn |
lsn.asExpr().getConstantValue() = value and
lsn.flowsTo(this.getArgument(0))
)
}
LiteralSetting() { value = this.getArgument(0).getALocalSource().getConstantValue() }
string getValueText() { result = value.toString() }
@@ -262,8 +217,7 @@ private module Settings {
private class AllowForgeryProtectionSetting extends Settings::BooleanSetting,
CsrfProtectionSetting::Range {
AllowForgeryProtectionSetting() {
this.getReceiver() instanceof Config::ActionControllerNode and
this.getMethodName() = "allow_forgery_protection="
this = Config::actionController().getAMethodCall("allow_forgery_protection=")
}
override boolean getVerificationSetting() { result = this.getValue() }
@@ -277,8 +231,7 @@ private class AllowForgeryProtectionSetting extends Settings::BooleanSetting,
private class EncryptedCookieCipherSetting extends Settings::StringlikeSetting,
CookieSecurityConfigurationSetting::Range {
EncryptedCookieCipherSetting() {
this.getReceiver() instanceof Config::ActionDispatchNode and
this.getMethodName() = "encrypted_cookie_cipher="
this = Config::actionDispatch().getAMethodCall("encrypted_cookie_cipher=")
}
OpenSslCipher getCipher() { this.getValueText() = result.getName() }
@@ -298,8 +251,7 @@ private class EncryptedCookieCipherSetting extends Settings::StringlikeSetting,
private class UseAuthenticatedCookieEncryptionSetting extends Settings::BooleanSetting,
CookieSecurityConfigurationSetting::Range {
UseAuthenticatedCookieEncryptionSetting() {
this.getReceiver() instanceof Config::ActionDispatchNode and
this.getMethodName() = "use_authenticated_cookie_encryption="
this = Config::actionDispatch().getAMethodCall("use_authenticated_cookie_encryption=")
}
boolean getDefaultValue() { result = true }
@@ -321,8 +273,7 @@ private class UseAuthenticatedCookieEncryptionSetting extends Settings::BooleanS
private class CookiesSameSiteProtectionSetting extends Settings::NillableStringlikeSetting,
CookieSecurityConfigurationSetting::Range {
CookiesSameSiteProtectionSetting() {
this.getReceiver() instanceof Config::ActionDispatchNode and
this.getMethodName() = "cookies_same_site_protection="
this = Config::actionDispatch().getAMethodCall("cookies_same_site_protection=")
}
string getDefaultValue() { result = "lax" }

View File

@@ -12,26 +12,15 @@ private import codeql.ruby.DataFlow
* Modeling for `railties`.
*/
module Railties {
private class IncludeOrPrependCall extends MethodCall {
IncludeOrPrependCall() { this.getMethodName() = ["include", "prepend"] }
private DataFlow::ConstRef generatorsActionsConst() {
result = DataFlow::getConst("Rails").getConst("Generators").getConst("Actions")
}
/**
* A class which `include`s `Rails::Generators::Actions`.
* Gets a class which is a descendent of `Rails::Generators::Actions`.
*/
private class GeneratorsActionsContext extends ClassDeclaration {
GeneratorsActionsContext() {
exists(IncludeOrPrependCall i |
i.getEnclosingModule() = this and
i.getArgument(0) =
API::getTopLevelMember("Rails")
.getMember("Generators")
.getMember("Actions")
.getAValueReachableFromSource()
.asExpr()
.getExpr()
)
}
private DataFlow::ClassNode generatorsActionsClass() {
result = generatorsActionsConst().getADescendentModule()
}
/**
@@ -40,8 +29,7 @@ module Railties {
*/
private class ExecuteCommandCall extends SystemCommandExecution::Range, DataFlow::CallNode {
ExecuteCommandCall() {
this.asExpr().getExpr().getEnclosingModule() instanceof GeneratorsActionsContext and
this.getMethodName() = "execute_command"
this = generatorsActionsClass().getAnInstanceSelf().getAMethodCall("execute_command")
}
override DataFlow::Node getAnArgument() { result = this.getArgument([0, 1]) }
@@ -54,8 +42,10 @@ module Railties {
*/
private class ExecuteCommandWrapperCall extends SystemCommandExecution::Range, DataFlow::CallNode {
ExecuteCommandWrapperCall() {
this.asExpr().getExpr().getEnclosingModule() instanceof GeneratorsActionsContext and
this.getMethodName() = ["rake", "rails_command", "git"]
this =
generatorsActionsClass()
.getAnInstanceSelf()
.getAMethodCall(["rake", "rails_command", "git"])
}
override DataFlow::Node getAnArgument() { result = this.getArgument(0) }

View File

@@ -88,11 +88,7 @@ module UnsafeDeserialization {
private predicate isOjModePair(CfgNodes::ExprNodes::PairCfgNode p, string modeValue) {
p.getKey().getConstantValue().isStringlikeValue("mode") and
exists(DataFlow::LocalSourceNode symbolLiteral, DataFlow::Node value |
symbolLiteral.asExpr().getExpr().getConstantValue().isSymbol(modeValue) and
symbolLiteral.flowsTo(value) and
value.asExpr() = p.getValue()
)
DataFlow::exprNode(p.getValue()).getALocalSource().getConstantValue().isSymbol(modeValue)
}
/**

View File

@@ -180,11 +180,10 @@ private module Shared {
private predicate isFlowFromLocals0(
CfgNodes::ExprNodes::ElementReferenceCfgNode refNode, string hashKey, ErbFile erb
) {
exists(DataFlow::Node argNode, CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode |
exists(DataFlow::Node argNode |
argNode.asExpr() = refNode.getArgument(0) and
refNode.getReceiver().getExpr().(MethodCall).getMethodName() = "local_assigns" and
argNode.getALocalSource() = DataFlow::exprNode(strNode) and
strNode.getExpr().getConstantValue().isStringlikeValue(hashKey) and
argNode.getALocalSource().getConstantValue().isStringlikeValue(hashKey) and
erb = refNode.getFile()
)
}

View File

@@ -1,5 +1,5 @@
name: codeql/ruby-all
version: 0.4.2
version: 0.4.3-dev
groups: ruby
extractor: ruby
dbscheme: ruby.dbscheme

View File

@@ -17,18 +17,11 @@ import codeql.ruby.frameworks.ActionController
import DataFlow::PathGraph
/**
* A call to `request` in an ActionController controller class.
* Gets a call to `request` in an ActionController controller class.
* This probably refers to the incoming HTTP request object.
*/
class ActionControllerRequest extends DataFlow::Node {
ActionControllerRequest() {
exists(DataFlow::CallNode c |
c.asExpr().getExpr().getEnclosingModule() instanceof ActionControllerControllerClass and
c.getMethodName() = "request"
|
c.flowsTo(this)
)
}
DataFlow::LocalSourceNode request() {
result = any(ActionControllerClass cls).getSelf().getAMethodCall("request")
}
/**
@@ -36,9 +29,11 @@ class ActionControllerRequest extends DataFlow::Node {
*/
class WeakParams extends DataFlow::CallNode {
WeakParams() {
this.getReceiver() instanceof ActionControllerRequest and
this.getMethodName() =
["path_parameters", "query_parameters", "request_parameters", "GET", "POST"]
this =
request()
.getAMethodCall([
"path_parameters", "query_parameters", "request_parameters", "GET", "POST"
])
}
}

View File

@@ -1,5 +1,5 @@
name: codeql/ruby-queries
version: 0.4.2
version: 0.4.3-dev
groups:
- ruby
- queries

View File

@@ -20,7 +20,7 @@ import codeql.ruby.ast.Literal
from AmbiguousPathCall call
where
// there is not a constant string argument
not exists(call.getPathArgument().asExpr().getExpr().getConstantValue()) and
not exists(call.getPathArgument().getConstantValue()) and
// if it's a format string, then the first argument is not a constant string
not call.getPathArgument().getALocalSource().asExpr().getExpr().(StringLiteral).getComponent(0)
instanceof StringTextComponent

View File

@@ -2833,6 +2833,29 @@ operations/operations.rb:
# 96| getStmt: [AssignMulExpr] ... *= ...
# 96| getAnOperand/getLeftOperand: [GlobalVariableAccess] $global_var
# 96| getAnOperand/getRightOperand: [IntegerLiteral] 6
# 98| getStmt: [AssignExpr] ... = ...
# 98| getAnOperand/getLeftOperand: [ConstantAssignment] CONSTANT1
# 98| getAnOperand/getRightOperand: [IntegerLiteral] 5
# 99| getStmt: [AssignAddExpr] ... += ...
# 99| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT2
# 99| getAnOperand/getRightOperand: [IntegerLiteral] 6
# 100| getStmt: [AssignLogicalOrExpr] ... ||= ...
# 100| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT3
# 100| getAnOperand/getRightOperand: [IntegerLiteral] 7
# 101| getStmt: [AssignLogicalOrExpr] ... ||= ...
# 101| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] MemberConstant
# 101| getScopeExpr: [ConstantReadAccess] Foo
# 101| getAnOperand/getRightOperand: [IntegerLiteral] 8
# 102| getStmt: [AssignLogicalOrExpr] ... ||= ...
# 102| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] OtherConstant
# 102| getScopeExpr: [MethodCall] call to bar
# 102| getReceiver: [MethodCall] call to foo
# 102| getReceiver: [SelfVariableAccess] self
# 102| getArgument: [IntegerLiteral] 1
# 102| getAnOperand/getRightOperand: [IntegerLiteral] 7
# 103| getStmt: [AssignLogicalOrExpr] ... ||= ...
# 103| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT4
# 103| getAnOperand/getRightOperand: [IntegerLiteral] 7
params/params.rb:
# 1| [Toplevel] params.rb
# 4| getStmt: [Method] identifier_method_params

View File

@@ -865,6 +865,51 @@ operations/operations.rb:
# 96| getAnOperand/getRightOperand: [MulExpr] ... * ...
# 96| getAnOperand/getLeftOperand/getReceiver: [GlobalVariableAccess] $global_var
# 96| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 6
# 99| [AssignAddExpr] ... += ...
# 99| getDesugared: [AssignExpr] ... = ...
# 99| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT2
# 99| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 99| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] CONSTANT2
# 99| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 6
# 100| [AssignLogicalOrExpr] ... ||= ...
# 100| getDesugared: [AssignExpr] ... = ...
# 100| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT3
# 100| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ...
# 100| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] CONSTANT3
# 100| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 7
# 101| [AssignLogicalOrExpr] ... ||= ...
# 101| getDesugared: [StmtSequence] ...
# 101| getStmt: [AssignExpr] ... = ...
# 101| getAnOperand/getRightOperand: [ConstantReadAccess] Foo
# 101| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 101| getStmt: [AssignExpr] ... = ...
# 101| getAnOperand/getLeftOperand: [ConstantAssignment] MemberConstant
# 101| getScopeExpr: [LocalVariableAccess] __synth__0
# 101| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ...
# 101| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] MemberConstant
# 101| getScopeExpr: [LocalVariableAccess] __synth__0
# 101| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 8
# 102| [AssignLogicalOrExpr] ... ||= ...
# 102| getDesugared: [StmtSequence] ...
# 102| getStmt: [AssignExpr] ... = ...
# 102| getAnOperand/getRightOperand: [MethodCall] call to bar
# 102| getReceiver: [MethodCall] call to foo
# 102| getReceiver: [SelfVariableAccess] self
# 102| getArgument: [IntegerLiteral] 1
# 102| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 102| getStmt: [AssignExpr] ... = ...
# 102| getAnOperand/getLeftOperand: [ConstantAssignment] OtherConstant
# 102| getScopeExpr: [LocalVariableAccess] __synth__0
# 102| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ...
# 102| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] OtherConstant
# 102| getScopeExpr: [LocalVariableAccess] __synth__0
# 102| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 7
# 103| [AssignLogicalOrExpr] ... ||= ...
# 103| getDesugared: [AssignExpr] ... = ...
# 103| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT4
# 103| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ...
# 103| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] CONSTANT4
# 103| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 7
params/params.rb:
# 8| [HashLiteral] {...}
# 8| getDesugared: [MethodCall] call to []

View File

@@ -5537,6 +5537,46 @@ operations/operations.rb:
# 96| 0: [GlobalVariable] $global_var
# 96| 1: [ReservedWord] *=
# 96| 2: [Integer] 6
# 98| 66: [Assignment] Assignment
# 98| 0: [Constant] CONSTANT1
# 98| 1: [ReservedWord] =
# 98| 2: [Integer] 5
# 99| 67: [OperatorAssignment] OperatorAssignment
# 99| 0: [Constant] CONSTANT2
# 99| 1: [ReservedWord] +=
# 99| 2: [Integer] 6
# 100| 68: [OperatorAssignment] OperatorAssignment
# 100| 0: [Constant] CONSTANT3
# 100| 1: [ReservedWord] ||=
# 100| 2: [Integer] 7
# 101| 69: [OperatorAssignment] OperatorAssignment
# 101| 0: [ScopeResolution] ScopeResolution
# 101| 0: [Constant] Foo
# 101| 1: [ReservedWord] ::
# 101| 2: [Constant] MemberConstant
# 101| 1: [ReservedWord] ||=
# 101| 2: [Integer] 8
# 102| 70: [OperatorAssignment] OperatorAssignment
# 102| 0: [ScopeResolution] ScopeResolution
# 102| 0: [Call] Call
# 102| 0: [Call] Call
# 102| 0: [Identifier] foo
# 102| 1: [ArgumentList] ArgumentList
# 102| 0: [ReservedWord] (
# 102| 1: [Integer] 1
# 102| 2: [ReservedWord] )
# 102| 1: [ReservedWord] .
# 102| 2: [Identifier] bar
# 102| 1: [ReservedWord] ::
# 102| 2: [Constant] OtherConstant
# 102| 1: [ReservedWord] ||=
# 102| 2: [Integer] 7
# 103| 71: [OperatorAssignment] OperatorAssignment
# 103| 0: [ScopeResolution] ScopeResolution
# 103| 0: [ReservedWord] ::
# 103| 1: [Constant] CONSTANT4
# 103| 1: [ReservedWord] ||=
# 103| 2: [Integer] 7
# 1| [Comment] # Start with assignments to all the identifiers used below, so that they are
# 2| [Comment] # interpreted as variables.
# 22| [Comment] # Unary operations

View File

@@ -888,6 +888,13 @@ exprValue
| operations/operations.rb:92:10:92:10 | 4 | 4 | int |
| operations/operations.rb:95:15:95:15 | 5 | 5 | int |
| operations/operations.rb:96:16:96:16 | 6 | 6 | int |
| operations/operations.rb:98:13:98:13 | 5 | 5 | int |
| operations/operations.rb:99:14:99:14 | 6 | 6 | int |
| operations/operations.rb:100:15:100:15 | 7 | 7 | int |
| operations/operations.rb:101:25:101:25 | 8 | 8 | int |
| operations/operations.rb:102:5:102:5 | 1 | 1 | int |
| operations/operations.rb:102:31:102:31 | 7 | 7 | int |
| operations/operations.rb:103:17:103:17 | 7 | 7 | int |
| params/params.rb:41:46:41:46 | 7 | 7 | int |
| params/params.rb:47:19:47:21 | :bar | :bar | symbol |
| params/params.rb:47:24:47:24 | 2 | 2 | int |
@@ -1764,6 +1771,13 @@ exprCfgNodeValue
| operations/operations.rb:92:10:92:10 | 4 | 4 | int |
| operations/operations.rb:95:15:95:15 | 5 | 5 | int |
| operations/operations.rb:96:16:96:16 | 6 | 6 | int |
| operations/operations.rb:98:13:98:13 | 5 | 5 | int |
| operations/operations.rb:99:14:99:14 | 6 | 6 | int |
| operations/operations.rb:100:15:100:15 | 7 | 7 | int |
| operations/operations.rb:101:25:101:25 | 8 | 8 | int |
| operations/operations.rb:102:5:102:5 | 1 | 1 | int |
| operations/operations.rb:102:31:102:31 | 7 | 7 | int |
| operations/operations.rb:103:17:103:17 | 7 | 7 | int |
| params/params.rb:41:46:41:46 | 7 | 7 | int |
| params/params.rb:47:19:47:21 | :bar | :bar | symbol |
| params/params.rb:47:24:47:24 | 2 | 2 | int |

View File

@@ -52,6 +52,19 @@ assignments
| operations.rb:95:1:95:15 | ... = ... | = | operations.rb:95:1:95:11 | $global_var | operations.rb:95:15:95:15 | 5 | AssignExpr |
| operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr |
| operations.rb:96:1:96:16 | ... = ... | = | operations.rb:96:1:96:11 | $global_var | operations.rb:96:13:96:14 | ... * ... | AssignExpr |
| operations.rb:98:1:98:13 | ... = ... | = | operations.rb:98:1:98:9 | CONSTANT1 | operations.rb:98:13:98:13 | 5 | AssignExpr |
| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AssignAddExpr |
| operations.rb:99:1:99:14 | ... = ... | = | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:11:99:12 | ... + ... | AssignExpr |
| operations.rb:100:1:100:15 | ... = ... | = | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:11:100:13 | ... \|\| ... | AssignExpr |
| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr |
| operations.rb:101:1:101:3 | ... = ... | = | operations.rb:101:1:101:3 | __synth__0 | operations.rb:101:1:101:3 | Foo | AssignExpr |
| operations.rb:101:1:101:25 | ... = ... | = | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:21:101:23 | ... \|\| ... | AssignExpr |
| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr |
| operations.rb:102:1:102:10 | ... = ... | = | operations.rb:102:1:102:10 | __synth__0 | operations.rb:102:1:102:10 | call to bar | AssignExpr |
| operations.rb:102:1:102:31 | ... = ... | = | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:27:102:29 | ... \|\| ... | AssignExpr |
| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr |
| operations.rb:103:1:103:17 | ... = ... | = | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:13:103:15 | ... \|\| ... | AssignExpr |
| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | AssignLogicalOrExpr |
assignOperations
| operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AssignAddExpr |
| operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | AssignSubExpr |
@@ -69,6 +82,11 @@ assignOperations
| operations.rb:89:3:89:9 | ... += ... | += | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AssignAddExpr |
| operations.rb:92:3:92:10 | ... /= ... | /= | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | AssignDivExpr |
| operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr |
| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AssignAddExpr |
| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr |
| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr |
| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr |
| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | AssignLogicalOrExpr |
assignArithmeticOperations
| operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AssignAddExpr |
| operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | AssignSubExpr |
@@ -79,9 +97,14 @@ assignArithmeticOperations
| operations.rb:89:3:89:9 | ... += ... | += | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AssignAddExpr |
| operations.rb:92:3:92:10 | ... /= ... | /= | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | AssignDivExpr |
| operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr |
| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AssignAddExpr |
assignLogicalOperations
| operations.rb:77:2:77:8 | ... &&= ... | &&= | operations.rb:77:2:77:2 | x | operations.rb:77:8:77:8 | y | AssignLogicalAndExpr |
| operations.rb:78:2:78:8 | ... \|\|= ... | \|\|= | operations.rb:78:2:78:2 | a | operations.rb:78:8:78:8 | b | AssignLogicalOrExpr |
| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr |
| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr |
| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr |
| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | AssignLogicalOrExpr |
assignBitwiseOperations
| operations.rb:81:2:81:8 | ... <<= ... | <<= | operations.rb:81:2:81:2 | x | operations.rb:81:8:81:8 | 2 | AssignLShiftExpr |
| operations.rb:82:2:82:8 | ... >>= ... | >>= | operations.rb:82:2:82:2 | y | operations.rb:82:8:82:8 | 3 | AssignRShiftExpr |

View File

@@ -40,6 +40,11 @@ binaryOperations
| operations.rb:89:6:89:7 | ... + ... | + | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AddExpr |
| operations.rb:92:7:92:8 | ... / ... | / | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | DivExpr |
| operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | MulExpr |
| operations.rb:99:11:99:12 | ... + ... | + | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AddExpr |
| operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | LogicalOrExpr |
| operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | LogicalOrExpr |
| operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | LogicalOrExpr |
| operations.rb:103:13:103:15 | ... \|\| ... | \|\| | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | LogicalOrExpr |
binaryArithmeticOperations
| operations.rb:32:1:32:7 | ... + ... | + | operations.rb:32:1:32:1 | w | operations.rb:32:5:32:7 | 234 | AddExpr |
| operations.rb:33:1:33:6 | ... - ... | - | operations.rb:33:1:33:1 | x | operations.rb:33:5:33:6 | 17 | SubExpr |
@@ -56,6 +61,7 @@ binaryArithmeticOperations
| operations.rb:89:6:89:7 | ... + ... | + | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AddExpr |
| operations.rb:92:7:92:8 | ... / ... | / | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | DivExpr |
| operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | MulExpr |
| operations.rb:99:11:99:12 | ... + ... | + | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AddExpr |
binaryLogicalOperations
| operations.rb:40:1:40:10 | ... && ... | && | operations.rb:40:1:40:3 | foo | operations.rb:40:8:40:10 | bar | LogicalAndExpr |
| operations.rb:41:1:41:11 | ... and ... | and | operations.rb:41:1:41:3 | baz | operations.rb:41:9:41:11 | qux | LogicalAndExpr |
@@ -63,6 +69,10 @@ binaryLogicalOperations
| operations.rb:43:1:43:6 | ... \|\| ... | \|\| | operations.rb:43:1:43:1 | x | operations.rb:43:6:43:6 | y | LogicalOrExpr |
| operations.rb:77:4:77:6 | ... && ... | && | operations.rb:77:2:77:2 | x | operations.rb:77:8:77:8 | y | LogicalAndExpr |
| operations.rb:78:4:78:6 | ... \|\| ... | \|\| | operations.rb:78:2:78:2 | a | operations.rb:78:8:78:8 | b | LogicalOrExpr |
| operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | LogicalOrExpr |
| operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | LogicalOrExpr |
| operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | LogicalOrExpr |
| operations.rb:103:13:103:15 | ... \|\| ... | \|\| | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | LogicalOrExpr |
binaryBitwiseOperations
| operations.rb:46:1:46:6 | ... << ... | << | operations.rb:46:1:46:1 | x | operations.rb:46:6:46:6 | 3 | LShiftExpr |
| operations.rb:47:1:47:7 | ... >> ... | >> | operations.rb:47:1:47:1 | y | operations.rb:47:6:47:7 | 16 | RShiftExpr |

View File

@@ -194,3 +194,39 @@
| operations.rb:96:1:96:16 | ... = ... | = | operations.rb:96:13:96:14 | ... * ... | AssignExpr |
| operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | MulExpr |
| operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:16:96:16 | 6 | MulExpr |
| operations.rb:98:1:98:13 | ... = ... | = | operations.rb:98:1:98:9 | CONSTANT1 | AssignExpr |
| operations.rb:98:1:98:13 | ... = ... | = | operations.rb:98:13:98:13 | 5 | AssignExpr |
| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | AssignAddExpr |
| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:14:99:14 | 6 | AssignAddExpr |
| operations.rb:99:1:99:14 | ... = ... | = | operations.rb:99:1:99:9 | CONSTANT2 | AssignExpr |
| operations.rb:99:1:99:14 | ... = ... | = | operations.rb:99:11:99:12 | ... + ... | AssignExpr |
| operations.rb:99:11:99:12 | ... + ... | + | operations.rb:99:1:99:9 | CONSTANT2 | AddExpr |
| operations.rb:99:11:99:12 | ... + ... | + | operations.rb:99:14:99:14 | 6 | AddExpr |
| operations.rb:100:1:100:15 | ... = ... | = | operations.rb:100:1:100:9 | CONSTANT3 | AssignExpr |
| operations.rb:100:1:100:15 | ... = ... | = | operations.rb:100:11:100:13 | ... \|\| ... | AssignExpr |
| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | AssignLogicalOrExpr |
| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr |
| operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:1:100:9 | CONSTANT3 | LogicalOrExpr |
| operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:15:100:15 | 7 | LogicalOrExpr |
| operations.rb:101:1:101:3 | ... = ... | = | operations.rb:101:1:101:3 | Foo | AssignExpr |
| operations.rb:101:1:101:3 | ... = ... | = | operations.rb:101:1:101:3 | __synth__0 | AssignExpr |
| operations.rb:101:1:101:25 | ... = ... | = | operations.rb:101:1:101:19 | MemberConstant | AssignExpr |
| operations.rb:101:1:101:25 | ... = ... | = | operations.rb:101:21:101:23 | ... \|\| ... | AssignExpr |
| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | AssignLogicalOrExpr |
| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr |
| operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:1:101:19 | MemberConstant | LogicalOrExpr |
| operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:25:101:25 | 8 | LogicalOrExpr |
| operations.rb:102:1:102:10 | ... = ... | = | operations.rb:102:1:102:10 | __synth__0 | AssignExpr |
| operations.rb:102:1:102:10 | ... = ... | = | operations.rb:102:1:102:10 | call to bar | AssignExpr |
| operations.rb:102:1:102:31 | ... = ... | = | operations.rb:102:1:102:25 | OtherConstant | AssignExpr |
| operations.rb:102:1:102:31 | ... = ... | = | operations.rb:102:27:102:29 | ... \|\| ... | AssignExpr |
| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | AssignLogicalOrExpr |
| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr |
| operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:1:102:25 | OtherConstant | LogicalOrExpr |
| operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:31:102:31 | 7 | LogicalOrExpr |
| operations.rb:103:1:103:17 | ... = ... | = | operations.rb:103:1:103:11 | CONSTANT4 | AssignExpr |
| operations.rb:103:1:103:17 | ... = ... | = | operations.rb:103:13:103:15 | ... \|\| ... | AssignExpr |
| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:1:103:11 | CONSTANT4 | AssignLogicalOrExpr |
| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:17:103:17 | 7 | AssignLogicalOrExpr |
| operations.rb:103:13:103:15 | ... \|\| ... | \|\| | operations.rb:103:1:103:11 | CONSTANT4 | LogicalOrExpr |
| operations.rb:103:13:103:15 | ... \|\| ... | \|\| | operations.rb:103:17:103:17 | 7 | LogicalOrExpr |

View File

@@ -94,3 +94,10 @@ end
$global_var = 5
$global_var *= 6
CONSTANT1 = 5
CONSTANT2 += 6
CONSTANT3 ||= 7
Foo::MemberConstant ||= 8
foo(1).bar::OtherConstant ||= 7
::CONSTANT4 ||= 7

View File

@@ -3762,6 +3762,81 @@ cfg.rb:
# 215| self
#-----| -> call to something_else
constant_compound_assign.rb:
# 1| Foo
#-----| -> foo_before
# 1| enter constant_compound_assign.rb
#-----| -> Foo
# 1| exit constant_compound_assign.rb
# 1| exit constant_compound_assign.rb (normal)
#-----| -> exit constant_compound_assign.rb
# 2| foo_before
#-----| -> FOO_CONSTANT
# 5| FOO_CONSTANT
#-----| -> FOO_CONSTANT
# 5| FOO_CONSTANT
#-----| true -> ... || ...
#-----| false -> 123
# 5| ... = ...
#-----| -> foo_after
# 5| ... || ...
#-----| -> ... = ...
# 5| 123
#-----| -> ... || ...
# 7| foo_after
#-----| -> top_before
# 11| top_before
#-----| -> TOP_CONSTANT
# 14| TOP_CONSTANT
#-----| -> TOP_CONSTANT
# 14| TOP_CONSTANT
#-----| true -> ... || ...
#-----| false -> 123
# 14| ... = ...
#-----| -> top_after
# 14| ... || ...
#-----| -> ... = ...
# 14| 123
#-----| -> ... || ...
# 16| top_after
#-----| -> TOP_CONSTANT2
# 19| TOP_CONSTANT2
#-----| -> TOP_CONSTANT2
# 19| TOP_CONSTANT2
#-----| true -> ... || ...
#-----| false -> 123
# 19| ... = ...
#-----| -> top_after2
# 19| ... || ...
#-----| -> ... = ...
# 19| 123
#-----| -> ... || ...
# 21| top_after2
#-----| -> exit constant_compound_assign.rb (normal)
desugar.rb:
# 1| enter m1
#-----| -> x

View File

@@ -212,6 +212,9 @@ positionalArguments
| cfg.rb:205:1:205:23 | call to bar | cfg.rb:205:10:205:10 | 1 |
| cfg.rb:205:1:205:23 | call to bar | cfg.rb:205:12:205:12 | 2 |
| cfg.rb:205:4:205:5 | call to == | cfg.rb:205:1:205:3 | __synth__0__1 |
| constant_compound_assign.rb:5:18:5:20 | ... \|\| ... | constant_compound_assign.rb:5:22:5:24 | 123 |
| constant_compound_assign.rb:14:14:14:16 | ... \|\| ... | constant_compound_assign.rb:14:18:14:20 | 123 |
| constant_compound_assign.rb:19:17:19:19 | ... \|\| ... | constant_compound_assign.rb:19:21:19:23 | 123 |
| desugar.rb:2:5:2:6 | ... + ... | desugar.rb:2:8:2:8 | 1 |
| desugar.rb:6:3:6:13 | call to count= | desugar.rb:6:17:6:17 | ... = ... |
| desugar.rb:10:3:10:10 | call to []= | desugar.rb:10:9:10:9 | 0 |

View File

@@ -0,0 +1,22 @@
module Foo
def foo_before
end
FOO_CONSTANT ||= 123
def foo_after
end
end
def top_before
end
TOP_CONSTANT ||= 123
def top_after
end
::TOP_CONSTANT2 ||= 123
def top_after2
end

View File

@@ -3,7 +3,6 @@
| array_flow.rb:180:28:180:46 | # $ hasValueFlow=19 | Missing result:hasValueFlow=19 |
| array_flow.rb:226:10:226:13 | ...[...] | Unexpected result: hasValueFlow=25 |
| array_flow.rb:242:14:242:14 | x | Unexpected result: hasValueFlow=27.2 |
| array_flow.rb:255:16:255:56 | # $ hasValueFlow=28.1 $ hasValueFlow=28.2 | Missing result:hasValueFlow=28.1 |
| array_flow.rb:319:10:319:13 | ...[...] | Unexpected result: hasValueFlow=36.1 |
| array_flow.rb:320:10:320:13 | ...[...] | Unexpected result: hasValueFlow=36.1 |
| array_flow.rb:321:10:321:13 | ...[...] | Unexpected result: hasValueFlow=36.1 |
@@ -26,7 +25,6 @@
| array_flow.rb:490:10:490:13 | ...[...] | Unexpected result: hasValueFlow=54.5 |
| array_flow.rb:494:10:494:13 | ...[...] | Unexpected result: hasValueFlow=54.2 |
| array_flow.rb:494:10:494:13 | ...[...] | Unexpected result: hasValueFlow=54.3 |
| array_flow.rb:564:16:564:56 | # $ hasValueFlow=62.1 $ hasValueFlow=62.2 | Missing result:hasValueFlow=62.1 |
| array_flow.rb:575:16:575:34 | # $ hasValueFlow=63 | Missing result:hasValueFlow=63 |
| array_flow.rb:580:19:580:37 | # $ hasValueFlow=64 | Missing result:hasValueFlow=64 |
| array_flow.rb:582:16:582:34 | # $ hasValueFlow=64 | Missing result:hasValueFlow=64 |

View File

@@ -1,5 +1,17 @@
failures
edges
| captured_variables.rb:1:24:1:24 | x : | captured_variables.rb:2:20:2:20 | x |
| captured_variables.rb:1:24:1:24 | x : | captured_variables.rb:2:20:2:20 | x |
| captured_variables.rb:5:20:5:30 | call to source : | captured_variables.rb:1:24:1:24 | x : |
| captured_variables.rb:5:20:5:30 | call to source : | captured_variables.rb:1:24:1:24 | x : |
| captured_variables.rb:21:33:21:33 | x : | captured_variables.rb:23:14:23:14 | x |
| captured_variables.rb:21:33:21:33 | x : | captured_variables.rb:23:14:23:14 | x |
| captured_variables.rb:27:29:27:39 | call to source : | captured_variables.rb:21:33:21:33 | x : |
| captured_variables.rb:27:29:27:39 | call to source : | captured_variables.rb:21:33:21:33 | x : |
| captured_variables.rb:32:31:32:31 | x : | captured_variables.rb:34:14:34:14 | x |
| captured_variables.rb:32:31:32:31 | x : | captured_variables.rb:34:14:34:14 | x |
| captured_variables.rb:38:27:38:37 | call to source : | captured_variables.rb:32:31:32:31 | x : |
| captured_variables.rb:38:27:38:37 | call to source : | captured_variables.rb:32:31:32:31 | x : |
| instance_variables.rb:10:19:10:19 | x : | instance_variables.rb:11:18:11:18 | x : |
| instance_variables.rb:10:19:10:19 | x : | instance_variables.rb:11:18:11:18 | x : |
| instance_variables.rb:11:18:11:18 | x : | instance_variables.rb:11:9:11:14 | [post] self [@field] : |
@@ -152,6 +164,24 @@ edges
| instance_variables.rb:84:6:84:10 | foo13 [@field] : | instance_variables.rb:84:6:84:20 | call to get_field |
| instance_variables.rb:84:6:84:10 | foo13 [@field] : | instance_variables.rb:84:6:84:20 | call to get_field |
nodes
| captured_variables.rb:1:24:1:24 | x : | semmle.label | x : |
| captured_variables.rb:1:24:1:24 | x : | semmle.label | x : |
| captured_variables.rb:2:20:2:20 | x | semmle.label | x |
| captured_variables.rb:2:20:2:20 | x | semmle.label | x |
| captured_variables.rb:5:20:5:30 | call to source : | semmle.label | call to source : |
| captured_variables.rb:5:20:5:30 | call to source : | semmle.label | call to source : |
| captured_variables.rb:21:33:21:33 | x : | semmle.label | x : |
| captured_variables.rb:21:33:21:33 | x : | semmle.label | x : |
| captured_variables.rb:23:14:23:14 | x | semmle.label | x |
| captured_variables.rb:23:14:23:14 | x | semmle.label | x |
| captured_variables.rb:27:29:27:39 | call to source : | semmle.label | call to source : |
| captured_variables.rb:27:29:27:39 | call to source : | semmle.label | call to source : |
| captured_variables.rb:32:31:32:31 | x : | semmle.label | x : |
| captured_variables.rb:32:31:32:31 | x : | semmle.label | x : |
| captured_variables.rb:34:14:34:14 | x | semmle.label | x |
| captured_variables.rb:34:14:34:14 | x | semmle.label | x |
| captured_variables.rb:38:27:38:37 | call to source : | semmle.label | call to source : |
| captured_variables.rb:38:27:38:37 | call to source : | semmle.label | call to source : |
| instance_variables.rb:10:19:10:19 | x : | semmle.label | x : |
| instance_variables.rb:10:19:10:19 | x : | semmle.label | x : |
| instance_variables.rb:11:9:11:14 | [post] self [@field] : | semmle.label | [post] self [@field] : |
@@ -335,6 +365,9 @@ subpaths
| instance_variables.rb:84:6:84:10 | foo13 [@field] : | instance_variables.rb:13:5:15:7 | self in get_field [@field] : | instance_variables.rb:14:9:14:21 | return : | instance_variables.rb:84:6:84:20 | call to get_field |
| instance_variables.rb:84:6:84:10 | foo13 [@field] : | instance_variables.rb:13:5:15:7 | self in get_field [@field] : | instance_variables.rb:14:9:14:21 | return : | instance_variables.rb:84:6:84:20 | call to get_field |
#select
| captured_variables.rb:2:20:2:20 | x | captured_variables.rb:5:20:5:30 | call to source : | captured_variables.rb:2:20:2:20 | x | $@ | captured_variables.rb:5:20:5:30 | call to source : | call to source : |
| captured_variables.rb:23:14:23:14 | x | captured_variables.rb:27:29:27:39 | call to source : | captured_variables.rb:23:14:23:14 | x | $@ | captured_variables.rb:27:29:27:39 | call to source : | call to source : |
| captured_variables.rb:34:14:34:14 | x | captured_variables.rb:38:27:38:37 | call to source : | captured_variables.rb:34:14:34:14 | x | $@ | captured_variables.rb:38:27:38:37 | call to source : | call to source : |
| instance_variables.rb:20:10:20:13 | @foo | instance_variables.rb:19:12:19:21 | call to taint : | instance_variables.rb:20:10:20:13 | @foo | $@ | instance_variables.rb:19:12:19:21 | call to taint : | call to taint : |
| instance_variables.rb:25:6:25:18 | call to get_field | instance_variables.rb:24:15:24:23 | call to taint : | instance_variables.rb:25:6:25:18 | call to get_field | $@ | instance_variables.rb:24:15:24:23 | call to taint : | call to taint : |
| instance_variables.rb:29:6:29:18 | call to inc_field | instance_variables.rb:28:15:28:22 | call to taint : | instance_variables.rb:29:6:29:18 | call to inc_field | $@ | instance_variables.rb:28:15:28:22 | call to taint : | call to taint : |

Some files were not shown because too many files have changed in this diff Show More