Skip to content

Commit f11004a

Browse files
authored
Merge pull request #22611 from asgerf/unified/call-graph-static
Unified: Add flow through calls (static calls only)
2 parents b34cfbc + fd90099 commit f11004a

23 files changed

Lines changed: 1277 additions & 598 deletions

‎unified/extractor/src/languages/swift/swift.rs‎

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -596,26 +596,31 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
596596
),
597597
// A function parameter. With two names (`firstName`+`secondName`) the
598598
// first is the external argument label and the second the internal name;
599-
// with one name it is just the internal name. The declared type is
600-
// emitted; the default value is optional.
599+
// with one name it is both the external and internal name.
601600
rule!(
602601
(functionParameter
603602
firstName: @@first
604-
secondName: _? @@second
603+
secondName: @@second
605604
type: @ty
606605
defaultValue: (initializerClause value: @val)?)
607606
=>
608-
parameter {
609-
let (external, name) = match second {
610-
Some(second) => (Some(tree!((identifier #{first}))), second),
611-
None => (None, first),
612-
};
613-
tree!((parameter
614-
external_name_node: {external}
615-
pattern: (identifier #{name})
616-
type: {ty}
617-
default: {val}))
618-
}
607+
(parameter
608+
external_name_node: (identifier #{first})
609+
pattern: (identifier #{second})
610+
type: {ty}
611+
default: {val})
612+
),
613+
rule!(
614+
(functionParameter
615+
firstName: @@first
616+
type: @ty
617+
defaultValue: (initializerClause value: @val)?)
618+
=>
619+
(parameter
620+
external_name_node: (identifier #{first}) // duplicate the parameter name
621+
pattern: (identifier #{first})
622+
type: {ty}
623+
default: {val})
619624
),
620625
// Swift's `[T](...)` array-type constructor syntax is parsed as a call
621626
// whose callee is an `arrayExpr` containing `T`. For a generic `T`,

‎unified/extractor/tests/corpus/swift/functions/function-with-default-parameter-value.output‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ top_level source="⟨body⟩"
6868
function_declaration source="⟨body⟩⟨name_node⟩⟨parameter⟩"
6969
name_node: identifier "greet" source="greet"
7070
parameter:
71-
parameter source="⟨pattern⟩: ⟨type⟩ = ⟨default⟩"
71+
parameter source="⟨external_name_node⟩⟨pattern⟩: ⟨type⟩ = ⟨default⟩"
72+
external_name_node: identifier "name" source="name"
7273
type: identifier "String" source="String"
7374
pattern: identifier "name" source="name"
7475
default: string_literal "\"world\"" source="\"world\""

‎unified/extractor/tests/corpus/swift/types/class-with-initializer.output‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ top_level source="⟨body⟩"
100100
type: identifier "Int" source="Int"
101101
constructor_declaration source="⟨body⟩⟨parameter⟩"
102102
parameter:
103-
parameter source="⟨pattern⟩: ⟨type⟩"
103+
parameter source="⟨external_name_node⟩⟨pattern⟩: ⟨type⟩"
104+
external_name_node: identifier "x" source="x"
104105
type: identifier "Int" source="Int"
105106
pattern: identifier "x" source="x"
106107
body:

‎unified/ql/consistency-queries/DataFlowConsistency.ql‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ private import unified
22
private import codeql.unified.internal.dataflow.AllDataFlow
33
private import codeql.dataflow.internal.DataFlowImplConsistency
44

5-
module ConsistencyInput implements InputSig<Location, DataFlowInput> { }
5+
module ConsistencyInput implements InputSig<Location, DataFlowInput> {
6+
predicate argHasPostUpdateExclude(DataFlowInput::ArgumentNode n) {
7+
not exists(n.getBasicBlock()) // ignore unreachable data flow nodes
8+
}
9+
}
610

711
module ConsistencyOutput =
812
MakeConsistency<Location, DataFlowInput, TaintTrackingInput, ConsistencyInput>;

‎unified/ql/lib/codeql/unified/internal/AnalysisQuality.qll‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
private import unified
22
private import codeql.util.ReportStats
33
private import codeql.unified.internal.NameBinding
4+
private import codeql.unified.internal.dataflow.DataFlowCall
5+
private import codeql.unified.internal.dataflow.DataFlowCallable
6+
private import codeql.unified.internal.dataflow.CallGraph
47

58
/** Stats about name nodes that static name binding could resolve. */
69
module StaticNameResolutionStats implements EntityStatsSig {
@@ -87,3 +90,21 @@ module FilesCoveredByModuleManifestStats implements EntityStatsSig {
8790

8891
module FilesCoveredByModuleManifestStatsReport =
8992
EntityReportStats<FilesCoveredByModuleManifestStats>;
93+
94+
module CallGraphStats implements EntityStatsSig {
95+
class Candidate extends CallExpr {
96+
Candidate() { exists(DataFlowCall c | c.asExplicitCall() = this) }
97+
98+
DataFlowCall getDataFlowCall() { result.asExplicitCall() = this }
99+
100+
DataFlowCallable getTarget() { result = viableCallable(this.getDataFlowCall()) }
101+
102+
predicate isOk() { exists(this.getTarget()) }
103+
}
104+
105+
string getOkText() { result = "calls with call target" }
106+
107+
string getNotOkText() { result = "calls with missing call target" }
108+
}
109+
110+
module CallGraphStatsReport = EntityReportStats<CallGraphStats>;

‎unified/ql/lib/codeql/unified/internal/FacadeAst.qll‎

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,19 @@ module Unified {
117117
class Argument extends G::Argument {
118118
/** Gets the name of this argument. */
119119
string getName() { result = this.getNameNode().getValue() }
120+
121+
/** Holds if this is a positional argument. */
122+
predicate isPositional() { not exists(this.getName()) }
123+
124+
/** Gets the 0-based index of this argument among the positional arguments in the surrounding call or tuple. */
125+
int getPositionalIndex() {
126+
this =
127+
rank[result + 1](Argument a |
128+
a.getParent() = this.getParent() and a.isPositional()
129+
|
130+
a order by a.getParentIndex()
131+
)
132+
}
120133
}
121134

122135
class AssociatedTypeDeclaration extends G::AssociatedTypeDeclaration {
@@ -172,8 +185,28 @@ module Unified {
172185
}
173186

174187
class Parameter extends G::Parameter {
175-
/** Gets the external name of this parameter. */
176-
string getExternalName() { result = this.getExternalNameNode().getValue() }
188+
/**
189+
* Gets the external name of this parameter.
190+
*
191+
* Has no result for pseudo-names like `_` that indicate that this is actually a positional parameter.
192+
*/
193+
string getExternalName() { result = this.getExternalNameNode().getValue() and not result = "_" }
194+
195+
/** Gets the callable on which this parameter appears. */
196+
Callable getDeclaringCallable() { result = this.getParent() }
197+
198+
/** Holds if this is a positional parameter. */
199+
predicate isPositional() { not exists(this.getExternalName()) }
200+
201+
/** Gets the 0-based index of this parameter among the positional parameters of the declaring callable. */
202+
int getPositionalIndex() {
203+
this =
204+
rank[result + 1](Parameter p |
205+
p.getDeclaringCallable() = this.getDeclaringCallable() and p.isPositional()
206+
|
207+
p order by p.getParentIndex()
208+
)
209+
}
177210
}
178211

179212
class TypeAliasDeclaration extends G::TypeAliasDeclaration {

‎unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,3 +474,11 @@ class PotentialLocalNameAccess extends IdentifierExpr {
474474
/** Holds if this is one of the binding sites for a name, such as the `x` in `let x = 123`. */
475475
predicate isBindingSite() { this instanceof NameBinding }
476476
}
477+
478+
/** Gets the implicitly-declared variable through which the given callable refers to its receiver. */
479+
LocalVariable getImplicitReceiverVariable(Callable callable) {
480+
exists(string name |
481+
name = any(NameBindingPlugin p).getImplicitReceiverParameterName(callable) and
482+
result.(LocalNameBindingOutput::ImplicitLocal).hasNameAndScope(name, callable)
483+
)
484+
}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
/** Re-exports all the files in the internal dataflow folder (except DataFlowPublic). */
22

3+
import CallGraph
34
import Content
5+
import DataFlowCall
6+
import DataFlowCallable
47
import DataFlowGraph
58
import DataFlowInstantiation
69
import DataFlowNode
710
import DataFlowPlugin
8-
import Step
911
import LocalSsa
12+
import ParameterPositions
13+
import Step
1014
import TaintTrackingInstantiation
1115
import VariableRefKind
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
private import unified
2+
private import AllDataFlow
3+
private import codeql.unified.internal.NameBinding as N
4+
5+
private Callable getCallableFromNameBinding(NameBinding binding) {
6+
binding = result.(FunctionDeclaration).getNameNode()
7+
}
8+
9+
DataFlowCallable viableCallable(DataFlowCall c) {
10+
exists(CallExpr call, Callable callable, NameBinding target |
11+
c.asExplicitCall() = call and
12+
target = N::getStaticBindingTarget(N::getIdentifierFromRef(call.getCallee())) and
13+
callable = getCallableFromNameBinding(target) and
14+
result.asSourceCallable() = callable
15+
)
16+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
private import unified
2+
private import AllDataFlow
3+
private import codeql.unified.internal.ExprPositions
4+
5+
private newtype TDataFlowCall =
6+
TExplicitCall(CallExpr call) {
7+
not isInBindingContext(call, _) // ignore constructor patterns
8+
}
9+
10+
/**
11+
* A call site, covering explicit calls such as `foo(1,2)`, as well as implicit
12+
* calls and calls derived from library models.
13+
*
14+
* Currently only explicit calls are implemented.
15+
*/
16+
class DataFlowCall extends TDataFlowCall {
17+
/** Gets the `CallExpr` wrapped by this dataflow call, if any. */
18+
CallExpr asExplicitCall() { this = TExplicitCall(result) }
19+
20+
/** Gets a textual representation of this call. */
21+
string toString() { result = this.asExplicitCall().toString() }
22+
23+
/** Gets the location of this call, if any. */
24+
Location getLocation() { result = this.asExplicitCall().getLocation() }
25+
26+
/** Gets the callable containing this call. */
27+
DataFlowCallable getEnclosingCallable() {
28+
result.asSourceCallable() = this.asExplicitCall().getEnclosingCallable()
29+
}
30+
}

0 commit comments

Comments
 (0)