Skip to content

Commit 7e66dc3

Browse files
authored
Merge pull request #114 from microsoft/powershell-flow-into-this
PS: Support flow through `this`
2 parents 878bd5b + fb8d67f commit 7e66dc3

File tree

19 files changed

+1303
-1141
lines changed

19 files changed

+1303
-1141
lines changed

powershell/ql/lib/powershell.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,5 @@ import semmle.code.powershell.IndexExpr
7979
import semmle.code.powershell.HashTable
8080
import semmle.code.powershell.SplitExpr
8181
import semmle.code.powershell.CommentEntity
82-
import semmle.code.powershell.Variable
82+
import semmle.code.powershell.Variable
83+
import semmle.code.powershell.internal.Internal::Public

powershell/ql/lib/semmle/code/powershell/Command.qll

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -65,30 +65,3 @@ class Cmd extends @command, CmdBase {
6565

6666
Redirection getARedirection() { result = this.getRedirection(_) }
6767
}
68-
69-
/**
70-
* An argument to a command.
71-
*
72-
* The argument may be named or positional.
73-
*/
74-
class Argument extends Expr {
75-
Cmd cmd;
76-
77-
Argument() { cmd.getAnArgument() = this }
78-
79-
Cmd getCmd() { result = cmd }
80-
81-
int getPosition() { cmd.getPositionalArgument(result) = this }
82-
83-
string getName() { cmd.getNamedArgument(result) = this }
84-
}
85-
86-
/** A positional argument to a command. */
87-
class PositionalArgument extends Argument {
88-
PositionalArgument() { not this instanceof NamedArgument }
89-
}
90-
91-
/** A named argument to a command. */
92-
class NamedArgument extends Argument {
93-
NamedArgument() { this = cmd.getNamedArgument(_) }
94-
}

powershell/ql/lib/semmle/code/powershell/Function.qll

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,28 @@ abstract private class AbstractFunction extends Ast {
3333
/** Gets the number of function parameters. */
3434
final int getNumberOfFunctionParameters() { result = count(this.getAFunctionParameter()) }
3535

36-
/** Gets the number of parameters (both function and block). */
36+
/**
37+
* Gets the number of parameters (both function and block).
38+
* Note: This excludes the `this` parameter.
39+
*/
3740
final int getNumberOfParameters() { result = count(this.getAParameter()) }
3841

39-
/** Gets the i'th parameter of this function, if any. */
42+
/**
43+
* Gets the i'th parameter of this function, if any.
44+
*
45+
* This does not include the `this` parameter.
46+
*/
4047
final Parameter getParameter(int i) {
4148
result = this.getFunctionParameter(i)
4249
or
4350
result = this.getBody().getParamBlock().getParameter(i)
4451
}
4552

53+
final Parameter getThisParameter() {
54+
result.isThis() and
55+
result.getFunction() = this
56+
}
57+
4658
/** Gets any parameter of this function. */
4759
final Parameter getAParameter() { result = this.getParameter(_) }
4860

powershell/ql/lib/semmle/code/powershell/IndexExpr.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import powershell
2-
private import internal.ExplicitWrite
2+
private import internal.ExplicitWrite::Private
33

44
class IndexExpr extends @index_expression, Expr {
55
override string toString() { result = "...[...]" }

powershell/ql/lib/semmle/code/powershell/Variable.qll

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
private import powershell
22
private import semmle.code.powershell.controlflow.internal.Scope
3-
private import internal.Internal as Internal
3+
private import internal.Parameter::Private as Internal
44

55
private predicate isFunctionParameterImpl(Internal::Parameter p, Function f, int i) {
66
function_definition_parameter(f, i, p)
@@ -42,7 +42,8 @@ private newtype TParameterImpl =
4242
TInternalParameter(Internal::Parameter p) or
4343
TUnderscore(Scope scope) {
4444
exists(VarAccess va | va.getUserPath() = "_" and scope = va.getEnclosingScope())
45-
}
45+
} or
46+
TThisParameter(Scope scope) { exists(scope.getEnclosingFunction().getDeclaringType()) }
4647

4748
private class ParameterImpl extends TParameterImpl {
4849
abstract Location getLocation();
@@ -109,9 +110,22 @@ private class Underscore extends ParameterImpl, TUnderscore {
109110
final override Scope getEnclosingScope() { result = scope }
110111
}
111112

113+
private class ThisParameter extends ParameterImpl, TThisParameter {
114+
Scope scope;
115+
116+
ThisParameter() { this = TThisParameter(scope) }
117+
118+
override Location getLocation() { result = scope.getLocation() }
119+
120+
override string getName() { result = "this" }
121+
122+
final override Scope getEnclosingScope() { result = scope }
123+
}
124+
112125
private newtype TVariable =
113126
TLocalVariable(string name, Scope scope) {
114127
not isParameterImpl(name, scope) and
128+
not name = "this" and // This is modeled as a parameter
115129
exists(VarAccess va | va.getUserPath() = name and scope = va.getEnclosingScope())
116130
} or
117131
TParameter(ParameterImpl p)
@@ -186,8 +200,11 @@ class Parameter extends AbstractLocalScopeVariable, TParameter {
186200

187201
predicate hasDefaultValue() { exists(this.getDefaultValue()) }
188202

203+
/** Holds if this is the `this` parameter. */
204+
predicate isThis() { p instanceof ThisParameter }
205+
189206
/**
190-
* Gets the index of this parameter.
207+
* Gets the index of this parameter, if any.
191208
*
192209
* The parameter may be in a parameter block or a function parameter.
193210
*/

powershell/ql/lib/semmle/code/powershell/VariableExpression.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import powershell
2-
private import internal.ExplicitWrite
2+
private import internal.ExplicitWrite::Private
33

44
private predicate isParameterName(@variable_expression ve) { parameter(_, ve, _, _) }
55

powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,11 @@ module ExprNodes {
217217
/** Gets the name of this argument, if any. */
218218
string getName() { result = e.getName() }
219219

220-
StmtNodes::CmdCfgNode getCmd() { result.getAnArgument() = this }
220+
/** Holds if `this` is a qualifier to a call. */
221+
predicate isQualifier() { e.isQualifier() }
222+
223+
/** Gets the call for which this is an argument. */
224+
CallCfgNode getCall() { result.getAnArgument() = this or result.getQualifier() = this }
221225
}
222226

223227
private class InvokeMemberChildMapping extends ExprChildMapping, InvokeMemberExpr {

powershell/ql/lib/semmle/code/powershell/controlflow/internal/Scope.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,11 @@ class Scope extends Ast, @script_block {
3636
* This may be both function paramters and parameter block parameters.
3737
*/
3838
Parameter getAParameter() { result = this.getParameter(_) }
39+
40+
Parameter getThisParameter() {
41+
exists(Function func |
42+
func.getBody() = this and
43+
result = func.getThisParameter()
44+
)
45+
}
3946
}

powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowDispatch.qll

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ abstract class LibraryCallable extends string {
3737
LibraryCallable() { any() }
3838

3939
/** Gets a call to this library callable. */
40-
Cmd getACall() { none() }
40+
Call getACall() { none() }
4141
}
4242

4343
/**
@@ -222,7 +222,8 @@ private module Cached {
222222

223223
cached
224224
newtype TArgumentPosition =
225-
TKeywordArgumentPosition(string name) { name = any(CmdParameter p).getName() } or
225+
TThisArgumentPosition() or
226+
TKeywordArgumentPosition(string name) { name = any(Argument p).getName() } or
226227
TPositionalArgumentPosition(int pos, NamedSet ns) {
227228
exists(CfgNodes::CallCfgNode call |
228229
call = ns.getABindingCall() and
@@ -232,7 +233,8 @@ private module Cached {
232233

233234
cached
234235
newtype TParameterPosition =
235-
TKeywordParameter(string name) { name = any(CmdParameter p).getName() } or
236+
TThisParameterPosition() or
237+
TKeywordParameter(string name) { name = any(Argument p).getName() } or
236238
TPositionalParameter(int pos, NamedSet ns) {
237239
exists(CfgNodes::CallCfgNode call |
238240
call = ns.getABindingCall() and
@@ -245,6 +247,9 @@ import Cached
245247

246248
/** A parameter position. */
247249
class ParameterPosition extends TParameterPosition {
250+
/** Holds if this position represents a `this` parameter. */
251+
predicate isThis() { this = TThisParameterPosition() }
252+
248253
/**
249254
* Holds if this position represents a positional parameter at position `pos`
250255
* with function is called with exactly the named parameters from the set `ns`
@@ -256,6 +261,8 @@ class ParameterPosition extends TParameterPosition {
256261

257262
/** Gets a textual representation of this position. */
258263
string toString() {
264+
this.isThis() and result = "this"
265+
or
259266
exists(int pos, NamedSet ns |
260267
this.isPositional(pos, ns) and result = "pos(" + pos + ", " + ns.toString() + ")"
261268
)
@@ -266,13 +273,18 @@ class ParameterPosition extends TParameterPosition {
266273

267274
/** An argument position. */
268275
class ArgumentPosition extends TArgumentPosition {
276+
/** Holds if this position represents a `this` argument. */
277+
predicate isThis() { this = TThisArgumentPosition() }
278+
269279
/** Holds if this position represents a positional argument at position `pos`. */
270280
predicate isPositional(int pos, NamedSet ns) { this = TPositionalArgumentPosition(pos, ns) }
271281

272282
predicate isKeyword(string name) { this = TKeywordArgumentPosition(name) }
273283

274284
/** Gets a textual representation of this position. */
275285
string toString() {
286+
this.isThis() and result = "this"
287+
or
276288
exists(int pos, NamedSet ns |
277289
this.isPositional(pos, ns) and result = "pos(" + pos + ", " + ns.toString() + ")"
278290
)
@@ -284,6 +296,8 @@ class ArgumentPosition extends TArgumentPosition {
284296
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
285297
pragma[nomagic]
286298
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
299+
ppos.isThis() and apos.isThis()
300+
or
287301
exists(string name |
288302
ppos.isKeyword(name) and
289303
apos.isKeyword(name)

powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPrivate.qll

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,9 @@ private module ParameterNodes {
407407
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
408408
parameter.getDeclaringScope() = c.asCfgScope() and
409409
(
410+
pos.isThis() and
411+
parameter.isThis()
412+
or
410413
pos.isKeyword(parameter.getName())
411414
or
412415
// Given a function f with parameters x, y we map
@@ -432,7 +435,9 @@ private module ParameterNodes {
432435
)
433436
}
434437

435-
override CfgScope getCfgScope() { result.getAParameter() = parameter }
438+
override CfgScope getCfgScope() {
439+
result.getAParameter() = parameter or result.getThisParameter() = parameter
440+
}
436441

437442
override Location getLocationImpl() { result = parameter.getLocation() }
438443

@@ -458,7 +463,7 @@ module ArgumentNodes {
458463
ExplicitArgumentNode() { this.asExpr() = arg }
459464

460465
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
461-
arg.getCmd() = call.asCall() and
466+
arg.getCall() = call.asCall() and
462467
(
463468
pos.isKeyword(arg.getName())
464469
or
@@ -467,6 +472,9 @@ module ArgumentNodes {
467472
ns.getAnExactBindingCall() = call.asCall() and
468473
pos.isPositional(i, ns)
469474
)
475+
or
476+
arg.isQualifier() and
477+
pos.isThis()
470478
)
471479
}
472480
}

0 commit comments

Comments
 (0)