Merge branch 'main' into redsun82/bazel-9

This commit is contained in:
Paolo Tranquilli
2026-02-16 09:31:50 +01:00
44 changed files with 1195 additions and 738 deletions

View File

@@ -1726,9 +1726,7 @@ private module Cached {
SsaImpl::ssaFlow(n, succ) and
bb1 = n.getBasicBlock() and
bb2 = succ.getBasicBlock() and
bb1 != bb2 and
bb2.dominates(bb1) and
bb1.getASuccessor+() = bb2
bb2.strictlyDominates(bb1)
)
}

View File

@@ -44,5 +44,5 @@ NHibernate,3,,,,,,,,,,,,3,,,,,,,,,,
Newtonsoft.Json,,,91,,,,,,,,,,,,,,,,,,,73,18
ServiceStack,194,,7,27,,,,,75,,,,92,,,,,,,,,7,
SourceGenerators,,,5,,,,,,,,,,,,,,,,,,,,5
System,59,47,12491,,6,5,12,,,4,1,,31,2,,6,15,17,4,3,,6378,6113
System,59,47,12495,,6,5,12,,,4,1,,31,2,,6,15,17,4,3,,6382,6113
Windows.Security.Cryptography.Core,1,,,,,,,1,,,,,,,,,,,,,,,
1 package sink source summary sink:code-injection sink:encryption-decryptor sink:encryption-encryptor sink:encryption-keyprop sink:encryption-symmetrickey sink:file-content-store sink:html-injection sink:js-injection sink:log-injection sink:sql-injection source:commandargs source:database source:environment source:file source:file-write source:remote source:stdin source:windows-registry summary:taint summary:value
44 Newtonsoft.Json 91 73 18
45 ServiceStack 194 7 27 75 92 7
46 SourceGenerators 5 5
47 System 59 47 12491 12495 6 5 12 4 1 31 2 6 15 17 4 3 6378 6382 6113
48 Windows.Security.Cryptography.Core 1 1

View File

@@ -8,7 +8,7 @@ C# framework & library support
Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting`
`ServiceStack <https://servicestack.net/>`_,"``ServiceStack.*``, ``ServiceStack``",,7,194,
System,"``System.*``, ``System``",47,12491,59,5
System,"``System.*``, ``System``",47,12495,59,5
Others,"``Amazon.Lambda.APIGatewayEvents``, ``Amazon.Lambda.Core``, ``Dapper``, ``ILCompiler``, ``ILLink.RoslynAnalyzer``, ``ILLink.Shared``, ``ILLink.Tasks``, ``Internal.IL``, ``Internal.Pgo``, ``Internal.TypeSystem``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.AspNetCore.Components``, ``Microsoft.AspNetCore.Http``, ``Microsoft.AspNetCore.Mvc``, ``Microsoft.AspNetCore.WebUtilities``, ``Microsoft.CSharp``, ``Microsoft.Data.SqlClient``, ``Microsoft.Diagnostics.Tools.Pgo``, ``Microsoft.DotNet.Build.Tasks``, ``Microsoft.DotNet.PlatformAbstractions``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.Diagnostics.Metrics``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.JSInterop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``Mono.Linker``, ``MySql.Data.MySqlClient``, ``NHibernate``, ``Newtonsoft.Json``, ``SourceGenerators``, ``Windows.Security.Cryptography.Core``",60,2406,162,4
Totals,,107,14904,415,9
Totals,,107,14908,415,9

View File

@@ -1,9 +1,9 @@
package,sink,source,summary,sink:command-injection,sink:credentials-key,sink:jwt,sink:log-injection,sink:nosql-injection,sink:path-injection,sink:regex-use[0],sink:regex-use[1],sink:regex-use[c],sink:request-forgery,sink:request-forgery[TCP Addr + Port],sink:sql-injection,sink:url-redirection,sink:url-redirection[0],sink:url-redirection[receiver],sink:xpath-injection,source:commandargs,source:database,source:environment,source:file,source:remote,source:stdin,summary:taint,summary:value
,,,8,,,,,,,,,,,,,,,,,,,,,,,3,5
,,,9,,,,,,,,,,,,,,,,,,,,,,,3,6
archive/tar,,,5,,,,,,,,,,,,,,,,,,,,,,,5,
archive/zip,,,6,,,,,,,,,,,,,,,,,,,,,,,6,
bufio,,,17,,,,,,,,,,,,,,,,,,,,,,,17,
bytes,,,43,,,,,,,,,,,,,,,,,,,,,,,43,
bytes,,,44,,,,,,,,,,,,,,,,,,,,,,,44,
clevergo.tech/clevergo,1,,,,,,,,,,,,,,,,,1,,,,,,,,,
cloud.google.com/go/bigquery,1,,,,,,,,,,,,,,1,,,,,,,,,,,,
compress/bzip2,,,1,,,,,,,,,,,,,,,,,,,,,,,1,
@@ -18,7 +18,7 @@ context,,,5,,,,,,,,,,,,,,,,,,,,,,,5,
crypto,,,10,,,,,,,,,,,,,,,,,,,,,,,10,
database/sql,30,18,12,,,,,,,,,,,,30,,,,,,18,,,,,12,
encoding,,,81,,,,,,,,,,,,,,,,,,,,,,,81,
errors,,,3,,,,,,,,,,,,,,,,,,,,,,,3,
errors,,,4,,,,,,,,,,,,,,,,,,,,,,,4,
expvar,,,6,,,,,,,,,,,,,,,,,,,,,,,6,
fmt,3,,16,,,,3,,,,,,,,,,,,,,,,,,,16,
github.com/ChrisTrenkamp/goxpath,3,,,,,,,,,,,,,,,,,,3,,,,,,,,
1 package sink source summary sink:command-injection sink:credentials-key sink:jwt sink:log-injection sink:nosql-injection sink:path-injection sink:regex-use[0] sink:regex-use[1] sink:regex-use[c] sink:request-forgery sink:request-forgery[TCP Addr + Port] sink:sql-injection sink:url-redirection sink:url-redirection[0] sink:url-redirection[receiver] sink:xpath-injection source:commandargs source:database source:environment source:file source:remote source:stdin summary:taint summary:value
2 8 9 3 5 6
3 archive/tar 5 5
4 archive/zip 6 6
5 bufio 17 17
6 bytes 43 44 43 44
7 clevergo.tech/clevergo 1 1
8 cloud.google.com/go/bigquery 1 1
9 compress/bzip2 1 1
18 crypto 10 10
19 database/sql 30 18 12 30 18 12
20 encoding 81 81
21 errors 3 4 3 4
22 expvar 6 6
23 fmt 3 16 3 16
24 github.com/ChrisTrenkamp/goxpath 3 3

View File

@@ -32,7 +32,7 @@ Go framework & library support
`Revel <http://revel.github.io/>`_,"``github.com/revel/revel*``, ``github.com/robfig/revel*``",46,20,4
`SendGrid <https://github.com/sendgrid/sendgrid-go>`_,``github.com/sendgrid/sendgrid-go*``,,1,
`Squirrel <https://github.com/Masterminds/squirrel>`_,"``github.com/Masterminds/squirrel*``, ``github.com/lann/squirrel*``, ``gopkg.in/Masterminds/squirrel``",81,,96
`Standard library <https://pkg.go.dev/std>`_,"````, ``archive/*``, ``bufio``, ``bytes``, ``cmp``, ``compress/*``, ``container/*``, ``context``, ``crypto``, ``crypto/*``, ``database/*``, ``debug/*``, ``embed``, ``encoding``, ``encoding/*``, ``errors``, ``expvar``, ``flag``, ``fmt``, ``go/*``, ``hash``, ``hash/*``, ``html``, ``html/*``, ``image``, ``image/*``, ``index/*``, ``io``, ``io/*``, ``log``, ``log/*``, ``maps``, ``math``, ``math/*``, ``mime``, ``mime/*``, ``net``, ``net/*``, ``os``, ``os/*``, ``path``, ``path/*``, ``plugin``, ``reflect``, ``reflect/*``, ``regexp``, ``regexp/*``, ``slices``, ``sort``, ``strconv``, ``strings``, ``sync``, ``sync/*``, ``syscall``, ``syscall/*``, ``testing``, ``testing/*``, ``text/*``, ``time``, ``time/*``, ``unicode``, ``unicode/*``, ``unsafe``, ``weak``",52,609,104
`Standard library <https://pkg.go.dev/std>`_,"````, ``archive/*``, ``bufio``, ``bytes``, ``cmp``, ``compress/*``, ``container/*``, ``context``, ``crypto``, ``crypto/*``, ``database/*``, ``debug/*``, ``embed``, ``encoding``, ``encoding/*``, ``errors``, ``expvar``, ``flag``, ``fmt``, ``go/*``, ``hash``, ``hash/*``, ``html``, ``html/*``, ``image``, ``image/*``, ``index/*``, ``io``, ``io/*``, ``log``, ``log/*``, ``maps``, ``math``, ``math/*``, ``mime``, ``mime/*``, ``net``, ``net/*``, ``os``, ``os/*``, ``path``, ``path/*``, ``plugin``, ``reflect``, ``reflect/*``, ``regexp``, ``regexp/*``, ``slices``, ``sort``, ``strconv``, ``strings``, ``sync``, ``sync/*``, ``syscall``, ``syscall/*``, ``testing``, ``testing/*``, ``text/*``, ``time``, ``time/*``, ``unicode``, ``unicode/*``, ``unsafe``, ``weak``",52,612,104
`XORM <https://xorm.io>`_,"``github.com/go-xorm/xorm*``, ``xorm.io/xorm*``",,,68
`XPath <https://github.com/antchfx/xpath>`_,``github.com/antchfx/xpath*``,,,4
`appleboy/gin-jwt <https://github.com/appleboy/gin-jwt>`_,``github.com/appleboy/gin-jwt*``,,,1
@@ -74,5 +74,5 @@ Go framework & library support
`xpathparser <https://github.com/santhosh-tekuri/xpathparser>`_,``github.com/santhosh-tekuri/xpathparser*``,,,2
`yaml <https://gopkg.in/yaml.v3>`_,``gopkg.in/yaml*``,,9,
`zap <https://go.uber.org/zap>`_,``go.uber.org/zap*``,,11,33
Totals,,688,1069,1557
Totals,,688,1072,1557

View File

@@ -0,0 +1,4 @@
---
category: deprecated
---
* The `UnreachableBlocks.qll` library has been deprecated.

View File

@@ -1,8 +1,10 @@
/**
* DEPRECATED: This module is no longer maintained, and will be removed in a future release.
*
* Provides classes and predicates for identifying unreachable blocks under a "closed-world" assumption.
*/
overlay[local?]
module;
deprecated module;
import java
import semmle.code.java.controlflow.Guards

View File

@@ -1,5 +1,8 @@
/**
* DEPRECATED: This module is no longer maintained, and will be removed in a future release.
*/
overlay[local?]
module;
deprecated module;
import java
import semmle.code.java.controlflow.UnreachableBlocks

View File

@@ -1,12 +0,0 @@
| unreachableblocks/Unreachable.java:3:14:3:24 | Exceptional Exit |
| unreachableblocks/Unreachable.java:3:14:3:24 | Exceptional Exit |
| unreachableblocks/Unreachable.java:5:14:5:19 | Exceptional Exit |
| unreachableblocks/Unreachable.java:6:14:8:3 | { ... } |
| unreachableblocks/Unreachable.java:9:21:11:3 | { ... } |
| unreachableblocks/Unreachable.java:12:22:14:3 | { ... } |
| unreachableblocks/Unreachable.java:17:3:17:9 | case ... |
| unreachableblocks/Unreachable.java:19:3:19:9 | case ... |
| unreachableblocks/Unreachable.java:24:3:24:9 | case ... |
| unreachableblocks/Unreachable.java:26:3:26:10 | case ... |
| unreachableblocks/Unreachable.java:27:3:27:10 | default |
| unreachableblocks/Unreachable.java:32:18:32:28 | Exceptional Exit |

View File

@@ -1,5 +0,0 @@
import default
import semmle.code.java.controlflow.UnreachableBlocks
from UnreachableBasicBlock unreachableBasicBlock
select unreachableBasicBlock

View File

@@ -1,35 +0,0 @@
package unreachableblocks;
public class Unreachable {
private boolean privateFalse = false;
public void method() {
if (false) {
// unreachable
}
if (privateFalse) {
// unreachable
}
if (methodFalse()) {
// unreachable
}
switch (7) {
case 5: // unreachable
break;
case 6: // unreachable
System.out.println("dead"); // unreachable
case 7:
case 8: // reachable from 7
break; // reachable
case 9: //unreachable
break;
case 10: // unreachable
default:
break; //unreachable
}
}
private boolean methodFalse() {
return privateFalse;
}
}

View File

@@ -26,6 +26,8 @@ private module Input implements InputSig<Location, PythonDataFlow> {
or
// TODO: Implement post-updates for **kwargs, see tests added in https://github.com/github/codeql/pull/14936
exists(ArgumentPosition apos | n.argumentOf(_, apos) and apos.isDictSplat())
or
missingArgumentCallExclude(n)
}
predicate reverseReadExclude(Node n) {
@@ -134,6 +136,18 @@ private module Input implements InputSig<Location, PythonDataFlow> {
other.getNode().getScope() = f
)
}
predicate missingArgumentCallExclude(ArgumentNode arg) {
// We overapproximate the argument nodes in order to not rely on the global `getCallArg`
// predicate.
// Because of this, we must exclude the cases where we have an approximation but no actual
// argument node.
arg = getCallArgApproximation() and not getCallArg(_, _, _, arg, _)
or
// Likewise, capturing closure arguments do not have corresponding argument nodes in some cases.
arg instanceof SynthCapturedVariablesArgumentNode and
not arg.argumentOf(_, _)
}
}
import MakeConsistency<Location, PythonDataFlow, PythonTaintTracking, Input>

View File

@@ -1714,36 +1714,66 @@ private class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNodeImpl
* This is also known as the environment part of a closure.
*
* This is used for tracking flow through captured variables.
*
* TODO:
* We might want a synthetic node here, but currently that incurs problems
* with non-monotonic recursion, because of the use of `resolveCall` in the
* char pred. This may be solvable by using
* `CallGraphConstruction::Make` in stead of
* `CallGraphConstruction::Simple::Make` appropriately.
*/
class CapturedVariablesArgumentNode extends CfgNode, ArgumentNode {
CallNode callNode;
class SynthCapturedVariablesArgumentNode extends Node, TSynthCapturedVariablesArgumentNode {
ControlFlowNode callable;
CapturedVariablesArgumentNode() {
node = callNode.getFunction() and
exists(Function target | resolveCall(callNode, target, _) |
target = any(VariableCapture::CapturedVariable v).getACapturingScope()
)
}
SynthCapturedVariablesArgumentNode() { this = TSynthCapturedVariablesArgumentNode(callable) }
/** Gets the `CallNode` corresponding to this captured variables argument node. */
CallNode getCallNode() { result.getFunction() = callable }
/** Gets the `CfgNode` that corresponds to this synthetic node. */
CfgNode getUnderlyingNode() { result.asCfgNode() = callable }
override Scope getScope() { result = callable.getScope() }
override Location getLocation() { result = callable.getLocation() }
override string toString() { result = "Capturing closure argument" }
}
/** A captured variables argument node viewed as an argument node. Needed because `argumentOf` is a global predicate. */
class CapturedVariablesArgumentNodeAsArgumentNode extends ArgumentNode,
SynthCapturedVariablesArgumentNode
{
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
callNode = call.getNode() and
pos.isLambdaSelf()
exists(CallNode callNode | callNode = this.getCallNode() |
callNode = call.getNode() and
exists(Function target | resolveCall(callNode, target, _) |
target = any(VariableCapture::CapturedVariable v).getACapturingScope()
) and
pos.isLambdaSelf()
)
}
}
/** A synthetic node representing the values of captured variables after the output has been computed. */
class SynthCapturedVariablesArgumentPostUpdateNode extends PostUpdateNodeImpl,
TSynthCapturedVariablesArgumentPostUpdateNode
{
ControlFlowNode callable;
SynthCapturedVariablesArgumentPostUpdateNode() {
this = TSynthCapturedVariablesArgumentPostUpdateNode(callable)
}
/** Gets the `PostUpdateNode` (for a `CfgNode`) that corresponds to this synthetic node. */
PostUpdateNode getUnderlyingNode() { result.getPreUpdateNode().asCfgNode() = callable }
override string toString() { result = "[post] Capturing closure argument" }
override Scope getScope() { result = callable.getScope() }
override Location getLocation() { result = callable.getLocation() }
override SynthCapturedVariablesArgumentNode getPreUpdateNode() {
result = TSynthCapturedVariablesArgumentNode(callable)
}
}
/** A synthetic node representing the values of variables captured by a comprehension. */
class SynthCompCapturedVariablesArgumentNode extends Node, TSynthCompCapturedVariablesArgumentNode,
ArgumentNode
{
class SynthCompCapturedVariablesArgumentNode extends Node, TSynthCompCapturedVariablesArgumentNode {
Comp comp;
SynthCompCapturedVariablesArgumentNode() { this = TSynthCompCapturedVariablesArgumentNode(comp) }
@@ -1755,7 +1785,11 @@ class SynthCompCapturedVariablesArgumentNode extends Node, TSynthCompCapturedVar
override Location getLocation() { result = comp.getLocation() }
Comp getComprehension() { result = comp }
}
class SynthCompCapturedVariablesArgumentNodeAsArgumentNode extends SynthCompCapturedVariablesArgumentNode,
ArgumentNode
{
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
call.(ComprehensionCall).getComprehension() = comp and
pos.isLambdaSelf()

View File

@@ -1128,6 +1128,14 @@ predicate nodeIsHidden(Node n) {
n instanceof SynthCaptureNode
or
n instanceof SynthCapturedVariablesParameterNode
or
n instanceof SynthCapturedVariablesArgumentNode
or
n instanceof SynthCapturedVariablesArgumentPostUpdateNode
or
n instanceof SynthCompCapturedVariablesArgumentNode
or
n instanceof SynthCompCapturedVariablesArgumentPostUpdateNode
}
class LambdaCallKind = Unit;

View File

@@ -76,15 +76,7 @@ newtype TNode =
node.getNode() = any(Comp c).getIterable()
} or
/** A node representing a global (module-level) variable in a specific module. */
TModuleVariableNode(Module m, GlobalVariable v) {
v.getScope() = m and
(
v.escapes()
or
isAccessedThroughImportStar(m) and
ImportStar::globalNameDefinedInModule(v.getId(), m)
)
} or
TModuleVariableNode(Module m, GlobalVariable v) { v.getScope() = m } or
/**
* A synthetic node representing that an iterable sequence flows to consumer.
*/
@@ -129,6 +121,20 @@ newtype TNode =
f = any(VariableCapture::CapturedVariable v).getACapturingScope() and
exists(TFunction(f))
} or
/**
* A synthetic node representing the values of the variables captured
* by the callable being called.
*/
TSynthCapturedVariablesArgumentNode(ControlFlowNode callable) {
callable = any(CallNode c).getFunction()
} or
/**
* A synthetic node representing the values of the variables captured
* by the callable being called, after the output has been computed.
*/
TSynthCapturedVariablesArgumentPostUpdateNode(ControlFlowNode callable) {
callable = any(CallNode c).getFunction()
} or
/** A synthetic node representing the values of variables captured by a comprehension. */
TSynthCompCapturedVariablesArgumentNode(Comp comp) {
comp.getFunction() = any(VariableCapture::CapturedVariable v).getACapturingScope()
@@ -347,27 +353,51 @@ abstract class ArgumentNode extends Node {
final ExtractedDataFlowCall getCall() { this.argumentOf(result, _) }
}
/** Gets an overapproximation of the argument nodes that are included in `getCallArg`. */
Node getCallArgApproximation() {
// pre-update nodes for calls
result = any(CallCfgNode c).(PostUpdateNode).getPreUpdateNode()
or
// self parameters in methods
exists(Class c | result.asExpr() = c.getAMethod().getArg(0))
or
// the object part of an attribute expression (which might be a bound method)
result.asCfgNode() = any(AttrNode a).getObject()
or
// the function part of any call
result.asCfgNode() = any(CallNode c).getFunction()
}
/** Gets the extracted argument nodes that do not rely on `getCallArg`. */
private Node implicitArgumentNode() {
// for potential summaries we allow all normal call arguments
normalCallArg(_, result, _)
or
// and self arguments
result.asCfgNode() = any(CallNode c).getFunction().(AttrNode).getObject()
or
// for comprehensions, we allow the synthetic `iterable` argument
result.asExpr() = any(Comp c).getIterable()
}
/**
* A data flow node that represents a call argument found in the source code.
*/
class ExtractedArgumentNode extends ArgumentNode {
ExtractedArgumentNode() {
// for resolved calls, we need to allow all argument nodes
getCallArg(_, _, _, this, _)
this = getCallArgApproximation()
or
// for potential summaries we allow all normal call arguments
normalCallArg(_, this, _)
or
// and self arguments
this.asCfgNode() = any(CallNode c).getFunction().(AttrNode).getObject()
or
// for comprehensions, we allow the synthetic `iterable` argument
this.asExpr() = any(Comp c).getIterable()
this = implicitArgumentNode()
}
final override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
this = call.getArgument(pos) and
call instanceof ExtractedDataFlowCall
call instanceof ExtractedDataFlowCall and
(
this = implicitArgumentNode()
or
this = getCallArgApproximation() and getCallArg(_, _, _, this, _)
)
}
}
@@ -440,13 +470,17 @@ class ModuleVariableNode extends Node, TModuleVariableNode {
/** Gets a node that reads this variable. */
Node getARead() {
result.asCfgNode() = var.getALoad().getAFlowNode() and
// Ignore reads that happen when the module is imported. These are only executed once.
not result.getScope() = mod
result = this.getALocalRead()
or
this = import_star_read(result)
}
/** Gets a node that reads this variable, excluding reads that happen through `from ... import *`. */
Node getALocalRead() {
result.asCfgNode() = var.getALoad().getAFlowNode() and
not result.getScope() = mod
}
/** Gets an `EssaNode` that corresponds to an assignment of this global variable. */
Node getAWrite() {
any(EssaNodeDefinition def).definedBy(var, result.asCfgNode().(DefinitionNode))
@@ -466,8 +500,6 @@ class ModuleVariableNode extends Node, TModuleVariableNode {
override Location getLocation() { result = mod.getLocation() }
}
private predicate isAccessedThroughImportStar(Module m) { m = ImportStar::getStarImported(_) }
private ModuleVariableNode import_star_read(Node n) {
resolved_import_star_module(result.getModule(), result.getVariable().getId(), n)
}

View File

@@ -67,7 +67,7 @@ class LocalSourceNode extends Node {
or
// We explicitly include any read of a global variable, as some of these may have local flow going
// into them.
this = any(ModuleVariableNode mvn).getARead()
this = any(ModuleVariableNode v).getALocalRead()
or
// We include all scope entry definitions, as these act as the local source within the scope they
// enter.
@@ -248,7 +248,7 @@ private module Cached {
pragma[nomagic]
private predicate localSourceFlowStep(Node nodeFrom, Node nodeTo) {
simpleLocalFlowStep(nodeFrom, nodeTo, _) and
not nodeTo = any(ModuleVariableNode v).getARead()
not nodeTo = any(ModuleVariableNode v).getALocalRead()
}
/**

View File

@@ -114,6 +114,12 @@ private Flow::ClosureNode asClosureNode(Node n) {
result.(Flow::ExprNode).getExpr().getNode() = comp
)
or
// For captured variable argument nodes (and their post-update variants), we use the closure node
// for the underlying node.
result = asClosureNode(n.(SynthCapturedVariablesArgumentNode).getUnderlyingNode())
or
result = asClosureNode(n.(SynthCapturedVariablesArgumentPostUpdateNode).getUnderlyingNode())
or
// TODO: Should the `Comp`s above be excluded here?
result.(Flow::ExprNode).getExpr() = n.(CfgNode).getNode()
or

View File

@@ -8,7 +8,9 @@ module MaximalFlowTest implements FlowTestSig {
predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
source != sink and
MaximalFlows::flow(source, sink)
MaximalFlows::flow(source, sink) and
// exclude ModuleVariableNodes (which have location 0:0:0:0)
not sink instanceof DataFlow::ModuleVariableNode
}
}
@@ -33,7 +35,7 @@ module MaximalFlowsConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node node) {
exists(node.getLocation().getFile().getRelativePath()) and
not any(CallNode c).getArg(_) = node.asCfgNode() and
not node instanceof DataFlow::ArgumentNode and
not isArgumentNode(node, _, _) and
not node.asCfgNode().(NameNode).getId().matches("SINK%") and
not DataFlow::localFlowStep(node, _)
}

View File

@@ -9,7 +9,7 @@ module CallGraphConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) {
node instanceof DataFlowPrivate::ReturnNode
or
node instanceof DataFlow::ArgumentNode
DataFlowPrivate::isArgumentNode(node, _, _)
}
predicate isSink(DataFlow::Node node) {

View File

@@ -15,6 +15,7 @@
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.internal.DataFlowDispatch
import semmle.python.dataflow.new.internal.DataFlowPrivate
predicate initSelfCallOverridden(
Function init, DataFlow::Node self, DataFlow::MethodCallNode call, Function target,
@@ -39,7 +40,7 @@ predicate readsFromSelf(Function method) {
self.getParameter() = method.getArg(0) and
DataFlow::localFlow(self, sink)
|
sink instanceof DataFlow::ArgumentNode
isArgumentNode(sink, _, _)
or
sink = any(DataFlow::AttrRead a).getObject()
)

View File

@@ -13,21 +13,27 @@ attacker being able to influence behavior by modifying unexpected files.
<recommendation>
<p>
Validate user input before using it to construct a file path, either using an off-the-shelf library function
like <code>werkzeug.utils.secure_filename</code>, or by performing custom validation.
Validate paths constructed from untrusted user input before using them to access files.
</p>
<p>
Ideally, follow these rules:
The choice of validation depends on the use case.
</p>
<ul>
<li>Do not allow more than a single "." character.</li>
<li>Do not allow directory separators such as "/" or "\" (depending on the file system).</li>
<li>Do not rely on simply replacing problematic sequences such as "../". For example, after
applying this filter to ".../...//", the resulting string would still be "../".</li>
<li>Use an allowlist of known good patterns.</li>
</ul>
<p>
If you want to allow paths spanning multiple folders, a common strategy is to make sure that the constructed
file path is contained within a safe root folder. First, normalize the path using <code>os.path.normpath</code> or
<code>os.path.realpath</code> (make sure to use the latter if symlinks are a consideration)
to remove any internal ".." segments and/or follow links. Then check that the normalized path starts with the
root folder. Note that the normalization step is important, since otherwise even a path that starts with the root
folder could be used to access files outside the root folder.
</p>
<p>
More restrictive options include using a library function like <code>werkzeug.utils.secure_filename</code> to eliminate
any special characters from the file path, or restricting the path to a known list of safe paths. These options are
safe, but can only be used in particular circumstances.
</p>
</recommendation>
<example>

View File

@@ -1,4 +1,5 @@
| test.py:5:9:5:16 | ControlFlowNode for __init__ | test.py:4:1:4:20 | ControlFlowNode for ClassExpr | __init__ | test.py:5:5:5:28 | ControlFlowNode for FunctionExpr |
| test.py:6:9:6:16 | ControlFlowNode for Attribute | test.py:6:9:6:12 | ControlFlowNode for self | foo | test.py:6:20:6:22 | ControlFlowNode for foo |
| test.py:9:1:9:9 | ControlFlowNode for Attribute | test.py:0:0:0:0 | ModuleVariableNode in Module test for myobj | foo | test.py:9:13:9:17 | ControlFlowNode for StringLiteral |
| test.py:9:1:9:9 | ControlFlowNode for Attribute | test.py:9:1:9:5 | ControlFlowNode for myobj | foo | test.py:9:13:9:17 | ControlFlowNode for StringLiteral |
| test.py:12:1:12:25 | ControlFlowNode for setattr() | test.py:12:9:12:13 | ControlFlowNode for myobj | foo | test.py:12:23:12:24 | ControlFlowNode for IntegerLiteral |

View File

@@ -1,6 +1,9 @@
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:0:0:0:0 | ModuleVariableNode in Module test for obfuscated_id |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | ControlFlowNode for obfuscated_id |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id | test.py:0:0:0:0 | ModuleVariableNode in Module test for obfuscated_id |
| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:2:3:2:3 | ControlFlowNode for y |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:2:7:2:7 | ControlFlowNode for x |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:3:3:3:3 | ControlFlowNode for z |
@@ -8,26 +11,33 @@
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:2:3:2:3 | ControlFlowNode for y | test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:2:3:2:3 | ControlFlowNode for y | test.py:3:3:3:3 | ControlFlowNode for z |
| test.py:2:3:2:3 | ControlFlowNode for y | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:3:2:3 | ControlFlowNode for y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:3:2:3 | ControlFlowNode for y | test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:2:3:2:3 | ControlFlowNode for y | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:2:3:2:3 | ControlFlowNode for y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:3:3:3 | ControlFlowNode for z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:3:7:3:7 | ControlFlowNode for y |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:2:7:2:7 | ControlFlowNode for x | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:3:3:3:3 | ControlFlowNode for z | test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:3:3:3:3 | ControlFlowNode for z | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:3:3:3 | ControlFlowNode for z | test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:3:3:3:3 | ControlFlowNode for z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | ControlFlowNode for z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:4:10:4:10 | ControlFlowNode for z | test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:6:1:6:1 | ControlFlowNode for a | test.py:0:0:0:0 | ModuleVariableNode in Module test for a |
| test.py:6:1:6:1 | ControlFlowNode for a | test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:6:1:6:1 | ControlFlowNode for a | test.py:1:19:1:19 | ControlFlowNode for x |
| test.py:6:1:6:1 | ControlFlowNode for a | test.py:2:3:2:3 | ControlFlowNode for y |
| test.py:6:1:6:1 | ControlFlowNode for a | test.py:2:7:2:7 | ControlFlowNode for x |
@@ -37,6 +47,8 @@
| test.py:6:1:6:1 | ControlFlowNode for a | test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:6:1:6:1 | ControlFlowNode for a | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:6:1:6:1 | ControlFlowNode for a | test.py:7:19:7:19 | ControlFlowNode for a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:0:0:0:0 | ModuleVariableNode in Module test for a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:1:19:1:19 | ControlFlowNode for x |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:2:3:2:3 | ControlFlowNode for y |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:2:7:2:7 | ControlFlowNode for x |
@@ -47,7 +59,10 @@
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:19:7:19 | ControlFlowNode for a |
| test.py:7:1:7:1 | ControlFlowNode for b | test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | ControlFlowNode for x |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:2:3:2:3 | ControlFlowNode for y |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:2:7:2:7 | ControlFlowNode for x |

View File

@@ -1,5 +1,6 @@
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | ControlFlowNode for obfuscated_id |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id | test.py:0:0:0:0 | ModuleVariableNode in Module test for obfuscated_id |
| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:2:3:2:3 | ControlFlowNode for y |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:2:3:2:3 | ControlFlowNode for y |
@@ -31,10 +32,13 @@
| test.py:3:7:3:7 | ControlFlowNode for y | test.py:3:3:3:3 | ControlFlowNode for z |
| test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:4:10:4:10 | ControlFlowNode for z | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:6:1:6:1 | ControlFlowNode for a | test.py:0:0:0:0 | ModuleVariableNode in Module test for a |
| test.py:6:1:6:1 | ControlFlowNode for a | test.py:7:19:7:19 | ControlFlowNode for a |
| test.py:6:1:6:1 | ControlFlowNode for a | test.py:7:19:7:19 | ControlFlowNode for a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:1:6:1 | ControlFlowNode for a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:1:6:1 | ControlFlowNode for a |
| test.py:7:1:7:1 | ControlFlowNode for b | test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | ControlFlowNode for x |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:1:19:1:19 | ControlFlowNode for x |

View File

@@ -1,3 +1,8 @@
| test.py:0:0:0:0 | ModuleVariableNode in Module test for __name__ | test.py:0:0:0:0 | ModuleVariableNode in Module test for __name__ |
| test.py:0:0:0:0 | ModuleVariableNode in Module test for __package__ | test.py:0:0:0:0 | ModuleVariableNode in Module test for __package__ |
| test.py:0:0:0:0 | ModuleVariableNode in Module test for a | test.py:0:0:0:0 | ModuleVariableNode in Module test for a |
| test.py:0:0:0:0 | ModuleVariableNode in Module test for b | test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:0:0:0:0 | ModuleVariableNode in Module test for obfuscated_id | test.py:0:0:0:0 | ModuleVariableNode in Module test for obfuscated_id |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:1:1:21 | ControlFlowNode for FunctionExpr |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | ControlFlowNode for obfuscated_id |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
@@ -31,7 +36,9 @@
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:19:7:19 | ControlFlowNode for a |
| test.py:7:1:7:1 | ControlFlowNode for b | test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:7:5:7:17 | Capturing closure argument | test.py:7:5:7:17 | Capturing closure argument |
| test.py:7:5:7:17 | ControlFlowNode for obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:7:5:7:17 | [post] Capturing closure argument | test.py:7:5:7:17 | [post] Capturing closure argument |
| test.py:7:5:7:17 | [post] ControlFlowNode for obfuscated_id | test.py:7:5:7:17 | [post] ControlFlowNode for obfuscated_id |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |

View File

@@ -1,7 +1,12 @@
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:0:0:0:0 | ModuleVariableNode in Module test for obfuscated_id |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:0:0:0:0 | ModuleVariableNode in Module test for a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral | test.py:7:19:7:19 | ControlFlowNode for a |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:1:7:1 | ControlFlowNode for b |

View File

@@ -1,3 +1,8 @@
| test.py:0:0:0:0 | ModuleVariableNode in Module test for __name__ |
| test.py:0:0:0:0 | ModuleVariableNode in Module test for __package__ |
| test.py:0:0:0:0 | ModuleVariableNode in Module test for a |
| test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:0:0:0:0 | ModuleVariableNode in Module test for obfuscated_id |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr |
| test.py:1:1:1:21 | SynthDictSplatParameterNode |
| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id |
@@ -10,7 +15,9 @@
| test.py:6:1:6:1 | ControlFlowNode for a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral |
| test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:7:5:7:17 | Capturing closure argument |
| test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:7:5:7:17 | [post] Capturing closure argument |
| test.py:7:5:7:17 | [post] ControlFlowNode for obfuscated_id |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:7:5:7:20 | [pre] ControlFlowNode for obfuscated_id() |

View File

@@ -1,3 +1,8 @@
| test.py:0:0:0:0 | ModuleVariableNode in Module test for __name__ |
| test.py:0:0:0:0 | ModuleVariableNode in Module test for __package__ |
| test.py:0:0:0:0 | ModuleVariableNode in Module test for a |
| test.py:0:0:0:0 | ModuleVariableNode in Module test for b |
| test.py:0:0:0:0 | ModuleVariableNode in Module test for obfuscated_id |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr |
| test.py:1:1:1:21 | SynthDictSplatParameterNode |
| test.py:1:5:1:17 | ControlFlowNode for obfuscated_id |
@@ -10,7 +15,9 @@
| test.py:6:1:6:1 | ControlFlowNode for a |
| test.py:6:5:6:6 | ControlFlowNode for IntegerLiteral |
| test.py:7:1:7:1 | ControlFlowNode for b |
| test.py:7:5:7:17 | Capturing closure argument |
| test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:7:5:7:17 | [post] Capturing closure argument |
| test.py:7:5:7:17 | [post] ControlFlowNode for obfuscated_id |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:7:5:7:20 | [pre] ControlFlowNode for obfuscated_id() |

View File

@@ -8,9 +8,9 @@ g = [5] # $writes=g
g1, g2 = [6], [7] # $writes=g1 writes=g2
# Assignment that's only referenced in this scope. This one will not give rise to a `ModuleVariableNode`.
# Assignment that's only referenced in this scope.
unreferenced_g = [8]
unreferenced_g = [8] # $writes=unreferenced_g
print(unreferenced_g)
# Testing modifications of globals
@@ -34,7 +34,7 @@ g_ins.append(75)
# A global with multiple potential definitions
import unknown_module
import unknown_module # $writes=unknown_module
if unknown_module.attr:
g_mult = [200] # $writes=g_mult
else:
@@ -46,7 +46,7 @@ g_redef = [400] # $writes=g_redef
if unknown_module.attr:
g_redef = [500] # $writes=g_redef
def global_access():
def global_access(): # $writes=global_access
l = 5
print(g) # $reads=g
print(g1) # $reads=g1
@@ -59,12 +59,12 @@ def global_access():
def print_g_mod(): # $writes=print_g_mod
print(g_mod) # $reads=g_mod
def global_mod():
def global_mod(): # $writes=global_mod
global g_mod
g_mod += [150] # $reads,writes=g_mod
print_g_mod() # $reads=print_g_mod
def global_inside_local_function():
def global_inside_local_function(): # $writes=global_inside_local_function
def local_function():
print(g) # $reads=g
local_function()
@@ -76,21 +76,21 @@ def global_inside_local_function():
import foo_module # $writes=foo_module
def use_foo():
def use_foo(): # $writes=use_foo
print(foo_module.attr) # $reads=foo_module
# Partial imports
from bar import baz_attr, quux_attr # $writes=baz_attr writes=quux_attr
def use_partial_import():
def use_partial_import(): # $writes=use_partial_import
print(baz_attr, quux_attr) # $reads=baz_attr reads=quux_attr
# Aliased imports
from spam_module import ham_attr as eggs_attr # $writes=eggs_attr
def use_aliased_import():
def use_aliased_import(): # $writes=use_aliased_import
print(eggs_attr) # $reads=eggs_attr
# Import star (unlikely to work unless we happen to extract/model the referenced module)
@@ -99,23 +99,23 @@ def use_aliased_import():
from unknown import *
def secretly_use_unknown():
def secretly_use_unknown(): # $writes=secretly_use_unknown
print(unknown_attr) # $reads=unknown_attr
# Known modules
from known import *
def secretly_use_known():
def secretly_use_known(): # $writes=secretly_use_known
print(known_attr) # $reads=known_attr
# Local import in function
def imports_locally():
def imports_locally(): # $writes=imports_locally
import mod1
# Global import hidden in function
def imports_stuff():
def imports_stuff(): # $writes=imports_stuff
global mod2
import mod2 # $writes=mod2

View File

@@ -2,6 +2,7 @@ module_tracker
| import_as_attr.py:1:6:1:11 | ControlFlowNode for ImportExpr |
module_attr_tracker
| import_as_attr.py:0:0:0:0 | ModuleVariableNode in Module import_as_attr for attr_ref |
| import_as_attr.py:0:0:0:0 | ModuleVariableNode in Module import_as_attr for x |
| import_as_attr.py:1:20:1:35 | ControlFlowNode for ImportMember |
| import_as_attr.py:1:28:1:35 | ControlFlowNode for attr_ref |
| import_as_attr.py:3:1:3:1 | ControlFlowNode for x |

View File

@@ -17,7 +17,9 @@ module InlinePoorMansFunctionResolutionTest implements TestSig {
) and
// exclude decorator calls (which with our extractor rewrites does reference the
// function)
not ref.asExpr() = func.getDefinition().(FunctionExpr).getADecoratorCall()
not ref.asExpr() = func.getDefinition().(FunctionExpr).getADecoratorCall() and
// exclude ModuleVariableNodes (which have location 0:0:0:0)
not ref instanceof DataFlow::ModuleVariableNode
|
value = func.getName() and
tag = "resolved" and

View File

@@ -0,0 +1,64 @@
/**
* Provides classes and helper predicates for associated types.
*/
private import rust
private import codeql.rust.internal.PathResolution
private import TypeMention
private import Type
private import TypeInference
/** An associated type, that is, a type alias in a trait block. */
final class AssocType extends TypeAlias {
Trait trait;
AssocType() { this = trait.getAssocItemList().getAnAssocItem() }
Trait getTrait() { result = trait }
string getText() { result = this.getName().getText() }
}
/** Gets an associated type of `trait` or of a supertrait of `trait`. */
AssocType getTraitAssocType(Trait trait) { result.getTrait() = trait.getSupertrait*() }
/** Holds if `path` is of the form `<type as trait>::name` */
pragma[nomagic]
predicate pathTypeAsTraitAssoc(Path path, TypeRepr typeRepr, Path traitPath, string name) {
exists(PathSegment segment |
segment = path.getQualifier().getSegment() and
typeRepr = segment.getTypeRepr() and
traitPath = segment.getTraitTypeRepr().getPath() and
name = path.getText()
)
}
/**
* Holds if `assoc` is accessed on `tp` in `path`.
*
* That is, this is the case when `path` is of the form `<tp as
* Trait>::AssocType` or `tp::AssocType`; and `AssocType` resolves to `assoc`.
*/
predicate tpAssociatedType(TypeParam tp, AssocType assoc, Path path) {
resolvePath(path.getQualifier()) = tp and
resolvePath(path) = assoc
or
exists(PathTypeRepr typeRepr, Path traitPath, string name |
pathTypeAsTraitAssoc(path, typeRepr, traitPath, name) and
tp = resolvePath(typeRepr.getPath()) and
assoc = resolvePath(traitPath).(TraitItemNode).getAssocItem(name)
)
}
/**
* Holds if `bound` is a type bound for `tp` that gives rise to `assoc` being
* present for `tp`.
*/
predicate tpBoundAssociatedType(
TypeParam tp, TypeBound bound, Path path, TraitItemNode trait, AssocType assoc
) {
bound = tp.getATypeBound() and
path = bound.getTypeRepr().(PathTypeRepr).getPath() and
trait = resolvePath(path) and
assoc = getTraitAssocType(trait)
}

View File

@@ -8,11 +8,7 @@ private import codeql.rust.elements.internal.generated.Raw
private import codeql.rust.elements.internal.generated.Synth
private import codeql.rust.frameworks.stdlib.Stdlib
private import codeql.rust.frameworks.stdlib.Builtins as Builtins
/** Gets a type alias of `trait` or of a supertrait of `trait`. */
private TypeAlias getTraitTypeAlias(Trait trait) {
result = trait.getSupertrait*().getAssocItemList().getAnAssocItem()
}
private import AssociatedType
/**
* Holds if a dyn trait type for the trait `trait` should have a type parameter
@@ -31,7 +27,7 @@ private TypeAlias getTraitTypeAlias(Trait trait) {
*/
private predicate dynTraitTypeParameter(Trait trait, AstNode n) {
trait = any(DynTraitTypeRepr dt).getTrait() and
n = [trait.getGenericParamList().getATypeParam().(AstNode), getTraitTypeAlias(trait)]
n = [trait.getGenericParamList().getATypeParam().(AstNode), getTraitAssocType(trait)]
}
cached
@@ -43,8 +39,11 @@ newtype TType =
TNeverType() or
TUnknownType() or
TTypeParamTypeParameter(TypeParam t) or
TAssociatedTypeTypeParameter(Trait trait, TypeAlias typeAlias) {
getTraitTypeAlias(trait) = typeAlias
TAssociatedTypeTypeParameter(Trait trait, AssocType typeAlias) {
getTraitAssocType(trait) = typeAlias
} or
TTypeParamAssociatedTypeTypeParameter(TypeParam tp, AssocType assoc) {
tpAssociatedType(tp, assoc, _)
} or
TDynTraitTypeParameter(Trait trait, AstNode n) { dynTraitTypeParameter(trait, n) } or
TImplTraitTypeParameter(ImplTraitTypeRepr implTrait, TypeParam tp) {
@@ -464,6 +463,52 @@ class AssociatedTypeTypeParameter extends TypeParameter, TAssociatedTypeTypePara
override Location getLocation() { result = typeAlias.getLocation() }
}
/**
* A type parameter corresponding to an associated type accessed on a type
* parameter, for example `T::AssociatedType` where `T` is a type parameter.
*
* These type parameters are created when a function signature accesses an
* associated type on a type parameter. For example, in
* ```rust
* fn foo<T: SomeTrait>(arg: T::Assoc) { }
* ```
* we create a `TypeParamAssociatedTypeTypeParameter` for `Assoc` on `T` and the
* mention `T::Assoc` resolves to this type parameter. If denoting the type
* parameter by `T_Assoc` then the above function is treated as if it was
* ```rust
* fn foo<T: SomeTrait<Assoc = T_Assoc>, T_Assoc>(arg: T_Assoc) { }
* ```
*/
class TypeParamAssociatedTypeTypeParameter extends TypeParameter,
TTypeParamAssociatedTypeTypeParameter
{
private TypeParam typeParam;
private AssocType assoc;
TypeParamAssociatedTypeTypeParameter() {
this = TTypeParamAssociatedTypeTypeParameter(typeParam, assoc)
}
/** Gets the type parameter that this associated type is accessed on. */
TypeParam getTypeParam() { result = typeParam }
/** Gets the associated type alias. */
AssocType getTypeAlias() { result = assoc }
/** Gets a path that accesses this type parameter. */
Path getAPath() { tpAssociatedType(typeParam, assoc, result) }
override ItemNode getDeclaringItem() { result.getTypeParam(_) = typeParam }
override string toString() {
result =
typeParam.toString() + "::" + assoc.getName().getText() + "[" +
assoc.getTrait().getName().getText() + "]"
}
override Location getLocation() { result = typeParam.getLocation() }
}
/** Gets the associated type type-parameter corresponding directly to `typeAlias`. */
AssociatedTypeTypeParameter getAssociatedTypeTypeParameter(TypeAlias typeAlias) {
result.isDirect() and result.getTypeAlias() = typeAlias

View File

@@ -108,6 +108,10 @@ private module Input implements InputSig1<Location>, InputSig2<PreTypeMention> {
id2 = idOfTypeParameterAstNode(tp0.(AssociatedTypeTypeParameter).getTypeAlias())
or
kind = 4 and
id1 = idOfTypeParameterAstNode(tp0.(TypeParamAssociatedTypeTypeParameter).getTypeParam()) and
id2 = idOfTypeParameterAstNode(tp0.(TypeParamAssociatedTypeTypeParameter).getTypeAlias())
or
kind = 5 and
id1 = 0 and
exists(AstNode node | id2 = idOfTypeParameterAstNode(node) |
node = tp0.(TypeParamTypeParameter).getTypeParam() or
@@ -270,13 +274,21 @@ private class FunctionDeclaration extends Function {
this = i.asSome().getAnAssocItem()
}
TypeParam getTypeParam(ImplOrTraitItemNodeOption i) {
i = parent and
result = [this.getGenericParamList().getATypeParam(), i.asSome().getTypeParam(_)]
}
TypeParameter getTypeParameter(ImplOrTraitItemNodeOption i, TypeParameterPosition ppos) {
typeParamMatchPosition(this.getTypeParam(i), result, ppos)
or
// For every `TypeParam` of this function, any associated types accessed on
// the type parameter are also type parameters.
ppos.isImplicit() and
result.(TypeParamAssociatedTypeTypeParameter).getTypeParam() = this.getTypeParam(i)
or
i = parent and
(
typeParamMatchPosition(this.getGenericParamList().getATypeParam(), result, ppos)
or
typeParamMatchPosition(i.asSome().getTypeParam(_), result, ppos)
or
ppos.isImplicit() and result = TSelfTypeParameter(i.asSome())
or
ppos.isImplicit() and result.(AssociatedTypeTypeParameter).getTrait() = i.asSome()
@@ -2477,10 +2489,10 @@ private module MethodCallMatchingInput implements MatchingWithEnvironmentInputSi
additional predicate decodeDerefChainBorrow(
string derefChainBorrow, DerefChain derefChain, BorrowKind borrow
) {
exists(string regexp |
regexp = "^(.*);(.*)$" and
derefChain = derefChainBorrow.regexpCapture(regexp, 1) and
borrow.toString() = derefChainBorrow.regexpCapture(regexp, 2)
exists(int i |
i = derefChainBorrow.indexOf(";") and
derefChain = derefChainBorrow.prefix(i) and
borrow.toString() = derefChainBorrow.suffix(i + 1)
)
}
@@ -2604,27 +2616,30 @@ private Type inferMethodCallTypeNonSelf(AstNode n, boolean isReturn, TypePath pa
}
/**
* Gets the type of `n` at `path` after applying `derefChain` and `borrow`,
* where `n` is the `self` argument of a method call.
* Gets the type of `n` at `path` after applying `derefChain`, where `n` is the
* `self` argument of a method call.
*
* The predicate recursively pops the head of `derefChain` until it becomes
* empty, at which point the inferred type can be applied back to `n`.
*/
pragma[nomagic]
private Type inferMethodCallTypeSelf(
AstNode n, DerefChain derefChain, BorrowKind borrow, TypePath path
) {
exists(MethodCallMatchingInput::AccessPosition apos, string derefChainBorrow |
result = inferMethodCallType0(_, apos, n, derefChainBorrow, path) and
private Type inferMethodCallTypeSelf(AstNode n, DerefChain derefChain, TypePath path) {
exists(
MethodCallMatchingInput::AccessPosition apos, string derefChainBorrow, BorrowKind borrow,
TypePath path0
|
result = inferMethodCallType0(_, apos, n, derefChainBorrow, path0) and
apos.isSelf() and
MethodCallMatchingInput::decodeDerefChainBorrow(derefChainBorrow, derefChain, borrow)
)
or
// adjust for implicit borrow
exists(TypePath path0, BorrowKind borrow0 |
result = inferMethodCallTypeSelf(n, derefChain, borrow0, path0) and
path0.isCons(borrow0.getRefType().getPositionalTypeParameter(0), path) and
borrow.isNoBorrow()
|
borrow.isNoBorrow() and
path = path0
or
// adjust for implicit borrow
exists(TypePath prefix |
prefix = TypePath::singleton(borrow.getRefType().getPositionalTypeParameter(0)) and
path0 = prefix.appendInverse(path)
)
)
or
// adjust for implicit deref
@@ -2632,9 +2647,8 @@ private Type inferMethodCallTypeSelf(
DerefChain derefChain0, Type t0, TypePath path0, DerefImplItemNode impl, Type selfParamType,
TypePath selfPath
|
t0 = inferMethodCallTypeSelf(n, derefChain0, borrow, path0) and
t0 = inferMethodCallTypeSelf(n, derefChain0, path0) and
derefChain0.isCons(impl, derefChain) and
borrow.isNoBorrow() and
selfParamType = impl.resolveSelfTypeAt(selfPath)
|
result = selfParamType and
@@ -2653,7 +2667,7 @@ private Type inferMethodCallTypeSelf(
private Type inferMethodCallTypePreCheck(AstNode n, boolean isReturn, TypePath path) {
result = inferMethodCallTypeNonSelf(n, isReturn, path)
or
result = inferMethodCallTypeSelf(n, DerefChain::nil(), TNoBorrowKind(), path) and
result = inferMethodCallTypeSelf(n, DerefChain::nil(), path) and
isReturn = false
}

View File

@@ -6,6 +6,7 @@ private import codeql.rust.frameworks.stdlib.Stdlib
private import Type
private import TypeAbstraction
private import TypeInference
private import AssociatedType
bindingset[trait, name]
pragma[inline_late]
@@ -319,6 +320,22 @@ private module MkTypeMention<getAdditionalPathTypeAtSig/2 getAdditionalPathTypeA
tp = TAssociatedTypeTypeParameter(resolved, alias) and
path.isEmpty()
)
or
// If this path is a type parameter bound, then any associated types
// accessed on the type parameter, which originate from this bound, should
// be instantiated into the bound, as explained in the comment for
// `TypeParamAssociatedTypeTypeParameter`.
// ```rust
// fn foo<T: SomeTrait<Assoc = T_Assoc>, T_Assoc>(arg: T_Assoc) { }
// ^^^^^^^^^ ^^^^^ ^^^^^^^
// this path result
// ```
exists(TypeParam typeParam, Trait trait, AssocType assoc |
tpBoundAssociatedType(typeParam, _, this, trait, assoc) and
tp = TAssociatedTypeTypeParameter(resolved, assoc) and
result = TTypeParamAssociatedTypeTypeParameter(typeParam, assoc) and
path.isEmpty()
)
}
bindingset[name]
@@ -372,6 +389,8 @@ private module MkTypeMention<getAdditionalPathTypeAtSig/2 getAdditionalPathTypeA
or
// Handles paths of the form `Self::AssocType` within a trait block
result = TAssociatedTypeTypeParameter(resolvePath(this.getQualifier()), resolved)
or
result.(TypeParamAssociatedTypeTypeParameter).getAPath() = this
}
override Type resolvePathTypeAt(TypePath typePath) {
@@ -690,11 +709,10 @@ private predicate pathConcreteTypeAssocType(
|
// path of the form `<Type as Trait>::AssocType`
// ^^^ tm ^^^^^^^^^ name
exists(string name |
name = path.getText() and
trait = resolvePath(qualifier.getSegment().getTraitTypeRepr().getPath()) and
getTraitAssocType(trait, name) = alias and
tm = qualifier.getSegment().getTypeRepr()
exists(string name, Path traitPath |
pathTypeAsTraitAssoc(path, tm, traitPath, name) and
trait = resolvePath(traitPath) and
getTraitAssocType(trait, name) = alias
)
or
// path of the form `Self::AssocType` within an `impl` block

View File

@@ -8,7 +8,7 @@ private import codeql.rust.internal.typeinference.TypeInference
private import utils.test.InlineExpectationsTest
private module ResolveTest implements TestSig {
string getARelevantTag() { result = "item" }
string getARelevantTag() { result = ["item", "target", "item_not_target"] }
private predicate itemAt(ItemNode i, string filepath, int line) {
i.getLocation().hasLocationInfo(filepath, _, _, line, _)
@@ -36,19 +36,37 @@ private module ResolveTest implements TestSig {
)
}
private Item getCallExprTarget(Path p) {
exists(CallExpr ce |
p = ce.getFunction().(PathExpr).getPath() and
result = ce.getResolvedTarget()
)
}
predicate hasActualResult(Location location, string element, string tag, string value) {
exists(AstNode n |
exists(AstNode n, ItemNode i |
not n = any(Path parent).getQualifier() and
location = n.getLocation() and
n.fromSource() and
not location.getFile().getAbsolutePath().matches("%proc_macro.rs") and
not n.isFromMacroExpansion() and
element = n.toString() and
tag = "item"
item(i, value)
|
item(resolvePath(n), value)
i = resolvePath(n) and
(
if exists(getCallExprTarget(n)) and not i = getCallExprTarget(n)
then tag = "item_not_target"
else tag = "item"
)
or
item(n.(MethodCallExpr).getStaticTarget(), value)
tag = "target" and
(
i = n.(MethodCallExpr).getStaticTarget()
or
i = getCallExprTarget(n) and
not i = resolvePath(n)
)
)
}
}

View File

@@ -189,18 +189,18 @@ mod m8 {
#[rustfmt::skip]
pub fn g() {
let x = MyStruct {}; // $ item=I50
MyTrait::f(&x); // $ item=I48
MyTrait::f(&x); // $ item_not_target=I48 target=I53
MyStruct::f(&x); // $ item=I53
<MyStruct as // $ item=I50
MyTrait // $ item=I47
> // $ MISSING: item=52
::f(&x); // $ item=I48
::f(&x); // $ item_not_target=I48 target=I53
let x = MyStruct {}; // $ item=I50
x.f(); // $ item=I53
x.f(); // $ target=I53
let x = MyStruct {}; // $ item=I50
x.g(); // $ item=I54
x.g(); // $ target=I54
MyStruct::h(&x); // $ item=I74
x.h(); // $ item=I74
x.h(); // $ target=I74
} // I55
} // I46
@@ -316,7 +316,7 @@ mod m15 {
fn f(&self) {
println!("m15::Trait2::f"); // $ item=println
Self::g(self); // $ item=I80
self.g(); // $ item=I80
self.g(); // $ target=I80
} // Trait2::f
} // I82
@@ -331,7 +331,7 @@ mod m15 {
fn f(&self, tt: TT) { // $ item=ITT
Self::g(self); // $ item=I80
TT::g(&tt); // $ item=I80
self.g(); // $ item=I80
self.g(); // $ target=I80
}
} // ITrait3
@@ -343,7 +343,7 @@ mod m15 {
fn f(&self) {
println!("m15::<S as Trait1>::f"); // $ item=println
Self::g(self); // $ item=I77
self.g(); // $ item=I77
self.g(); // $ target=I77
} // I76
fn g(&self) {
@@ -365,12 +365,12 @@ mod m15 {
let x = S; // $ item=I81
<S // $ item=I81
as Trait1 // $ item=I79
>::f(&x); // $ item=Trait1::f
>::f(&x); // $ item_not_target=Trait1::f target=I76
<S // $ item=I81
as Trait2 // $ item=I82
>::f(&x); // $ item=Trait2::f
>::f(&x); // $ item_not_target=Trait2::f target=I78
S::g(&x); // $ item=I77
x.g(); // $ item=I77
x.g(); // $ target=I77
} // I75
}
@@ -383,12 +383,12 @@ mod m16 {
; // Trait1::f
fn g(&self) -> T {// $ item=I84
self.f() // $ item=Trait1::f
self.f() // $ target=Trait1::f
} // I85
fn h(&self) -> T { // $ item=I84
Self::g(&self); // $ item=I85
self.g() // $ item=I85
self.g() // $ target=I85
} // I96
const c: T // $ item=I84
@@ -405,7 +405,7 @@ mod m16 {
fn f(&self) -> T { // $ item=I87
println!("m16::Trait2::f"); // $ item=println
Self::g(self); // $ item=I85
self.g(); // $ item=I85
self.g(); // $ target=I85
Self::c // $ item=I94
} // Trait2::f
} // I89
@@ -420,7 +420,7 @@ mod m16 {
fn f(&self) -> S { // $ item=I90
println!("m16::<S as Trait1<S>>::f"); // $ item=println
Self::g(self); // $ item=I92
self.g() // $ item=I92
self.g() // $ target=I92
} // I91
fn g(&self) -> S { // $ item=I90
@@ -452,16 +452,16 @@ mod m16 {
as Trait1<
S // $ item=I90
> // $ item=I86
>::f(&x); // $ item=Trait1::f
>::f(&x); // $ item_not_target=Trait1::f target=I91
<S // $ item=I90
as Trait2<
S // $ item=I90
> // $ item=I89
>::f(&x); // $ item=Trait2::f
>::f(&x); // $ item_not_target=Trait2::f target=I93
S::g(&x); // $ item=I92
x.g(); // $ item=I92
x.g(); // $ target=I92
S::h(&x); // $ item=I96
x.h(); // $ item=I96
x.h(); // $ target=I96
S::c; // $ item=I95
<S // $ item=I90
as Trait1<
@@ -564,13 +564,13 @@ mod m16 {
#[rustfmt::skip]
fn foo() {
S3::<i32>:: // $ item=i32
Assoc(); // $ item=S3i32AssocFunc $ SPURIOUS: item=S3boolAssocFunc (the spurious target is later filtered away by type inference)
Assoc(); // $ item=S3i32AssocFunc item_not_target=S3boolAssocFunc
S3::<bool>:: // $ item=bool
f1(); // $ item=S3boolf1 $ SPURIOUS: item=S3i32f1 (the spurious target is later filtered away by type inference)
f1(); // $ item=S3boolf1 item_not_target=S3i32f1
S3::<i32>:: // $ item=i32
f1(); // $ item=S3i32f1 $ SPURIOUS: item=S3boolf1 (the spurious target is later filtered away by type inference)
f1(); // $ item=S3i32f1 item_not_target=S3boolf1
}
}
@@ -628,7 +628,7 @@ mod trait_visibility {
{
// The `Bar` trait is not visible, but we can refer to its method
// with a full path.
m::Bar::a_method(&x); // $ item=Bar::a_method
m::Bar::a_method(&x); // $ item_not_target=Bar::a_method target=X_Bar::a_method
}
} // trait_visibility::f
}
@@ -652,7 +652,7 @@ mod m17 {
fn g<T: // I5
MyTrait // $ item=I2
>(x: T) { // $ item=I5
x.f(); // $ item=I1
x.f(); // $ target=I1
T::f(&x); // $ item=I1
MyTrait::f(&x); // $ item=I1
} // I6
@@ -735,7 +735,7 @@ mod m23 {
#[rustfmt::skip]
pub fn f() {
let x = S; // $ item=I4
x.f(); // $ item=I5
x.f(); // $ target=I5
} // I108
}
@@ -760,7 +760,7 @@ mod m24 {
T: TraitA // $ item=I111 item=I1151
{
fn call_trait_a(&self) {
self.data.trait_a_method(); // $ item=I110
self.data.trait_a_method(); // $ target=I110
} // I116
}
@@ -772,8 +772,8 @@ mod m24 {
T: TraitA, // $ item=I111 item=I1161
{
fn call_both(&self) {
self.data.trait_a_method(); // $ item=I110
self.data.trait_b_method(); // $ item=I112
self.data.trait_a_method(); // $ target=I110
self.data.trait_b_method(); // $ target=I112
} // I117
}
@@ -798,8 +798,8 @@ mod m24 {
let impl_obj = Implementor; // $ item=I118
let generic = GenericStruct { data: impl_obj }; // $ item=I115
generic.call_trait_a(); // $ item=I116
generic.call_both(); // $ item=I117
generic.call_trait_a(); // $ target=I116
generic.call_both(); // $ target=I117
// Access through where clause type parameter constraint
GenericStruct::<Implementor>::call_trait_a(&generic); // $ item=I116 item=I118
@@ -1132,7 +1132,7 @@ fn main() {
zelf::h(); // $ item=I25
z_changed(); // $ item=I122
AStruct::z_on_type(); // $ item=I124
AStruct {}.z_on_instance(); // $ item=I123 item=I125
AStruct {}.z_on_instance(); // $ item=I123 target=I125
impl_with_attribute_macro::test(); // $ item=impl_with_attribute_macro::test
patterns::test(); // $ item=patterns::test
}

View File

@@ -30,7 +30,7 @@ fn int_div(
) -> Result<i32> // $ item=my::Result $ item=i32
{
if y == 0 {
return Err("Div by zero".to_string()); // $ item=Err item=to_string
return Err("Div by zero".to_string()); // $ item=Err target=to_string
}
Ok(x / y) // $ item=Ok
}

View File

@@ -7,7 +7,7 @@ impl<A> Wrapper<A> {
}
}
#[derive(Debug, Default)]
#[derive(Debug, Default, Clone, Copy)]
struct S;
#[derive(Debug, Default)]
@@ -260,13 +260,65 @@ mod type_param_access_associated_type {
)
}
// Associated type accessed on a type parameter of an impl block
impl<TI> Wrapper<TI>
where
TI: GetSet,
{
fn extract(&self) -> TI::Output {
self.0.get() // $ fieldof=Wrapper target=GetSet::get
}
}
// Associated type accessed on another associated type
fn tp_nested_assoc_type<T: GetSet>(thing: T) -> <<T as GetSet>::Output as GetSet>::Output
where
<T as GetSet>::Output: GetSet,
{
thing.get().get() // $ target=GetSet::get target=GetSet::get
}
pub trait GetSetWrap {
type Assoc: GetSet;
// GetSetWrap::get_wrap
fn get_wrap(&self) -> Self::Assoc;
}
impl GetSetWrap for S {
type Assoc = S;
// S::get_wrap
fn get_wrap(&self) -> Self::Assoc {
S
}
}
// Nested associated type accessed on a type parameter of an impl block
impl<TI> Wrapper<TI>
where
TI: GetSetWrap,
{
fn extract2(&self) -> <<TI as GetSetWrap>::Assoc as GetSet>::Output {
self.0.get_wrap().get() // $ fieldof=Wrapper target=GetSetWrap::get_wrap $ MISSING: target=GetSet::get
}
}
pub fn test() {
let _o1 = tp_with_as(S); // $ target=tp_with_as MISSING: type=_o1:S3
let _o2 = tp_without_as(S); // $ target=tp_without_as MISSING: type=_o2:S3
let _o1 = tp_with_as(S); // $ target=tp_with_as type=_o1:S3
let _o2 = tp_without_as(S); // $ target=tp_without_as type=_o2:S3
let (
_o3, // $ MISSING: type=_o3:S3
_o4, // $ MISSING: type=_o4:bool
_o4, // $ type=_o4:bool
) = tp_assoc_from_supertrait(S); // $ target=tp_assoc_from_supertrait
let _o5 = tp_nested_assoc_type(Wrapper(S)); // $ target=tp_nested_assoc_type MISSING: type=_o5:S3
let w = Wrapper(S);
let _extracted = w.extract(); // $ target=extract type=_extracted:S3
let _extracted2 = w.extract2(); // $ target=extract2 MISSING: type=_extracted2:S3
}
}

View File

@@ -1748,7 +1748,7 @@ mod overloadable_operators {
let i64_mul = 17i64 * 18i64; // $ type=i64_mul:i64 target=mul
let i64_div = 19i64 / 20i64; // $ type=i64_div:i64 target=div
let i64_rem = 21i64 % 22i64; // $ type=i64_rem:i64 target=rem
let i64_param_add = param_add(1i64, 2i64); // $ target=param_add $ MISSING: type=i64_param_add:i64
let i64_param_add = param_add(1i64, 2i64); // $ target=param_add $ type=i64_param_add:i64
// Arithmetic assignment operators
let mut i64_add_assign = 23i64;
@@ -2053,7 +2053,7 @@ mod indexers {
let xs: [S; 1] = [S];
let x = xs[0].foo(); // $ target=foo type=x:S target=index
let y = param_index(vec, 0); // $ target=param_index $ MISSING: type=y:S
let y = param_index(vec, 0); // $ target=param_index $ type=y:S
analyze_slice(&xs); // $ target=analyze_slice
}

View File

@@ -95,7 +95,7 @@ module Make<RegexTreeViewSig TreeImpl> {
)
}
string toString() { result = this.(InfiniteRepetitionQuantifier).toString() }
string toString() { result = super.toString() }
}
/**

View File

@@ -104,11 +104,11 @@ module Make<RegexTreeViewSig TreeImpl> {
private class RegexpCharacterConstant instanceof RegExpConstant {
RegexpCharacterConstant() { this.isCharacter() }
string toString() { result = this.(RegExpConstant).toString() }
string toString() { result = super.toString() }
RegExpTerm getRootTerm() { result = this.(RegExpConstant).getRootTerm() }
RegExpTerm getRootTerm() { result = super.getRootTerm() }
string getValue() { result = this.(RegExpConstant).getValue() }
string getValue() { result = super.getValue() }
}
/**
@@ -578,11 +578,11 @@ module Make<RegexTreeViewSig TreeImpl> {
)
}
string toString() { result = this.(RegExpTerm).toString() }
string toString() { result = super.toString() }
RegExpTerm getAChild() { result = this.(RegExpTerm).getChild(_) }
RegExpTerm getAChild() { result = super.getChild(_) }
RegExpTerm getChild(int i) { result = this.(RegExpTerm).getChild(i) }
RegExpTerm getChild(int i) { result = super.getChild(i) }
}
/**
@@ -601,11 +601,11 @@ module Make<RegexTreeViewSig TreeImpl> {
)
}
string toString() { result = this.(RegExpTerm).toString() }
string toString() { result = super.toString() }
RegExpTerm getAChild() { result = this.(RegExpTerm).getAChild() }
RegExpTerm getAChild() { result = super.getAChild() }
RegExpTerm getChild(int i) { result = this.(RegExpTerm).getChild(i) }
RegExpTerm getChild(int i) { result = super.getChild(i) }
}
/**
@@ -621,11 +621,11 @@ module Make<RegexTreeViewSig TreeImpl> {
)
}
string toString() { result = this.(RegExpTerm).toString() }
string toString() { result = super.toString() }
RegExpTerm getAChild() { result = this.(RegExpTerm).getAChild() }
RegExpTerm getAChild() { result = super.getAChild() }
RegExpTerm getChild(int i) { result = this.(RegExpTerm).getChild(i) }
RegExpTerm getChild(int i) { result = super.getChild(i) }
}
/**

View File

@@ -78,6 +78,9 @@ module Make<LocationSig Location, InputSig<Location> Input> {
/** Holds if this list is empty. */
predicate isEmpty() { this = "" }
bindingset[this]
private int stringLength() { result = super.length() }
/** Gets the length of this list. */
bindingset[this]
pragma[inline_late]
@@ -115,19 +118,23 @@ module Make<LocationSig Location, InputSig<Location> Input> {
/** Holds if this list starts with `e`, followed by `suffix`. */
bindingset[this]
predicate isCons(Element e, UnboundList suffix) {
exists(string regexp | regexp = "^([0-9]+)\\.(.*)$" |
e = decode(this.regexpCapture(regexp, 1)) and
suffix = this.regexpCapture(regexp, 2)
exists(string elem |
// it is more efficient to not create a capture group for the suffix, since
// `regexpCapture` will then always join in both groups, only to afterwards filter
// based on the requested group (the group number is not part of the binding set
// of `regexpCapture`)
elem = this.regexpCapture("^([0-9]+)\\..*$", 1) and
e = decode(elem) and
suffix = this.suffix(elem.length() + 1)
)
}
/** Holds if this list starts with `prefix`, followed by `e`. */
bindingset[this]
predicate isSnoc(UnboundList prefix, Element e) {
exists(string regexp | regexp = "^(|.+\\.)([0-9]+)\\.$" |
prefix = this.regexpCapture(regexp, 1) and
e = decode(this.regexpCapture(regexp, 2))
)
// same remark as above about not using multiple capture groups
prefix = this.regexpCapture("^(|.+\\.)[0-9]+\\.$", 1) and
e = decode(this.substring(prefix.stringLength(), this.stringLength() - 1))
}
/** Gets the head of this list, if any. */