Merge branch 'main' into redsun82/rules_rust-update

This commit is contained in:
Paolo Tranquilli
2025-02-11 12:07:08 +01:00
25 changed files with 1292 additions and 1043 deletions

View File

@@ -10,7 +10,7 @@ toolchain go1.23.1
// bazel mod tidy
require (
golang.org/x/mod v0.23.0
golang.org/x/tools v0.29.0
golang.org/x/tools v0.30.0
)
require golang.org/x/sync v0.10.0 // indirect
require golang.org/x/sync v0.11.0 // indirect

View File

@@ -2,7 +2,7 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=

View File

@@ -463,6 +463,21 @@ private predicate interestingCond(SsaSourceVariable npecand, ConditionBlock cond
not cond.getCondition().(Expr).getAChildExpr*() = npecand.getAnAccess()
}
pragma[nomagic]
private ConditionBlock ssaIntegerGuard(SsaVariable v, boolean branch, int k, boolean is_k) {
result.getCondition() = integerGuard(v.getAUse(), branch, k, is_k)
}
pragma[nomagic]
private ConditionBlock ssaIntBoundGuard(SsaVariable v, boolean branch_with_lower_bound_k, int k) {
result.getCondition() = intBoundGuard(v.getAUse(), branch_with_lower_bound_k, k)
}
pragma[nomagic]
private ConditionBlock ssaEnumConstEquality(SsaVariable v, boolean polarity, EnumConstant c) {
result.getCondition() = enumConstEquality(v.getAUse(), polarity, c)
}
/** A pair of correlated conditions for a given NPE candidate. */
private predicate correlatedConditions(
SsaSourceVariable npecand, ConditionBlock cond1, ConditionBlock cond2, boolean inverted
@@ -485,25 +500,23 @@ private predicate correlatedConditions(
inverted = branch1.booleanXor(branch2)
)
or
exists(SsaVariable v, VarRead rv1, VarRead rv2, int k, boolean branch1, boolean branch2 |
rv1 = v.getAUse() and
rv2 = v.getAUse() and
cond1.getCondition() = integerGuard(rv1, branch1, k, true) and
cond1.getCondition() = integerGuard(rv1, branch1.booleanNot(), k, false) and
cond2.getCondition() = integerGuard(rv2, branch2, k, true) and
cond2.getCondition() = integerGuard(rv2, branch2.booleanNot(), k, false) and
exists(SsaVariable v, int k, boolean branch1, boolean branch2 |
cond1 = ssaIntegerGuard(v, branch1, k, true) and
cond1 = ssaIntegerGuard(v, branch1.booleanNot(), k, false) and
cond2 = ssaIntegerGuard(v, branch2, k, true) and
cond2 = ssaIntegerGuard(v, branch2.booleanNot(), k, false) and
inverted = branch1.booleanXor(branch2)
)
or
exists(SsaVariable v, int k, boolean branch1, boolean branch2 |
cond1.getCondition() = intBoundGuard(v.getAUse(), branch1, k) and
cond2.getCondition() = intBoundGuard(v.getAUse(), branch2, k) and
cond1 = ssaIntBoundGuard(v, branch1, k) and
cond2 = ssaIntBoundGuard(v, branch2, k) and
inverted = branch1.booleanXor(branch2)
)
or
exists(SsaVariable v, EnumConstant c, boolean pol1, boolean pol2 |
cond1.getCondition() = enumConstEquality(v.getAUse(), pol1, c) and
cond2.getCondition() = enumConstEquality(v.getAUse(), pol2, c) and
cond1 = ssaEnumConstEquality(v, pol1, c) and
cond2 = ssaEnumConstEquality(v, pol2, c) and
inverted = pol1.booleanXor(pol2)
)
or

File diff suppressed because it is too large Load Diff

View File

@@ -57,7 +57,7 @@ class BaseSsaSourceVariable extends TBaseSsaSourceVariable {
}
cached
private module SsaImpl {
private module BaseSsaImpl {
/** Gets the destination variable of an update of a tracked variable. */
cached
BaseSsaSourceVariable getDestVar(VariableUpdate upd) {
@@ -440,7 +440,7 @@ private module SsaImpl {
}
}
private import SsaImpl
private import BaseSsaImpl
private import SsaDefReaches
import SsaPublic

View File

@@ -10,6 +10,8 @@ private import FlowSummaryImpl as FlowSummaryImpl
private import DataFlowImplCommon as DataFlowImplCommon
private import semmle.code.java.controlflow.Guards
private import semmle.code.java.dataflow.RangeUtils
private import semmle.code.java.dataflow.SSA
private import SsaImpl as SsaImpl
/** Gets a string for approximating the name of a field. */
string approximateFieldContent(FieldContent fc) { result = fc.getField().getName().prefix(1) }
@@ -21,8 +23,34 @@ private predicate deadcode(Expr e) {
)
}
module SsaFlow {
module Impl = SsaImpl::DataFlowIntegration;
Impl::Node asNode(Node n) {
n = TSsaNode(result)
or
result.(Impl::ExprNode).getExpr() = n.asExpr()
or
result.(Impl::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr()
or
TExplicitParameterNode(result.(Impl::ParameterNode).getParameter()) = n
}
predicate localFlowStep(
SsaImpl::Impl::DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep
) {
Impl::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo), isUseStep)
}
predicate localMustFlowStep(SsaImpl::Impl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
Impl::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo))
}
}
cached
private module Cached {
private import semmle.code.java.controlflow.internal.GuardsLogic as GuardsLogic
cached
newtype TNode =
TExprNode(Expr e) {
@@ -31,6 +59,7 @@ private module Cached {
not e.getType() instanceof VoidType and
not e.getParent*() instanceof Annotation
} or
TSsaNode(SsaFlow::Impl::SsaNode node) or
TExplicitParameterNode(Parameter p) { exists(p.getCallable().getBody()) } or
TImplicitVarargsArray(Call c) {
c.getCallee().isVarargs() and
@@ -137,6 +166,8 @@ module Public {
result = this.(FieldValueNode).getField().getType()
or
result instanceof TypeObject and this instanceof AdditionalNode
or
result = this.(SsaNode).getDefinitionExt().getSourceVariable().getType()
}
/** Gets the callable in which this node occurs. */
@@ -358,6 +389,18 @@ module Public {
private import Public
class SsaNode extends Node, TSsaNode {
private SsaFlow::Impl::SsaNode node;
SsaNode() { this = TSsaNode(node) }
SsaImpl::Impl::DefinitionExt getDefinitionExt() { result = node.getDefinitionExt() }
override Location getLocation() { result = node.getLocation() }
override string toString() { result = node.toString() }
}
private class NewExpr extends PostUpdateNode, TExprNode {
NewExpr() { exists(ClassInstanceExpr cie | this = TExprNode(cie)) }
@@ -398,7 +441,8 @@ module Private {
result.asSummarizedCallable() = n.(FlowSummaryNode).getSummarizedCallable() or
result.asCallable() = n.(CaptureNode).getSynthesizedCaptureNode().getEnclosingCallable() or
result.asFieldScope() = n.(FieldValueNode).getField() or
result.asCallable() = any(Expr e | n.(AdditionalNode).nodeAt(e, _)).getEnclosingCallable()
result.asCallable() = any(Expr e | n.(AdditionalNode).nodeAt(e, _)).getEnclosingCallable() or
result.asCallable() = n.(SsaNode).getDefinitionExt().getBasicBlock().getEnclosingCallable()
}
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */

View File

@@ -581,7 +581,11 @@ predicate forceHighPrecision(Content c) {
}
/** Holds if `n` should be hidden from path explanations. */
predicate nodeIsHidden(Node n) { n instanceof FlowSummaryNode }
predicate nodeIsHidden(Node n) {
n instanceof FlowSummaryNode
or
n instanceof SsaNode
}
class LambdaCallKind = Method; // the "apply" method in the functional interface

View File

@@ -10,6 +10,7 @@ private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.dataflow.FlowSteps
private import semmle.code.java.dataflow.FlowSummary
private import semmle.code.java.dataflow.InstanceAccess
private import semmle.code.java.dataflow.internal.SsaImpl as SsaImpl
private import FlowSummaryImpl as FlowSummaryImpl
private import TaintTrackingUtil as TaintTrackingUtil
private import DataFlowNodes
@@ -101,6 +102,10 @@ predicate hasNonlocalValue(FieldRead fr) {
)
}
private predicate capturedVariableRead(Node n) {
n.asExpr().(VarRead).getVariable() instanceof CapturedVariable
}
cached
private module Cached {
/**
@@ -110,7 +115,7 @@ private module Cached {
predicate localFlowStep(Node node1, Node node2) {
simpleLocalFlowStep0(node1, node2, _)
or
adjacentUseUse(node1.asExpr(), node2.asExpr())
SsaFlow::localFlowStep(_, node1, node2, _)
or
// Simple flow through library code is included in the exposed local
// step relation, even though flow is technically inter-procedural
@@ -127,6 +132,20 @@ private module Cached {
predicate simpleLocalFlowStep(Node node1, Node node2, string model) {
simpleLocalFlowStep0(node1, node2, model)
or
exists(boolean isUseStep |
SsaFlow::localFlowStep(_, node1, node2, isUseStep) and
not capturedVariableRead(node2) and
model = ""
|
isUseStep = false
or
isUseStep = true and
not exists(FieldRead fr |
hasNonlocalValue(fr) and fr.getField().isStatic() and fr = node1.asExpr()
) and
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(node1, _)
)
or
any(AdditionalValueStep a).step(node1, node2) and
pragma[only_bind_out](node1.getEnclosingCallable()) =
pragma[only_bind_out](node2.getEnclosingCallable()) and
@@ -149,14 +168,7 @@ predicate localMustFlowStep(Node node1, Node node2) {
node2.(ImplicitInstanceAccess).getInstanceAccess().(OwnInstanceAccess).getEnclosingCallable()
)
or
exists(SsaImplicitInit init |
init.isParameterDefinition(node1.asParameter()) and init.getAUse() = node2.asExpr()
)
or
exists(SsaExplicitUpdate upd |
upd.getDefiningExpr().(VariableAssign).getSource() = node1.asExpr() and
upd.getAUse() = node2.asExpr()
)
SsaFlow::localMustFlowStep(_, node1, node2)
or
node2.asExpr().(CastingExpr).getExpr() = node1.asExpr()
or
@@ -168,10 +180,6 @@ predicate localMustFlowStep(Node node1, Node node2) {
import Cached
private predicate capturedVariableRead(Node n) {
n.asExpr().(VarRead).getVariable() instanceof CapturedVariable
}
/**
* Holds if there is a data flow step from `e1` to `e2` that only steps from
* child to parent in the AST.
@@ -213,34 +221,8 @@ predicate simpleAstFlowStep(Expr e1, Expr e2) {
private predicate simpleLocalFlowStep0(Node node1, Node node2, string model) {
(
TaintTrackingUtil::forceCachingInSameStage() and
// Variable flow steps through adjacent def-use and use-use pairs.
exists(SsaExplicitUpdate upd |
upd.getDefiningExpr().(VariableAssign).getSource() = node1.asExpr() or
upd.getDefiningExpr().(AssignOp) = node1.asExpr() or
upd.getDefiningExpr().(RecordBindingVariableExpr) = node1.asExpr()
|
node2.asExpr() = upd.getAFirstUse() and
not capturedVariableRead(node2)
)
or
exists(SsaImplicitInit init |
init.isParameterDefinition(node1.asParameter()) and
node2.asExpr() = init.getAFirstUse() and
not capturedVariableRead(node2)
)
or
adjacentUseUse(node1.asExpr(), node2.asExpr()) and
not exists(FieldRead fr |
hasNonlocalValue(fr) and fr.getField().isStatic() and fr = node1.asExpr()
) and
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(node1, _) and
not capturedVariableRead(node2)
or
ThisFlow::adjacentThisRefs(node1, node2)
or
adjacentUseUse(node1.(PostUpdateNode).getPreUpdateNode().asExpr(), node2.asExpr()) and
not capturedVariableRead(node2)
or
ThisFlow::adjacentThisRefs(node1.(PostUpdateNode).getPreUpdateNode(), node2)
or
simpleAstFlowStep(node1.asExpr(), node2.asExpr())
@@ -404,11 +386,7 @@ signature predicate guardChecksSig(Guard g, Expr e, boolean branch);
module BarrierGuard<guardChecksSig/3 guardChecks> {
/** Gets a node that is safely guarded by the given guard check. */
Node getABarrierNode() {
exists(Guard g, SsaVariable v, boolean branch, VarRead use |
guardChecks(g, v.getAUse(), branch) and
use = v.getAUse() and
g.controls(use.getBasicBlock(), branch) and
result.asExpr() = use
)
SsaFlow::asNode(result) =
SsaImpl::DataFlowIntegration::BarrierGuard<guardChecks/3>::getABarrierNode()
}
}

View File

@@ -0,0 +1,746 @@
import java
private import codeql.ssa.Ssa as SsaImplCommon
private import semmle.code.java.dataflow.SSA
private import semmle.code.java.dispatch.VirtualDispatch
private import semmle.code.java.dispatch.WrappedInvocation
private import semmle.code.java.controlflow.Guards as Guards
predicate fieldAccessInCallable(FieldAccess fa, Field f, Callable c) {
f = fa.getField() and
c = fa.getEnclosingCallable()
}
cached
newtype TSsaSourceVariable =
TLocalVar(Callable c, LocalScopeVariable v) {
c = v.getCallable() or c = v.getAnAccess().getEnclosingCallable()
} or
TPlainField(Callable c, Field f) {
exists(FieldRead fr |
fieldAccessInCallable(fr, f, c) and
(fr.isOwnFieldAccess() or f.isStatic())
)
} or
TEnclosingField(Callable c, Field f, RefType t) {
exists(FieldRead fr | fieldAccessInCallable(fr, f, c) and fr.isEnclosingFieldAccess(t))
} or
TQualifiedField(Callable c, SsaSourceVariable q, InstanceField f) {
exists(FieldRead fr | fieldAccessInCallable(fr, f, c) and fr.getQualifier() = q.getAnAccess())
}
private module TrackedVariablesImpl {
/** Gets the number of accesses of `f`. */
private int numberOfAccesses(SsaSourceField f) {
result = strictcount(FieldAccess fa | fa = f.getAnAccess())
}
/** Holds if `f` is accessed inside a loop. */
private predicate loopAccessed(SsaSourceField f) {
exists(LoopStmt l, FieldRead fr | fr = f.getAnAccess() |
l.getBody() = fr.getEnclosingStmt().getEnclosingStmt*() or
l.getCondition() = fr.getParent*() or
l.(ForStmt).getAnUpdate() = fr.getParent*()
)
}
/** Holds if `f` is accessed more than once or inside a loop. */
private predicate multiAccessed(SsaSourceField f) { loopAccessed(f) or 1 < numberOfAccesses(f) }
/**
* Holds if `f` is a field that is interesting as a basis for SSA.
*
* - A field that is read twice is interesting as we want to know whether the
* reads refer to the same value.
* - A field that is both written and read is interesting as we want to know
* whether the read might get the written value.
* - A field that is read in a loop is interesting as we want to know whether
* the value is the same in different iterations (that is, whether the SSA
* definition can be placed outside the loop).
* - A volatile field is never interesting, since all reads must reread from
* memory and we are forced to assume that the value can change at any point.
*/
cached
predicate trackField(SsaSourceField f) { multiAccessed(f) and not f.isVolatile() }
/**
* The variables that form the basis of the non-trivial SSA construction.
* Fields that aren't tracked get a trivial SSA construction (a definition
* prior to every read).
*/
class TrackedVar extends SsaSourceVariable {
TrackedVar() {
this = TLocalVar(_, _) or
trackField(this)
}
}
class TrackedField extends TrackedVar, SsaSourceField { }
}
private import TrackedVariablesImpl
private predicate untrackedFieldWrite(BasicBlock bb, int i, SsaSourceVariable v) {
v =
any(SsaSourceField nf |
bb.getNode(i + 1) = nf.getAnAccess().(FieldRead).getControlFlowNode() and not trackField(nf)
)
}
/** Gets the definition point of a nested class in the parent scope. */
private ControlFlowNode parentDef(NestedClass nc) {
nc.(AnonymousClass).getClassInstanceExpr().getControlFlowNode() = result or
nc.(LocalClass).getLocalTypeDeclStmt().getControlFlowNode() = result
}
/**
* Gets the enclosing type of a nested class.
*
* Differs from `RefType.getEnclosingType()` by including anonymous classes defined by lambdas.
*/
private RefType desugaredGetEnclosingType(NestedClass inner) {
exists(ControlFlowNode node |
node = parentDef(inner) and
node.getEnclosingCallable().getDeclaringType() = result
)
}
/**
* Gets the control flow node at which the variable is read to get the value for
* a `VarAccess` inside a closure. `capturedvar` is the variable in its defining
* scope, and `closurevar` is the variable in the closure.
*/
private ControlFlowNode captureNode(TrackedVar capturedvar, TrackedVar closurevar) {
exists(
LocalScopeVariable v, Callable inner, Callable outer, NestedClass innerclass, VarAccess va
|
va.getVariable() = v and
inner = va.getEnclosingCallable() and
outer = v.getCallable() and
inner != outer and
inner.getDeclaringType() = innerclass and
result = parentDef(desugaredGetEnclosingType*(innerclass)) and
result.getEnclosingStmt().getEnclosingCallable() = outer and
capturedvar = TLocalVar(outer, v) and
closurevar = TLocalVar(inner, v)
)
}
/** Holds if the value of `v` is captured in `b` at index `i`. */
private predicate variableCapture(TrackedVar capturedvar, TrackedVar closurevar, BasicBlock b, int i) {
b.getNode(i) = captureNode(capturedvar, closurevar)
}
/** Holds if `n` must update the locally tracked variable `v`. */
pragma[nomagic]
private predicate certainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
exists(VariableUpdate a | a.getControlFlowNode() = n | getDestVar(a) = v) and
b.getNode(i) = n and
hasDominanceInformation(b)
or
certainVariableUpdate(v.getQualifier(), n, b, i)
}
/** Holds if `v` has an implicit definition at the entry, `b`, of the callable. */
pragma[nomagic]
private predicate hasEntryDef(TrackedVar v, BasicBlock b) {
exists(LocalScopeVariable l, Callable c |
v = TLocalVar(c, l) and c.getBody().getControlFlowNode() = b
|
l instanceof Parameter or
l.getCallable() != c
)
or
v instanceof SsaSourceField and v.getEnclosingCallable().getBody().getControlFlowNode() = b
}
/** Holds if `n` might update the locally tracked variable `v`. */
pragma[nomagic]
private predicate uncertainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
exists(Call c | c = n.asCall() | updatesNamedField(c, v, _)) and
b.getNode(i) = n and
hasDominanceInformation(b)
or
uncertainVariableUpdate(v.getQualifier(), n, b, i)
}
private module SsaInput implements SsaImplCommon::InputSig<Location> {
private import java as J
private import semmle.code.java.controlflow.Dominance as Dom
class BasicBlock = J::BasicBlock;
class ControlFlowNode = J::ControlFlowNode;
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { Dom::bbIDominates(result, bb) }
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getABBSuccessor() }
class SourceVariable = SsaSourceVariable;
/**
* Holds if the `i`th node of basic block `bb` is a (potential) write to source
* variable `v`. The Boolean `certain` indicates whether the write is certain.
*
* This includes implicit writes via calls.
*/
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
certainVariableUpdate(v, _, bb, i) and
certain = true
or
untrackedFieldWrite(bb, i, v) and
certain = true
or
hasEntryDef(v, bb) and
i = 0 and
certain = true
or
uncertainVariableUpdate(v, _, bb, i) and
certain = false
}
/**
* Holds if the `i`th of basic block `bb` reads source variable `v`.
*
* This includes implicit reads via calls.
*/
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
exists(VarRead use |
v.getAnAccess() = use and bb.getNode(i) = use.getControlFlowNode() and certain = true
)
or
variableCapture(v, _, bb, i) and
certain = false
}
}
import SsaImplCommon::Make<Location, SsaInput> as Impl
final class Definition = Impl::Definition;
final class WriteDefinition = Impl::WriteDefinition;
final class UncertainWriteDefinition = Impl::UncertainWriteDefinition;
final class PhiNode = Impl::PhiNode;
class UntrackedDef extends Definition {
private VarRead read;
UntrackedDef() { ssaUntrackedDef(this, read) }
string toString() { result = read.toString() }
Location getLocation() { result = read.getLocation() }
}
cached
private module Cached {
/** Gets the destination variable of an update of a tracked variable. */
cached
TrackedVar getDestVar(VariableUpdate upd) {
result.getAnAccess() = upd.(Assignment).getDest()
or
exists(LocalVariableDecl v | v = upd.(LocalVariableDeclExpr).getVariable() |
result = TLocalVar(v.getCallable(), v)
)
or
result.getAnAccess() = upd.(UnaryAssignExpr).getExpr()
}
cached
predicate ssaExplicitUpdate(SsaUpdate def, VariableUpdate upd) {
exists(SsaSourceVariable v, BasicBlock bb, int i |
def.definesAt(v, bb, i) and
certainVariableUpdate(v, upd.getControlFlowNode(), bb, i) and
getDestVar(upd) = def.getSourceVariable()
)
}
cached
predicate ssaUntrackedDef(Definition def, VarRead read) {
exists(SsaSourceVariable v, BasicBlock bb, int i |
def.definesAt(v, bb, i) and
untrackedFieldWrite(bb, i, v) and
read.getControlFlowNode() = bb.getNode(i + 1)
)
}
/*
* The SSA construction for a field `f` relies on implicit update nodes at
* every call site that conceivably could reach an update of the field.
*
* At a first approximation we need to find update paths of the form:
* Callable --(callEdge)-->* Callable(setter of f)
*
* This can be improved by excluding paths ending in:
* Constructor --(intraInstanceCallEdge)-->+ Method(setter of this.f)
* as these updates are guaranteed not to alias with the `f` under
* consideration.
*
* This set of paths can be expressed positively by noting that those
* that set `this.f` end in zero or more `intraInstanceCallEdge`s between
* methods, and before those is either the originating `Call` or a
* `crossInstanceCallEdge`.
*/
/**
* Holds if `fw` is a field write that is not relevant as an implicit SSA
* update, since it is an initialization and therefore cannot alias.
*/
private predicate init(FieldWrite fw) {
fw.getEnclosingCallable() instanceof InitializerMethod
or
fw.getEnclosingCallable() instanceof Constructor and fw.isOwnFieldAccess()
or
exists(LocalVariableDecl v |
v.getAnAccess() = fw.getQualifier() and
forex(VariableAssign va | va.getDestVar() = v and exists(va.getSource()) |
va.getSource() instanceof ClassInstanceExpr
)
)
}
/**
* Holds if `fw` is an update of `f` in `c` that is relevant for SSA construction.
*/
cached
predicate relevantFieldUpdate(Callable c, Field f, FieldWrite fw) {
fw = f.getAnAccess() and
not init(fw) and
fw.getEnclosingCallable() = c and
exists(TrackedField nf | nf.getField() = f)
}
/** Holds if `c` can change the value of `this.f` and is relevant for SSA construction. */
private predicate setsOwnField(Method c, Field f) {
exists(FieldWrite fw | relevantFieldUpdate(c, f, fw) and fw.isOwnFieldAccess())
}
/**
* Holds if `c` can change the value of `f` and is relevant for SSA
* construction excluding those cases covered by `setsOwnField`.
*/
private predicate setsOtherField(Callable c, Field f) {
exists(FieldWrite fw | relevantFieldUpdate(c, f, fw) and not fw.isOwnFieldAccess())
}
pragma[nomagic]
private predicate innerclassSupertypeStar(InnerClass t1, RefType t2) {
t1.getASourceSupertype*().getSourceDeclaration() = t2
}
/**
* Holds if `(c1,m2)` is a call edge to a method that does not change the value
* of `this`.
*
* Constructor-to-constructor calls can also be intra-instance, but are not
* included, as this does not affect whether a call chain ends in
*
* ```
* Constructor --(intraInstanceCallEdge)-->+ Method(setter of this.f)
* ```
*/
private predicate intraInstanceCallEdge(Callable c1, Method m2) {
exists(MethodCall ma, RefType t1 |
ma.getCaller() = c1 and
m2 = viableImpl_v2(ma) and
not m2.isStatic() and
(
not exists(ma.getQualifier()) or
ma.getQualifier() instanceof ThisAccess or
ma.getQualifier() instanceof SuperAccess
) and
c1.getDeclaringType() = t1 and
if t1 instanceof InnerClass
then
innerclassSupertypeStar(t1, ma.getCallee().getSourceDeclaration().getDeclaringType()) and
not exists(ma.getQualifier().(ThisAccess).getQualifier()) and
not exists(ma.getQualifier().(SuperAccess).getQualifier())
else any()
)
}
private Callable tgt(Call c) {
result = viableImpl_v2(c)
or
result = getRunnerTarget(c)
or
c instanceof ConstructorCall and result = c.getCallee().getSourceDeclaration()
}
/** Holds if `(c1,c2)` is an edge in the call graph. */
private predicate callEdge(Callable c1, Callable c2) {
exists(Call c | c.getCaller() = c1 and c2 = tgt(c))
}
/** Holds if `(c1,c2)` is an edge in the call graph excluding `intraInstanceCallEdge`. */
private predicate crossInstanceCallEdge(Callable c1, Callable c2) {
callEdge(c1, c2) and not intraInstanceCallEdge(c1, c2)
}
/**
* Holds if `call` is relevant as a potential update of `f`. This requires the
* existence of an update to `f` somewhere.
*/
private predicate relevantCall(Call call, TrackedField f) {
call.getEnclosingCallable() = f.getEnclosingCallable() and
relevantFieldUpdate(_, f.getField(), _)
}
private predicate source(Call call, TrackedField f, Field field, Callable c, boolean fresh) {
relevantCall(call, f) and
field = f.getField() and
c = tgt(call) and
if c instanceof Constructor then fresh = true else fresh = false
}
/**
* A callable in a potential call-chain between a source that cares about the
* value of some field `f` and a sink that may overwrite `f`. The boolean
* `fresh` indicates whether the instance `this` in `c` has been freshly
* allocated along the call-chain.
*/
private newtype TCallableNode =
MkCallableNode(Callable c, boolean fresh) { source(_, _, _, c, fresh) or edge(_, c, fresh) }
private predicate edge(TCallableNode n, Callable c2, boolean f2) {
exists(Callable c1, boolean f1 | n = MkCallableNode(c1, f1) |
intraInstanceCallEdge(c1, c2) and f2 = f1
or
crossInstanceCallEdge(c1, c2) and
if c2 instanceof Constructor then f2 = true else f2 = false
)
}
private predicate edge(TCallableNode n1, TCallableNode n2) {
exists(Callable c2, boolean f2 |
edge(n1, c2, f2) and
n2 = MkCallableNode(c2, f2)
)
}
pragma[noinline]
private predicate source(Call call, TrackedField f, Field field, TCallableNode n) {
exists(Callable c, boolean fresh |
source(call, f, field, c, fresh) and
n = MkCallableNode(c, fresh)
)
}
private predicate sink(Callable c, Field f, TCallableNode n) {
setsOwnField(c, f) and n = MkCallableNode(c, false)
or
setsOtherField(c, f) and n = MkCallableNode(c, _)
}
private predicate prunedNode(TCallableNode n) {
sink(_, _, n)
or
exists(TCallableNode mid | edge(n, mid) and prunedNode(mid))
}
private predicate prunedEdge(TCallableNode n1, TCallableNode n2) {
prunedNode(n1) and
prunedNode(n2) and
edge(n1, n2)
}
private predicate edgePlus(TCallableNode c1, TCallableNode c2) = fastTC(prunedEdge/2)(c1, c2)
/**
* Holds if there exists a call-chain originating in `call` that can update `f` on some instance
* where `f` and `call` share the same enclosing callable in which a
* `FieldRead` of `f` is reachable from `call`.
*/
pragma[noopt]
private predicate updatesNamedFieldImpl(Call call, TrackedField f, Callable setter) {
exists(TCallableNode src, TCallableNode sink, Field field |
source(call, f, field, src) and
sink(setter, field, sink) and
(src = sink or edgePlus(src, sink))
)
}
bindingset[call, f]
pragma[inline_late]
private predicate updatesNamedField0(Call call, TrackedField f, Callable setter) {
updatesNamedField(call, f, setter)
}
cached
predicate defUpdatesNamedField(SsaImplicitUpdate def, TrackedField f, Callable setter) {
f = def.getSourceVariable() and
updatesNamedField0(def.getCfgNode().asCall(), f, setter)
}
cached
predicate ssaUncertainImplicitUpdate(SsaImplicitUpdate def) {
exists(SsaSourceVariable v, BasicBlock bb, int i |
def.definesAt(v, bb, i) and
uncertainVariableUpdate(v, _, bb, i)
)
}
cached
predicate ssaImplicitInit(WriteDefinition def) {
exists(SsaSourceVariable v, BasicBlock bb, int i |
def.definesAt(v, bb, i) and
hasEntryDef(v, bb) and
i = 0
)
}
pragma[nomagic]
private predicate captureDefReaches(Definition def, SsaInput::BasicBlock bb2, int i2) {
variableCapture(def.getSourceVariable(), _, _, _) and
exists(SsaInput::BasicBlock bb1, int i1 |
Impl::adjacentDefRead(def, bb1, i1, bb2, i2) and
def.definesAt(_, bb1, i1)
)
or
exists(SsaInput::BasicBlock bb3, int i3 |
captureDefReaches(def, bb3, i3) and
SsaInput::variableRead(bb3, i3, _, _) and
Impl::adjacentDefRead(def, bb3, i3, bb2, i2)
)
}
/** Holds if `init` is a closure variable that captures the value of `capturedvar`. */
cached
predicate captures(SsaImplicitInit init, SsaVariable capturedvar) {
exists(BasicBlock bb, int i |
captureDefReaches(capturedvar, bb, i) and
variableCapture(capturedvar.getSourceVariable(), init.getSourceVariable(), bb, i)
)
}
/**
* Holds if the SSA definition of `v` at `def` reaches `redef` without crossing another
* SSA definition of `v`.
*/
cached
predicate ssaDefReachesUncertainDef(TrackedSsaDef def, SsaUncertainImplicitUpdate redef) {
Impl::uncertainWriteDefinitionInput(redef, def)
}
pragma[nomagic]
private predicate defReaches(Definition def, DataFlowIntegration::Node node) {
exists(DataFlowIntegration::SsaDefinitionExtNode nodeFrom |
nodeFrom.getDefinitionExt() = def and
DataFlowIntegrationImpl::localFlowStep(_, nodeFrom, node, false)
)
or
exists(DataFlowIntegration::Node mid |
defReaches(def, mid) and
DataFlowIntegrationImpl::localFlowStep(_, mid, node, _)
|
// flow into phi input node
mid instanceof DataFlowIntegration::SsaInputNode
or
// flow into definition
mid instanceof DataFlowIntegration::SsaDefinitionExtNode
)
}
/**
* Holds if the value defined at `def` can reach `use` without passing through
* any other uses, but possibly through phi nodes and uncertain implicit updates.
*/
cached
predicate firstUse(Definition def, VarRead use) {
exists(DataFlowIntegration::ExprNode nodeTo |
nodeTo.getExpr() = use and
defReaches(def, nodeTo)
)
}
cached
VarRead getAUse(Definition def) {
exists(SsaSourceVariable v, BasicBlock bb, int i |
Impl::ssaDefReachesRead(v, def, bb, i) and
result.getControlFlowNode() = bb.getNode(i) and
result = v.getAnAccess()
)
}
cached
predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def) {
Impl::ssaDefReachesEndOfBlock(bb, def, _)
}
cached
predicate phiHasInputFromBlock(PhiNode phi, Definition inp, BasicBlock bb) {
Impl::phiHasInputFromBlock(phi, inp, bb)
}
cached
module DataFlowIntegration {
import DataFlowIntegrationImpl
cached
predicate localFlowStep(Impl::DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep) {
not def instanceof UntrackedDef and
DataFlowIntegrationImpl::localFlowStep(def, nodeFrom, nodeTo, isUseStep)
}
cached
predicate localMustFlowStep(Impl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
not def instanceof UntrackedDef and
DataFlowIntegrationImpl::localMustFlowStep(def, nodeFrom, nodeTo)
}
signature predicate guardChecksSig(Guards::Guard g, Expr e, boolean branch);
cached // nothing is actually cached
module BarrierGuard<guardChecksSig/3 guardChecks> {
private predicate guardChecksAdjTypes(
DataFlowIntegrationInput::Guard g, DataFlowIntegrationInput::Expr e, boolean branch
) {
guardChecks(g, e, branch)
}
private Node getABarrierNodeImpl() {
result = DataFlowIntegrationImpl::BarrierGuard<guardChecksAdjTypes/3>::getABarrierNode()
}
predicate getABarrierNode = getABarrierNodeImpl/0;
}
}
cached
module SsaPublic {
pragma[nomagic]
private predicate useReaches(VarRead use, DataFlowIntegration::Node node, boolean sameVar) {
exists(DataFlowIntegration::ExprNode nodeFrom |
nodeFrom.getExpr() = use and
DataFlowIntegration::localFlowStep(_, nodeFrom, node, true) and
sameVar = true
)
or
exists(DataFlowIntegration::Node mid, boolean sameVarMid |
useReaches(use, mid, sameVarMid) and
DataFlowIntegration::localFlowStep(_, mid, node, _)
|
exists(Impl::DefinitionExt def |
// flow into definition
def = mid.(DataFlowIntegration::SsaDefinitionExtNode).getDefinitionExt()
or
// flow into phi input node
def = mid.(DataFlowIntegration::SsaInputNode).getDefinitionExt()
|
if def instanceof Impl::PhiReadNode then sameVar = sameVarMid else sameVar = false
)
)
}
/**
* Holds if `use1` and `use2` form an adjacent use-use-pair of the same SSA
* variable, that is, the value read in `use1` can reach `use2` without passing
* through any other use or any SSA definition of the variable.
*/
cached
predicate adjacentUseUseSameVar(VarRead use1, VarRead use2) {
exists(DataFlowIntegration::ExprNode nodeTo |
nodeTo.getExpr() = use2 and
useReaches(use1, nodeTo, true)
)
}
/**
* Holds if `use1` and `use2` form an adjacent use-use-pair of the same
* `SsaSourceVariable`, that is, the value read in `use1` can reach `use2`
* without passing through any other use or any SSA definition of the variable
* except for phi nodes and uncertain implicit updates.
*/
cached
predicate adjacentUseUse(VarRead use1, VarRead use2) {
exists(DataFlowIntegration::ExprNode nodeTo |
nodeTo.getExpr() = use2 and
useReaches(use1, nodeTo, _)
)
}
}
/**
* Provides internal implementation predicates that are not cached and should not
* be used outside of this file.
*/
cached // needed to avoid compilation error; has no actual effect
module Internal {
predicate updatesNamedField = updatesNamedFieldImpl/3; // use alias to avoid caching
}
}
import Cached
private import Internal
/**
* An SSA definition excluding those variables that use a trivial SSA construction.
*/
private class TrackedSsaDef extends Definition {
TrackedSsaDef() { not this.getSourceVariable() = any(SsaSourceField f | not trackField(f)) }
/**
* Holds if this SSA definition occurs at the specified position.
* Phi nodes are placed at index -1.
*/
predicate definesAt(TrackedVar v, BasicBlock b, int i) { super.definesAt(v, b, i) }
}
private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInputSig {
private import java as J
class Expr instanceof J::Expr {
string toString() { result = super.toString() }
Location getLocation() { result = super.getLocation() }
predicate hasCfgNode(BasicBlock bb, int i) {
super.getControlFlowNode() = bb.(J::BasicBlock).getNode(i)
}
}
Expr getARead(Definition def) { result = getAUse(def) }
class Parameter = J::Parameter;
predicate ssaDefAssigns(Impl::WriteDefinition def, Expr value) {
exists(VariableUpdate upd | upd = def.(SsaExplicitUpdate).getDefiningExpr() |
value = upd.(VariableAssign).getSource() or
value = upd.(AssignOp) or
value = upd.(RecordBindingVariableExpr)
)
}
predicate ssaDefInitializesParam(Impl::WriteDefinition def, Parameter p) {
def.(SsaImplicitInit).getSourceVariable() =
any(SsaSourceVariable v |
v.getVariable() = p and
v.getEnclosingCallable() = p.getCallable()
)
}
predicate allowFlowIntoUncertainDef(UncertainWriteDefinition def) {
def instanceof SsaUncertainImplicitUpdate
}
class Guard extends Guards::Guard {
predicate hasCfgNode(BasicBlock bb, int i) {
this = bb.getNode(i).asExpr()
or
this = bb.getNode(i).asStmt()
}
}
/** Holds if the guard `guard` controls block `bb` upon evaluating to `branch`. */
predicate guardControlsBlock(Guard guard, BasicBlock bb, boolean branch) {
guard.controls(bb, branch)
}
/** Gets an immediate conditional successor of basic block `bb`, if any. */
BasicBlock getAConditionalBasicBlockSuccessor(BasicBlock bb, boolean branch) {
result = bb.(Guards::ConditionBlock).getTestSuccessor(branch)
}
}
private module DataFlowIntegrationImpl = Impl::DataFlowIntegration<DataFlowIntegrationInput>;

View File

@@ -6,46 +6,36 @@ import java
import semmle.code.java.dataflow.SSA
private import semmle.code.java.frameworks.Assertions
private predicate emptyDecl(SsaExplicitUpdate ssa) {
exists(LocalVariableDeclExpr decl |
decl = ssa.getDefiningExpr() and
not exists(decl.getInit()) and
not exists(EnhancedForStmt for | for.getVariable() = decl)
)
private predicate emptyDecl(LocalVariableDeclExpr decl) {
not exists(decl.getInit()) and
not exists(EnhancedForStmt for | for.getVariable() = decl)
}
/** A dead variable update. */
predicate deadLocal(VariableUpdate upd) {
upd.getDestVar() instanceof LocalScopeVariable and
not exists(SsaExplicitUpdate ssa | upd = ssa.getDefiningExpr()) and
not emptyDecl(upd) and
not readImplicitly(upd, _)
}
/**
* A dead SSA variable. Excludes parameters, and phi nodes are never dead, so only includes `VariableUpdate`s.
* A dead variable update that is expected to be dead as indicated by an assertion.
*/
predicate deadLocal(SsaExplicitUpdate ssa) {
ssa.getSourceVariable().getVariable() instanceof LocalScopeVariable and
not exists(ssa.getAUse()) and
not exists(SsaPhiNode phi | phi.getAPhiInput() = ssa) and
not exists(SsaImplicitInit init | init.captures(ssa)) and
not emptyDecl(ssa) and
not readImplicitly(ssa, _)
}
predicate expectedDead(VariableUpdate upd) { assertFail(upd.getBasicBlock(), _) }
/**
* A dead SSA variable that is expected to be dead as indicated by an assertion.
* A dead update that is overwritten by a live update.
*/
predicate expectedDead(SsaExplicitUpdate ssa) {
deadLocal(ssa) and
assertFail(ssa.getBasicBlock(), _)
}
/**
* A dead SSA variable that is overwritten by a live SSA definition.
*/
predicate overwritten(SsaExplicitUpdate ssa) {
deadLocal(ssa) and
exists(SsaExplicitUpdate overwrite |
overwrite.getSourceVariable() = ssa.getSourceVariable() and
predicate overwritten(VariableUpdate upd) {
deadLocal(upd) and
exists(VariableUpdate overwrite |
overwrite.getDestVar() = upd.getDestVar() and
not deadLocal(overwrite) and
not overwrite.getDefiningExpr() instanceof LocalVariableDeclExpr and
not overwrite instanceof LocalVariableDeclExpr and
exists(BasicBlock bb1, BasicBlock bb2, int i, int j |
bb1.getNode(i) = ssa.getCfgNode() and
bb2.getNode(j) = overwrite.getCfgNode()
bb1.getNode(i) = upd.getControlFlowNode() and
bb2.getNode(j) = overwrite.getControlFlowNode()
|
bb1.getABBSuccessor+() = bb2
or
@@ -63,9 +53,9 @@ predicate read(LocalScopeVariable v) {
readImplicitly(_, v)
}
private predicate readImplicitly(SsaExplicitUpdate ssa, LocalScopeVariable v) {
v = ssa.getSourceVariable().getVariable() and
exists(TryStmt try | try.getAResourceVariable() = ssa.getDefiningExpr().getDestVar())
predicate readImplicitly(VariableUpdate upd, LocalScopeVariable v) {
v = upd.getDestVar() and
exists(TryStmt try | try.getAResourceVariable() = upd.getDestVar())
}
/**

View File

@@ -40,13 +40,12 @@ predicate excludedInit(Type t, Expr decl) {
)
}
from VariableUpdate def, LocalScopeVariable v, SsaExplicitUpdate ssa
from VariableUpdate def, LocalScopeVariable v
where
def = ssa.getDefiningExpr() and
v = ssa.getSourceVariable().getVariable() and
deadLocal(ssa) and
not expectedDead(ssa) and
overwritten(ssa) and
def.getDestVar() = v and
deadLocal(def) and
not expectedDead(def) and
overwritten(def) and
not exists(LocalVariableDeclExpr decl | def = decl |
excludedInit(decl.getVariable().getType(), decl.getInit())
)

View File

@@ -11,13 +11,12 @@
import java
import DeadLocals
from VariableUpdate def, LocalScopeVariable v, SsaExplicitUpdate ssa
from VariableUpdate def, LocalScopeVariable v
where
def = ssa.getDefiningExpr() and
v = ssa.getSourceVariable().getVariable() and
deadLocal(ssa) and
not expectedDead(ssa) and
not overwritten(ssa) and
def.getDestVar() = v and
deadLocal(def) and
not expectedDead(def) and
not overwritten(def) and
read(v) and
not def.(AssignExpr).getSource() instanceof NullLiteral and
(def instanceof Assignment or def.(UnaryAssignExpr).getParent() instanceof ExprStmt)

View File

@@ -1,7 +1,10 @@
| A.java:14:14:14:16 | "A" : String | A.java:14:7:14:20 | SSA def(a) : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:14:11:14:20 | f2(...) : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:15:16:15:16 | a : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:15:16:15:22 | get(...) : String |
| A.java:14:14:14:16 | "A" : String | A.java:18:8:18:15 | p : String |
| A.java:14:14:14:16 | "A" : String | A.java:18:25:40:3 | SSA def(p) : String |
| A.java:14:14:14:16 | "A" : String | A.java:28:7:38:5 | SSA def(a) : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:28:11:38:5 | new (...) : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:28:11:38:5 | p : String |
| A.java:14:14:14:16 | "A" : String | A.java:30:14:30:16 | parameter this : new A(...) { ... } [p] |
@@ -13,11 +16,16 @@
| A.java:14:14:14:16 | "A" : String | A.java:35:26:35:27 | this : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:39:12:39:12 | a : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:39:12:39:12 | p : String |
| A.java:21:11:21:13 | "B" : String | A.java:14:7:14:20 | SSA def(a) : new A(...) { ... } [String s] |
| A.java:21:11:21:13 | "B" : String | A.java:14:11:14:20 | f2(...) : new A(...) { ... } [String s] |
| A.java:21:11:21:13 | "B" : String | A.java:15:16:15:16 | a : new A(...) { ... } [String s] |
| A.java:21:11:21:13 | "B" : String | A.java:15:16:15:22 | get(...) : String |
| A.java:21:11:21:13 | "B" : String | A.java:21:7:21:13 | ...=... : String |
| A.java:21:11:21:13 | "B" : String | A.java:21:7:21:13 | SSA def(s) : String |
| A.java:21:11:21:13 | "B" : String | A.java:21:7:21:13 | [input] SSA phi(s) : String |
| A.java:21:11:21:13 | "B" : String | A.java:25:5:25:26 | SSA phi(s) : String |
| A.java:21:11:21:13 | "B" : String | A.java:25:5:25:26 | phi(String s) : String |
| A.java:21:11:21:13 | "B" : String | A.java:28:7:38:5 | SSA def(a) : new A(...) { ... } [String s] |
| A.java:21:11:21:13 | "B" : String | A.java:28:11:38:5 | String s : String |
| A.java:21:11:21:13 | "B" : String | A.java:28:11:38:5 | new (...) : new A(...) { ... } [String s] |
| A.java:21:11:21:13 | "B" : String | A.java:30:14:30:16 | parameter this : new A(...) { ... } [String s] |
@@ -29,11 +37,16 @@
| A.java:21:11:21:13 | "B" : String | A.java:35:26:35:27 | this : new A(...) { ... } [String s] |
| A.java:21:11:21:13 | "B" : String | A.java:39:12:39:12 | String s : String |
| A.java:21:11:21:13 | "B" : String | A.java:39:12:39:12 | a : new A(...) { ... } [String s] |
| A.java:23:11:23:13 | "C" : String | A.java:14:7:14:20 | SSA def(a) : new A(...) { ... } [String s] |
| A.java:23:11:23:13 | "C" : String | A.java:14:11:14:20 | f2(...) : new A(...) { ... } [String s] |
| A.java:23:11:23:13 | "C" : String | A.java:15:16:15:16 | a : new A(...) { ... } [String s] |
| A.java:23:11:23:13 | "C" : String | A.java:15:16:15:22 | get(...) : String |
| A.java:23:11:23:13 | "C" : String | A.java:23:7:23:13 | ...=... : String |
| A.java:23:11:23:13 | "C" : String | A.java:23:7:23:13 | SSA def(s) : String |
| A.java:23:11:23:13 | "C" : String | A.java:23:7:23:13 | [input] SSA phi(s) : String |
| A.java:23:11:23:13 | "C" : String | A.java:25:5:25:26 | SSA phi(s) : String |
| A.java:23:11:23:13 | "C" : String | A.java:25:5:25:26 | phi(String s) : String |
| A.java:23:11:23:13 | "C" : String | A.java:28:7:38:5 | SSA def(a) : new A(...) { ... } [String s] |
| A.java:23:11:23:13 | "C" : String | A.java:28:11:38:5 | String s : String |
| A.java:23:11:23:13 | "C" : String | A.java:28:11:38:5 | new (...) : new A(...) { ... } [String s] |
| A.java:23:11:23:13 | "C" : String | A.java:30:14:30:16 | parameter this : new A(...) { ... } [String s] |
@@ -47,16 +60,20 @@
| A.java:23:11:23:13 | "C" : String | A.java:39:12:39:12 | a : new A(...) { ... } [String s] |
| A.java:25:22:25:24 | "D" : String | A.java:4:5:4:7 | parameter this [Return] : Box [elem] |
| A.java:25:22:25:24 | "D" : String | A.java:4:9:4:16 | e : String |
| A.java:25:22:25:24 | "D" : String | A.java:4:19:4:31 | SSA def(e) : String |
| A.java:25:22:25:24 | "D" : String | A.java:4:21:4:24 | this <.field> [post update] : Box [elem] |
| A.java:25:22:25:24 | "D" : String | A.java:4:21:4:28 | ...=... : String |
| A.java:25:22:25:24 | "D" : String | A.java:4:28:4:28 | e : String |
| A.java:25:22:25:24 | "D" : String | A.java:6:12:6:18 | parameter this : Box [elem] |
| A.java:25:22:25:24 | "D" : String | A.java:6:31:6:34 | elem : String |
| A.java:25:22:25:24 | "D" : String | A.java:6:31:6:34 | this <.field> : Box [elem] |
| A.java:25:22:25:24 | "D" : String | A.java:14:7:14:20 | SSA def(a) : new A(...) { ... } [Box b1, ... (2)] |
| A.java:25:22:25:24 | "D" : String | A.java:14:11:14:20 | f2(...) : new A(...) { ... } [Box b1, ... (2)] |
| A.java:25:22:25:24 | "D" : String | A.java:15:16:15:16 | a : new A(...) { ... } [Box b1, ... (2)] |
| A.java:25:22:25:24 | "D" : String | A.java:15:16:15:22 | get(...) : String |
| A.java:25:22:25:24 | "D" : String | A.java:25:9:25:25 | SSA def(b1) : Box [elem] |
| A.java:25:22:25:24 | "D" : String | A.java:25:14:25:25 | new Box(...) : Box [elem] |
| A.java:25:22:25:24 | "D" : String | A.java:28:7:38:5 | SSA def(a) : new A(...) { ... } [Box b1, ... (2)] |
| A.java:25:22:25:24 | "D" : String | A.java:28:11:38:5 | Box b1 : Box [elem] |
| A.java:25:22:25:24 | "D" : String | A.java:28:11:38:5 | new (...) : new A(...) { ... } [Box b1, ... (2)] |
| A.java:25:22:25:24 | "D" : String | A.java:30:14:30:16 | parameter this : new A(...) { ... } [Box b1, ... (2)] |
@@ -71,16 +88,19 @@
| A.java:25:22:25:24 | "D" : String | A.java:39:12:39:12 | a : new A(...) { ... } [Box b1, ... (2)] |
| A.java:27:16:27:18 | "E" : String | A.java:5:10:5:16 | parameter this [Return] : Box [elem] |
| A.java:27:16:27:18 | "E" : String | A.java:5:18:5:25 | e : String |
| A.java:27:16:27:18 | "E" : String | A.java:5:28:5:40 | SSA def(e) : String |
| A.java:27:16:27:18 | "E" : String | A.java:5:30:5:33 | this <.field> [post update] : Box [elem] |
| A.java:27:16:27:18 | "E" : String | A.java:5:30:5:37 | ...=... : String |
| A.java:27:16:27:18 | "E" : String | A.java:5:37:5:37 | e : String |
| A.java:27:16:27:18 | "E" : String | A.java:6:12:6:18 | parameter this : Box [elem] |
| A.java:27:16:27:18 | "E" : String | A.java:6:31:6:34 | elem : String |
| A.java:27:16:27:18 | "E" : String | A.java:6:31:6:34 | this <.field> : Box [elem] |
| A.java:27:16:27:18 | "E" : String | A.java:14:7:14:20 | SSA def(a) : new A(...) { ... } [Box b2, ... (2)] |
| A.java:27:16:27:18 | "E" : String | A.java:14:11:14:20 | f2(...) : new A(...) { ... } [Box b2, ... (2)] |
| A.java:27:16:27:18 | "E" : String | A.java:15:16:15:16 | a : new A(...) { ... } [Box b2, ... (2)] |
| A.java:27:16:27:18 | "E" : String | A.java:15:16:15:22 | get(...) : String |
| A.java:27:16:27:18 | "E" : String | A.java:27:5:27:6 | b2 [post update] : Box [elem] |
| A.java:27:16:27:18 | "E" : String | A.java:28:7:38:5 | SSA def(a) : new A(...) { ... } [Box b2, ... (2)] |
| A.java:27:16:27:18 | "E" : String | A.java:28:11:38:5 | Box b2 : Box [elem] |
| A.java:27:16:27:18 | "E" : String | A.java:28:11:38:5 | new (...) : new A(...) { ... } [Box b2, ... (2)] |
| A.java:27:16:27:18 | "E" : String | A.java:30:14:30:16 | parameter this : new A(...) { ... } [Box b2, ... (2)] |

View File

@@ -1,4 +1,6 @@
| A.java:5:18:5:21 | null | A.java:2:13:2:20 | o |
| A.java:5:18:5:21 | null | A.java:5:12:5:21 | SSA def(src) |
| A.java:5:18:5:21 | null | A.java:5:18:5:21 | null |
| A.java:5:18:5:21 | null | A.java:6:12:6:18 | SSA def(x) |
| A.java:5:18:5:21 | null | A.java:6:16:6:18 | src |
| A.java:5:18:5:21 | null | A.java:7:10:7:10 | x |

View File

@@ -3,12 +3,14 @@ edges
| A.java:12:14:12:18 | src(...) : Object | A.java:12:5:12:5 | b [post update] : Box [elem] |
| A.java:12:14:12:18 | src(...) : Object | A.java:12:5:12:18 | ...=... : Object |
| A.java:13:12:13:12 | b : Box [elem] | A.java:17:13:17:16 | f1(...) : Box [elem] |
| A.java:17:13:17:16 | f1(...) : Box [elem] | A.java:18:8:18:8 | b : Box [elem] |
| A.java:17:9:17:16 | SSA def(b) : Box [elem] | A.java:18:8:18:8 | b : Box [elem] |
| A.java:17:13:17:16 | f1(...) : Box [elem] | A.java:17:9:17:16 | SSA def(b) : Box [elem] |
| A.java:18:8:18:8 | b : Box [elem] | A.java:21:11:21:15 | b : Box [elem] |
#select
| 0 | A.java:12:5:12:5 | b [post update] : Box [elem] |
| 0 | A.java:12:5:12:18 | ...=... : Object |
| 0 | A.java:13:12:13:12 | b : Box [elem] |
| 1 | A.java:17:9:17:16 | SSA def(b) : Box [elem] |
| 1 | A.java:17:13:17:16 | f1(...) : Box [elem] |
| 1 | A.java:18:8:18:8 | b : Box [elem] |
| 2 | A.java:21:11:21:15 | b : Box [elem] |

View File

@@ -2,7 +2,8 @@ edges
| A.java:4:16:4:18 | parameter this [Return] [elem] | A.java:22:17:22:25 | new Box(...) [elem] |
| A.java:4:16:4:18 | this <constr(this)> [post update] [elem] | A.java:4:16:4:18 | parameter this [Return] [elem] |
| A.java:5:19:5:22 | elem | A.java:24:10:24:19 | other.elem |
| A.java:22:17:22:25 | new Box(...) [elem] | A.java:23:13:23:17 | other [elem] |
| A.java:22:9:22:25 | SSA def(other) [elem] | A.java:23:13:23:17 | other [elem] |
| A.java:22:17:22:25 | new Box(...) [elem] | A.java:22:9:22:25 | SSA def(other) [elem] |
| A.java:23:13:23:17 | other [elem] | A.java:24:10:24:14 | other [elem] |
| A.java:23:13:23:17 | other [post update] [elem] | A.java:24:10:24:14 | other [elem] |
| A.java:24:10:24:14 | other [elem] | A.java:24:10:24:19 | other.elem |
@@ -10,6 +11,7 @@ edges
| A.java:28:5:28:5 | b [post update] [elem] | A.java:27:16:27:20 | b [Return] [elem] |
| A.java:28:14:28:25 | new Object(...) | A.java:28:5:28:5 | b [post update] [elem] |
#select
| 0 | A.java:22:9:22:25 | SSA def(other) [elem] |
| 0 | A.java:22:17:22:25 | new Box(...) [elem] |
| 0 | A.java:23:13:23:17 | other [elem] |
| 0 | A.java:23:13:23:17 | other [post update] [elem] |

View File

@@ -1,9 +1,13 @@
| TestSwitchExpr.java:4:15:4:22 | o |
| TestSwitchExpr.java:7:16:7:28 | SSA def(x1) |
| TestSwitchExpr.java:7:21:7:28 | source(...) |
| TestSwitchExpr.java:8:16:8:30 | SSA def(x2) |
| TestSwitchExpr.java:8:21:8:30 | switch (...) |
| TestSwitchExpr.java:10:24:10:25 | x1 |
| TestSwitchExpr.java:12:16:12:30 | SSA def(x3) |
| TestSwitchExpr.java:12:21:12:30 | switch (...) |
| TestSwitchExpr.java:13:38:13:39 | x2 |
| TestSwitchExpr.java:16:16:16:30 | SSA def(x4) |
| TestSwitchExpr.java:16:21:16:30 | switch (...) |
| TestSwitchExpr.java:19:23:19:24 | x3 |
| TestSwitchExpr.java:23:14:23:15 | x4 |

View File

@@ -1,19 +1,24 @@
| Test.java:12:15:12:47 | SSA def(inp) |
| Test.java:12:21:12:47 | new FileInputStream(...) |
| Test.java:14:21:14:39 | buffer(...) |
| Test.java:14:36:14:38 | inp |
| Test.java:15:16:15:54 | SSA def(lines) |
| Test.java:15:24:15:54 | readLines(...) |
| Test.java:15:42:15:44 | inp |
| Test.java:16:18:16:45 | readFully(...) |
| Test.java:16:36:16:38 | inp |
| Test.java:17:22:17:55 | toBufferedInputStream(...) |
| Test.java:17:52:17:54 | inp |
| Test.java:18:10:18:71 | SSA def(bufread) |
| Test.java:18:20:18:71 | toBufferedReader(...) |
| Test.java:18:45:18:70 | new InputStreamReader(...) |
| Test.java:18:67:18:69 | inp |
| Test.java:19:19:19:48 | toByteArray(...) |
| Test.java:19:39:19:41 | inp |
| Test.java:20:10:20:50 | SSA def(chars) |
| Test.java:20:18:20:50 | toCharArray(...) |
| Test.java:20:38:20:40 | inp |
| Test.java:21:10:21:43 | SSA def(s) |
| Test.java:21:14:21:43 | toString(...) |
| Test.java:21:31:21:33 | inp |
| Test.java:22:20:22:52 | toInputStream(...) |

View File

@@ -10,11 +10,13 @@
| A.java:20:16:20:16 | this <.field> |
| A.java:21:12:21:20 | getThis(...) |
| A.java:21:12:21:20 | this <.method> |
| A.java:25:7:25:17 | SSA def(a) |
| A.java:25:11:25:17 | new A(...) |
| A.java:25:11:25:17 | new A(...) [pre constructor] |
| A.java:26:12:26:12 | a |
| A.java:26:12:26:22 | getThis(...) |
| A.java:26:12:26:36 | getThisWrap(...) |
| A.java:27:7:27:17 | SSA def(c) |
| A.java:27:11:27:17 | new C(...) |
| A.java:27:11:27:17 | new C(...) [pre constructor] |
| A.java:28:5:28:5 | c |

View File

@@ -25,6 +25,12 @@ public class GuardTest {
}
String s2 = "string";
if (!isSafe(s2)) {
s2 = null;
}
sink(s2);
}
}

View File

@@ -1,7 +1,4 @@
| Fields.java:13:5:13:17 | x | Fields.java:13:11:13:16 | x | SSA def(x) |
| Fields.java:13:5:13:17 | x | Fields.java:15:5:15:10 | ...=... | SSA def(x) |
| Fields.java:13:5:13:17 | x | Fields.java:18:5:18:15 | ...=... | SSA def(x) |
| Fields.java:13:5:13:17 | x | Fields.java:20:5:20:10 | ...=... | SSA def(x) |
| Fields.java:13:15:13:16 | this.xs | Fields.java:12:19:21:3 | { ... } | SSA init(this.xs) |
| Fields.java:13:15:13:16 | this.xs | Fields.java:14:5:14:9 | upd(...) | SSA impl upd[nonlocal](this.xs) |
| Fields.java:13:15:13:16 | this.xs | Fields.java:17:7:17:11 | upd(...) | SSA impl upd[nonlocal](this.xs) |
@@ -10,33 +7,17 @@
| Fields.java:24:5:24:28 | f | Fields.java:24:12:24:27 | f | SSA def(f) |
| Fields.java:24:5:24:28 | f | Fields.java:43:7:43:22 | ...=... | SSA def(f) |
| Fields.java:24:5:24:28 | f | Fields.java:44:5:44:13 | <Expr>; | SSA phi(f) |
| Fields.java:25:5:25:19 | y | Fields.java:25:11:25:18 | y | SSA def(y) |
| Fields.java:25:5:25:19 | y | Fields.java:29:5:29:12 | ...=... | SSA def(y) |
| Fields.java:25:5:25:19 | y | Fields.java:33:5:33:12 | ...=... | SSA def(y) |
| Fields.java:25:5:25:19 | y | Fields.java:37:5:37:12 | ...=... | SSA def(y) |
| Fields.java:25:5:25:19 | y | Fields.java:40:5:40:12 | ...=... | SSA def(y) |
| Fields.java:25:5:25:19 | y | Fields.java:44:5:44:12 | ...=... | SSA def(y) |
| Fields.java:25:5:25:19 | y | Fields.java:46:5:46:12 | ...=... | SSA def(y) |
| Fields.java:25:15:25:18 | f.xs | Fields.java:24:12:24:27 | f | SSA impl upd[explicit qualifier](f.xs) |
| Fields.java:25:15:25:18 | f.xs | Fields.java:28:5:28:12 | f(...) | SSA impl upd[nonlocal](f.xs) |
| Fields.java:25:15:25:18 | f.xs | Fields.java:32:5:32:9 | f(...) | SSA impl upd[nonlocal](f.xs) |
| Fields.java:25:15:25:18 | f.xs | Fields.java:39:5:39:21 | ...=... | SSA def(f.xs) |
| Fields.java:25:15:25:18 | f.xs | Fields.java:43:7:43:22 | ...=... | SSA impl upd[explicit qualifier](f.xs) |
| Fields.java:25:15:25:18 | f.xs | Fields.java:44:5:44:13 | <Expr>; | SSA phi(f.xs) |
| Fields.java:26:5:26:17 | z | Fields.java:26:11:26:16 | z | SSA def(z) |
| Fields.java:26:5:26:17 | z | Fields.java:30:5:30:10 | ...=... | SSA def(z) |
| Fields.java:26:5:26:17 | z | Fields.java:34:5:34:10 | ...=... | SSA def(z) |
| Fields.java:26:5:26:17 | z | Fields.java:38:5:38:10 | ...=... | SSA def(z) |
| Fields.java:26:5:26:17 | z | Fields.java:41:5:41:10 | ...=... | SSA def(z) |
| Fields.java:26:5:26:17 | z | Fields.java:47:5:47:10 | ...=... | SSA def(z) |
| Fields.java:26:15:26:16 | this.xs | Fields.java:23:19:49:3 | { ... } | SSA init(this.xs) |
| Fields.java:26:15:26:16 | this.xs | Fields.java:28:5:28:12 | f(...) | SSA impl upd[nonlocal](this.xs) |
| Fields.java:26:15:26:16 | this.xs | Fields.java:32:5:32:9 | f(...) | SSA impl upd[nonlocal](this.xs) |
| Fields.java:26:15:26:16 | this.xs | Fields.java:36:5:36:19 | ...=... | SSA def(this.xs) |
| Fields.java:27:5:27:19 | w | Fields.java:27:11:27:18 | w | SSA def(w) |
| Fields.java:27:5:27:19 | w | Fields.java:31:5:31:12 | ...=... | SSA def(w) |
| Fields.java:27:5:27:19 | w | Fields.java:35:5:35:12 | ...=... | SSA def(w) |
| Fields.java:27:5:27:19 | w | Fields.java:48:5:48:12 | ...=... | SSA def(w) |
| Fields.java:27:15:27:18 | Fields.stat | Fields.java:23:19:49:3 | { ... } | SSA init(Fields.stat) |
| Fields.java:27:15:27:18 | Fields.stat | Fields.java:24:16:24:27 | new Fields(...) | SSA impl upd[nonlocal](Fields.stat) |
| Fields.java:27:15:27:18 | Fields.stat | Fields.java:28:5:28:12 | f(...) | SSA impl upd[nonlocal](Fields.stat) |
@@ -58,15 +39,12 @@
| Nested.java:18:5:23:6 | h2 | Nested.java:18:15:23:5 | h2 | SSA def(h2) |
| Nested.java:20:9:20:40 | hnest | Nested.java:20:19:20:39 | hnest | SSA def(hnest) |
| Nested.java:24:5:24:31 | getInt(..).obj2 | Nested.java:30:23:30:36 | { ... } | SSA init(getInt(..).obj2) |
| Nested.java:24:5:24:31 | obj2 | Nested.java:24:12:24:30 | obj2 | SSA def(obj2) |
| Nested.java:24:5:24:31 | obj2 | Nested.java:26:7:26:25 | ...=... | SSA def(obj2) |
| Nested.java:24:5:24:31 | obj2 | Nested.java:28:7:28:25 | ...=... | SSA def(obj2) |
| Nested.java:24:5:24:31 | obj2 | Nested.java:30:5:30:37 | var ...; | SSA phi(obj2) |
| Nested.java:30:5:30:37 | hash2 | Nested.java:30:15:30:36 | hash2 | SSA def(hash2) |
| Nested.java:33:21:33:26 | p3 | Nested.java:33:29:42:3 | { ... } | SSA init(p3) |
| Nested.java:34:5:34:11 | getInt(..).x3 | Nested.java:37:20:37:25 | { ... } | SSA init(getInt(..).x3) |
| Nested.java:34:5:34:11 | getInt(..).x3 | Nested.java:40:20:40:25 | { ... } | SSA init(getInt(..).x3) |
| Nested.java:34:5:34:11 | x3 | Nested.java:34:9:34:10 | x3 | SSA def(x3) |
| Nested.java:34:5:34:11 | x3 | Nested.java:36:7:36:12 | ...=... | SSA def(x3) |
| Nested.java:34:5:34:11 | x3 | Nested.java:39:7:39:12 | ...=... | SSA def(x3) |
| Test.java:4:8:4:16 | param | Test.java:4:19:32:2 | { ... } | SSA init(param) |
@@ -78,16 +56,12 @@
| Test.java:6:3:6:12 | x | Test.java:19:3:19:3 | ; | SSA phi(x) |
| Test.java:6:3:6:12 | x | Test.java:27:19:27:19 | i | SSA phi(x) |
| Test.java:6:3:6:12 | x | Test.java:28:4:28:9 | ...+=... | SSA def(x) |
| Test.java:7:3:7:8 | y | Test.java:7:7:7:7 | y | SSA def(y) |
| Test.java:7:3:7:8 | y | Test.java:11:4:11:10 | ...=... | SSA def(y) |
| Test.java:7:3:7:8 | y | Test.java:14:4:14:8 | ...=... | SSA def(y) |
| Test.java:7:3:7:8 | y | Test.java:15:4:15:9 | ...+=... | SSA def(y) |
| Test.java:7:3:7:8 | y | Test.java:19:3:19:3 | ; | SSA phi(y) |
| Test.java:7:3:7:8 | y | Test.java:20:10:20:10 | x | SSA phi(y) |
| Test.java:7:3:7:8 | y | Test.java:24:4:24:9 | ...-=... | SSA def(y) |
| Test.java:8:3:8:8 | z | Test.java:8:7:8:7 | z | SSA def(z) |
| Test.java:8:3:8:8 | z | Test.java:12:4:12:8 | ...=... | SSA def(z) |
| Test.java:8:3:8:8 | z | Test.java:17:4:17:8 | ...=... | SSA def(z) |
| Test.java:27:8:27:16 | i | Test.java:27:12:27:16 | i | SSA def(i) |
| Test.java:27:8:27:16 | i | Test.java:27:19:27:19 | i | SSA phi(i) |
| Test.java:27:8:27:16 | i | Test.java:27:25:27:27 | ...++ | SSA def(i) |
@@ -101,5 +75,4 @@
| TestInstanceOfPattern.java:18:22:18:29 | s | TestInstanceOfPattern.java:18:29:18:29 | s | SSA def(s) |
| TestInstanceOfPattern.java:21:8:21:8 | this.s | TestInstanceOfPattern.java:21:8:21:8 | s | SSA impl upd[untracked](this.s) |
| TestInstanceOfPattern.java:24:13:24:22 | obj | TestInstanceOfPattern.java:24:25:30:2 | { ... } | SSA init(obj) |
| TestInstanceOfPattern.java:25:22:25:29 | s | TestInstanceOfPattern.java:25:29:25:29 | s | SSA def(s) |
| TestInstanceOfPattern.java:25:34:25:34 | this.s | TestInstanceOfPattern.java:24:25:30:2 | { ... } | SSA init(this.s) |

View File

@@ -78,6 +78,12 @@ private import codeql.rust.elements.internal.PathResolution
/** Holds if `p` may resolve to multiple items including `i`. */
query predicate multiplePathResolutions(Path p, ItemNode i) {
i = resolvePath(p) and
// `use foo::bar` may use both a type `bar` and a value `bar`
not p =
any(UseTree use |
not use.isGlob() and
not use.hasUseTreeList()
).getPath() and
strictcount(resolvePath(p)) > 1
}

View File

@@ -5,6 +5,31 @@
private import rust
private import codeql.rust.elements.internal.generated.ParentChild
private newtype TNamespace =
TTypeNamespace() or
TValueNamespace()
/**
* A namespace.
*
* Either the _value_ namespace or the _type_ namespace, see
* https://doc.rust-lang.org/reference/names/namespaces.html.
*/
final class Namespace extends TNamespace {
/** Holds if this is the value namespace. */
predicate isValue() { this = TValueNamespace() }
/** Holds if this is the type namespace. */
predicate isType() { this = TTypeNamespace() }
/** Gets a textual representation of this namespace. */
string toString() {
this.isValue() and result = "value"
or
this.isType() and result = "type"
}
}
/**
* An item that may be referred to by a path, and which is a node in
* the _item graph_.
@@ -46,11 +71,15 @@ private import codeql.rust.elements.internal.generated.ParentChild
* - https://doc.rust-lang.org/reference/names/scopes.html
* - https://doc.rust-lang.org/reference/paths.html
* - https://doc.rust-lang.org/reference/visibility-and-privacy.html
* - https://doc.rust-lang.org/reference/names/namespaces.html
*/
abstract class ItemNode extends AstNode {
/** Gets the (original) name of this item. */
abstract string getName();
/** Gets the namespace that this item belongs to, if any. */
abstract Namespace getNamespace();
/** Gets the visibility of this item, if any. */
abstract Visibility getVisibility();
@@ -143,30 +172,44 @@ abstract private class ModuleLikeNode extends ItemNode {
private class SourceFileItemNode extends ModuleLikeNode, SourceFile {
override string getName() { result = "(source file)" }
override Namespace getNamespace() {
result.isType() // can be referenced with `super`
}
override Visibility getVisibility() { none() }
}
private class ConstItemNode extends ItemNode instanceof Const {
override string getName() { result = Const.super.getName().getText() }
override Namespace getNamespace() { result.isValue() }
override Visibility getVisibility() { result = Const.super.getVisibility() }
}
private class EnumItemNode extends ItemNode instanceof Enum {
override string getName() { result = Enum.super.getName().getText() }
override Namespace getNamespace() { result.isType() }
override Visibility getVisibility() { result = Enum.super.getVisibility() }
}
private class VariantItemNode extends ItemNode instanceof Variant {
override string getName() { result = Variant.super.getName().getText() }
override Namespace getNamespace() {
if super.getFieldList() instanceof RecordFieldList then result.isType() else result.isValue()
}
override Visibility getVisibility() { result = Variant.super.getVisibility() }
}
private class FunctionItemNode extends ItemNode instanceof Function {
override string getName() { result = Function.super.getName().getText() }
override Namespace getNamespace() { result.isValue() }
override Visibility getVisibility() { result = Function.super.getVisibility() }
}
@@ -184,57 +227,92 @@ abstract private class ImplOrTraitItemNode extends ItemNode {
}
}
private class ImplItemNode extends ImplOrTraitItemNode instanceof Impl {
class ImplItemNode extends ImplOrTraitItemNode instanceof Impl {
ItemNode resolveSelfTy() { result = resolvePath(super.getSelfTy().(PathTypeRepr).getPath()) }
override string getName() { result = "(impl)" }
override Namespace getNamespace() {
result.isType() // can be referenced with `Self`
}
override Visibility getVisibility() { result = Impl.super.getVisibility() }
}
private class MacroCallItemNode extends ItemNode instanceof MacroCall {
override string getName() { result = "(macro call)" }
override Namespace getNamespace() { none() }
override Visibility getVisibility() { none() }
}
private class ModuleItemNode extends ModuleLikeNode instanceof Module {
override string getName() { result = Module.super.getName().getText() }
override Namespace getNamespace() { result.isType() }
override Visibility getVisibility() { result = Module.super.getVisibility() }
}
private class StructItemNode extends ItemNode instanceof Struct {
override string getName() { result = Struct.super.getName().getText() }
override Namespace getNamespace() {
result.isType() // the struct itself
or
not super.getFieldList() instanceof RecordFieldList and
result.isValue() // the constructor
}
override Visibility getVisibility() { result = Struct.super.getVisibility() }
}
private class TraitItemNode extends ImplOrTraitItemNode instanceof Trait {
class TraitItemNode extends ImplOrTraitItemNode instanceof Trait {
override string getName() { result = Trait.super.getName().getText() }
override Namespace getNamespace() { result.isType() }
override Visibility getVisibility() { result = Trait.super.getVisibility() }
}
class TypeAliasItemNode extends ItemNode instanceof TypeAlias {
override string getName() { result = TypeAlias.super.getName().getText() }
override Namespace getNamespace() { result.isType() }
override Visibility getVisibility() { result = TypeAlias.super.getVisibility() }
}
private class UnionItemNode extends ItemNode instanceof Union {
override string getName() { result = Union.super.getName().getText() }
override Namespace getNamespace() { result.isType() }
override Visibility getVisibility() { result = Union.super.getVisibility() }
}
private class UseItemNode extends ItemNode instanceof Use {
override string getName() { result = "(use)" }
override Namespace getNamespace() { none() }
override Visibility getVisibility() { none() }
}
private class BlockExprItemNode extends ItemNode instanceof BlockExpr {
override string getName() { result = "(block expr)" }
override Namespace getNamespace() { none() }
override Visibility getVisibility() { none() }
}
private class TypeParamItemNode extends ItemNode instanceof TypeParam {
override string getName() { result = TypeParam.super.getName().getText() }
override Namespace getNamespace() { result.isType() }
override Visibility getVisibility() { none() }
}
@@ -320,19 +398,22 @@ private predicate useTreeDeclares(UseTree tree, string name) {
}
/**
* Holds if `item` explicitly declares a sub item named `name`. This includes
* items declared by `use` statements, except for glob imports.
* Holds if `item` explicitly declares a sub item named `name` in the
* namespace `ns`. This includes items declared by `use` statements,
* except for glob imports.
*/
pragma[nomagic]
private predicate declares(ItemNode item, string name) {
private predicate declares(ItemNode item, Namespace ns, string name) {
exists(ItemNode child | child.getImmediateParent() = item |
child.getName() = name
child.getName() = name and
child.getNamespace() = ns
or
useTreeDeclares(child.(Use).getUseTree(), name)
useTreeDeclares(child.(Use).getUseTree(), name) and
exists(ns) // `use foo::bar` can refer to both a value and a type
)
or
exists(MacroCallItemNode call |
declares(call, name) and
declares(call, ns, name) and
call.getImmediateParent() = item
)
}
@@ -351,19 +432,20 @@ private class RelevantPath extends Path {
/**
* Holds if the unqualified path `p` references an item named `name`, and `name`
* may be looked up inside enclosing item `encl`.
* may be looked up in the `ns` namespace inside enclosing item `encl`.
*/
pragma[nomagic]
private predicate unqualifiedPathLookup(RelevantPath p, string name, ItemNode encl) {
private predicate unqualifiedPathLookup(RelevantPath p, string name, Namespace ns, ItemNode encl) {
exists(ItemNode encl0 |
// lookup in the immediately enclosing item
p.isUnqualified(name) and
encl0.getADescendant() = p
encl0.getADescendant() = p and
exists(ns)
or
// lookup in an outer scope, but only if the item is not declared in inner scope
exists(ItemNode mid |
unqualifiedPathLookup(p, name, mid) and
not declares(mid, name)
unqualifiedPathLookup(p, name, ns, mid) and
not declares(mid, ns, name)
|
// nested modules do not have unqualified access to items from outer modules,
// except for items declared at top-level in the source file
@@ -374,16 +456,30 @@ private predicate unqualifiedPathLookup(RelevantPath p, string name, ItemNode en
|
// functions in `impl` blocks need to use explicit `Self::` to access other
// functions in the `impl` block
if encl0 instanceof ImplOrTraitItemNode then encl = encl0.getImmediateParent() else encl = encl0
if encl0 instanceof ImplOrTraitItemNode and ns.isValue()
then encl = encl0.getImmediateParent()
else encl = encl0
)
}
/** Gets the item that `path` resolves to, if any. */
cached
ItemNode resolvePath(RelevantPath path) {
exists(ItemNode encl, string name |
unqualifiedPathLookup(path, name, encl) and
result = encl.getASuccessor(name)
pragma[nomagic]
private ItemNode getASuccessor(ItemNode pred, string name, Namespace ns) {
result = pred.getASuccessor(name) and
ns = result.getNamespace()
}
pragma[nomagic]
private ItemNode resolvePath0(RelevantPath path) {
exists(ItemNode encl, Namespace ns, string name, ItemNode res |
unqualifiedPathLookup(path, name, ns, encl) and
res = getASuccessor(encl, name, ns)
|
if
not any(RelevantPath parent).getQualifier() = path and
name = "Self" and
res instanceof ImplItemNode
then result = res.(ImplItemNode).resolveSelfTy()
else result = res
)
or
exists(ItemNode q, string name |
@@ -394,6 +490,47 @@ ItemNode resolvePath(RelevantPath path) {
result = resolveUseTreeListItem(_, _, path)
}
/** Holds if path `p` must be looked up in namespace `n`. */
private predicate pathUsesNamespace(Path p, Namespace n) {
n.isValue() and
(
p = any(PathExpr pe).getPath()
or
p = any(TupleStructPat tsp).getPath()
)
or
n.isType() and
(
p = any(Visibility v).getPath()
or
p = any(RecordExpr re).getPath()
or
p = any(PathTypeRepr ptr).getPath()
or
p = any(RecordPat rp).getPath()
or
p =
any(UseTree use |
use.isGlob()
or
use.hasUseTreeList()
).getPath()
or
p = any(Path parent).getQualifier()
)
}
/** Gets the item that `path` resolves to, if any. */
cached
ItemNode resolvePath(RelevantPath path) {
result = resolvePath0(path) and
(
pathUsesNamespace(path, result.getNamespace())
or
not pathUsesNamespace(path, _)
)
}
pragma[nomagic]
private ItemNode resolvePathQualifier(RelevantPath path, string name) {
result = resolvePath(path.getQualifier()) and
@@ -452,14 +589,14 @@ pragma[nomagic]
private predicate useImportEdge(Use use, string name, ItemNode item) {
exists(UseTree tree, ItemNode used |
used = resolveUseTreeListItem(use, tree) and
not exists(tree.getUseTreeList()) and
not tree.hasUseTreeList() and
if tree.isGlob()
then
exists(ItemNode encl |
exists(ItemNode encl, Namespace ns |
encl.getADescendant() = use and
item = used.getASuccessor(name) and
item = getASuccessor(used, name, ns) and
// glob imports can be shadowed
not declares(encl, name)
not declares(encl, ns, name)
)
else item = used
|

View File

@@ -212,6 +212,74 @@ mod m10 {
}
}
mod m11 {
pub struct Foo {} // I61
fn Foo() {} // I62
pub fn f() {
let _ = Foo {}; // $ item=I61
Foo(); // $ item=I62
} // I63
mod f {} // I66
pub enum Bar {
FooBar {}, // I64
} // I65
use Bar::FooBar; // $ item=I64
fn FooBar() {} // I65
#[rustfmt::skip]
fn g(x: Foo) { // $ item=I61
let _ = FooBar {}; // $ item=I64
let _ = FooBar(); // $ item=I65
}
struct S; // I67
enum E {
C, // I68
}
use E::C; // $ item=I68
fn h() {
let _ = S; // $ item=I67
let _ = C; // $ item=I68
}
}
mod m12 {
#[rustfmt::skip]
trait MyParamTrait<
T // I69
> {
type AssociatedType; // I70
fn f(
&self,
x: T // $ item=I69
) -> Self::AssociatedType; // $ item=I70
}
}
mod m13 {
pub fn f() {} // I71
pub struct f {} // I72
mod m14 {
use crate::m13::f; // $ item=I71 item=I72
#[rustfmt::skip]
fn g(x: f) { // $ item=I72
let _ = f {}; // $ item=I72
f(); // $ item=I71
}
}
}
fn main() {
my::nested::nested1::nested2::f(); // $ item=I4
my::f(); // $ item=I38
@@ -230,4 +298,5 @@ fn main() {
m7::f(); // $ item=I45
m8::g(); // $ item=I55
m9::f(); // $ item=I57
m11::f(); // $ item=I63
}

View File

@@ -13,6 +13,11 @@ mod
| main.rs:139:1:182:1 | mod m8 |
| main.rs:184:1:192:1 | mod m9 |
| main.rs:194:1:213:1 | mod m10 |
| main.rs:215:1:252:1 | mod m11 |
| main.rs:225:5:225:12 | mod f |
| main.rs:254:1:266:1 | mod m12 |
| main.rs:268:1:281:1 | mod m13 |
| main.rs:272:5:280:5 | mod m14 |
| my2/mod.rs:1:1:1:16 | mod nested2 |
| my2/nested2.rs:1:1:11:1 | mod nested3 |
| my2/nested2.rs:2:5:10:5 | mod nested4 |
@@ -38,7 +43,7 @@ resolvePath
| main.rs:30:17:30:21 | super | main.rs:18:5:36:5 | mod m2 |
| main.rs:30:17:30:24 | ...::f | main.rs:19:9:21:9 | fn f |
| main.rs:33:17:33:17 | f | main.rs:19:9:21:9 | fn f |
| main.rs:40:9:40:13 | super | main.rs:1:1:233:2 | SourceFile |
| main.rs:40:9:40:13 | super | main.rs:1:1:302:2 | SourceFile |
| main.rs:40:9:40:17 | ...::m1 | main.rs:13:1:37:1 | mod m1 |
| main.rs:40:9:40:21 | ...::m2 | main.rs:18:5:36:5 | mod m2 |
| main.rs:40:9:40:24 | ...::g | main.rs:23:9:27:9 | fn g |
@@ -50,7 +55,7 @@ resolvePath
| main.rs:61:17:61:19 | Foo | main.rs:59:9:59:21 | struct Foo |
| main.rs:64:13:64:15 | Foo | main.rs:53:5:53:17 | struct Foo |
| main.rs:66:5:66:5 | f | main.rs:55:5:62:5 | fn f |
| main.rs:68:5:68:8 | self | main.rs:1:1:233:2 | SourceFile |
| main.rs:68:5:68:8 | self | main.rs:1:1:302:2 | SourceFile |
| main.rs:68:5:68:11 | ...::i | main.rs:71:1:83:1 | fn i |
| main.rs:74:13:74:15 | Foo | main.rs:48:1:48:13 | struct Foo |
| main.rs:81:17:81:19 | Foo | main.rs:77:9:79:9 | struct Foo |
@@ -64,7 +69,7 @@ resolvePath
| main.rs:87:57:87:66 | ...::g | my2/nested2.rs:7:9:9:9 | fn g |
| main.rs:87:80:87:86 | nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 |
| main.rs:100:5:100:22 | f_defined_in_macro | main.rs:99:18:99:42 | fn f_defined_in_macro |
| main.rs:117:13:117:17 | super | main.rs:1:1:233:2 | SourceFile |
| main.rs:117:13:117:17 | super | main.rs:1:1:302:2 | SourceFile |
| main.rs:117:13:117:21 | ...::m5 | main.rs:103:1:107:1 | mod m5 |
| main.rs:118:9:118:9 | f | main.rs:104:5:106:5 | fn f |
| main.rs:118:9:118:9 | f | main.rs:110:5:112:5 | fn f |
@@ -99,42 +104,65 @@ resolvePath
| main.rs:207:7:209:7 | MyStruct::<...> | main.rs:195:5:201:5 | struct MyStruct |
| main.rs:208:9:208:9 | T | main.rs:204:14:204:14 | TypeParam |
| main.rs:211:9:211:16 | MyStruct | main.rs:195:5:201:5 | struct MyStruct |
| main.rs:216:5:216:6 | my | main.rs:1:1:1:7 | mod my |
| main.rs:216:5:216:14 | ...::nested | my.rs:1:1:1:15 | mod nested |
| main.rs:216:5:216:23 | ...::nested1 | my/nested.rs:1:1:17:1 | mod nested1 |
| main.rs:216:5:216:32 | ...::nested2 | my/nested.rs:2:5:11:5 | mod nested2 |
| main.rs:216:5:216:35 | ...::f | my/nested.rs:3:9:5:9 | fn f |
| main.rs:217:5:217:6 | my | main.rs:1:1:1:7 | mod my |
| main.rs:217:5:217:9 | ...::f | my.rs:5:1:7:1 | fn f |
| main.rs:218:5:218:11 | nested2 | my2/mod.rs:1:1:1:16 | mod nested2 |
| main.rs:218:5:218:20 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 |
| main.rs:218:5:218:29 | ...::nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 |
| main.rs:218:5:218:32 | ...::f | my2/nested2.rs:3:9:5:9 | fn f |
| main.rs:219:5:219:5 | f | my2/nested2.rs:3:9:5:9 | fn f |
| main.rs:220:5:220:5 | g | my2/nested2.rs:7:9:9:9 | fn g |
| main.rs:221:5:221:9 | crate | main.rs:1:1:233:2 | SourceFile |
| main.rs:221:5:221:12 | ...::h | main.rs:50:1:69:1 | fn h |
| main.rs:222:5:222:6 | m1 | main.rs:13:1:37:1 | mod m1 |
| main.rs:222:5:222:10 | ...::m2 | main.rs:18:5:36:5 | mod m2 |
| main.rs:222:5:222:13 | ...::g | main.rs:23:9:27:9 | fn g |
| main.rs:223:5:223:6 | m1 | main.rs:13:1:37:1 | mod m1 |
| main.rs:223:5:223:10 | ...::m2 | main.rs:18:5:36:5 | mod m2 |
| main.rs:223:5:223:14 | ...::m3 | main.rs:29:9:35:9 | mod m3 |
| main.rs:223:5:223:17 | ...::h | main.rs:30:27:34:13 | fn h |
| main.rs:224:5:224:6 | m4 | main.rs:39:1:46:1 | mod m4 |
| main.rs:224:5:224:9 | ...::i | main.rs:42:5:45:5 | fn i |
| main.rs:225:5:225:5 | h | main.rs:50:1:69:1 | fn h |
| main.rs:226:5:226:11 | f_alias | my2/nested2.rs:3:9:5:9 | fn f |
| main.rs:227:5:227:11 | g_alias | my2/nested2.rs:7:9:9:9 | fn g |
| main.rs:228:5:228:5 | j | main.rs:97:1:101:1 | fn j |
| main.rs:229:5:229:6 | m6 | main.rs:109:1:120:1 | mod m6 |
| main.rs:229:5:229:9 | ...::g | main.rs:114:5:119:5 | fn g |
| main.rs:230:5:230:6 | m7 | main.rs:122:1:137:1 | mod m7 |
| main.rs:230:5:230:9 | ...::f | main.rs:129:5:136:5 | fn f |
| main.rs:231:5:231:6 | m8 | main.rs:139:1:182:1 | mod m8 |
| main.rs:231:5:231:9 | ...::g | main.rs:169:5:181:5 | fn g |
| main.rs:232:5:232:6 | m9 | main.rs:184:1:192:1 | mod m9 |
| main.rs:232:5:232:9 | ...::f | main.rs:187:5:191:5 | fn f |
| main.rs:221:17:221:19 | Foo | main.rs:216:5:216:21 | struct Foo |
| main.rs:222:9:222:11 | Foo | main.rs:218:5:218:15 | fn Foo |
| main.rs:231:9:231:11 | Bar | main.rs:227:5:229:5 | enum Bar |
| main.rs:231:9:231:19 | ...::FooBar | main.rs:228:9:228:17 | FooBar |
| main.rs:236:13:236:15 | Foo | main.rs:216:5:216:21 | struct Foo |
| main.rs:237:17:237:22 | FooBar | main.rs:228:9:228:17 | FooBar |
| main.rs:238:17:238:22 | FooBar | main.rs:233:5:233:18 | fn FooBar |
| main.rs:246:9:246:9 | E | main.rs:241:15:244:5 | enum E |
| main.rs:246:9:246:12 | ...::C | main.rs:243:9:243:9 | C |
| main.rs:249:17:249:17 | S | main.rs:241:5:241:13 | struct S |
| main.rs:250:17:250:17 | C | main.rs:243:9:243:9 | C |
| main.rs:263:16:263:16 | T | main.rs:257:7:257:7 | TypeParam |
| main.rs:264:14:264:17 | Self | main.rs:255:5:265:5 | trait MyParamTrait |
| main.rs:264:14:264:33 | ...::AssociatedType | main.rs:259:9:259:28 | TypeAlias |
| main.rs:273:13:273:17 | crate | main.rs:1:1:302:2 | SourceFile |
| main.rs:273:13:273:22 | ...::m13 | main.rs:268:1:281:1 | mod m13 |
| main.rs:273:13:273:25 | ...::f | main.rs:269:5:269:17 | fn f |
| main.rs:273:13:273:25 | ...::f | main.rs:269:19:270:19 | struct f |
| main.rs:276:17:276:17 | f | main.rs:269:19:270:19 | struct f |
| main.rs:277:21:277:21 | f | main.rs:269:19:270:19 | struct f |
| main.rs:278:13:278:13 | f | main.rs:269:5:269:17 | fn f |
| main.rs:284:5:284:6 | my | main.rs:1:1:1:7 | mod my |
| main.rs:284:5:284:14 | ...::nested | my.rs:1:1:1:15 | mod nested |
| main.rs:284:5:284:23 | ...::nested1 | my/nested.rs:1:1:17:1 | mod nested1 |
| main.rs:284:5:284:32 | ...::nested2 | my/nested.rs:2:5:11:5 | mod nested2 |
| main.rs:284:5:284:35 | ...::f | my/nested.rs:3:9:5:9 | fn f |
| main.rs:285:5:285:6 | my | main.rs:1:1:1:7 | mod my |
| main.rs:285:5:285:9 | ...::f | my.rs:5:1:7:1 | fn f |
| main.rs:286:5:286:11 | nested2 | my2/mod.rs:1:1:1:16 | mod nested2 |
| main.rs:286:5:286:20 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 |
| main.rs:286:5:286:29 | ...::nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 |
| main.rs:286:5:286:32 | ...::f | my2/nested2.rs:3:9:5:9 | fn f |
| main.rs:287:5:287:5 | f | my2/nested2.rs:3:9:5:9 | fn f |
| main.rs:288:5:288:5 | g | my2/nested2.rs:7:9:9:9 | fn g |
| main.rs:289:5:289:9 | crate | main.rs:1:1:302:2 | SourceFile |
| main.rs:289:5:289:12 | ...::h | main.rs:50:1:69:1 | fn h |
| main.rs:290:5:290:6 | m1 | main.rs:13:1:37:1 | mod m1 |
| main.rs:290:5:290:10 | ...::m2 | main.rs:18:5:36:5 | mod m2 |
| main.rs:290:5:290:13 | ...::g | main.rs:23:9:27:9 | fn g |
| main.rs:291:5:291:6 | m1 | main.rs:13:1:37:1 | mod m1 |
| main.rs:291:5:291:10 | ...::m2 | main.rs:18:5:36:5 | mod m2 |
| main.rs:291:5:291:14 | ...::m3 | main.rs:29:9:35:9 | mod m3 |
| main.rs:291:5:291:17 | ...::h | main.rs:30:27:34:13 | fn h |
| main.rs:292:5:292:6 | m4 | main.rs:39:1:46:1 | mod m4 |
| main.rs:292:5:292:9 | ...::i | main.rs:42:5:45:5 | fn i |
| main.rs:293:5:293:5 | h | main.rs:50:1:69:1 | fn h |
| main.rs:294:5:294:11 | f_alias | my2/nested2.rs:3:9:5:9 | fn f |
| main.rs:295:5:295:11 | g_alias | my2/nested2.rs:7:9:9:9 | fn g |
| main.rs:296:5:296:5 | j | main.rs:97:1:101:1 | fn j |
| main.rs:297:5:297:6 | m6 | main.rs:109:1:120:1 | mod m6 |
| main.rs:297:5:297:9 | ...::g | main.rs:114:5:119:5 | fn g |
| main.rs:298:5:298:6 | m7 | main.rs:122:1:137:1 | mod m7 |
| main.rs:298:5:298:9 | ...::f | main.rs:129:5:136:5 | fn f |
| main.rs:299:5:299:6 | m8 | main.rs:139:1:182:1 | mod m8 |
| main.rs:299:5:299:9 | ...::g | main.rs:169:5:181:5 | fn g |
| main.rs:300:5:300:6 | m9 | main.rs:184:1:192:1 | mod m9 |
| main.rs:300:5:300:9 | ...::f | main.rs:187:5:191:5 | fn f |
| main.rs:301:5:301:7 | m11 | main.rs:215:1:252:1 | mod m11 |
| main.rs:301:5:301:10 | ...::f | main.rs:220:5:223:5 | fn f |
| my2/mod.rs:5:5:5:11 | nested2 | my2/mod.rs:1:1:1:16 | mod nested2 |
| my2/mod.rs:5:5:5:20 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 |
| my2/mod.rs:5:5:5:29 | ...::nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 |