diff --git a/powershell/ql/lib/powershell.qll b/powershell/ql/lib/powershell.qll
new file mode 100644
index 00000000000..59fd70c14e9
--- /dev/null
+++ b/powershell/ql/lib/powershell.qll
@@ -0,0 +1,70 @@
+import semmle.code.powershell.File
+import semmle.code.powershell.Location
+import semmle.code.powershell.SourceLocation
+import semmle.code.powershell.Ast
+import semmle.code.powershell.Statement
+import semmle.code.powershell.Expression
+import semmle.code.powershell.CommandBase
+import semmle.code.powershell.AttributeBase
+import semmle.code.powershell.PipelineBase
+import semmle.code.powershell.BaseConstantExpression
+import semmle.code.powershell.ConstantExpression
+import semmle.code.powershell.MemberExpressionBase
+import semmle.code.powershell.Attribute
+import semmle.code.powershell.NamedAttributeArgument
+import semmle.code.powershell.TypeConstraint
+import semmle.code.powershell.VariableExpression
+import semmle.code.powershell.Parameter
+import semmle.code.powershell.ModuleSpecification
+import semmle.code.powershell.ParamBlock
+import semmle.code.powershell.NamedBlock
+import semmle.code.powershell.ScriptBlock
+import semmle.code.powershell.StringLiteral
+import semmle.code.powershell.AssignmentStatement
+import semmle.code.powershell.BinaryExpression
+import semmle.code.powershell.ScriptBlockExpr
+import semmle.code.powershell.TernaryExpression
+import semmle.code.powershell.UsingExpression
+import semmle.code.powershell.TrapStatement
+import semmle.code.powershell.StatementBlock
+import semmle.code.powershell.ArrayExpression
+import semmle.code.powershell.ArrayLiteral
+import semmle.code.powershell.CommandElement
+import semmle.code.powershell.Redirection
+import semmle.code.powershell.FileRedirection
+import semmle.code.powershell.MergingRedirection
+import semmle.code.powershell.LoopStmt
+import semmle.code.powershell.DoWhileStmt
+import semmle.code.powershell.DoUntilStmt
+import semmle.code.powershell.WhileStmt
+import semmle.code.powershell.ForStmt
+import semmle.code.powershell.ForEachStmt
+import semmle.code.powershell.GotoStmt
+import semmle.code.powershell.ContinueStmt
+import semmle.code.powershell.BreakStmt
+import semmle.code.powershell.ReturnStmt
+import semmle.code.powershell.UsingStmt
+import semmle.code.powershell.Type
+import semmle.code.powershell.Member
+import semmle.code.powershell.PropertyMember
+import semmle.code.powershell.FunctionMember
+import semmle.code.powershell.TryStmt
+import semmle.code.powershell.IfStmt
+import semmle.code.powershell.ExitStmt
+import semmle.code.powershell.LabeledStmt
+import semmle.code.powershell.DynamicStmt
+import semmle.code.powershell.DataStmt
+import semmle.code.powershell.Configuration
+import semmle.code.powershell.CatchClause
+import semmle.code.powershell.Command
+import semmle.code.powershell.CommandExpression
+import semmle.code.powershell.CommandParameter
+import semmle.code.powershell.ExpandableStringExpression
+import semmle.code.powershell.TypeExpression
+import semmle.code.powershell.ParenExpression
+import semmle.code.powershell.Chainable
+import semmle.code.powershell.Pipeline
+import semmle.code.powershell.StringConstantExpression
+import semmle.code.powershell.FunctionDefinition
+import semmle.code.powershell.InvokeMemberExpression
+import semmle.code.powershell.CommentEntity
diff --git a/powershell/ql/lib/qlpack.yml b/powershell/ql/lib/qlpack.yml
new file mode 100644
index 00000000000..28385cb7c15
--- /dev/null
+++ b/powershell/ql/lib/qlpack.yml
@@ -0,0 +1,9 @@
+name: microsoft-sdl/powershell-all
+version: 0.0.1
+groups:
+ - powershell
+ - microsoft-all
+dbscheme: semmlecode.powershell.dbscheme
+extractor: powershell
+library: true
+warnOnImplicitThis: true
\ No newline at end of file
diff --git a/powershell/ql/lib/semmle/code/powershell/ArrayExpression.qll b/powershell/ql/lib/semmle/code/powershell/ArrayExpression.qll
new file mode 100644
index 00000000000..b1841c94094
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/ArrayExpression.qll
@@ -0,0 +1,9 @@
+import powershell
+
+class ArrayExpr extends @array_expression, Expr {
+ override SourceLocation getLocation() { array_expression_location(this, result) }
+
+ StmtBlock getStatementBlock() { array_expression(this, result) }
+
+ override string toString() { result = "ArrayExpression at: " + this.getLocation().toString() }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/ArrayLiteral.qll b/powershell/ql/lib/semmle/code/powershell/ArrayLiteral.qll
new file mode 100644
index 00000000000..a5fd2fd177f
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/ArrayLiteral.qll
@@ -0,0 +1,11 @@
+import powershell
+
+class ArrayLiteral extends @array_literal, Expr {
+ override SourceLocation getLocation() { array_literal_location(this, result) }
+
+ Expr getElement(int index) { array_literal_element(this, index, result) }
+
+ Expr getAnElement() { array_literal_element(this, _, result) }
+
+ override string toString() { result = "ArrayLiteral at: " + this.getLocation().toString() }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/AssignmentStatement.qll b/powershell/ql/lib/semmle/code/powershell/AssignmentStatement.qll
new file mode 100644
index 00000000000..c1d1b2a57cb
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/AssignmentStatement.qll
@@ -0,0 +1,13 @@
+import powershell
+
+class AssignStmt extends @assignment_statement, Stmt {
+ override SourceLocation getLocation() { assignment_statement_location(this, result) }
+
+ int getKind() { assignment_statement(this, result, _, _) }
+
+ Expr getLeftHandSide() { assignment_statement(this, _, result, _) }
+
+ Stmt getRightHandSide() { assignment_statement(this, _, _, result) }
+
+ override string toString() { result = "AssignmentStatement at: " + this.getLocation().toString() }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/Ast.qll b/powershell/ql/lib/semmle/code/powershell/Ast.qll
new file mode 100644
index 00000000000..601f25f458f
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/Ast.qll
@@ -0,0 +1,9 @@
+import powershell
+
+class Ast extends @ast {
+ string toString() { none() }
+
+ Ast getParent() { parent(result, this) }
+
+ Location getLocation() { none() }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/Attribute.qll b/powershell/ql/lib/semmle/code/powershell/Attribute.qll
new file mode 100644
index 00000000000..88c8b466e0f
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/Attribute.qll
@@ -0,0 +1,21 @@
+import powershell
+
+class Attribute extends @attribute, AttributeBase {
+ override string toString() { result = this.getName() }
+
+ override SourceLocation getLocation() { attribute_location(this, result) }
+
+ string getName() { attribute(this, result, _, _) }
+
+ int getNumNamedArguments() { attribute(this, _, result, _) }
+
+ int getNumPositionalArguments() { attribute(this, _, _, result) }
+
+ NamedAttributeArgument getNamedArgument(int i) { attribute_named_argument(this, i, result) }
+
+ NamedAttributeArgument getANamedArgument() { result = this.getNamedArgument(_) }
+
+ Expr getPositionalArgument(int i) { attribute_positional_argument(this, i, result) }
+
+ Expr getAPositionalArgument() { result = this.getPositionalArgument(_) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/AttributeBase.qll b/powershell/ql/lib/semmle/code/powershell/AttributeBase.qll
new file mode 100644
index 00000000000..a0a5a36cdb1
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/AttributeBase.qll
@@ -0,0 +1,3 @@
+import powershell
+
+class AttributeBase extends @attribute_base, Ast { }
diff --git a/powershell/ql/lib/semmle/code/powershell/BaseConstantExpression.qll b/powershell/ql/lib/semmle/code/powershell/BaseConstantExpression.qll
new file mode 100644
index 00000000000..3569355ece4
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/BaseConstantExpression.qll
@@ -0,0 +1,3 @@
+import powershell
+
+class BaseConstExpr extends @base_constant_expression, Expr { }
diff --git a/powershell/ql/lib/semmle/code/powershell/BinaryExpression.qll b/powershell/ql/lib/semmle/code/powershell/BinaryExpression.qll
new file mode 100644
index 00000000000..4f3667e42db
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/BinaryExpression.qll
@@ -0,0 +1,15 @@
+import powershell
+
+class BinaryExpr extends @binary_expression, Expr {
+ override string toString() {
+ result = "...+..." // TODO
+ }
+
+ override SourceLocation getLocation() { binary_expression_location(this, result) }
+
+ private int getKind() { binary_expression(this, result, _, _) }
+
+ Expr getLeft() { binary_expression(this, _, result, _) }
+
+ Expr getRight() { binary_expression(this, _, _, result) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/BreakStmt.qll b/powershell/ql/lib/semmle/code/powershell/BreakStmt.qll
new file mode 100644
index 00000000000..dbec183d7b0
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/BreakStmt.qll
@@ -0,0 +1,7 @@
+import powershell
+
+class BreakStmt extends GotoStmt, Stmt {
+ override SourceLocation getLocation() { break_statement_location(this, result) }
+
+ override string toString() { result = "continue" }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/CatchClause.qll b/powershell/ql/lib/semmle/code/powershell/CatchClause.qll
new file mode 100644
index 00000000000..bf5ca9d8722
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/CatchClause.qll
@@ -0,0 +1,17 @@
+import powershell
+
+class CatchClause extends @catch_clause, Ast {
+ override SourceLocation getLocation() { catch_clause_location(this, result) }
+
+ override string toString() { result = "catch {...}" }
+
+ StmtBlock getBody() { catch_clause(this, result, _) } // TODO: Change @ast to @stmt_block in dbscheme
+
+ TypeConstraint getCatchType(int i) { catch_clause_catch_type(this, i, result) } // TODO: Change @ast to @type_constraint in dbscheme
+
+ TypeConstraint getACatchType() { result = this.getCatchType(_) }
+
+ predicate isCatchAll() { catch_clause(this, _, true) } // TODO: Should be equivalent to not exists(this.getACatchType())
+
+ TryStmt getTryStmt() { result.getACatchClause() = this }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/Chainable.qll b/powershell/ql/lib/semmle/code/powershell/Chainable.qll
new file mode 100644
index 00000000000..273ccc48113
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/Chainable.qll
@@ -0,0 +1,3 @@
+import powershell
+
+class Chainable extends @chainable, PipelineBase { }
diff --git a/powershell/ql/lib/semmle/code/powershell/Command.qll b/powershell/ql/lib/semmle/code/powershell/Command.qll
new file mode 100644
index 00000000000..29f17624493
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/Command.qll
@@ -0,0 +1,23 @@
+import powershell
+
+class Cmd extends @command, CmdBase {
+ override string toString() { result = this.getName() }
+
+ override SourceLocation getLocation() { command_location(this, result) }
+
+ string getName() { command(this, result, _, _, _) }
+
+ int getKind() { command(this, _, result, _, _) }
+
+ int getNumElements() { command(this, _, _, result, _) }
+
+ int getNumRedirection() { command(this, _, _, _, result) }
+
+ CmdElement getElement(int i) { command_command_element(this, i, result) }
+
+ Redirection getRedirection(int i) { command_redirection(this, i, result) }
+
+ CmdElement getAnElement() { result = this.getElement(_) }
+
+ Redirection getARedirection() { result = this.getRedirection(_) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/CommandBase.qll b/powershell/ql/lib/semmle/code/powershell/CommandBase.qll
new file mode 100644
index 00000000000..9f78e71e012
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/CommandBase.qll
@@ -0,0 +1,3 @@
+import powershell
+
+class CmdBase extends @command_base, Stmt { }
diff --git a/powershell/ql/lib/semmle/code/powershell/CommandElement.qll b/powershell/ql/lib/semmle/code/powershell/CommandElement.qll
new file mode 100644
index 00000000000..6dbdffa7250
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/CommandElement.qll
@@ -0,0 +1,3 @@
+import powershell
+
+class CmdElement extends @command_element, Ast { }
diff --git a/powershell/ql/lib/semmle/code/powershell/CommandExpression.qll b/powershell/ql/lib/semmle/code/powershell/CommandExpression.qll
new file mode 100644
index 00000000000..362fa3ad5b6
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/CommandExpression.qll
@@ -0,0 +1,15 @@
+import powershell
+
+class CmdExpr extends @command_expression, CmdBase {
+ override SourceLocation getLocation() { command_expression_location(this, result) }
+
+ Expr getExpression() { command_expression(this, result, _) }
+
+ int getNumRedirections() { command_expression(this, _, result) }
+
+ Redirection getRedirection(int i) { command_expression_redirection(this, i, result) }
+
+ Redirection getARedirection() { result = this.getRedirection(_) }
+
+ override string toString() { result = "CommandExpression at: " + this.getLocation().toString() }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/CommandParameter.qll b/powershell/ql/lib/semmle/code/powershell/CommandParameter.qll
new file mode 100644
index 00000000000..9e2faa21064
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/CommandParameter.qll
@@ -0,0 +1,11 @@
+import powershell
+
+class CmdParameter extends @command_parameter, CmdElement {
+ override SourceLocation getLocation() { command_parameter_location(this, result) }
+
+ string getName() { command_parameter(this, result) }
+
+ Expr getArgument() { command_parameter_argument(this, result) }
+
+ override string toString() { command_parameter(this, result) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/CommentEntity.qll b/powershell/ql/lib/semmle/code/powershell/CommentEntity.qll
new file mode 100644
index 00000000000..cd46c8cb83f
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/CommentEntity.qll
@@ -0,0 +1,9 @@
+import powershell
+
+class Comment extends @comment_entity {
+ SourceLocation getLocation() { comment_entity_location(this, result) }
+
+ StringLiteral getCommentContents() { comment_entity(this, result) }
+
+ string toString() { result = "Comment at: " + this.getLocation().toString() }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/Configuration.qll b/powershell/ql/lib/semmle/code/powershell/Configuration.qll
new file mode 100644
index 00000000000..d2c76a0cbe1
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/Configuration.qll
@@ -0,0 +1,15 @@
+import powershell
+
+class Configuration extends @configuration_definition, Stmt {
+ override SourceLocation getLocation() { configuration_definition_location(this, result) }
+
+ override string toString() { result = "Configuration" }
+
+ Expr getName() { configuration_definition(this, _, _, result) } // TODO: Change @ast to @expression in dbscheme
+
+ ScriptBlockExpr getBody() { configuration_definition(this, result, _, _) } // TODO: Change @ast to @script_block in dbscheme
+
+ predicate isMeta() { configuration_definition(this, _, 1, _) }
+
+ predicate isResource() { configuration_definition(this, _, 0, _) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/ConstantExpression.qll b/powershell/ql/lib/semmle/code/powershell/ConstantExpression.qll
new file mode 100644
index 00000000000..5d4e26c211e
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/ConstantExpression.qll
@@ -0,0 +1,11 @@
+import powershell
+
+class ConstExpr extends @constant_expression, BaseConstExpr {
+ override SourceLocation getLocation() { constant_expression_location(this, result) }
+
+ string getType() { constant_expression(this, result) }
+
+ StringLiteral getValue() { constant_expression_value(this, result) }
+
+ override string toString() { result = "ConstantExpression at: " + this.getLocation().toString() }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/ContinueStmt.qll b/powershell/ql/lib/semmle/code/powershell/ContinueStmt.qll
new file mode 100644
index 00000000000..79cd8053f77
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/ContinueStmt.qll
@@ -0,0 +1,7 @@
+import powershell
+
+class ContinueStmt extends GotoStmt, Stmt {
+ override SourceLocation getLocation() { continue_statement_location(this, result) }
+
+ override string toString() { result = "continue" }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/DataStmt.qll b/powershell/ql/lib/semmle/code/powershell/DataStmt.qll
new file mode 100644
index 00000000000..c5bd635f065
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/DataStmt.qll
@@ -0,0 +1,15 @@
+import powershell
+
+class DataStmt extends @data_statement, Stmt {
+ override SourceLocation getLocation() { data_statement_location(this, result) }
+
+ override string toString() { result = "data {...}" }
+
+ string getVariableName() { data_statement_variable(this, result) }
+
+ Expr getCmdAllowed(int i) { data_statement_commands_allowed(this, i, result) }
+
+ Expr getACmdAllowed() { result = this.getCmdAllowed(_) }
+
+ StmtBlock getBody() { data_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/DoUntilStmt.qll b/powershell/ql/lib/semmle/code/powershell/DoUntilStmt.qll
new file mode 100644
index 00000000000..a0b4f40c03a
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/DoUntilStmt.qll
@@ -0,0 +1,11 @@
+import powershell
+
+class DoUntilStmt extends @do_until_statement, LoopStmt {
+ override SourceLocation getLocation() { do_until_statement_location(this, result) }
+
+ override string toString() { result = "DoUntil" }
+
+ PipelineBase getCondition() { do_until_statement_condition(this, result) } // TODO: Change @ast to @pipeline_base in dbscheme
+
+ StmtBlock getBody() { do_until_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/DoWhileStmt.qll b/powershell/ql/lib/semmle/code/powershell/DoWhileStmt.qll
new file mode 100644
index 00000000000..8076e202347
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/DoWhileStmt.qll
@@ -0,0 +1,11 @@
+import powershell
+
+class DoWhileStmt extends @do_while_statement, LoopStmt {
+ override SourceLocation getLocation() { do_while_statement_location(this, result) }
+
+ override string toString() { result = "DoWhile" }
+
+ PipelineBase getCondition() { do_while_statement_condition(this, result) } // TODO: Change @ast to @pipeline_base in dbscheme
+
+ StmtBlock getBody() { do_while_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/DynamicStmt.qll b/powershell/ql/lib/semmle/code/powershell/DynamicStmt.qll
new file mode 100644
index 00000000000..49c647af51d
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/DynamicStmt.qll
@@ -0,0 +1,11 @@
+import powershell
+
+class DynamicStmt extends @dynamic_keyword_statement, Stmt {
+ override SourceLocation getLocation() { dynamic_keyword_statement_location(this, result) }
+
+ override string toString() { result = "&..." }
+
+ CmdElement getCmd(int i) { dynamic_keyword_statement_command_elements(this, i, result) }
+
+ CmdElement getACmd() { result = this.getCmd(_) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/ExitStmt.qll b/powershell/ql/lib/semmle/code/powershell/ExitStmt.qll
new file mode 100644
index 00000000000..d6444e41e6f
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/ExitStmt.qll
@@ -0,0 +1,12 @@
+import powershell
+
+class ExitStmt extends @exit_statement, Stmt {
+ override SourceLocation getLocation() { exit_statement_location(this, result) }
+
+ override string toString() { if this.hasPipeline() then result = "exit ..." else result = "exit" }
+
+ /** ..., if any. */
+ PipelineBase getPipeline() { exit_statement_pipeline(this, result) } // TODO: Change @ast to @pipeline_base in dbscheme
+
+ predicate hasPipeline() { exists(this.getPipeline()) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/ExpandableStringExpression.qll b/powershell/ql/lib/semmle/code/powershell/ExpandableStringExpression.qll
new file mode 100644
index 00000000000..1bf1a4d22e8
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/ExpandableStringExpression.qll
@@ -0,0 +1,17 @@
+import powershell
+
+class ExpandableStringExpression extends @expandable_string_expression, Expr {
+ override SourceLocation getLocation() { expandable_string_expression_location(this, result) }
+
+ override string toString() {
+ result = "ExpandableStringExpression at: " + this.getLocation().toString()
+ }
+
+ private int getKind() { expandable_string_expression(this, _, result, _) }
+
+ int getNumExprs() { expandable_string_expression(this, _, _, result) }
+
+ Expr getExpr(int i) { expandable_string_expression_nested_expression(this, i, result) }
+
+ Expr getAnExpr() { result = this.getExpr(_) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/Expression.qll b/powershell/ql/lib/semmle/code/powershell/Expression.qll
new file mode 100644
index 00000000000..97df75538c0
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/Expression.qll
@@ -0,0 +1,3 @@
+import powershell
+
+class Expr extends @expression, CmdElement { }
diff --git a/powershell/ql/lib/semmle/code/powershell/File.qll b/powershell/ql/lib/semmle/code/powershell/File.qll
new file mode 100644
index 00000000000..5fbadd59626
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/File.qll
@@ -0,0 +1,224 @@
+/**
+ * Provides classes representing filesystem files and folders.
+ * Based on csharp/ql/lib/semmle/code/csharp/File.qll
+ */
+
+/** A file or folder. */
+class Container extends @container {
+ /**
+ * Gets the absolute, canonical path of this container, using forward slashes
+ * as path separator.
+ *
+ * The path starts with a _root prefix_ followed by zero or more _path
+ * segments_ separated by forward slashes.
+ *
+ * The root prefix is of one of the following forms:
+ *
+ * 1. A single forward slash `/` (Unix-style)
+ * 2. An upper-case drive letter followed by a colon and a forward slash,
+ * such as `C:/` (Windows-style)
+ * 3. Two forward slashes, a computer name, and then another forward slash,
+ * such as `//FileServer/` (UNC-style)
+ *
+ * Path segments are never empty (that is, absolute paths never contain two
+ * contiguous slashes, except as part of a UNC-style root prefix). Also, path
+ * segments never contain forward slashes, and no path segment is of the
+ * form `.` (one dot) or `..` (two dots).
+ *
+ * Note that an absolute path never ends with a forward slash, except if it is
+ * a bare root prefix, that is, the path has no path segments. A container
+ * whose absolute path has no segments is always a `Folder`, not a `File`.
+ */
+ string getAbsolutePath() { none() }
+
+ /**
+ * Gets a URL representing the location of this container.
+ *
+ * For more information see [Providing URLs](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls).
+ */
+ string getURL() { none() }
+
+ /**
+ * Gets the relative path of this file or folder from the root folder of the
+ * analyzed source location. The relative path of the root folder itself is
+ * the empty string.
+ *
+ * This has no result if the container is outside the source root, that is,
+ * if the root folder is not a reflexive, transitive parent of this container.
+ */
+ string getRelativePath() {
+ exists(string absPath, string pref |
+ absPath = this.getAbsolutePath() and sourceLocationPrefix(pref)
+ |
+ absPath = pref and result = ""
+ or
+ absPath = pref.regexpReplaceAll("/$", "") + "/" + result and
+ not result.matches("/%")
+ )
+ }
+
+ /**
+ * Gets the base name of this container including extension, that is, the last
+ * segment of its absolute path, or the empty string if it has no segments.
+ *
+ * Here are some examples of absolute paths and the corresponding base names
+ * (surrounded with quotes to avoid ambiguity):
+ *
+ *
+ * | Absolute path | Base name |
+ * | "/tmp/tst.sql" | "tst.sql" |
+ * | "C:/Program Files (x86)" | "Program Files (x86)" |
+ * | "/" | "" |
+ * | "C:/" | "" |
+ * | "D:/" | "" |
+ * | "//FileServer/" | "" |
+ *
+ */
+ string getBaseName() {
+ result = this.getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1)
+ }
+
+ /**
+ * Gets the extension of this container, that is, the suffix of its base name
+ * after the last dot character, if any.
+ *
+ * In particular,
+ *
+ * - if the name does not include a dot, there is no extension, so this
+ * predicate has no result;
+ * - if the name ends in a dot, the extension is the empty string;
+ * - if the name contains multiple dots, the extension follows the last dot.
+ *
+ * Here are some examples of absolute paths and the corresponding extensions
+ * (surrounded with quotes to avoid ambiguity):
+ *
+ *
+ * | Absolute path | Extension |
+ * | "/tmp/tst.cs" | "cs" |
+ * | "/tmp/.classpath" | "classpath" |
+ * | "/bin/bash" | not defined |
+ * | "/tmp/tst2." | "" |
+ * | "/tmp/x.tar.gz" | "gz" |
+ *
+ */
+ string getExtension() {
+ result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3)
+ }
+
+ /**
+ * Gets the stem of this container, that is, the prefix of its base name up to
+ * (but not including) the last dot character if there is one, or the entire
+ * base name if there is not.
+ *
+ * Here are some examples of absolute paths and the corresponding stems
+ * (surrounded with quotes to avoid ambiguity):
+ *
+ *
+ * | Absolute path | Stem |
+ * | "/tmp/tst.cs" | "tst" |
+ * | "/tmp/.classpath" | "" |
+ * | "/bin/bash" | "bash" |
+ * | "/tmp/tst2." | "tst2" |
+ * | "/tmp/x.tar.gz" | "x.tar" |
+ *
+ */
+ string getStem() {
+ result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1)
+ }
+
+ /** Gets the parent container of this file or folder, if any. */
+ Container getParentContainer() { containerparent(result, this) }
+
+ /** Gets a file or sub-folder in this container. */
+ Container getAChildContainer() { this = result.getParentContainer() }
+
+ /** Gets a file in this container. */
+ File getAFile() { result = this.getAChildContainer() }
+
+ /** Gets the file in this container that has the given `baseName`, if any. */
+ File getFile(string baseName) {
+ result = this.getAFile() and
+ result.getBaseName() = baseName
+ }
+
+ /** Gets a sub-folder in this container. */
+ Folder getAFolder() { result = this.getAChildContainer() }
+
+ /** Gets the sub-folder in this container that has the given `baseName`, if any. */
+ Folder getFolder(string baseName) {
+ result = this.getAFolder() and
+ result.getBaseName() = baseName
+ }
+
+ /** Gets the file or sub-folder in this container that has the given `name`, if any. */
+ Container getChildContainer(string name) {
+ result = this.getAChildContainer() and
+ result.getBaseName() = name
+ }
+
+ /** Gets the file in this container that has the given `stem` and `extension`, if any. */
+ File getFile(string stem, string extension) {
+ result = this.getAChildContainer() and
+ result.getStem() = stem and
+ result.getExtension() = extension
+ }
+
+ /** Gets a sub-folder contained in this container. */
+ Folder getASubFolder() { result = this.getAChildContainer() }
+
+ /**
+ * Gets a textual representation of the path of this container.
+ *
+ * This is the absolute path of the container.
+ */
+ string toString() { result = this.getAbsolutePath() }
+}
+
+/** A folder. */
+class Folder extends Container, @folder {
+ override string getAbsolutePath() { folders(this, result) }
+
+ override string getURL() { result = "folder://" + this.getAbsolutePath() }
+}
+
+/** A file. */
+class File extends Container, @file {
+ override string getAbsolutePath() { files(this, result) }
+
+ /** Gets the number of lines in this file. */
+ int getNumberOfLines() { numlines(this, result, _, _) }
+
+ /** Gets the number of lines containing code in this file. */
+ int getNumberOfLinesOfCode() { numlines(this, _, result, _) }
+
+ /** Gets the number of lines containing comments in this file. */
+ int getNumberOfLinesOfComments() { numlines(this, _, _, result) }
+
+ override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
+
+ /** Holds if this file is a QL test stub file. */
+ pragma[noinline]
+ private predicate isStub() {
+ // this.extractedQlTest() and
+ this.getAbsolutePath().matches("%resources/stubs/%")
+ }
+
+ /** Holds if this file contains source code. */
+ predicate fromSource() {
+ this.getExtension() = "cs" and
+ not this.isStub()
+ }
+
+ /** Holds if this file is a library. */
+ predicate fromLibrary() {
+ not this.getBaseName() = "" and
+ not this.fromSource()
+ }
+}
+
+/**
+ * A source file.
+ */
+class SourceFile extends File {
+ SourceFile() { this.fromSource() }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/FileRedirection.qll b/powershell/ql/lib/semmle/code/powershell/FileRedirection.qll
new file mode 100644
index 00000000000..68dca062551
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/FileRedirection.qll
@@ -0,0 +1,7 @@
+import powershell
+
+class FileRedirection extends @file_redirection, Redirection {
+ override string toString() { result = "FileRedirection" }
+
+ override Location getLocation() { file_redirection_location(this, result) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/ForEachStmt.qll b/powershell/ql/lib/semmle/code/powershell/ForEachStmt.qll
new file mode 100644
index 00000000000..9a32a6dc7cb
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/ForEachStmt.qll
@@ -0,0 +1,16 @@
+import powershell
+
+class ForEachStmt extends @foreach_statement, LoopStmt {
+ override SourceLocation getLocation() { foreach_statement_location(this, result) }
+
+ override string toString() { result = "forach(... in ...)" }
+
+ StmtBlock getBody() { foreach_statement(this, _, _, result, _) } // TODO: Change @ast to @stmt_block in dbscheme
+
+ VarAccess getVariable() { foreach_statement(this, result, _, _, _) } // TODO: Change @ast to @variable_expression in dbscheme
+
+ /** ..., if any. */
+ PipelineBase getCondition() { foreach_statement(this, _, result, _, _) } // TODO: Change @ast to @pipeline_base in dbscheme
+
+ predicate isParallel() { foreach_statement(this, _, _, _, 1) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/ForStmt.qll b/powershell/ql/lib/semmle/code/powershell/ForStmt.qll
new file mode 100644
index 00000000000..3a89532e537
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/ForStmt.qll
@@ -0,0 +1,15 @@
+import powershell
+
+class ForStmt extends @for_statement, LoopStmt {
+ override SourceLocation getLocation() { for_statement_location(this, result) }
+
+ override string toString() { result = "for(...;...;...)" }
+
+ PipelineBase getInitializer() { for_statement_initializer(this, result) } // TODO: Change @ast to @pipeline_base in dbscheme
+
+ PipelineBase getCondition() { for_statement_condition(this, result) } // TODO: Change @ast to @pipeline_base in dbscheme
+
+ PipelineBase getIterator() { for_statement_iterator(this, result) } // TODO: Change @ast to @pipeline_base in dbscheme
+
+ StmtBlock getBody() { for_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/FunctionDefinition.qll b/powershell/ql/lib/semmle/code/powershell/FunctionDefinition.qll
new file mode 100644
index 00000000000..68867cbd845
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/FunctionDefinition.qll
@@ -0,0 +1,19 @@
+import powershell
+
+class Function extends @function_definition, Stmt {
+ override string toString() { result = "FunctionDefinition at: " + this.getLocation().toString() }
+
+ override SourceLocation getLocation() { function_definition_location(this, result) }
+
+ string getName() { function_definition(this, _, result, _, _) }
+
+ ScriptBlock getBody() { function_definition(this, result, _, _, _) }
+
+ predicate isFilter() { function_definition(this, _, _, true, _) }
+
+ predicate isWorkflow() { function_definition(this, _, _, _, true) }
+
+ Parameter getParameter(int i) { function_definition_parameter(this, i, result) }
+
+ Parameter getAParameter() { result = this.getParameter(_) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/FunctionMember.qll b/powershell/ql/lib/semmle/code/powershell/FunctionMember.qll
new file mode 100644
index 00000000000..e3498be9e0f
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/FunctionMember.qll
@@ -0,0 +1,31 @@
+import powershell
+
+class FunctionMember extends @function_member, Member {
+ override string getName() { function_member(this, _, _, _, _, _, _, result, _) }
+
+ override SourceLocation getLocation() { function_member_location(this, result) }
+
+ override string toString() { result = this.getName() }
+
+ ScriptBlock getBody() { function_member(this, result, _, _, _, _, _, _, _) }
+
+ override predicate isHidden() { function_member(this, _, _, true, _, _, _, _, _) }
+
+ override predicate isPrivate() { function_member(this, _, _, _, true, _, _, _, _) }
+
+ override predicate isPublic() { function_member(this, _, _, _, _, true, _, _, _) }
+
+ override predicate isStatic() { function_member(this, _, _, _, _, _, true, _, _) }
+
+ predicate isConstructor() { function_member(this, _, true, _, _, _, _, _, _) }
+
+ Parameter getParameter(int i) { function_member_parameter(this, i, result) }
+
+ Parameter getAParameter() { result = this.getParameter(_) }
+
+ TypeConstraint getTypeConstraint() { function_member_return_type(this, result) }
+}
+
+class Constructor extends FunctionMember {
+ Constructor() { this.isConstructor() }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/GotoStmt.qll b/powershell/ql/lib/semmle/code/powershell/GotoStmt.qll
new file mode 100644
index 00000000000..fac95b7793e
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/GotoStmt.qll
@@ -0,0 +1,8 @@
+import powershell
+
+/** A `break` or `continue` statement. */
+class GotoStmt extends @labelled_statement, Stmt { // TODO: Rename @labelled_statement to @goto_statement
+
+ /** ..., if any. */
+ Expr getLabel() { statement_label(this, result) } // TODO: Replace @ast with @expression
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/IfStmt.qll b/powershell/ql/lib/semmle/code/powershell/IfStmt.qll
new file mode 100644
index 00000000000..21ca8730cd5
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/IfStmt.qll
@@ -0,0 +1,18 @@
+import powershell
+
+class IfStmt extends @if_statement, Stmt {
+ override SourceLocation getLocation() { if_statement_location(this, result) }
+
+ override string toString() {
+ if this.hasElse() then result = "if (...) {...} else {...}" else result = "if (...) {...}"
+ }
+
+ PipelineBase getCondition(int i) { if_statement_clause(this, i, result, _) } // TODO: Change @ast to @pipeline_base in dbscheme
+
+ StmtBlock getThen(int i) { if_statement_clause(this, i, _, result) } // TODO: Change @ast to @statement_block in dbscheme
+
+ /** ..., if any. */
+ StmtBlock getElse() { if_statement_else(this, result) } // TODO: Change @ast to @stmt_block in dbscheme
+
+ predicate hasElse() { exists(this.getElse()) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/InvokeMemberExpression.qll b/powershell/ql/lib/semmle/code/powershell/InvokeMemberExpression.qll
new file mode 100644
index 00000000000..90e04f0c5d1
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/InvokeMemberExpression.qll
@@ -0,0 +1,15 @@
+import powershell
+
+class InvokeMemberExpression extends @invoke_member_expression, MemberExprBase {
+ override SourceLocation getLocation() { invoke_member_expression_location(this, result) }
+
+ Expr getExpression() { invoke_member_expression(this, result, _) }
+
+ CmdElement getMember() { invoke_member_expression(this, _, result) }
+
+ Expr getArgument(int i) { invoke_member_expression_argument(this, i, result) }
+
+ Expr getAnArgument() { invoke_member_expression_argument(this, _, result) }
+
+ override string toString() { result = "ArrayExpression at: " + this.getLocation().toString() }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/LabeledStmt.qll b/powershell/ql/lib/semmle/code/powershell/LabeledStmt.qll
new file mode 100644
index 00000000000..fff4e1712cc
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/LabeledStmt.qll
@@ -0,0 +1,5 @@
+import powershell
+
+class LabeledStmt extends @labeled_statement, Stmt {
+ string getLabel() { label(this, result) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/Location.qll b/powershell/ql/lib/semmle/code/powershell/Location.qll
new file mode 100644
index 00000000000..c08dcb83af1
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/Location.qll
@@ -0,0 +1,52 @@
+/**
+ * Provides the `Location` class to give a location for each
+ * program element.
+ *
+ * A `SourceLocation` provides a section of text in a source file
+ * containing the program element.
+ *
+ * Based on csharp/ql/lib/semmle/code/csharp/Location.qll
+ */
+
+import File
+
+/**
+ * A location of a program element.
+ */
+class Location extends @location {
+ /** Gets the file of the location. */
+ File getFile() { none() }
+
+ /**
+ * Holds if this element is at the specified location.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `filepath`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+ predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ none()
+ }
+
+ /** Gets a textual representation of this location. */
+ string toString() { none() }
+
+ /** Gets the 1-based line number (inclusive) where this location starts. */
+ int getStartLine() { this.hasLocationInfo(_, result, _, _, _) }
+
+ /** Gets the 1-based line number (inclusive) where this location ends. */
+ int getEndLine() { this.hasLocationInfo(_, _, _, result, _) }
+
+ /** Gets the 1-based column number (inclusive) where this location starts. */
+ int getStartColumn() { this.hasLocationInfo(_, _, result, _, _) }
+
+ /** Gets the 1-based column number (inclusive) where this location ends. */
+ int getEndColumn() { this.hasLocationInfo(_, _, _, _, result) }
+}
+
+/** An empty location. */
+class EmptyLocation extends Location {
+ EmptyLocation() { this.hasLocationInfo("", 0, 0, 0, 0) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/LoopStmt.qll b/powershell/ql/lib/semmle/code/powershell/LoopStmt.qll
new file mode 100644
index 00000000000..b8ac1e47206
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/LoopStmt.qll
@@ -0,0 +1,3 @@
+import powershell
+
+class LoopStmt extends @loop_statement, LabeledStmt { }
diff --git a/powershell/ql/lib/semmle/code/powershell/Member.qll b/powershell/ql/lib/semmle/code/powershell/Member.qll
new file mode 100644
index 00000000000..15bdcad8d7e
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/Member.qll
@@ -0,0 +1,17 @@
+import powershell
+
+class Member extends @member, Ast {
+ Type getDeclaringType() { result.getAMember() = this }
+
+ string getName() { none() }
+
+ override string toString() { result = this.getName() }
+
+ predicate isHidden() { none() }
+
+ predicate isPrivate() { none() }
+
+ predicate isPublic() { none() }
+
+ predicate isStatic() { none() }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/MemberExpressionBase.qll b/powershell/ql/lib/semmle/code/powershell/MemberExpressionBase.qll
new file mode 100644
index 00000000000..774cbd9b88a
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/MemberExpressionBase.qll
@@ -0,0 +1,3 @@
+import powershell
+
+class MemberExprBase extends @member_expression_base, Expr { }
diff --git a/powershell/ql/lib/semmle/code/powershell/MergingRedirection.qll b/powershell/ql/lib/semmle/code/powershell/MergingRedirection.qll
new file mode 100644
index 00000000000..b227fc3c585
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/MergingRedirection.qll
@@ -0,0 +1,7 @@
+import powershell
+
+class MergingRedirection extends @merging_redirection, Redirection {
+ override string toString() { result = "MergingRedirection" }
+
+ override Location getLocation() { merging_redirection_location(this, result) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/ModuleSpecification.qll b/powershell/ql/lib/semmle/code/powershell/ModuleSpecification.qll
new file mode 100644
index 00000000000..5e2a3d3d391
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/ModuleSpecification.qll
@@ -0,0 +1,17 @@
+import powershell
+
+class ModuleSpecification extends @module_specification {
+ string toString() { result = this.getName() }
+
+ string getName() { module_specification(this, result, _, _, _, _) }
+
+ string getGuid() { module_specification(this, _, result, _, _, _) }
+
+ string getMaxVersion() { module_specification(this, _, _, result, _, _) }
+
+ string getRequiredVersion() { module_specification(this, _, _, _, result, _) }
+
+ string getVersion() { module_specification(this, _, _, _, _, result) }
+
+ Location getLocation() { result instanceof EmptyLocation }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/NamedAttributeArgument.qll b/powershell/ql/lib/semmle/code/powershell/NamedAttributeArgument.qll
new file mode 100644
index 00000000000..ef6a667d213
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/NamedAttributeArgument.qll
@@ -0,0 +1,11 @@
+import powershell
+
+class NamedAttributeArgument extends @named_attribute_argument {
+ string toString() { result = this.getValue().toString() }
+
+ SourceLocation getLocation() { named_attribute_argument_location(this, result) }
+
+ string getName() { named_attribute_argument(this, result, _) }
+
+ Expr getValue() { named_attribute_argument(this, _, result) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/NamedBlock.qll b/powershell/ql/lib/semmle/code/powershell/NamedBlock.qll
new file mode 100644
index 00000000000..b6fc800e8bc
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/NamedBlock.qll
@@ -0,0 +1,19 @@
+import powershell
+
+class NamedBlock extends @named_block, Ast {
+ override string toString() { result = "{...}" }
+
+ override SourceLocation getLocation() { named_block_location(this, result) }
+
+ int getNumStatements() { named_block(this, result, _) }
+
+ int getNumTraps() { named_block(this, _, result) }
+
+ Stmt getStatement(int i) { named_block_statement(this, i, result) }
+
+ Stmt getAStatement() { result = this.getStatement(_) }
+
+ TrapStmt getTrap(int i) { named_block_trap(this, i, result) }
+
+ TrapStmt getATrap() { result = this.getTrap(_) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/ParamBlock.qll b/powershell/ql/lib/semmle/code/powershell/ParamBlock.qll
new file mode 100644
index 00000000000..5fd95828883
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/ParamBlock.qll
@@ -0,0 +1,19 @@
+import powershell
+
+class ParamBlock extends @param_block, Ast {
+ override string toString() { result = "ParamBlock" }
+
+ override SourceLocation getLocation() { param_block_location(this, result) }
+
+ int getNumAttributes() { param_block(this, result, _) }
+
+ int getNumParameters() { param_block(this, _, result) }
+
+ Attribute getAttribute(int i) { param_block_attribute(this, i, result) }
+
+ Attribute getAnAttribute() { result = this.getAttribute(_) }
+
+ Parameter getParameter(int i) { param_block_parameter(this, i, result) }
+
+ Parameter getAParameter() { result = this.getParameter(_) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/Parameter.qll b/powershell/ql/lib/semmle/code/powershell/Parameter.qll
new file mode 100644
index 00000000000..07c897d0320
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/Parameter.qll
@@ -0,0 +1,19 @@
+import powershell
+
+class Parameter extends @parameter, Ast {
+ override string toString() { result = this.getName().toString() }
+
+ VarAccess getName() { parameter(this, result, _, _) }
+
+ string getStaticType() { parameter(this, _, result, _) }
+
+ int getNumAttributes() { parameter(this, _, _, result) }
+
+ AttributeBase getAttribute(int i) { parameter_attribute(this, i, result) }
+
+ AttributeBase getAnAttribute() { result = this.getAttribute(_) }
+
+ Expr getDefaultValue() { parameter_default_value(this, result) }
+
+ override SourceLocation getLocation() { parameter_location(this, result) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/ParenExpression.qll b/powershell/ql/lib/semmle/code/powershell/ParenExpression.qll
new file mode 100644
index 00000000000..4290762417f
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/ParenExpression.qll
@@ -0,0 +1,9 @@
+import powershell
+
+class ParenExpression extends @paren_expression, Expr {
+ PipelineBase getExpression() { paren_expression(this, result) }
+
+ override SourceLocation getLocation() { paren_expression_location(this, result) }
+
+ override string toString() { result = "(...)" }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/Pipeline.qll b/powershell/ql/lib/semmle/code/powershell/Pipeline.qll
new file mode 100644
index 00000000000..0e37c658115
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/Pipeline.qll
@@ -0,0 +1,13 @@
+import powershell
+
+class Pipeline extends @pipeline, Chainable {
+ override string toString() { result = "...|..." }
+
+ override SourceLocation getLocation() { pipeline_location(this, result) }
+
+ int getNumComponents() { pipeline(this, result) }
+
+ CmdBase getComponent(int i) { pipeline_component(this, i, result) }
+
+ CmdBase getAComponent() { result = this.getComponent(_) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/PipelineBase.qll b/powershell/ql/lib/semmle/code/powershell/PipelineBase.qll
new file mode 100644
index 00000000000..bb2d8cab85e
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/PipelineBase.qll
@@ -0,0 +1,3 @@
+import powershell
+
+class PipelineBase extends @pipeline_base, Stmt { }
diff --git a/powershell/ql/lib/semmle/code/powershell/PropertyMember.qll b/powershell/ql/lib/semmle/code/powershell/PropertyMember.qll
new file mode 100644
index 00000000000..e9144dc78e4
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/PropertyMember.qll
@@ -0,0 +1,17 @@
+import powershell
+
+class PropertyMember extends @property_member, Member {
+ override string getName() { property_member(this, _, _, _, _, result, _) }
+
+ override SourceLocation getLocation() { property_member_location(this, result) }
+
+ override string toString() { result = this.getName() }
+
+ override predicate isHidden() { property_member(this, true, _, _, _, _, _) }
+
+ override predicate isPrivate() { property_member(this, _, true, _, _, _, _) }
+
+ override predicate isPublic() { property_member(this, _, _, true, _, _, _) }
+
+ override predicate isStatic() { property_member(this, _, _, _, true, _, _) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/Redirection.qll b/powershell/ql/lib/semmle/code/powershell/Redirection.qll
new file mode 100644
index 00000000000..f2a31116f61
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/Redirection.qll
@@ -0,0 +1,3 @@
+import powershell
+
+class Redirection extends @redirection, Ast { }
diff --git a/powershell/ql/lib/semmle/code/powershell/ReturnStmt.qll b/powershell/ql/lib/semmle/code/powershell/ReturnStmt.qll
new file mode 100644
index 00000000000..80d8a616287
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/ReturnStmt.qll
@@ -0,0 +1,13 @@
+import powershell
+
+class ReturnStmt extends @return_statement, Stmt {
+ override SourceLocation getLocation() { return_statement_location(this, result) }
+
+ override string toString() {
+ if this.hasPipeline() then result = "return ..." else result = "return"
+ }
+
+ PipelineBase getPipeline() { return_statement_pipeline(this, result) } // TODO: Change @ast to @pipeline_base in dbscheme
+
+ predicate hasPipeline() { exists(this.getPipeline()) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/ScriptBlock.qll b/powershell/ql/lib/semmle/code/powershell/ScriptBlock.qll
new file mode 100644
index 00000000000..c59ae410c9f
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/ScriptBlock.qll
@@ -0,0 +1,45 @@
+import powershell
+
+class ScriptBlock extends @script_block, Ast {
+ override string toString() { result = "ScriptBlock at: " + this.getLocation().toString() }
+
+ override SourceLocation getLocation() { script_block_location(this, result) }
+
+ int getNumUsings() { script_block(this, result, _, _, _, _) }
+
+ int getNumRequiredModules() { script_block(this, _, result, _, _, _) }
+
+ int getNumRequiredAssemblies() { script_block(this, _, _, result, _, _) }
+
+ int getNumRequiredPsEditions() { script_block(this, _, _, _, result, _) }
+
+ int getNumRequiredPsSnapIns() { script_block(this, _, _, _, _, result) }
+
+ Stmt getUsing(int i) { script_block_using(this, i, result) }
+
+ Stmt getAUsing() { result = this.getUsing(_) }
+
+ ParamBlock getParamBlock() { script_block_param_block(this, result) }
+
+ NamedBlock getBeginBlock() { script_block_begin_block(this, result) }
+
+ NamedBlock getCleanBlock() { script_block_clean_block(this, result) }
+
+ NamedBlock getDynamicParamBlock() { script_block_dynamic_param_block(this, result) }
+
+ NamedBlock getEndBlock() { script_block_end_block(this, result) }
+
+ NamedBlock getProcessBlock() { script_block_process_block(this, result) }
+
+ string getRequiredApplicationId() { script_block_required_application_id(this, result) }
+
+ boolean getRequiresElevation() { script_block_requires_elevation(this, result) }
+
+ string getRequiredPsVersion() { script_block_required_ps_version(this, result) }
+
+ ModuleSpecification getModuleSpecification(int i) {
+ script_block_required_module(this, i, result)
+ }
+
+ ModuleSpecification getAModuleSpecification() { result = this.getModuleSpecification(_) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/ScriptBlockExpr.qll b/powershell/ql/lib/semmle/code/powershell/ScriptBlockExpr.qll
new file mode 100644
index 00000000000..78dd5abbc2e
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/ScriptBlockExpr.qll
@@ -0,0 +1,9 @@
+import powershell
+
+class ScriptBlockExpr extends @script_block_expression, Expr {
+ override SourceLocation getLocation() { script_block_expression_location(this, result) }
+
+ override string toString() { result = "{...}" }
+
+ ScriptBlock getBody() { script_block_expression(this, result) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/SourceLocation.qll b/powershell/ql/lib/semmle/code/powershell/SourceLocation.qll
new file mode 100644
index 00000000000..8c70f06f7bc
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/SourceLocation.qll
@@ -0,0 +1,25 @@
+import powershell
+
+/**
+ * A location in source code, comprising of a source file and a segment of text
+ * within the file.
+ */
+class SourceLocation extends Location, @location_default {
+ override File getFile() { locations_default(this, result, _, _, _, _) }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ exists(File f | locations_default(this, f, startline, startcolumn, endline, endcolumn) |
+ filepath = f.getAbsolutePath()
+ )
+ }
+
+ override string toString() {
+ exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
+ this.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ |
+ result = filepath + ":" + startline + ":" + startcolumn + ":" + endline + ":" + endcolumn
+ )
+ }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/Statement.qll b/powershell/ql/lib/semmle/code/powershell/Statement.qll
new file mode 100644
index 00000000000..19bed527915
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/Statement.qll
@@ -0,0 +1,3 @@
+import powershell
+
+class Stmt extends @statement, Ast { }
diff --git a/powershell/ql/lib/semmle/code/powershell/StatementBlock.qll b/powershell/ql/lib/semmle/code/powershell/StatementBlock.qll
new file mode 100644
index 00000000000..4ea36e1e9ac
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/StatementBlock.qll
@@ -0,0 +1,19 @@
+import powershell
+
+class StmtBlock extends @statement_block, Ast {
+ override SourceLocation getLocation() { statement_block_location(this, result) }
+
+ int getNumStatements() { statement_block(this, result, _) }
+
+ int getNumTraps() { statement_block(this, _, result) }
+
+ Stmt getStatement(int index) { statement_block_statement(this, index, result) }
+
+ Stmt getAStatement() { result = this.getStatement(_) }
+
+ TrapStmt getTrapStatement(int index) { statement_block_trap(this, index, result) }
+
+ TrapStmt getATrapStatement() { result = this.getTrapStatement(_) }
+
+ override string toString() { result = "StatementBlock at: " + this.getLocation().toString() }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/StringConstantExpression.qll b/powershell/ql/lib/semmle/code/powershell/StringConstantExpression.qll
new file mode 100644
index 00000000000..788b2def636
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/StringConstantExpression.qll
@@ -0,0 +1,10 @@
+import powershell
+
+class StringConstExpression extends @string_constant_expression, BaseConstExpr {
+ StringLiteral getValue() { string_constant_expression(this, result) }
+
+ /** Get the full string literal with all its parts concatenated */
+ override string toString() { result = getValue().toString() }
+
+ override SourceLocation getLocation() { string_constant_expression_location(this, result) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/StringLiteral.qll b/powershell/ql/lib/semmle/code/powershell/StringLiteral.qll
new file mode 100644
index 00000000000..4eb9a9d77a4
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/StringLiteral.qll
@@ -0,0 +1,14 @@
+import powershell
+
+class StringLiteral extends @string_literal {
+ int getNumContinuations() { string_literal_line(this, result, _) }
+
+ string getContinuation(int index) { string_literal_line(this, index, result) }
+
+ /** Get the full string literal with all its parts concatenated */
+ string toString() {
+ result = concat(int i | i = [0 .. getNumContinuations()] | getContinuation(i), "\n")
+ }
+
+ SourceLocation getLocation() { string_literal_location(this, result) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/TernaryExpression.qll b/powershell/ql/lib/semmle/code/powershell/TernaryExpression.qll
new file mode 100644
index 00000000000..9df624d2d6c
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/TernaryExpression.qll
@@ -0,0 +1,13 @@
+import powershell
+
+class ConditionalExpr extends @ternary_expression, Expr {
+ override string toString() { result = "...?...:..." }
+
+ override SourceLocation getLocation() { ternary_expression_location(this, result) }
+
+ Expr getCondition() { ternary_expression(this, result, _, _) }
+
+ Expr getIfFalse() { ternary_expression(this, _, result, _) }
+
+ Expr getIfTrue() { ternary_expression(this, _, _, result) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/TokenKind.qll b/powershell/ql/lib/semmle/code/powershell/TokenKind.qll
new file mode 100644
index 00000000000..a179d361291
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/TokenKind.qll
@@ -0,0 +1,1345 @@
+class TokenKind extends @token_kind {
+ string toString() { none() }
+
+ string getDescription() { none() }
+
+ int getValue() { none() }
+}
+
+class Ampersand extends @ampersand, TokenKind {
+ override int getValue() { result = 28 }
+
+ override string getDescription() { result = "The invocation operator '&'." }
+
+ override string toString() { result = "Ampersand" }
+}
+
+class And extends @and, TokenKind {
+ override int getValue() { result = 53 }
+
+ override string getDescription() { result = "The logical and operator '-and'." }
+
+ override string toString() { result = "And" }
+}
+
+class AndAnd extends @andAnd, TokenKind {
+ override int getValue() { result = 26 }
+
+ override string getDescription() { result = "The (unimplemented) operator '&&'." }
+
+ override string toString() { result = "AndAnd" }
+}
+
+class As extends @as, TokenKind {
+ override int getValue() { result = 94 }
+
+ override string getDescription() { result = "The type conversion operator '-as'." }
+
+ override string toString() { result = "As" }
+}
+
+class Assembly extends @assembly, TokenKind {
+ override int getValue() { result = 165 }
+
+ override string getDescription() { result = "The 'assembly' keyword" }
+
+ override string toString() { result = "Assembly" }
+}
+
+class AtCurly extends @atCurly, TokenKind {
+ override int getValue() { result = 23 }
+
+ override string getDescription() { result = "The opening token of a hash expression '@{'." }
+
+ override string toString() { result = "AtCurly" }
+}
+
+class AtParen extends @atParen, TokenKind {
+ override int getValue() { result = 22 }
+
+ override string getDescription() { result = "The opening token of an array expression '@('." }
+
+ override string toString() { result = "AtParen" }
+}
+
+class Band extends @band, TokenKind {
+ override int getValue() { result = 56 }
+
+ override string getDescription() { result = "The bitwise and operator '-band'." }
+
+ override string toString() { result = "Band" }
+}
+
+class Base extends @base, TokenKind {
+ override int getValue() { result = 168 }
+
+ override string getDescription() { result = "The 'base' keyword" }
+
+ override string toString() { result = "Base" }
+}
+
+class Begin extends @begin, TokenKind {
+ override int getValue() { result = 119 }
+
+ override string getDescription() { result = "The 'begin' keyword." }
+
+ override string toString() { result = "Begin" }
+}
+
+class Bnot extends @bnot, TokenKind {
+ override int getValue() { result = 52 }
+
+ override string getDescription() { result = "The bitwise not operator '-bnot'." }
+
+ override string toString() { result = "Bnot" }
+}
+
+class Bor extends @bor, TokenKind {
+ override int getValue() { result = 57 }
+
+ override string getDescription() { result = "The bitwise or operator '-bor'." }
+
+ override string toString() { result = "Bor" }
+}
+
+class Break extends @break, TokenKind {
+ override int getValue() { result = 120 }
+
+ override string getDescription() { result = "The 'break' keyword." }
+
+ override string toString() { result = "Break" }
+}
+
+class Bxor extends @bxor, TokenKind {
+ override int getValue() { result = 58 }
+
+ override string getDescription() { result = "The bitwise exclusive or operator '-xor'." }
+
+ override string toString() { result = "Bxor" }
+}
+
+class Catch extends @catch, TokenKind {
+ override int getValue() { result = 121 }
+
+ override string getDescription() { result = "The 'catch' keyword." }
+
+ override string toString() { result = "Catch" }
+}
+
+class Ccontains extends @ccontains, TokenKind {
+ override int getValue() { result = 87 }
+
+ override string getDescription() { result = "The case sensitive contains operator '-ccontains'." }
+
+ override string toString() { result = "Ccontains" }
+}
+
+class Ceq extends @ceq, TokenKind {
+ override int getValue() { result = 76 }
+
+ override string getDescription() { result = "The case sensitive equal operator '-ceq'." }
+
+ override string toString() { result = "Ceq" }
+}
+
+class Cge extends @cge, TokenKind {
+ override int getValue() { result = 78 }
+
+ override string getDescription() {
+ result = "The case sensitive greater than or equal operator '-cge'."
+ }
+
+ override string toString() { result = "Cge" }
+}
+
+class Cgt extends @cgt, TokenKind {
+ override int getValue() { result = 79 }
+
+ override string getDescription() { result = "The case sensitive greater than operator '-cgt'." }
+
+ override string toString() { result = "Cgt" }
+}
+
+class Cin extends @cin, TokenKind {
+ override int getValue() { result = 89 }
+
+ override string getDescription() { result = "The case sensitive in operator '-cin'." }
+
+ override string toString() { result = "Cin" }
+}
+
+class Class extends @class, TokenKind {
+ override int getValue() { result = 122 }
+
+ override string getDescription() { result = "The 'class' keyword." }
+
+ override string toString() { result = "Class" }
+}
+
+class Cle extends @cle, TokenKind {
+ override int getValue() { result = 81 }
+
+ override string getDescription() {
+ result = "The case sensitive less than or equal operator '-cle'."
+ }
+
+ override string toString() { result = "Cle" }
+}
+
+class Clean extends @clean, TokenKind {
+ override int getValue() { result = 170 }
+
+ override string getDescription() { result = "The 'clean' keyword." }
+
+ override string toString() { result = "Clean" }
+}
+
+class Clike extends @clike, TokenKind {
+ override int getValue() { result = 82 }
+
+ override string getDescription() { result = "The case sensitive like operator '-clike'." }
+
+ override string toString() { result = "Clike" }
+}
+
+class Clt extends @clt, TokenKind {
+ override int getValue() { result = 80 }
+
+ override string getDescription() { result = "The case sensitive less than operator '-clt'." }
+
+ override string toString() { result = "Clt" }
+}
+
+class Cmatch extends @cmatch, TokenKind {
+ override int getValue() { result = 84 }
+
+ override string getDescription() { result = "The case sensitive match operator '-cmatch'." }
+
+ override string toString() { result = "Cmatch" }
+}
+
+class Cne extends @cne, TokenKind {
+ override int getValue() { result = 77 }
+
+ override string getDescription() { result = "The case sensitive not equal operator '-cne'." }
+
+ override string toString() { result = "Cne" }
+}
+
+class Cnotcontains extends @cnotcontains, TokenKind {
+ override int getValue() { result = 88 }
+
+ override string getDescription() {
+ result = "The case sensitive not contains operator '-cnotcontains'."
+ }
+
+ override string toString() { result = "Cnotcontains" }
+}
+
+class Cnotin extends @cnotin, TokenKind {
+ override int getValue() { result = 90 }
+
+ override string getDescription() { result = "The case sensitive not in operator '-notin'." }
+
+ override string toString() { result = "Cnotin" }
+}
+
+class Cnotlike extends @cnotlike, TokenKind {
+ override int getValue() { result = 83 }
+
+ override string getDescription() { result = "The case sensitive notlike operator '-cnotlike'." }
+
+ override string toString() { result = "Cnotlike" }
+}
+
+class Cnotmatch extends @cnotmatch, TokenKind {
+ override int getValue() { result = 85 }
+
+ override string getDescription() {
+ result = "The case sensitive not match operator '-cnotmatch'."
+ }
+
+ override string toString() { result = "Cnotmatch" }
+}
+
+class Colon extends @colon, TokenKind {
+ override int getValue() { result = 99 }
+
+ override string getDescription() {
+ result =
+ "The PS class base class and implemented interfaces operator ':'. Also used in base class ctor calls."
+ }
+
+ override string toString() { result = "Colon" }
+}
+
+class ColonColon extends @colonColon, TokenKind {
+ override int getValue() { result = 34 }
+
+ override string getDescription() { result = "The static member access operator '::'." }
+
+ override string toString() { result = "ColonColon" }
+}
+
+class Comma extends @comma, TokenKind {
+ override int getValue() { result = 30 }
+
+ override string getDescription() { result = "The unary or binary array operator ','." }
+
+ override string toString() { result = "Comma" }
+}
+
+class CommandToken extends @command_token, TokenKind {
+ override int getValue() { result = 166 }
+
+ override string getDescription() { result = "The 'command' keyword" }
+
+ override string toString() { result = "Command" }
+}
+
+class Comment extends @comment, TokenKind {
+ override int getValue() { result = 10 }
+
+ override string getDescription() { result = "A single line comment, or a delimited comment." }
+
+ override string toString() { result = "Comment" }
+}
+
+class Configuration extends @configuration, TokenKind {
+ override int getValue() { result = 155 }
+
+ override string getDescription() { result = "The 'configuration' keyword" }
+
+ override string toString() { result = "Configuration" }
+}
+
+class Continue extends @continue, TokenKind {
+ override int getValue() { result = 123 }
+
+ override string getDescription() { result = "The 'continue' keyword." }
+
+ override string toString() { result = "Continue" }
+}
+
+class Creplace extends @creplace, TokenKind {
+ override int getValue() { result = 86 }
+
+ override string getDescription() { result = "The case sensitive replace operator '-creplace'." }
+
+ override string toString() { result = "Creplace" }
+}
+
+class Csplit extends @csplit, TokenKind {
+ override int getValue() { result = 91 }
+
+ override string getDescription() { result = "The case sensitive split operator '-csplit'." }
+
+ override string toString() { result = "Csplit" }
+}
+
+class Data extends @data, TokenKind {
+ override int getValue() { result = 124 }
+
+ override string getDescription() { result = "The 'data' keyword." }
+
+ override string toString() { result = "Data" }
+}
+
+class Default extends @default, TokenKind {
+ override int getValue() { result = 169 }
+
+ override string getDescription() { result = "The 'default' keyword" }
+
+ override string toString() { result = "Default" }
+}
+
+class Define extends @define, TokenKind {
+ override int getValue() { result = 125 }
+
+ override string getDescription() { result = "The (unimplemented) 'define' keyword." }
+
+ override string toString() { result = "Define" }
+}
+
+class Divide extends @divide, TokenKind {
+ override int getValue() { result = 38 }
+
+ override string getDescription() { result = "The division operator '/'." }
+
+ override string toString() { result = "Divide" }
+}
+
+class DivideEquals extends @divideEquals, TokenKind {
+ override int getValue() { result = 46 }
+
+ override string getDescription() { result = "The division assignment operator '/='." }
+
+ override string toString() { result = "DivideEquals" }
+}
+
+class Do extends @do, TokenKind {
+ override int getValue() { result = 126 }
+
+ override string getDescription() { result = "The 'do' keyword." }
+
+ override string toString() { result = "Do" }
+}
+
+class DollarParen extends @dollarParen, TokenKind {
+ override int getValue() { result = 24 }
+
+ override string getDescription() { result = "The opening token of a sub-expression '$('." }
+
+ override string toString() { result = "DollarParen" }
+}
+
+class Dot extends @dot, TokenKind {
+ override int getValue() { result = 35 }
+
+ override string getDescription() {
+ result = "The instance member access or dot source invocation operator '.'."
+ }
+
+ override string toString() { result = "Dot" }
+}
+
+class DotDot extends @dotDot, TokenKind {
+ override int getValue() { result = 33 }
+
+ override string getDescription() { result = "The range operator '..'." }
+
+ override string toString() { result = "DotDot" }
+}
+
+class DynamicKeyword extends @dynamicKeyword, TokenKind {
+ override int getValue() { result = 156 }
+
+ override string getDescription() { result = "The token kind for dynamic keywords" }
+
+ override string toString() { result = "DynamicKeyword" }
+}
+
+class Dynamicparam extends @dynamicparam, TokenKind {
+ override int getValue() { result = 127 }
+
+ override string getDescription() { result = "The 'dynamicparam' keyword." }
+
+ override string toString() { result = "Dynamicparam" }
+}
+
+class Else extends @else, TokenKind {
+ override int getValue() { result = 128 }
+
+ override string getDescription() { result = "The 'else' keyword." }
+
+ override string toString() { result = "Else" }
+}
+
+class ElseIf extends @elseIf, TokenKind {
+ override int getValue() { result = 129 }
+
+ override string getDescription() { result = "The 'elseif' keyword." }
+
+ override string toString() { result = "ElseIf" }
+}
+
+class End extends @end, TokenKind {
+ override int getValue() { result = 130 }
+
+ override string getDescription() { result = "The 'end' keyword." }
+
+ override string toString() { result = "End" }
+}
+
+class EndOfInput extends @endOfInput, TokenKind {
+ override int getValue() { result = 11 }
+
+ override string getDescription() { result = "Marks the end of the input script or file." }
+
+ override string toString() { result = "EndOfInput" }
+}
+
+class Enum extends @enum, TokenKind {
+ override int getValue() { result = 161 }
+
+ override string getDescription() { result = "The 'enum' keyword" }
+
+ override string toString() { result = "Enum" }
+}
+
+class Equals extends @equals, TokenKind {
+ override int getValue() { result = 42 }
+
+ override string getDescription() { result = "The assignment operator '='." }
+
+ override string toString() { result = "Equals" }
+}
+
+class Exclaim extends @exclaim, TokenKind {
+ override int getValue() { result = 36 }
+
+ override string getDescription() { result = "The logical not operator '!'." }
+
+ override string toString() { result = "Exclaim" }
+}
+
+class Exit extends @exit, TokenKind {
+ override int getValue() { result = 131 }
+
+ override string getDescription() { result = "The 'exit' keyword." }
+
+ override string toString() { result = "Exit" }
+}
+
+class Filter extends @filter, TokenKind {
+ override int getValue() { result = 132 }
+
+ override string getDescription() { result = "The 'filter' keyword." }
+
+ override string toString() { result = "Filter" }
+}
+
+class Finally extends @finally, TokenKind {
+ override int getValue() { result = 133 }
+
+ override string getDescription() { result = "The 'finally' keyword." }
+
+ override string toString() { result = "Finally" }
+}
+
+class For extends @for, TokenKind {
+ override int getValue() { result = 134 }
+
+ override string getDescription() { result = "The 'for' keyword." }
+
+ override string toString() { result = "For" }
+}
+
+class Foreach extends @foreach, TokenKind {
+ override int getValue() { result = 135 }
+
+ override string getDescription() { result = "The 'foreach' keyword." }
+
+ override string toString() { result = "Foreach" }
+}
+
+class Format extends @format, TokenKind {
+ override int getValue() { result = 50 }
+
+ override string getDescription() { result = "The string format operator '-f'." }
+
+ override string toString() { result = "Format" }
+}
+
+class From extends @from, TokenKind {
+ override int getValue() { result = 136 }
+
+ override string getDescription() { result = "The (unimplemented) 'from' keyword." }
+
+ override string toString() { result = "From" }
+}
+
+class Function extends @function, TokenKind {
+ override int getValue() { result = 137 }
+
+ override string getDescription() { result = "The 'function' keyword." }
+
+ override string toString() { result = "Function" }
+}
+
+class Generic extends @generic, TokenKind {
+ override int getValue() { result = 7 }
+
+ override string getDescription() {
+ result =
+ "A token that is only valid as a command name, command argument, function name, or configuration name. It may contain characters not allowed in identifiers. Tokens with this kind are always instances of StringLiteralToken or StringExpandableToken if the token contains variable references or subexpressions."
+ }
+
+ override string toString() { result = "Generic" }
+}
+
+class HereStringExpandable extends @hereStringExpandable, TokenKind {
+ override int getValue() { result = 15 }
+
+ override string getDescription() {
+ result =
+ "A double quoted here string literal. Tokens with this kind are always instances of StringExpandableToken. even if there are no nested tokens to expand."
+ }
+
+ override string toString() { result = "HereStringExpandable" }
+}
+
+class HereStringLiteral extends @hereStringLiteral, TokenKind {
+ override int getValue() { result = 14 }
+
+ override string getDescription() {
+ result =
+ "A single quoted here string literal. Tokens with this kind are always instances of StringLiteralToken."
+ }
+
+ override string toString() { result = "HereStringLiteral" }
+}
+
+class Hidden extends @hidden, TokenKind {
+ override int getValue() { result = 167 }
+
+ override string getDescription() { result = "The 'hidden' keyword" }
+
+ override string toString() { result = "Hidden" }
+}
+
+class Icontains extends @icontains, TokenKind {
+ override int getValue() { result = 71 }
+
+ override string getDescription() {
+ result = "The case insensitive contains operator '-icontains' or '-contains'."
+ }
+
+ override string toString() { result = "Icontains" }
+}
+
+class Identifier extends @identifier, TokenKind {
+ override int getValue() { result = 6 }
+
+ override string getDescription() {
+ result =
+ "A simple identifier, always begins with a letter or '', and is followed by letters, numbers, or ''."
+ }
+
+ override string toString() { result = "Identifier" }
+}
+
+class Ieq extends @ieq, TokenKind {
+ override int getValue() { result = 60 }
+
+ override string getDescription() {
+ result = "The case insensitive equal operator '-ieq' or '-eq'."
+ }
+
+ override string toString() { result = "Ieq" }
+}
+
+class If extends @if, TokenKind {
+ override int getValue() { result = 138 }
+
+ override string getDescription() { result = "The 'if' keyword." }
+
+ override string toString() { result = "If" }
+}
+
+class Ige extends @ige, TokenKind {
+ override int getValue() { result = 62 }
+
+ override string getDescription() {
+ result = "The case insensitive greater than or equal operator '-ige' or '-ge'."
+ }
+
+ override string toString() { result = "Ige" }
+}
+
+class Igt extends @igt, TokenKind {
+ override int getValue() { result = 63 }
+
+ override string getDescription() {
+ result = "The case insensitive greater than operator '-igt' or '-gt'."
+ }
+
+ override string toString() { result = "Igt" }
+}
+
+class Iin extends @iin, TokenKind {
+ override int getValue() { result = 73 }
+
+ override string getDescription() { result = "The case insensitive in operator '-iin' or '-in'." }
+
+ override string toString() { result = "Iin" }
+}
+
+class Ile extends @ile, TokenKind {
+ override int getValue() { result = 65 }
+
+ override string getDescription() {
+ result = "The case insensitive less than or equal operator '-ile' or '-le'."
+ }
+
+ override string toString() { result = "Ile" }
+}
+
+class Ilike extends @ilike, TokenKind {
+ override int getValue() { result = 66 }
+
+ override string getDescription() {
+ result = "The case insensitive like operator '-ilike' or '-like'."
+ }
+
+ override string toString() { result = "Ilike" }
+}
+
+class Ilt extends @ilt, TokenKind {
+ override int getValue() { result = 64 }
+
+ override string getDescription() {
+ result = "The case insensitive less than operator '-ilt' or '-lt'."
+ }
+
+ override string toString() { result = "Ilt" }
+}
+
+class Imatch extends @imatch, TokenKind {
+ override int getValue() { result = 68 }
+
+ override string getDescription() {
+ result = "The case insensitive match operator '-imatch' or '-match'."
+ }
+
+ override string toString() { result = "Imatch" }
+}
+
+class In extends @in, TokenKind {
+ override int getValue() { result = 139 }
+
+ override string getDescription() { result = "The 'in' keyword." }
+
+ override string toString() { result = "In" }
+}
+
+class Ine extends @ine, TokenKind {
+ override int getValue() { result = 61 }
+
+ override string getDescription() {
+ result = "The case insensitive not equal operator '-ine' or '-ne'."
+ }
+
+ override string toString() { result = "Ine" }
+}
+
+class InlineScript extends @inlineScript, TokenKind {
+ override int getValue() { result = 154 }
+
+ override string getDescription() { result = "The 'InlineScript' keyword" }
+
+ override string toString() { result = "InlineScript" }
+}
+
+class Inotcontains extends @inotcontains, TokenKind {
+ override int getValue() { result = 72 }
+
+ override string getDescription() {
+ result = "The case insensitive notcontains operator '-inotcontains' or '-notcontains'."
+ }
+
+ override string toString() { result = "Inotcontains" }
+}
+
+class Inotin extends @inotin, TokenKind {
+ override int getValue() { result = 74 }
+
+ override string getDescription() {
+ result = "The case insensitive notin operator '-inotin' or '-notin'"
+ }
+
+ override string toString() { result = "Inotin" }
+}
+
+class Inotlike extends @inotlike, TokenKind {
+ override int getValue() { result = 67 }
+
+ override string getDescription() {
+ result = "The case insensitive not like operator '-inotlike' or '-notlike'."
+ }
+
+ override string toString() { result = "Inotlike" }
+}
+
+class Inotmatch extends @inotmatch, TokenKind {
+ override int getValue() { result = 69 }
+
+ override string getDescription() {
+ result = "The case insensitive not match operator '-inotmatch' or '-notmatch'."
+ }
+
+ override string toString() { result = "Inotmatch" }
+}
+
+class Interface extends @interface, TokenKind {
+ override int getValue() { result = 160 }
+
+ override string getDescription() { result = "The 'interface' keyword" }
+
+ override string toString() { result = "Interface" }
+}
+
+class Ireplace extends @ireplace, TokenKind {
+ override int getValue() { result = 70 }
+
+ override string getDescription() {
+ result = "The case insensitive replace operator '-ireplace' or '-replace'."
+ }
+
+ override string toString() { result = "Ireplace" }
+}
+
+class Is extends @is, TokenKind {
+ override int getValue() { result = 92 }
+
+ override string getDescription() { result = "The type test operator '-is'." }
+
+ override string toString() { result = "Is" }
+}
+
+class IsNot extends @isNot, TokenKind {
+ override int getValue() { result = 93 }
+
+ override string getDescription() { result = "The type test operator '-isnot'." }
+
+ override string toString() { result = "IsNot" }
+}
+
+class Isplit extends @isplit, TokenKind {
+ override int getValue() { result = 75 }
+
+ override string getDescription() {
+ result = "The case insensitive split operator '-isplit' or '-split'."
+ }
+
+ override string toString() { result = "Isplit" }
+}
+
+class Join extends @join, TokenKind {
+ override int getValue() { result = 59 }
+
+ override string getDescription() { result = "The join operator '-join'." }
+
+ override string toString() { result = "Join" }
+}
+
+class Label extends @label, TokenKind {
+ override int getValue() { result = 5 }
+
+ override string getDescription() {
+ result =
+ "A label token - always begins with ':', followed by the label name. Tokens with this kind are always instances of LabelToken."
+ }
+
+ override string toString() { result = "Label" }
+}
+
+class LBracket extends @lBracket, TokenKind {
+ override int getValue() { result = 20 }
+
+ override string getDescription() { result = "The opening square brace token '['." }
+
+ override string toString() { result = "LBracket" }
+}
+
+class LCurly extends @lCurly, TokenKind {
+ override int getValue() { result = 18 }
+
+ override string getDescription() { result = "The opening curly brace token '{'." }
+
+ override string toString() { result = "LCurly" }
+}
+
+class LineContinuation extends @lineContinuation, TokenKind {
+ override int getValue() { result = 9 }
+
+ override string getDescription() {
+ result = "A line continuation (backtick followed by newline)."
+ }
+
+ override string toString() { result = "LineContinuation" }
+}
+
+class LParen extends @lParen, TokenKind {
+ override int getValue() { result = 16 }
+
+ override string getDescription() { result = "The opening parenthesis token '('." }
+
+ override string toString() { result = "LParen" }
+}
+
+class Minus extends @minus, TokenKind {
+ override int getValue() { result = 41 }
+
+ override string getDescription() { result = "The substraction operator '-'." }
+
+ override string toString() { result = "Minus" }
+}
+
+class MinusEquals extends @minusEquals, TokenKind {
+ override int getValue() { result = 44 }
+
+ override string getDescription() { result = "The subtraction assignment operator '-='." }
+
+ override string toString() { result = "MinusEquals" }
+}
+
+class MinusMinus extends @minusMinus, TokenKind {
+ override int getValue() { result = 31 }
+
+ override string getDescription() { result = "The pre-decrement operator '--'." }
+
+ override string toString() { result = "MinusMinus" }
+}
+
+class Module extends @module, TokenKind {
+ override int getValue() { result = 163 }
+
+ override string getDescription() { result = "The 'module' keyword" }
+
+ override string toString() { result = "Module" }
+}
+
+class Multiply extends @multiply, TokenKind {
+ override int getValue() { result = 37 }
+
+ override string getDescription() { result = "The multiplication operator '*'." }
+
+ override string toString() { result = "Multiply" }
+}
+
+class MultiplyEquals extends @multiplyEquals, TokenKind {
+ override int getValue() { result = 45 }
+
+ override string getDescription() { result = "The multiplication assignment operator '*='." }
+
+ override string toString() { result = "MultiplyEquals" }
+}
+
+class Namespace extends @namespace, TokenKind {
+ override int getValue() { result = 162 }
+
+ override string getDescription() { result = "The 'namespace' keyword" }
+
+ override string toString() { result = "Namespace" }
+}
+
+class NewLine extends @newLine, TokenKind {
+ override int getValue() { result = 8 }
+
+ override string getDescription() { result = "A newline (one of '\n', '\r', or '\r\n')." }
+
+ override string toString() { result = "NewLine" }
+}
+
+class Not extends @not, TokenKind {
+ override int getValue() { result = 51 }
+
+ override string getDescription() { result = "The logical not operator '-not'." }
+
+ override string toString() { result = "Not" }
+}
+
+class Number extends @number, TokenKind {
+ override int getValue() { result = 4 }
+
+ override string getDescription() {
+ result =
+ "Any numerical literal token. Tokens with this kind are always instances of NumberToken."
+ }
+
+ override string toString() { result = "Number" }
+}
+
+class Or extends @or, TokenKind {
+ override int getValue() { result = 54 }
+
+ override string getDescription() { result = "The logical or operator '-or'." }
+
+ override string toString() { result = "Or" }
+}
+
+class OrOr extends @orOr, TokenKind {
+ override int getValue() { result = 27 }
+
+ override string getDescription() { result = "The (unimplemented) operator '||'." }
+
+ override string toString() { result = "OrOr" }
+}
+
+class Parallel extends @parallel, TokenKind {
+ override int getValue() { result = 152 }
+
+ override string getDescription() { result = "The 'parallel' keyword." }
+
+ override string toString() { result = "Parallel" }
+}
+
+class Param extends @param, TokenKind {
+ override int getValue() { result = 140 }
+
+ override string getDescription() { result = "The 'param' keyword." }
+
+ override string toString() { result = "Param" }
+}
+
+class ParameterToken extends @parameter_token, TokenKind {
+ override int getValue() { result = 3 }
+
+ override string getDescription() {
+ result =
+ "A parameter to a command, always begins with a dash ('-'), followed by the parameter name. Tokens with this kind are always instances of ParameterToken."
+ }
+
+ override string toString() { result = "Parameter" }
+}
+
+class Pipe extends @pipe, TokenKind {
+ override int getValue() { result = 29 }
+
+ override string getDescription() { result = "The pipe operator '|'." }
+
+ override string toString() { result = "Pipe" }
+}
+
+class Plus extends @plus, TokenKind {
+ override int getValue() { result = 40 }
+
+ override string getDescription() { result = "The addition operator '+'." }
+
+ override string toString() { result = "Plus" }
+}
+
+class PlusEquals extends @plusEquals, TokenKind {
+ override int getValue() { result = 43 }
+
+ override string getDescription() { result = "The addition assignment operator '+='." }
+
+ override string toString() { result = "PlusEquals" }
+}
+
+class PlusPlus extends @plusPlus, TokenKind {
+ override int getValue() { result = 32 }
+
+ override string getDescription() { result = "The pre-increment operator '++'." }
+
+ override string toString() { result = "PlusPlus" }
+}
+
+class PostfixMinusMinus extends @postfixMinusMinus, TokenKind {
+ override int getValue() { result = 96 }
+
+ override string getDescription() { result = "The post-decrement operator '--'." }
+
+ override string toString() { result = "PostfixMinusMinus" }
+}
+
+class PostfixPlusPlus extends @postfixPlusPlus, TokenKind {
+ override int getValue() { result = 95 }
+
+ override string getDescription() { result = "The post-increment operator '++'." }
+
+ override string toString() { result = "PostfixPlusPlus" }
+}
+
+class Private extends @private, TokenKind {
+ override int getValue() { result = 158 }
+
+ override string getDescription() { result = "The 'private' keyword" }
+
+ override string toString() { result = "Private" }
+}
+
+class Process extends @process, TokenKind {
+ override int getValue() { result = 141 }
+
+ override string getDescription() { result = "The 'process' keyword." }
+
+ override string toString() { result = "Process" }
+}
+
+class Public extends @public, TokenKind {
+ override int getValue() { result = 157 }
+
+ override string getDescription() { result = "The 'public' keyword" }
+
+ override string toString() { result = "Public" }
+}
+
+class QuestionDot extends @questionDot, TokenKind {
+ override int getValue() { result = 103 }
+
+ override string getDescription() { result = "The null conditional member access operator '?.'." }
+
+ override string toString() { result = "QuestionDot" }
+}
+
+class QuestionLBracket extends @questionLBracket, TokenKind {
+ override int getValue() { result = 104 }
+
+ override string getDescription() { result = "The null conditional index access operator '?[]'." }
+
+ override string toString() { result = "QuestionLBracket" }
+}
+
+class QuestionMark extends @questionMark, TokenKind {
+ override int getValue() { result = 100 }
+
+ override string getDescription() { result = "The ternary operator '?'." }
+
+ override string toString() { result = "QuestionMark" }
+}
+
+class QuestionQuestion extends @questionQuestion, TokenKind {
+ override int getValue() { result = 102 }
+
+ override string getDescription() { result = "The null coalesce operator '??'." }
+
+ override string toString() { result = "QuestionQuestion" }
+}
+
+class QuestionQuestionEquals extends @questionQuestionEquals, TokenKind {
+ override int getValue() { result = 101 }
+
+ override string getDescription() { result = "The null conditional assignment operator '??='." }
+
+ override string toString() { result = "QuestionQuestionEquals" }
+}
+
+class RBracket extends @rBracket, TokenKind {
+ override int getValue() { result = 21 }
+
+ override string getDescription() { result = "The closing square brace token ']'." }
+
+ override string toString() { result = "RBracket" }
+}
+
+class RCurly extends @rCurly, TokenKind {
+ override int getValue() { result = 19 }
+
+ override string getDescription() { result = "The closing curly brace token '}'." }
+
+ override string toString() { result = "RCurly" }
+}
+
+class RedirectInStd extends @redirectInStd, TokenKind {
+ override int getValue() { result = 49 }
+
+ override string getDescription() {
+ result = "The (unimplemented) stdin redirection operator '<'."
+ }
+
+ override string toString() { result = "RedirectInStd" }
+}
+
+class RedirectionToken extends @redirection_token, TokenKind {
+ override int getValue() { result = 48 }
+
+ override string getDescription() { result = "A redirection operator such as '2>&1' or '>>'." }
+
+ override string toString() { result = "Redirection" }
+}
+
+class Rem extends @rem, TokenKind {
+ override int getValue() { result = 39 }
+
+ override string getDescription() { result = "The modulo division (remainder) operator '%'." }
+
+ override string toString() { result = "Rem" }
+}
+
+class RemainderEquals extends @remainderEquals, TokenKind {
+ override int getValue() { result = 47 }
+
+ override string getDescription() {
+ result = "The modulo division (remainder) assignment operator '%='."
+ }
+
+ override string toString() { result = "RemainderEquals" }
+}
+
+class Return extends @return, TokenKind {
+ override int getValue() { result = 142 }
+
+ override string getDescription() { result = "The 'return' keyword." }
+
+ override string toString() { result = "Return" }
+}
+
+class RParen extends @rParen, TokenKind {
+ override int getValue() { result = 17 }
+
+ override string getDescription() { result = "The closing parenthesis token ')'." }
+
+ override string toString() { result = "RParen" }
+}
+
+class Semi extends @semi, TokenKind {
+ override int getValue() { result = 25 }
+
+ override string getDescription() { result = "The statement terminator ';'." }
+
+ override string toString() { result = "Semi" }
+}
+
+class Sequence extends @sequence, TokenKind {
+ override int getValue() { result = 153 }
+
+ override string getDescription() { result = "The 'sequence' keyword." }
+
+ override string toString() { result = "Sequence" }
+}
+
+class Shl extends @shl, TokenKind {
+ override int getValue() { result = 97 }
+
+ override string getDescription() { result = "The shift left operator." }
+
+ override string toString() { result = "Shl" }
+}
+
+class Shr extends @shr, TokenKind {
+ override int getValue() { result = 98 }
+
+ override string getDescription() { result = "The shift right operator." }
+
+ override string toString() { result = "Shr" }
+}
+
+class SplattedVariable extends @splattedVariable, TokenKind {
+ override int getValue() { result = 2 }
+
+ override string getDescription() {
+ result =
+ "A splatted variable token, always begins with '@' and followed by the variable name. Tokens with this kind are always instances of VariableToken."
+ }
+
+ override string toString() { result = "SplattedVariable" }
+}
+
+class Static extends @static, TokenKind {
+ override int getValue() { result = 159 }
+
+ override string getDescription() { result = "The 'static' keyword" }
+
+ override string toString() { result = "Static" }
+}
+
+class StringExpandable extends @stringExpandable, TokenKind {
+ override int getValue() { result = 13 }
+
+ override string getDescription() {
+ result =
+ "A double quoted string literal. Tokens with this kind are always instances of StringExpandableToken even if there are no nested tokens to expand."
+ }
+
+ override string toString() { result = "StringExpandable" }
+}
+
+class StringLiteralToken extends @stringLiteral_token, TokenKind {
+ override int getValue() { result = 12 }
+
+ override string getDescription() {
+ result =
+ "A single quoted string literal. Tokens with this kind are always instances of StringLiteralToken."
+ }
+
+ override string toString() { result = "StringLiteral" }
+}
+
+class Switch extends @switch, TokenKind {
+ override int getValue() { result = 143 }
+
+ override string getDescription() { result = "The 'switch' keyword." }
+
+ override string toString() { result = "Switch" }
+}
+
+class Throw extends @throw, TokenKind {
+ override int getValue() { result = 144 }
+
+ override string getDescription() { result = "The 'throw' keyword." }
+
+ override string toString() { result = "Throw" }
+}
+
+class Trap extends @trap, TokenKind {
+ override int getValue() { result = 145 }
+
+ override string getDescription() { result = "The 'trap' keyword." }
+
+ override string toString() { result = "Trap" }
+}
+
+class Try extends @try, TokenKind {
+ override int getValue() { result = 146 }
+
+ override string getDescription() { result = "The 'try' keyword." }
+
+ override string toString() { result = "Try" }
+}
+
+class Type extends @type, TokenKind {
+ override int getValue() { result = 164 }
+
+ override string getDescription() { result = "The 'type' keyword" }
+
+ override string toString() { result = "Type" }
+}
+
+class Unknown extends @unknown, TokenKind {
+ override int getValue() { result = 0 }
+
+ override string getDescription() { result = "An unknown token, signifies an error condition." }
+
+ override string toString() { result = "Unknown" }
+}
+
+class Until extends @until, TokenKind {
+ override int getValue() { result = 147 }
+
+ override string getDescription() { result = "The 'until' keyword." }
+
+ override string toString() { result = "Until" }
+}
+
+class Using extends @using, TokenKind {
+ override int getValue() { result = 148 }
+
+ override string getDescription() { result = "The (unimplemented) 'using' keyword." }
+
+ override string toString() { result = "Using" }
+}
+
+class Var extends @var, TokenKind {
+ override int getValue() { result = 149 }
+
+ override string getDescription() { result = "The (unimplemented) 'var' keyword." }
+
+ override string toString() { result = "Var" }
+}
+
+class Variable extends @variable, TokenKind {
+ override int getValue() { result = 1 }
+
+ override string getDescription() {
+ result =
+ "A variable token, always begins with '$' and followed by the variable name, possibly enclose in curly braces. Tokens with this kind are always instances of VariableToken."
+ }
+
+ override string toString() { result = "Variable" }
+}
+
+class While extends @while, TokenKind {
+ override int getValue() { result = 150 }
+
+ override string getDescription() { result = "The 'while' keyword." }
+
+ override string toString() { result = "While" }
+}
+
+class Workflow extends @workflow, TokenKind {
+ override int getValue() { result = 151 }
+
+ override string getDescription() { result = "The 'workflow' keyword." }
+
+ override string toString() { result = "Workflow" }
+}
+
+class Xor extends @xor, TokenKind {
+ override int getValue() { result = 55 }
+
+ override string getDescription() { result = "The logical exclusive or operator '-xor'." }
+
+ override string toString() { result = "Xor" }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/TrapStatement.qll b/powershell/ql/lib/semmle/code/powershell/TrapStatement.qll
new file mode 100644
index 00000000000..34da4d2e111
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/TrapStatement.qll
@@ -0,0 +1,7 @@
+import powershell
+
+class TrapStmt extends @trap_statement, Stmt {
+ override SourceLocation getLocation() { trap_statement_location(this, result) }
+
+ override string toString() { result = "TrapStatement at: " + this.getLocation().toString() }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/TryStmt.qll b/powershell/ql/lib/semmle/code/powershell/TryStmt.qll
new file mode 100644
index 00000000000..5c2b4f33b9e
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/TryStmt.qll
@@ -0,0 +1,17 @@
+import powershell
+
+class TryStmt extends @try_statement, Stmt {
+ override SourceLocation getLocation() { try_statement_location(this, result) }
+
+ override string toString() { result = "try {...}" }
+
+ CatchClause getCatchClause(int i) { try_statement_catch_clause(this, i, result) }
+
+ CatchClause getACatchClause() { result = this.getCatchClause(_) }
+
+ /** ..., if any. */
+ StmtBlock getFinally() { try_statement_finally(this, result) }
+
+ StmtBlock getBody() { try_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme
+
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/Type.qll b/powershell/ql/lib/semmle/code/powershell/Type.qll
new file mode 100644
index 00000000000..ef6d61b3991
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/Type.qll
@@ -0,0 +1,13 @@
+import powershell
+
+class Type extends @type_definition, Stmt {
+ override SourceLocation getLocation() { type_definition_location(this, result) }
+
+ override string toString() { result = this.getName() }
+
+ string getName() { type_definition(this, result, _, _, _, _) }
+
+ Member getMember(int i) { type_definition_members(this, i, result) }
+
+ Member getAMember() { result = this.getMember(_) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/TypeConstraint.qll b/powershell/ql/lib/semmle/code/powershell/TypeConstraint.qll
new file mode 100644
index 00000000000..970afcb5b70
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/TypeConstraint.qll
@@ -0,0 +1,13 @@
+import powershell
+
+class TypeConstraint extends @type_constraint, AttributeBase {
+ override SourceLocation getLocation() { type_constraint_location(this, result) }
+
+ /** Gets the assembly name. */
+ string getName() { type_constraint(this, result, _) }
+
+ /** Gets the full name of this type constraint including namespaces. */
+ string getFullName() { type_constraint(this, _, result) }
+
+ override string toString() { result = this.getName() }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/TypeExpression.qll b/powershell/ql/lib/semmle/code/powershell/TypeExpression.qll
new file mode 100644
index 00000000000..3d0b7c77748
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/TypeExpression.qll
@@ -0,0 +1,11 @@
+import powershell
+
+class TypeExpression extends @type_expression, Expr {
+ string getName() { type_expression(this, result, _) }
+
+ string getFullyQualifiedName() { type_expression(this, _, result) }
+
+ override string toString() { result = this.getName() }
+
+ override SourceLocation getLocation() { type_expression_location(this, result) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/UsingExpression.qll b/powershell/ql/lib/semmle/code/powershell/UsingExpression.qll
new file mode 100644
index 00000000000..6c2e4eff62f
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/UsingExpression.qll
@@ -0,0 +1,7 @@
+import powershell
+
+class UsingExpr extends @using_expression, Expr {
+ override string toString() { result = "$using..." }
+
+ override SourceLocation getLocation() { using_expression_location(this, result) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/UsingStmt.qll b/powershell/ql/lib/semmle/code/powershell/UsingStmt.qll
new file mode 100644
index 00000000000..37687cee46c
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/UsingStmt.qll
@@ -0,0 +1,7 @@
+import powershell
+
+class UsingStmt extends @using_statement, Stmt {
+ override SourceLocation getLocation() { using_statement_location(this, result) }
+
+ override string toString() { result = "using ..." }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/VariableExpression.qll b/powershell/ql/lib/semmle/code/powershell/VariableExpression.qll
new file mode 100644
index 00000000000..8c4ff8a3ef8
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/VariableExpression.qll
@@ -0,0 +1,29 @@
+import powershell
+
+class VarAccess extends @variable_expression, Expr {
+ override string toString() { result = this.getUserPath() }
+
+ override SourceLocation getLocation() { variable_expression_location(this, result) }
+
+ string getUserPath() { variable_expression(this, result, _, _, _, _, _, _, _, _, _, _) }
+
+ string getDriveName() { variable_expression(this, _, result, _, _, _, _, _, _, _, _, _) }
+
+ boolean isConstant() { variable_expression(this, _, _, result, _, _, _, _, _, _, _, _) }
+
+ boolean isGlobal() { variable_expression(this, _, _, _, result, _, _, _, _, _, _, _) }
+
+ boolean isLocal() { variable_expression(this, _, _, _, _, result, _, _, _, _, _, _) }
+
+ boolean isPrivate() { variable_expression(this, _, _, _, _, _, result, _, _, _, _, _) }
+
+ boolean isScript() { variable_expression(this, _, _, _, _, _, _, result, _, _, _, _) }
+
+ boolean isUnqualified() { variable_expression(this, _, _, _, _, _, _, _, result, _, _, _) }
+
+ boolean isUnscoped() { variable_expression(this, _, _, _, _, _, _, _, _, result, _, _) }
+
+ boolean isVariable() { variable_expression(this, _, _, _, _, _, _, _, _, _, result, _) }
+
+ boolean isDriveQualified() { variable_expression(this, _, _, _, _, _, _, _, _, _, _, result) }
+}
diff --git a/powershell/ql/lib/semmle/code/powershell/WhileStmt.qll b/powershell/ql/lib/semmle/code/powershell/WhileStmt.qll
new file mode 100644
index 00000000000..1b34927956e
--- /dev/null
+++ b/powershell/ql/lib/semmle/code/powershell/WhileStmt.qll
@@ -0,0 +1,11 @@
+import powershell
+
+class WhileStmt extends @while_statement, LoopStmt {
+ override SourceLocation getLocation() { while_statement_location(this, result) }
+
+ override string toString() { result = "while(...) {...}" }
+
+ PipelineBase getCondition() { while_statement_condition(this, result) } // TODO: Change @ast to @pipeline_base in dbscheme
+
+ StmtBlock getBody() { while_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme
+}
diff --git a/powershell/ql/test/library-tests/ast/Arrays/Arrays.ps1 b/powershell/ql/test/library-tests/ast/Arrays/Arrays.ps1
new file mode 100644
index 00000000000..fe0b980c568
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Arrays/Arrays.ps1
@@ -0,0 +1,15 @@
+$array1 = 1,2,"a",$true,$false,$null # 1-D array
+$array1[1] = 3
+$array1[2] = "b"
+
+$array2 = New-Object 'object[,]' 2,2 # 2-D array
+$array2[0,0] = "key1"
+$array2[1,0] = "key1"
+$array2[0,1] = "value1"
+$array2[1,1] = $null
+
+$array3 = @("a","b","c")
+$array3.count
+
+$array4 = [System.Collections.ArrayList]@()
+$array4.Add(1)
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Arrays/arrays.expected b/powershell/ql/test/library-tests/ast/Arrays/arrays.expected
new file mode 100644
index 00000000000..d8930c0ed0e
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Arrays/arrays.expected
@@ -0,0 +1,23 @@
+arrayExpr
+| Arrays.ps1:11:11:11:25 | ArrayExpression at: Arrays.ps1:11:11:11:25 | Arrays.ps1:11:13:11:24 | StatementBlock at: Arrays.ps1:11:13:11:24 |
+| Arrays.ps1:14:41:14:44 | ArrayExpression at: Arrays.ps1:14:41:14:44 | Arrays.ps1:0:0:0:0 | StatementBlock at: Arrays.ps1:0:0:0:0 |
+arrayLiteral
+| Arrays.ps1:1:11:1:37 | ArrayLiteral at: Arrays.ps1:1:11:1:37 | 0 | Arrays.ps1:1:11:1:12 | ConstantExpression at: Arrays.ps1:1:11:1:12 |
+| Arrays.ps1:1:11:1:37 | ArrayLiteral at: Arrays.ps1:1:11:1:37 | 1 | Arrays.ps1:1:13:1:14 | ConstantExpression at: Arrays.ps1:1:13:1:14 |
+| Arrays.ps1:1:11:1:37 | ArrayLiteral at: Arrays.ps1:1:11:1:37 | 2 | Arrays.ps1:1:15:1:18 | a |
+| Arrays.ps1:1:11:1:37 | ArrayLiteral at: Arrays.ps1:1:11:1:37 | 3 | Arrays.ps1:1:19:1:24 | true |
+| Arrays.ps1:1:11:1:37 | ArrayLiteral at: Arrays.ps1:1:11:1:37 | 4 | Arrays.ps1:1:25:1:31 | false |
+| Arrays.ps1:1:11:1:37 | ArrayLiteral at: Arrays.ps1:1:11:1:37 | 5 | Arrays.ps1:1:32:1:37 | null |
+| Arrays.ps1:5:34:5:37 | ArrayLiteral at: Arrays.ps1:5:34:5:37 | 0 | Arrays.ps1:5:34:5:35 | ConstantExpression at: Arrays.ps1:5:34:5:35 |
+| Arrays.ps1:5:34:5:37 | ArrayLiteral at: Arrays.ps1:5:34:5:37 | 1 | Arrays.ps1:5:36:5:37 | ConstantExpression at: Arrays.ps1:5:36:5:37 |
+| Arrays.ps1:6:9:6:12 | ArrayLiteral at: Arrays.ps1:6:9:6:12 | 0 | Arrays.ps1:6:9:6:10 | ConstantExpression at: Arrays.ps1:6:9:6:10 |
+| Arrays.ps1:6:9:6:12 | ArrayLiteral at: Arrays.ps1:6:9:6:12 | 1 | Arrays.ps1:6:11:6:12 | ConstantExpression at: Arrays.ps1:6:11:6:12 |
+| Arrays.ps1:7:9:7:12 | ArrayLiteral at: Arrays.ps1:7:9:7:12 | 0 | Arrays.ps1:7:9:7:10 | ConstantExpression at: Arrays.ps1:7:9:7:10 |
+| Arrays.ps1:7:9:7:12 | ArrayLiteral at: Arrays.ps1:7:9:7:12 | 1 | Arrays.ps1:7:11:7:12 | ConstantExpression at: Arrays.ps1:7:11:7:12 |
+| Arrays.ps1:8:9:8:12 | ArrayLiteral at: Arrays.ps1:8:9:8:12 | 0 | Arrays.ps1:8:9:8:10 | ConstantExpression at: Arrays.ps1:8:9:8:10 |
+| Arrays.ps1:8:9:8:12 | ArrayLiteral at: Arrays.ps1:8:9:8:12 | 1 | Arrays.ps1:8:11:8:12 | ConstantExpression at: Arrays.ps1:8:11:8:12 |
+| Arrays.ps1:9:9:9:12 | ArrayLiteral at: Arrays.ps1:9:9:9:12 | 0 | Arrays.ps1:9:9:9:10 | ConstantExpression at: Arrays.ps1:9:9:9:10 |
+| Arrays.ps1:9:9:9:12 | ArrayLiteral at: Arrays.ps1:9:9:9:12 | 1 | Arrays.ps1:9:11:9:12 | ConstantExpression at: Arrays.ps1:9:11:9:12 |
+| Arrays.ps1:11:13:11:24 | ArrayLiteral at: Arrays.ps1:11:13:11:24 | 0 | Arrays.ps1:11:13:11:16 | a |
+| Arrays.ps1:11:13:11:24 | ArrayLiteral at: Arrays.ps1:11:13:11:24 | 1 | Arrays.ps1:11:17:11:20 | b |
+| Arrays.ps1:11:13:11:24 | ArrayLiteral at: Arrays.ps1:11:13:11:24 | 2 | Arrays.ps1:11:21:11:24 | c |
diff --git a/powershell/ql/test/library-tests/ast/Arrays/arrays.ql b/powershell/ql/test/library-tests/ast/Arrays/arrays.ql
new file mode 100644
index 00000000000..18aa9ea5988
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Arrays/arrays.ql
@@ -0,0 +1,7 @@
+import powershell
+
+query predicate arrayExpr(ArrayExpr arrayExpr, StmtBlock subExpr) { subExpr = arrayExpr.getStatementBlock() }
+
+query predicate arrayLiteral(ArrayLiteral arrayLiteral, int i, Expr e) {
+ e = arrayLiteral.getElement(i)
+}
diff --git a/powershell/ql/test/library-tests/ast/Blocks/ParamBlock.ps1 b/powershell/ql/test/library-tests/ast/Blocks/ParamBlock.ps1
new file mode 100644
index 00000000000..79ff9379f09
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Blocks/ParamBlock.ps1
@@ -0,0 +1,5 @@
+[CmdletBinding()]
+param(
+ [Parameter()]
+ [string]$Parameter
+)
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Blocks/blocks.expected b/powershell/ql/test/library-tests/ast/Blocks/blocks.expected
new file mode 100644
index 00000000000..b2116a0389b
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Blocks/blocks.expected
@@ -0,0 +1 @@
+| ParamBlock.ps1:2:1:5:2 | ParamBlock | 0 | ParamBlock.ps1:3:5:4:23 | Parameter |
diff --git a/powershell/ql/test/library-tests/ast/Blocks/blocks.ql b/powershell/ql/test/library-tests/ast/Blocks/blocks.ql
new file mode 100644
index 00000000000..9c4288d3560
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Blocks/blocks.ql
@@ -0,0 +1,5 @@
+import powershell
+
+query predicate paramBlockHasParam(ParamBlock block, int i, Parameter p) {
+ p = block.getParameter(i)
+}
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Dynamic/DynamicExecution.ps1 b/powershell/ql/test/library-tests/ast/Dynamic/DynamicExecution.ps1
new file mode 100644
index 00000000000..0baac441a31
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Dynamic/DynamicExecution.ps1
@@ -0,0 +1,5 @@
+$foo = 'cmd.exe'
+Invoke-Expression $foo
+[scriptblock]::Create($foo)
+& ([scriptblock]::Create($foo))
+&"$foo"
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Dynamic/DynamicExecutionWithFunc.ps1 b/powershell/ql/test/library-tests/ast/Dynamic/DynamicExecutionWithFunc.ps1
new file mode 100644
index 00000000000..15ca86a939a
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Dynamic/DynamicExecutionWithFunc.ps1
@@ -0,0 +1,11 @@
+function ExecuteAThing {
+ param (
+ $userInput
+ )
+ $foo = 'cmd.exe' + $userInput;
+ Invoke-Expression $foo
+ [scriptblock]::Create($foo)
+ & ([scriptblock]::Create($foo))
+ &"$foo"
+ & 'cmd.exe' @($userInput)
+}
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Expressions/BinaryExpression.ps1 b/powershell/ql/test/library-tests/ast/Expressions/BinaryExpression.ps1
new file mode 100644
index 00000000000..11dd2df588e
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Expressions/BinaryExpression.ps1
@@ -0,0 +1,4 @@
+$val1 = 1
+$val2 = 2
+$result = $val1 + $val2
+$result
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Expressions/ConvertWithSecureString.ps1 b/powershell/ql/test/library-tests/ast/Expressions/ConvertWithSecureString.ps1
new file mode 100644
index 00000000000..761eb817a1d
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Expressions/ConvertWithSecureString.ps1
@@ -0,0 +1,2 @@
+$UserInput = Read-Host "Please enter your secure code"
+$EncryptedInput = ConvertTo-SecureString -String $UserInput -AsPlainText -Force
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Expressions/ExpandableString.ps1 b/powershell/ql/test/library-tests/ast/Expressions/ExpandableString.ps1
new file mode 100644
index 00000000000..844f0c958e3
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Expressions/ExpandableString.ps1
@@ -0,0 +1 @@
+"Name: $name`nDate: $([DateTime]::Now)"
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Expressions/SubExpression.ps1 b/powershell/ql/test/library-tests/ast/Expressions/SubExpression.ps1
new file mode 100644
index 00000000000..b381ca3e7e8
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Expressions/SubExpression.ps1
@@ -0,0 +1,2 @@
+$(Get-Date).AddDays(10)
+$(Get-Date).AddDays()
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Expressions/TernaryExpression.ps1 b/powershell/ql/test/library-tests/ast/Expressions/TernaryExpression.ps1
new file mode 100644
index 00000000000..481e78df2a2
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Expressions/TernaryExpression.ps1
@@ -0,0 +1 @@
+$var = (6 -gt 7) ? 1:2
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Expressions/expressions.expected b/powershell/ql/test/library-tests/ast/Expressions/expressions.expected
new file mode 100644
index 00000000000..a34db679319
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Expressions/expressions.expected
@@ -0,0 +1,19 @@
+binaryExpr
+| BinaryExpression.ps1:3:11:3:24 | ...+... | BinaryExpression.ps1:3:11:3:16 | val1 | BinaryExpression.ps1:3:19:3:24 | val2 |
+| TernaryExpression.ps1:1:9:1:16 | ...+... | TernaryExpression.ps1:1:9:1:10 | ConstantExpression at: TernaryExpression.ps1:1:9:1:10 | TernaryExpression.ps1:1:15:1:16 | ConstantExpression at: TernaryExpression.ps1:1:15:1:16 |
+cmdExpr
+| BinaryExpression.ps1:1:9:1:10 | CommandExpression at: BinaryExpression.ps1:1:9:1:10 | BinaryExpression.ps1:1:9:1:10 | ConstantExpression at: BinaryExpression.ps1:1:9:1:10 |
+| BinaryExpression.ps1:2:9:2:10 | CommandExpression at: BinaryExpression.ps1:2:9:2:10 | BinaryExpression.ps1:2:9:2:10 | ConstantExpression at: BinaryExpression.ps1:2:9:2:10 |
+| BinaryExpression.ps1:3:11:3:24 | CommandExpression at: BinaryExpression.ps1:3:11:3:24 | BinaryExpression.ps1:3:11:3:24 | ...+... |
+| BinaryExpression.ps1:4:1:4:8 | CommandExpression at: BinaryExpression.ps1:4:1:4:8 | BinaryExpression.ps1:4:1:4:8 | result |
+| ExpandableString.ps1:1:1:1:40 | CommandExpression at: ExpandableString.ps1:1:1:1:40 | ExpandableString.ps1:1:1:1:40 | ExpandableStringExpression at: ExpandableString.ps1:1:1:1:40 |
+| ExpandableString.ps1:1:23:1:38 | CommandExpression at: ExpandableString.ps1:1:23:1:38 | file://:0:0:0:0 | (no string representation) |
+| SubExpression.ps1:1:1:1:24 | CommandExpression at: SubExpression.ps1:1:1:1:24 | SubExpression.ps1:1:1:1:24 | ArrayExpression at: SubExpression.ps1:1:1:1:24 |
+| SubExpression.ps1:2:1:2:22 | CommandExpression at: SubExpression.ps1:2:1:2:22 | SubExpression.ps1:2:1:2:22 | ArrayExpression at: SubExpression.ps1:2:1:2:22 |
+| TernaryExpression.ps1:1:8:1:23 | CommandExpression at: TernaryExpression.ps1:1:8:1:23 | TernaryExpression.ps1:1:8:1:23 | ...?...:... |
+| TernaryExpression.ps1:1:9:1:16 | CommandExpression at: TernaryExpression.ps1:1:9:1:16 | TernaryExpression.ps1:1:9:1:16 | ...+... |
+invokeMemoryExpression
+| SubExpression.ps1:1:1:1:24 | ArrayExpression at: SubExpression.ps1:1:1:1:24 | file://:0:0:0:0 | (no string representation) | 0 | SubExpression.ps1:1:21:1:23 | ConstantExpression at: SubExpression.ps1:1:21:1:23 |
+expandableString
+| ExpandableString.ps1:1:1:1:40 | ExpandableStringExpression at: ExpandableString.ps1:1:1:1:40 | 0 | ExpandableString.ps1:1:8:1:13 | name |
+| ExpandableString.ps1:1:1:1:40 | ExpandableStringExpression at: ExpandableString.ps1:1:1:1:40 | 1 | file://:0:0:0:0 | (no string representation) |
diff --git a/powershell/ql/test/library-tests/ast/Expressions/expressions.ql b/powershell/ql/test/library-tests/ast/Expressions/expressions.ql
new file mode 100644
index 00000000000..a3aee8e22d5
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Expressions/expressions.ql
@@ -0,0 +1,19 @@
+import powershell
+
+query predicate binaryExpr(BinaryExpr e, Expr e1, Expr e2) {
+ e1 = e.getLeft() and
+ e2 = e.getRight()
+}
+
+query predicate cmdExpr(CmdExpr cmd, Expr e) {
+ e = cmd.getExpression()
+}
+
+query predicate invokeMemoryExpression(InvokeMemberExpression invoke, Expr e, int i, Expr arg) {
+ e = invoke.getExpression() and
+ arg = invoke.getArgument(i)
+}
+
+query predicate expandableString(ExpandableStringExpression expandable, int i, Expr e) {
+ e = expandable.getExpr(i)
+}
diff --git a/powershell/ql/test/library-tests/ast/Loops/DoUntil.ps1 b/powershell/ql/test/library-tests/ast/Loops/DoUntil.ps1
new file mode 100644
index 00000000000..3d3e502673b
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Loops/DoUntil.ps1
@@ -0,0 +1,7 @@
+DO
+{
+ “Starting Loop $a”
+ $a
+ $a++
+ “Now `$a is $a”
+} Until ($a -le 5)
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Loops/DoWhile.ps1 b/powershell/ql/test/library-tests/ast/Loops/DoWhile.ps1
new file mode 100644
index 00000000000..38794ad9ec7
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Loops/DoWhile.ps1
@@ -0,0 +1,7 @@
+DO
+{
+ “Starting Loop $a”
+ $a
+ $a++
+ “Now `$a is $a”
+} While ($a -le 5)
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Loops/While.ps1 b/powershell/ql/test/library-tests/ast/Loops/While.ps1
new file mode 100644
index 00000000000..588abe3c868
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Loops/While.ps1
@@ -0,0 +1,13 @@
+$var = 1
+while ($var -le 5)
+{
+ Write-Host The value of Var is: $var
+ $var++
+ if ($var -le 3){
+ continue;
+ }
+ else
+ {
+ break;
+ }
+}
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Loops/loops.ql b/powershell/ql/test/library-tests/ast/Loops/loops.ql
new file mode 100644
index 00000000000..55c750da7cb
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Loops/loops.ql
@@ -0,0 +1,16 @@
+import powershell
+
+query predicate doUntil(DoUntilStmt s, PipelineBase e, StmtBlock body) {
+ e = s.getCondition() and
+ body = s.getBody()
+}
+
+query predicate doWhile(DoWhileStmt s, PipelineBase e, StmtBlock body) {
+ e = s.getCondition() and
+ body = s.getBody()
+}
+
+query predicate while(WhileStmt s, PipelineBase e, StmtBlock body) {
+ e = s.getCondition() and
+ body = s.getBody()
+}
diff --git a/powershell/ql/test/library-tests/ast/Redirections/FileRedirection.ps1 b/powershell/ql/test/library-tests/ast/Redirections/FileRedirection.ps1
new file mode 100644
index 00000000000..701de90cc75
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Redirections/FileRedirection.ps1
@@ -0,0 +1,3 @@
+$(
+ Here is your current script
+) *>&1 > output.txt
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Redirections/redirections.expected b/powershell/ql/test/library-tests/ast/Redirections/redirections.expected
new file mode 100644
index 00000000000..968fcee62ee
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Redirections/redirections.expected
@@ -0,0 +1,2 @@
+| FileRedirection.ps1:3:3:3:7 | MergingRedirection |
+| FileRedirection.ps1:3:8:3:20 | FileRedirection |
diff --git a/powershell/ql/test/library-tests/ast/Redirections/redirections.ql b/powershell/ql/test/library-tests/ast/Redirections/redirections.ql
new file mode 100644
index 00000000000..b94f8807235
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Redirections/redirections.ql
@@ -0,0 +1,3 @@
+import powershell
+
+query predicate redirection(Redirection r) { any() }
diff --git a/powershell/ql/test/library-tests/ast/Statements/ExitStatement.ps1 b/powershell/ql/test/library-tests/ast/Statements/ExitStatement.ps1
new file mode 100644
index 00000000000..a4046370b8f
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Statements/ExitStatement.ps1
@@ -0,0 +1 @@
+exit -1
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Statements/IfStatement.ps1 b/powershell/ql/test/library-tests/ast/Statements/IfStatement.ps1
new file mode 100644
index 00000000000..3572c369f38
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Statements/IfStatement.ps1
@@ -0,0 +1,8 @@
+$x = 4
+
+if ($x -ge 3) {
+ "$x is greater than or equal to 3"
+}
+else {
+ "$x is less than 3"
+}
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Statements/TrapStatement.ps1 b/powershell/ql/test/library-tests/ast/Statements/TrapStatement.ps1
new file mode 100644
index 00000000000..6eeb40d34b7
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Statements/TrapStatement.ps1
@@ -0,0 +1,6 @@
+function TrapTest {
+ trap {"Error found."}
+ nonsenseString
+}
+
+TrapTest
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Statements/Try.ps1 b/powershell/ql/test/library-tests/ast/Statements/Try.ps1
new file mode 100644
index 00000000000..1f203269efb
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Statements/Try.ps1
@@ -0,0 +1,13 @@
+try {
+ $Exception = New-Object System.Xaml.XamlException -ArgumentList ("Bad XAML!", $null, 10, 2)
+ throw $Exception
+}
+catch [System.Net.WebException],[System.IO.IOException] {
+ "Unable to download MyDoc.doc from http://www.contoso.com."
+}
+catch {
+ "An error occurred that could not be resolved."
+}
+finally {
+ "The finally block is executed."
+}
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Statements/UseProcessBlockForPipelineCommand.ps1 b/powershell/ql/test/library-tests/ast/Statements/UseProcessBlockForPipelineCommand.ps1
new file mode 100644
index 00000000000..f11969e0672
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Statements/UseProcessBlockForPipelineCommand.ps1
@@ -0,0 +1,11 @@
+Function Get-Number
+{
+ [CmdletBinding()]
+ Param(
+ [Parameter(ValueFromPipeline)]
+ [int]
+ $Number
+ )
+
+ $Number
+}
\ No newline at end of file
diff --git a/powershell/ql/test/library-tests/ast/Statements/statements.expected b/powershell/ql/test/library-tests/ast/Statements/statements.expected
new file mode 100644
index 00000000000..35288e5bc58
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Statements/statements.expected
@@ -0,0 +1,38 @@
+| ExitStatement.ps1:1:1:1:8 | exit ... |
+| ExitStatement.ps1:1:6:1:8 | ...\|... |
+| ExitStatement.ps1:1:6:1:8 | CommandExpression at: ExitStatement.ps1:1:6:1:8 |
+| IfStatement.ps1:1:1:1:7 | AssignmentStatement at: IfStatement.ps1:1:1:1:7 |
+| IfStatement.ps1:1:6:1:7 | CommandExpression at: IfStatement.ps1:1:6:1:7 |
+| IfStatement.ps1:3:1:8:2 | if (...) {...} else {...} |
+| IfStatement.ps1:3:5:3:13 | ...\|... |
+| IfStatement.ps1:3:5:3:13 | CommandExpression at: IfStatement.ps1:3:5:3:13 |
+| IfStatement.ps1:4:2:4:36 | ...\|... |
+| IfStatement.ps1:4:2:4:36 | CommandExpression at: IfStatement.ps1:4:2:4:36 |
+| IfStatement.ps1:7:2:7:21 | ...\|... |
+| IfStatement.ps1:7:2:7:21 | CommandExpression at: IfStatement.ps1:7:2:7:21 |
+| TrapStatement.ps1:1:1:4:2 | FunctionDefinition at: TrapStatement.ps1:1:1:4:2 |
+| TrapStatement.ps1:2:5:2:26 | TrapStatement at: TrapStatement.ps1:2:5:2:26 |
+| TrapStatement.ps1:2:11:2:25 | ...\|... |
+| TrapStatement.ps1:2:11:2:25 | CommandExpression at: TrapStatement.ps1:2:11:2:25 |
+| TrapStatement.ps1:3:5:3:19 | ...\|... |
+| TrapStatement.ps1:3:5:3:19 | nonsenseString |
+| TrapStatement.ps1:6:1:6:9 | ...\|... |
+| TrapStatement.ps1:6:1:6:9 | TrapTest |
+| Try.ps1:1:1:13:2 | try {...} |
+| Try.ps1:2:4:2:95 | AssignmentStatement at: Try.ps1:2:4:2:95 |
+| Try.ps1:2:17:2:95 | ...\|... |
+| Try.ps1:2:17:2:95 | New-Object |
+| Try.ps1:2:69:2:94 | ...\|... |
+| Try.ps1:2:69:2:94 | CommandExpression at: Try.ps1:2:69:2:94 |
+| Try.ps1:3:11:3:21 | ...\|... |
+| Try.ps1:3:11:3:21 | CommandExpression at: Try.ps1:3:11:3:21 |
+| Try.ps1:6:5:6:64 | ...\|... |
+| Try.ps1:6:5:6:64 | CommandExpression at: Try.ps1:6:5:6:64 |
+| Try.ps1:9:5:9:52 | ...\|... |
+| Try.ps1:9:5:9:52 | CommandExpression at: Try.ps1:9:5:9:52 |
+| Try.ps1:12:5:12:37 | ...\|... |
+| Try.ps1:12:5:12:37 | CommandExpression at: Try.ps1:12:5:12:37 |
+| UseProcessBlockForPipelineCommand.ps1:1:1:11:2 | FunctionDefinition at: UseProcessBlockForPipelineCommand.ps1:1:1:11:2 |
+| UseProcessBlockForPipelineCommand.ps1:10:5:10:12 | ...\|... |
+| UseProcessBlockForPipelineCommand.ps1:10:5:10:12 | CommandExpression at: UseProcessBlockForPipelineCommand.ps1:10:5:10:12 |
+| file://:0:0:0:0 | (no string representation) |
diff --git a/powershell/ql/test/library-tests/ast/Statements/statements.ql b/powershell/ql/test/library-tests/ast/Statements/statements.ql
new file mode 100644
index 00000000000..ab8eb5b6fc6
--- /dev/null
+++ b/powershell/ql/test/library-tests/ast/Statements/statements.ql
@@ -0,0 +1,3 @@
+import powershell
+
+query predicate stmt(Stmt s) { any() }
\ No newline at end of file
diff --git a/powershell/ql/test/qlpack.yml b/powershell/ql/test/qlpack.yml
new file mode 100644
index 00000000000..ccd69e233fe
--- /dev/null
+++ b/powershell/ql/test/qlpack.yml
@@ -0,0 +1,9 @@
+name: microsoft-sdl/powershell-tests
+groups:
+ - powershell
+ - test
+dependencies:
+ microsoft-sdl/powershell-all: ${workspace}
+extractor: powershell
+tests: .
+warnOnImplicitThis: true
\ No newline at end of file