mirror of
https://github.com/github/codeql.git
synced 2026-04-28 02:05:14 +02:00
Merge pull request #13770 from geoffw0/parsemode3
Swift: Track regular expression parse modes set in code
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The regular expression library now understands mode flags specified by `Regex` methods and the `NSRegularExpression` initializer.
|
||||
@@ -36,22 +36,23 @@ abstract class RegexCreation extends DataFlow::Node {
|
||||
* created from.
|
||||
*/
|
||||
abstract DataFlow::Node getStringInput();
|
||||
|
||||
/**
|
||||
* Gets a dataflow node for the options input that might contain parse mode
|
||||
* flags (if any).
|
||||
*/
|
||||
DataFlow::Node getOptionsInput() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node where a `Regex` or `NSRegularExpression` object is created.
|
||||
* A data-flow node where a `Regex` object is created.
|
||||
*/
|
||||
private class StandardRegexCreation extends RegexCreation {
|
||||
private class RegexRegexCreation extends RegexCreation {
|
||||
DataFlow::Node input;
|
||||
|
||||
StandardRegexCreation() {
|
||||
RegexRegexCreation() {
|
||||
exists(CallExpr call |
|
||||
(
|
||||
call.getStaticTarget().(Method).hasQualifiedName("Regex", ["init(_:)", "init(_:as:)"]) or
|
||||
call.getStaticTarget()
|
||||
.(Method)
|
||||
.hasQualifiedName("NSRegularExpression", "init(pattern:options:)")
|
||||
) and
|
||||
call.getStaticTarget().(Method).hasQualifiedName("Regex", ["init(_:)", "init(_:as:)"]) and
|
||||
input.asExpr() = call.getArgument(0).getExpr() and
|
||||
this.asExpr() = call
|
||||
)
|
||||
@@ -60,6 +61,168 @@ private class StandardRegexCreation extends RegexCreation {
|
||||
override DataFlow::Node getStringInput() { result = input }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node where an `NSRegularExpression` object is created.
|
||||
*/
|
||||
private class NSRegularExpressionRegexCreation extends RegexCreation {
|
||||
DataFlow::Node input;
|
||||
|
||||
NSRegularExpressionRegexCreation() {
|
||||
exists(CallExpr call |
|
||||
call.getStaticTarget()
|
||||
.(Method)
|
||||
.hasQualifiedName("NSRegularExpression", "init(pattern:options:)") and
|
||||
input.asExpr() = call.getArgument(0).getExpr() and
|
||||
this.asExpr() = call
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getStringInput() { result = input }
|
||||
|
||||
override DataFlow::Node getOptionsInput() {
|
||||
result.asExpr() = this.asExpr().(CallExpr).getArgument(1).getExpr()
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TRegexParseMode =
|
||||
MkIgnoreCase() or // case insensitive
|
||||
MkVerbose() or // ignores whitespace and `#` comments within patterns
|
||||
MkDotAll() or // dot matches all characters, including line terminators
|
||||
MkMultiLine() or // `^` and `$` also match beginning and end of lines
|
||||
MkUnicode() // Unicode UAX 29 word boundary mode
|
||||
|
||||
/**
|
||||
* A regular expression parse mode flag.
|
||||
*/
|
||||
class RegexParseMode extends TRegexParseMode {
|
||||
/**
|
||||
* Gets the name of this parse mode flag.
|
||||
*/
|
||||
string getName() {
|
||||
this = MkIgnoreCase() and result = "IGNORECASE"
|
||||
or
|
||||
this = MkVerbose() and result = "VERBOSE"
|
||||
or
|
||||
this = MkDotAll() and result = "DOTALL"
|
||||
or
|
||||
this = MkMultiLine() and result = "MULTILINE"
|
||||
or
|
||||
this = MkUnicode() and result = "UNICODE"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this `RegexParseMode`.
|
||||
*/
|
||||
string toString() { result = this.getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A unit class for adding additional flow steps for regular expressions.
|
||||
*/
|
||||
class RegexAdditionalFlowStep extends Unit {
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` should be considered a flow
|
||||
* step for regular expressions.
|
||||
*/
|
||||
abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo);
|
||||
|
||||
/**
|
||||
* Holds if a regular expression parse mode is either set (`isSet` = true)
|
||||
* or unset (`isSet` = false) at `node`. Parse modes propagate through
|
||||
* array construction and regex construction.
|
||||
*/
|
||||
abstract predicate setsParseMode(DataFlow::Node node, RegexParseMode mode, boolean isSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* An additional flow step for `Regex`.
|
||||
*/
|
||||
class RegexRegexAdditionalFlowStep extends RegexAdditionalFlowStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
this.setsParseModeEdge(nodeFrom, nodeTo, _, _)
|
||||
}
|
||||
|
||||
override predicate setsParseMode(DataFlow::Node node, RegexParseMode mode, boolean isSet) {
|
||||
this.setsParseModeEdge(_, node, mode, isSet)
|
||||
}
|
||||
|
||||
private predicate setsParseModeEdge(
|
||||
DataFlow::Node nodeFrom, DataFlow::Node nodeTo, RegexParseMode mode, boolean isSet
|
||||
) {
|
||||
// `Regex` methods that modify the parse mode of an existing `Regex` object.
|
||||
exists(CallExpr ce |
|
||||
nodeFrom.asExpr() = ce.getQualifier() and
|
||||
nodeTo.asExpr() = ce and
|
||||
// decode the parse mode being set
|
||||
(
|
||||
ce.getStaticTarget().(Method).hasQualifiedName("Regex", "ignoresCase(_:)") and
|
||||
mode = MkIgnoreCase()
|
||||
or
|
||||
ce.getStaticTarget().(Method).hasQualifiedName("Regex", "dotMatchesNewlines(_:)") and
|
||||
mode = MkDotAll()
|
||||
or
|
||||
ce.getStaticTarget().(Method).hasQualifiedName("Regex", "anchorsMatchLineEndings(_:)") and
|
||||
mode = MkMultiLine()
|
||||
) and
|
||||
// decode the value being set
|
||||
if ce.getArgument(0).getExpr().(BooleanLiteralExpr).getValue() = false
|
||||
then isSet = false // mode is set to false
|
||||
else isSet = true // mode is set to true OR mode is set to default (=true) OR mode is set to an unknown value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An additional flow step for `NSRegularExpression`.
|
||||
*/
|
||||
class NSRegularExpressionRegexAdditionalFlowStep extends RegexAdditionalFlowStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { none() }
|
||||
|
||||
override predicate setsParseMode(DataFlow::Node node, RegexParseMode mode, boolean isSet) {
|
||||
// `NSRegularExpression.Options` values (these are typically combined, then passed into
|
||||
// the `NSRegularExpression` initializer).
|
||||
node.asExpr()
|
||||
.(MemberRefExpr)
|
||||
.getMember()
|
||||
.(FieldDecl)
|
||||
.hasQualifiedName("NSRegularExpression.Options", "caseInsensitive") and
|
||||
mode = MkIgnoreCase() and
|
||||
isSet = true
|
||||
or
|
||||
node.asExpr()
|
||||
.(MemberRefExpr)
|
||||
.getMember()
|
||||
.(FieldDecl)
|
||||
.hasQualifiedName("NSRegularExpression.Options", "allowCommentsAndWhitespace") and
|
||||
mode = MkVerbose() and
|
||||
isSet = true
|
||||
or
|
||||
node.asExpr()
|
||||
.(MemberRefExpr)
|
||||
.getMember()
|
||||
.(FieldDecl)
|
||||
.hasQualifiedName("NSRegularExpression.Options", "dotMatchesLineSeparators") and
|
||||
mode = MkDotAll() and
|
||||
isSet = true
|
||||
or
|
||||
node.asExpr()
|
||||
.(MemberRefExpr)
|
||||
.getMember()
|
||||
.(FieldDecl)
|
||||
.hasQualifiedName("NSRegularExpression.Options", "anchorsMatchLines") and
|
||||
mode = MkMultiLine() and
|
||||
isSet = true
|
||||
or
|
||||
node.asExpr()
|
||||
.(MemberRefExpr)
|
||||
.getMember()
|
||||
.(FieldDecl)
|
||||
.hasQualifiedName("NSRegularExpression.Options", "useUnicodeWordBoundaries") and
|
||||
mode = MkUnicode() and
|
||||
isSet = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call that evaluates a regular expression. For example, the call to `firstMatch` in:
|
||||
* ```
|
||||
@@ -91,6 +254,19 @@ abstract class RegexEval extends CallExpr {
|
||||
RegexUseFlow::flow(regexCreation, DataFlow::exprNode(this.getRegexInput()))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a parse mode that is set at this evaluation (in at least one path
|
||||
* from the creation of the regular expression object).
|
||||
*/
|
||||
RegexParseMode getAParseMode() {
|
||||
exists(DataFlow::Node setNode |
|
||||
// parse mode flag is set
|
||||
any(RegexAdditionalFlowStep s).setsParseMode(setNode, result, true) and
|
||||
// reaches this eval
|
||||
RegexParseModeFlow::flow(setNode, DataFlow::exprNode(this.getRegexInput()))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
*/
|
||||
|
||||
import swift
|
||||
private import RegexTracking
|
||||
private import codeql.swift.regex.Regex
|
||||
|
||||
/**
|
||||
* A `Expr` containing a regular expression term, that is, either
|
||||
@@ -317,14 +319,24 @@ abstract class RegExp extends Expr {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a mode (if any) of this regular expression. Can be any of:
|
||||
* Gets a mode (if any) of this regular expression in any evaluation. Can be
|
||||
* any of:
|
||||
* IGNORECASE
|
||||
* VERBOSE
|
||||
* DOTALL
|
||||
* MULTILINE
|
||||
* UNICODE
|
||||
*/
|
||||
string getAMode() { result = this.getModeFromPrefix() }
|
||||
string getAMode() {
|
||||
// mode flags from inside the regex string
|
||||
result = this.getModeFromPrefix()
|
||||
or
|
||||
// mode flags applied to the regex object before evaluation
|
||||
exists(RegexEval e |
|
||||
e.getARegex() = this and
|
||||
result = e.getAParseMode().getName()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th character could not be parsed.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import swift
|
||||
import codeql.swift.regex.RegexTreeView
|
||||
private import codeql.swift.dataflow.DataFlow
|
||||
import codeql.swift.dataflow.DataFlow
|
||||
private import ParseRegex
|
||||
private import codeql.swift.regex.Regex
|
||||
|
||||
@@ -37,7 +37,6 @@ private module RegexUseConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
// creation of the regex
|
||||
node instanceof RegexCreation
|
||||
// TODO: track parse mode flags.
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
@@ -46,9 +45,60 @@ private module RegexUseConfig implements DataFlow::ConfigSig {
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// TODO: flow through regex methods that return a modified regex.
|
||||
none()
|
||||
any(RegexAdditionalFlowStep s).step(nodeFrom, nodeTo)
|
||||
}
|
||||
}
|
||||
|
||||
module RegexUseFlow = DataFlow::Global<RegexUseConfig>;
|
||||
|
||||
/**
|
||||
* A data flow configuration for tracking regular expression parse mode
|
||||
* flags from wherever they are created or set through to regular expression
|
||||
* evaluation. The flow state encodes which parse mode flag was set.
|
||||
*/
|
||||
private module RegexParseModeConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState = RegexParseMode;
|
||||
|
||||
predicate isSource(DataFlow::Node node, FlowState flowstate) {
|
||||
// parse mode flag is set
|
||||
any(RegexAdditionalFlowStep s).setsParseMode(node, flowstate, true)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node, FlowState flowstate) {
|
||||
// evaluation of the regex
|
||||
node.asExpr() = any(RegexEval eval).getRegexInput() and
|
||||
exists(flowstate)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { none() }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node, FlowState flowstate) {
|
||||
// parse mode flag is unset
|
||||
any(RegexAdditionalFlowStep s).setsParseMode(node, flowstate, false)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// flow through array construction
|
||||
exists(ArrayExpr arr |
|
||||
nodeFrom.asExpr() = arr.getAnElement() and
|
||||
nodeTo.asExpr() = arr
|
||||
)
|
||||
or
|
||||
// flow through regex creation
|
||||
exists(RegexCreation create |
|
||||
nodeFrom = create.getOptionsInput() and
|
||||
nodeTo = create
|
||||
)
|
||||
or
|
||||
// additional flow steps for regular expression objects
|
||||
any(RegexAdditionalFlowStep s).step(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node nodeFrom, FlowState flowstateFrom, DataFlow::Node nodeTo, FlowState flowStateTo
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
module RegexParseModeFlow = DataFlow::GlobalWithState<RegexParseModeConfig>;
|
||||
|
||||
@@ -6327,84 +6327,72 @@ redos_variants.swift:
|
||||
# 579| [RegExpConstant, RegExpNormalChar] c
|
||||
|
||||
regex.swift:
|
||||
# 110| [RegExpDot] .
|
||||
# 111| [RegExpDot] .
|
||||
|
||||
# 110| [RegExpStar] .*
|
||||
# 111| [RegExpStar] .*
|
||||
#-----| 0 -> [RegExpDot] .
|
||||
|
||||
# 132| [RegExpDot] .
|
||||
# 133| [RegExpDot] .
|
||||
|
||||
# 132| [RegExpStar] .*
|
||||
# 133| [RegExpStar] .*
|
||||
#-----| 0 -> [RegExpDot] .
|
||||
|
||||
# 149| [RegExpDot] .
|
||||
# 150| [RegExpDot] .
|
||||
|
||||
# 149| [RegExpStar] .*
|
||||
# 150| [RegExpStar] .*
|
||||
#-----| 0 -> [RegExpDot] .
|
||||
|
||||
# 149| [RegExpDot] .
|
||||
# 150| [RegExpDot] .
|
||||
|
||||
# 149| [RegExpPlus] .+
|
||||
# 150| [RegExpPlus] .+
|
||||
#-----| 0 -> [RegExpDot] .
|
||||
|
||||
# 156| [RegExpGroup] ([\w.]+)
|
||||
# 157| [RegExpGroup] ([\w.]+)
|
||||
#-----| 0 -> [RegExpPlus] [\w.]+
|
||||
|
||||
# 156| [RegExpStar] ([\w.]+)*
|
||||
# 157| [RegExpStar] ([\w.]+)*
|
||||
#-----| 0 -> [RegExpGroup] ([\w.]+)
|
||||
|
||||
# 156| [RegExpCharacterClass] [\w.]
|
||||
# 157| [RegExpCharacterClass] [\w.]
|
||||
#-----| 0 -> [RegExpCharacterClassEscape] \w
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] .
|
||||
|
||||
# 156| [RegExpPlus] [\w.]+
|
||||
# 157| [RegExpPlus] [\w.]+
|
||||
#-----| 0 -> [RegExpCharacterClass] [\w.]
|
||||
|
||||
# 156| [RegExpCharacterClassEscape] \w
|
||||
# 157| [RegExpCharacterClassEscape] \w
|
||||
|
||||
# 156| [RegExpConstant, RegExpNormalChar] .
|
||||
# 157| [RegExpConstant, RegExpNormalChar] .
|
||||
|
||||
# 163| [RegExpConstant, RegExpNormalChar]
|
||||
# 163|
|
||||
|
||||
# 164| [RegExpConstant, RegExpEscape] \n
|
||||
# 164| [RegExpConstant, RegExpNormalChar]
|
||||
# 164|
|
||||
|
||||
# 165| [RegExpConstant, RegExpEscape] \n
|
||||
|
||||
# 175| [RegExpConstant, RegExpNormalChar] aa
|
||||
# 166| [RegExpConstant, RegExpEscape] \n
|
||||
|
||||
# 175| [RegExpAlt] aa|bb
|
||||
# 176| [RegExpConstant, RegExpNormalChar] aa
|
||||
|
||||
# 176| [RegExpAlt] aa|bb
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] aa
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] bb
|
||||
|
||||
# 175| [RegExpConstant, RegExpNormalChar] bb
|
||||
# 176| [RegExpConstant, RegExpNormalChar] bb
|
||||
|
||||
# 179| [RegExpConstant, RegExpNormalChar] aa
|
||||
# 180| [RegExpConstant, RegExpNormalChar] aa
|
||||
|
||||
# 179| [RegExpAlt] aa|
|
||||
# 179| bb
|
||||
# 180| [RegExpAlt] aa|
|
||||
# 180| bb
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] aa
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar]
|
||||
#-----| bb
|
||||
|
||||
# 179| [RegExpConstant, RegExpNormalChar]
|
||||
# 179| bb
|
||||
# 180| [RegExpConstant, RegExpNormalChar]
|
||||
# 180| bb
|
||||
|
||||
# 187| [RegExpCharacterClass] [a-z]
|
||||
# 188| [RegExpCharacterClass] [a-z]
|
||||
#-----| 0 -> [RegExpCharacterRange] a-z
|
||||
|
||||
# 187| [RegExpConstant, RegExpNormalChar] a
|
||||
|
||||
# 187| [RegExpCharacterRange] a-z
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] a
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] z
|
||||
|
||||
# 187| [RegExpConstant, RegExpNormalChar] z
|
||||
|
||||
# 188| [RegExpCharacterClass] [a-zA-Z]
|
||||
#-----| 0 -> [RegExpCharacterRange] a-z
|
||||
#-----| 1 -> [RegExpCharacterRange] A-Z
|
||||
|
||||
# 188| [RegExpConstant, RegExpNormalChar] a
|
||||
|
||||
# 188| [RegExpCharacterRange] a-z
|
||||
@@ -6413,189 +6401,208 @@ regex.swift:
|
||||
|
||||
# 188| [RegExpConstant, RegExpNormalChar] z
|
||||
|
||||
# 188| [RegExpConstant, RegExpNormalChar] A
|
||||
# 189| [RegExpCharacterClass] [a-zA-Z]
|
||||
#-----| 0 -> [RegExpCharacterRange] a-z
|
||||
#-----| 1 -> [RegExpCharacterRange] A-Z
|
||||
|
||||
# 188| [RegExpCharacterRange] A-Z
|
||||
# 189| [RegExpConstant, RegExpNormalChar] a
|
||||
|
||||
# 189| [RegExpCharacterRange] a-z
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] a
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] z
|
||||
|
||||
# 189| [RegExpConstant, RegExpNormalChar] z
|
||||
|
||||
# 189| [RegExpConstant, RegExpNormalChar] A
|
||||
|
||||
# 189| [RegExpCharacterRange] A-Z
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] A
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] Z
|
||||
|
||||
# 188| [RegExpConstant, RegExpNormalChar] Z
|
||||
# 189| [RegExpConstant, RegExpNormalChar] Z
|
||||
|
||||
# 191| [RegExpCharacterClass] [a-]
|
||||
# 192| [RegExpCharacterClass] [a-]
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] a
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] -
|
||||
|
||||
# 191| [RegExpConstant, RegExpNormalChar] a
|
||||
|
||||
# 191| [RegExpConstant, RegExpNormalChar] -
|
||||
|
||||
# 192| [RegExpCharacterClass] [-a]
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] -
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] a
|
||||
# 192| [RegExpConstant, RegExpNormalChar] a
|
||||
|
||||
# 192| [RegExpConstant, RegExpNormalChar] -
|
||||
|
||||
# 192| [RegExpConstant, RegExpNormalChar] a
|
||||
|
||||
# 193| [RegExpCharacterClass] [-]
|
||||
# 193| [RegExpCharacterClass] [-a]
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] -
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] a
|
||||
|
||||
# 193| [RegExpConstant, RegExpNormalChar] -
|
||||
|
||||
# 194| [RegExpCharacterClass] [*]
|
||||
# 193| [RegExpConstant, RegExpNormalChar] a
|
||||
|
||||
# 194| [RegExpCharacterClass] [-]
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] -
|
||||
|
||||
# 194| [RegExpConstant, RegExpNormalChar] -
|
||||
|
||||
# 195| [RegExpCharacterClass] [*]
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] *
|
||||
|
||||
# 194| [RegExpConstant, RegExpNormalChar] *
|
||||
# 195| [RegExpConstant, RegExpNormalChar] *
|
||||
|
||||
# 195| [RegExpCharacterClass] [^a]
|
||||
# 196| [RegExpCharacterClass] [^a]
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] a
|
||||
|
||||
# 195| [RegExpConstant, RegExpNormalChar] a
|
||||
|
||||
# 196| [RegExpCharacterClass] [a^]
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] a
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] ^
|
||||
|
||||
# 196| [RegExpConstant, RegExpNormalChar] a
|
||||
|
||||
# 196| [RegExpConstant, RegExpNormalChar] ^
|
||||
# 197| [RegExpCharacterClass] [a^]
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] a
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] ^
|
||||
|
||||
# 197| [RegExpCharacterClass] [\\]
|
||||
# 197| [RegExpConstant, RegExpNormalChar] a
|
||||
|
||||
# 197| [RegExpConstant, RegExpNormalChar] ^
|
||||
|
||||
# 198| [RegExpCharacterClass] [\\]
|
||||
#-----| 0 -> [RegExpConstant, RegExpEscape] \\
|
||||
|
||||
# 197| [RegExpConstant, RegExpEscape] \\
|
||||
|
||||
# 198| [RegExpCharacterClass] [\\\]]
|
||||
#-----| 0 -> [RegExpConstant, RegExpEscape] \\
|
||||
#-----| 1 -> [RegExpConstant, RegExpEscape] \]
|
||||
|
||||
# 198| [RegExpConstant, RegExpEscape] \\
|
||||
|
||||
# 198| [RegExpConstant, RegExpEscape] \]
|
||||
# 199| [RegExpCharacterClass] [\\\]]
|
||||
#-----| 0 -> [RegExpConstant, RegExpEscape] \\
|
||||
#-----| 1 -> [RegExpConstant, RegExpEscape] \]
|
||||
|
||||
# 199| [RegExpCharacterClass] [:]
|
||||
# 199| [RegExpConstant, RegExpEscape] \\
|
||||
|
||||
# 199| [RegExpConstant, RegExpEscape] \]
|
||||
|
||||
# 200| [RegExpCharacterClass] [:]
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] :
|
||||
|
||||
# 199| [RegExpConstant, RegExpNormalChar] :
|
||||
# 200| [RegExpConstant, RegExpNormalChar] :
|
||||
|
||||
# 200| [RegExpNamedCharacterProperty] [:digit:]
|
||||
# 201| [RegExpNamedCharacterProperty] [:digit:]
|
||||
|
||||
# 201| [RegExpNamedCharacterProperty] [:alnum:]
|
||||
# 202| [RegExpNamedCharacterProperty] [:alnum:]
|
||||
|
||||
# 204| [RegExpCharacterClass] []a]
|
||||
# 205| [RegExpCharacterClass] []a]
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] ]
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] a
|
||||
|
||||
# 204| [RegExpConstant, RegExpNormalChar] ]
|
||||
# 205| [RegExpConstant, RegExpNormalChar] ]
|
||||
|
||||
# 204| [RegExpConstant, RegExpNormalChar] a
|
||||
# 205| [RegExpConstant, RegExpNormalChar] a
|
||||
|
||||
# 205| [RegExpNamedCharacterProperty] [:aaaaa:]
|
||||
# 206| [RegExpNamedCharacterProperty] [:aaaaa:]
|
||||
|
||||
# 209| [RegExpGroup] (?i)
|
||||
# 211| [RegExpGroup] (?i)
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] i
|
||||
|
||||
# 209| [RegExpSequence] (?i)abc
|
||||
# 211| [RegExpSequence] (?i)abc
|
||||
#-----| 0 -> [RegExpGroup] (?i)
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 209| [RegExpConstant, RegExpNormalChar] i
|
||||
|
||||
# 209| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 210| [RegExpGroup] (?s)
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] s
|
||||
|
||||
# 210| [RegExpSequence] (?s)abc
|
||||
#-----| 0 -> [RegExpGroup] (?s)
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 210| [RegExpConstant, RegExpNormalChar] s
|
||||
|
||||
# 210| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 211| [RegExpGroup] (?is)
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] is
|
||||
|
||||
# 211| [RegExpSequence] (?is)abc
|
||||
#-----| 0 -> [RegExpGroup] (?is)
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 211| [RegExpConstant, RegExpNormalChar] is
|
||||
# 211| [RegExpConstant, RegExpNormalChar] i
|
||||
|
||||
# 211| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 212| [RegExpGroup] (?s)
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] s
|
||||
|
||||
# 212| [RegExpSequence] (?s)abc
|
||||
#-----| 0 -> [RegExpGroup] (?s)
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 212| [RegExpConstant, RegExpNormalChar] s
|
||||
|
||||
# 212| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 213| [RegExpGroup] (?is)
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] is
|
||||
|
||||
# 213| [RegExpSequence] (?is)abc
|
||||
#-----| 0 -> [RegExpGroup] (?is)
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 213| [RegExpConstant, RegExpNormalChar] is
|
||||
|
||||
# 213| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 214| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 215| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 216| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 217| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 219| [RegExpDot] .
|
||||
|
||||
# 219| [RegExpStar] .*
|
||||
#-----| 0 -> [RegExpDot] .
|
||||
|
||||
# 220| [RegExpDot] .
|
||||
|
||||
# 220| [RegExpStar] .*
|
||||
#-----| 0 -> [RegExpDot] .
|
||||
|
||||
# 221| [RegExpDot] .
|
||||
|
||||
# 221| [RegExpStar] .*
|
||||
#-----| 0 -> [RegExpDot] .
|
||||
|
||||
# 227| [RegExpGroup] (?i-s)
|
||||
# 214| [RegExpGroup] (?i-s)
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] i-s
|
||||
|
||||
# 227| [RegExpSequence] (?i-s)abc
|
||||
# 214| [RegExpSequence] (?i-s)abc
|
||||
#-----| 0 -> [RegExpGroup] (?i-s)
|
||||
#-----| 1 -> [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 227| [RegExpConstant, RegExpNormalChar] i-s
|
||||
# 214| [RegExpConstant, RegExpNormalChar] i-s
|
||||
|
||||
# 227| [RegExpConstant, RegExpNormalChar] abc
|
||||
# 214| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 230| [RegExpConstant, RegExpNormalChar] abc
|
||||
# 217| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 230| [RegExpSequence] abc(?i)def
|
||||
# 217| [RegExpSequence] abc(?i)def
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] abc
|
||||
#-----| 1 -> [RegExpGroup] (?i)
|
||||
#-----| 2 -> [RegExpConstant, RegExpNormalChar] def
|
||||
|
||||
# 230| [RegExpGroup] (?i)
|
||||
# 217| [RegExpGroup] (?i)
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] i
|
||||
|
||||
# 230| [RegExpConstant, RegExpNormalChar] i
|
||||
# 217| [RegExpConstant, RegExpNormalChar] i
|
||||
|
||||
# 230| [RegExpConstant, RegExpNormalChar] def
|
||||
# 217| [RegExpConstant, RegExpNormalChar] def
|
||||
|
||||
# 231| [RegExpConstant, RegExpNormalChar] abc
|
||||
# 218| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 231| [RegExpSequence] abc(?i:def)ghi
|
||||
# 218| [RegExpSequence] abc(?i:def)ghi
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] abc
|
||||
#-----| 1 -> [RegExpGroup] (?i:def)
|
||||
#-----| 2 -> [RegExpConstant, RegExpNormalChar] ghi
|
||||
|
||||
# 231| [RegExpGroup] (?i:def)
|
||||
# 218| [RegExpGroup] (?i:def)
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] i:def
|
||||
|
||||
# 231| [RegExpConstant, RegExpNormalChar] i:def
|
||||
# 218| [RegExpConstant, RegExpNormalChar] i:def
|
||||
|
||||
# 231| [RegExpConstant, RegExpNormalChar] ghi
|
||||
# 218| [RegExpConstant, RegExpNormalChar] ghi
|
||||
|
||||
# 232| [RegExpGroup] (?i)
|
||||
# 219| [RegExpGroup] (?i)
|
||||
#-----| 0 -> [RegExpConstant, RegExpNormalChar] i
|
||||
|
||||
# 232| [RegExpConstant, RegExpNormalChar] i
|
||||
# 219| [RegExpConstant, RegExpNormalChar] i
|
||||
|
||||
# 232| [RegExpConstant, RegExpNormalChar] abc
|
||||
# 219| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 232| [RegExpConstant, RegExpNormalChar] -i
|
||||
# 219| [RegExpConstant, RegExpNormalChar] -i
|
||||
|
||||
# 232| [RegExpConstant, RegExpNormalChar] def
|
||||
# 219| [RegExpConstant, RegExpNormalChar] def
|
||||
|
||||
# 222| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 223| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 224| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 225| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 226| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 227| [RegExpConstant, RegExpNormalChar] abc
|
||||
|
||||
# 230| [RegExpDot] .
|
||||
|
||||
# 230| [RegExpStar] .*
|
||||
#-----| 0 -> [RegExpDot] .
|
||||
|
||||
# 231| [RegExpDot] .
|
||||
|
||||
# 231| [RegExpStar] .*
|
||||
#-----| 0 -> [RegExpDot] .
|
||||
|
||||
# 232| [RegExpDot] .
|
||||
|
||||
# 232| [RegExpStar] .*
|
||||
#-----| 0 -> [RegExpDot] .
|
||||
|
||||
# 235| [RegExpDot] .
|
||||
|
||||
# 235| [RegExpStar] .*
|
||||
#-----| 0 -> [RegExpDot] .
|
||||
|
||||
@@ -17,8 +17,9 @@ struct Regex<Output> : RegexComponent {
|
||||
|
||||
init(_ pattern: String) throws where Output == AnyRegexOutput { }
|
||||
|
||||
func ignoresCase(_ ignoresCase: Bool = true) -> Regex<Regex<Output>.RegexOutput> { return self }
|
||||
func dotMatchesNewlines(_ dotMatchesNewlines: Bool = true) -> Regex<Regex<Output>.RegexOutput> { return self }
|
||||
func ignoresCase(_ ignoresCase: Bool = true) -> Regex<Regex<Output>.RegexOutput> { return 0 as! Self }
|
||||
func dotMatchesNewlines(_ dotMatchesNewlines: Bool = true) -> Regex<Regex<Output>.RegexOutput> { return 0 as! Self }
|
||||
func anchorsMatchLineEndings(_ matchLineEndings: Bool = true) -> Regex<Regex<Output>.RegexOutput> { return 0 as! Self }
|
||||
|
||||
func firstMatch(in string: String) throws -> Regex<Output>.Match? { return nil }
|
||||
func prefixMatch(in string: String) throws -> Regex<Output>.Match? { return nil }
|
||||
@@ -206,28 +207,38 @@ func myRegexpMethodsTests(b: Bool, str_unknown: String) throws {
|
||||
|
||||
// --- parse modes ---
|
||||
|
||||
// parse modes encoded in the string
|
||||
_ = try Regex("(?i)abc").firstMatch(in: input) // $ input=input modes=IGNORECASE regex=(?i)abc
|
||||
_ = try Regex("(?s)abc").firstMatch(in: input) // $ input=input modes=DOTALL regex=(?s)abc
|
||||
_ = try Regex("(?is)abc").firstMatch(in: input) // $ input=input modes="DOTALL | IGNORECASE" regex=(?is)abc
|
||||
|
||||
_ = try Regex("abc").dotMatchesNewlines(true).firstMatch(in: input) // $ input=input regex=abc MISSING: modes=DOTALL
|
||||
_ = try Regex("abc").dotMatchesNewlines(false).firstMatch(in: input) // $ input=input regex=abc
|
||||
_ = try Regex("abc").dotMatchesNewlines(true).dotMatchesNewlines(false).firstMatch(in: input) // $ input=input regex=abc
|
||||
_ = try Regex("abc").dotMatchesNewlines(false).dotMatchesNewlines(true).firstMatch(in: input) // $ input=input regex=abc MISSING: modes=DOTALL
|
||||
_ = try Regex("abc").dotMatchesNewlines().ignoresCase().firstMatch(in: input) // $ input=input regex=abc MISSING: modes="DOTALL | IGNORECASE"
|
||||
|
||||
_ = try NSRegularExpression(pattern: ".*", options: .caseInsensitive).firstMatch(in: input, range: NSMakeRange(0, input.utf16.count)) // $ regex=.* input=input MISSING: modes=IGNORECASE
|
||||
_ = try NSRegularExpression(pattern: ".*", options: .dotMatchesLineSeparators).firstMatch(in: input, range: NSMakeRange(0, input.utf16.count)) // $ regex=.* input=input MISSING: modes=DOTALL
|
||||
_ = try NSRegularExpression(pattern: ".*", options: [.caseInsensitive, .dotMatchesLineSeparators]).firstMatch(in: input, range: NSMakeRange(0, input.utf16.count)) // $ regex=.* input=input MISSING: modes="DOTALL | IGNORECASE"
|
||||
|
||||
_ = input.replacingOccurrences(of: ".*", with: "", options: [.regularExpression, .caseInsensitive]) // $ MISSING: regex=.* input=input modes=IGNORECASE
|
||||
|
||||
_ = NSString(string: "abc").replacingOccurrences(of: ".*", with: "", options: [.regularExpression, .caseInsensitive], range: NSMakeRange(0, inputNS.length)) // $ MISSING: regex=.* input=inputNS modes=IGNORECASE
|
||||
|
||||
_ = try Regex("(?i-s)abc").firstMatch(in: input) // $ input=input regex=(?i-s)abc MISSING: modes=IGNORECASE SPURIOUS: modes="DOTALL | IGNORECASE"
|
||||
|
||||
// these cases use parse modes on localized areas of the regex, which we don't currently support
|
||||
_ = try Regex("abc(?i)def").firstMatch(in: input) // $ input=input modes=IGNORECASE regex=abc(?i)def
|
||||
_ = try Regex("abc(?i:def)ghi").firstMatch(in: input) // $ input=input modes=IGNORECASE regex=abc(?i:def)ghi
|
||||
_ = try Regex("(?i)abc(?-i)def").firstMatch(in: input) // $ input=input modes=IGNORECASE regex=(?i)abc(?-i)def SPURIOUS: hasParseFailure=
|
||||
|
||||
// parse modes set through Regex
|
||||
_ = try Regex("abc").dotMatchesNewlines(true).firstMatch(in: input) // $ input=input regex=abc modes=DOTALL
|
||||
_ = try Regex("abc").dotMatchesNewlines(false).firstMatch(in: input) // $ input=input regex=abc
|
||||
_ = try Regex("abc").dotMatchesNewlines(true).dotMatchesNewlines(false).firstMatch(in: input) // $ input=input regex=abc
|
||||
_ = try Regex("abc").dotMatchesNewlines(false).dotMatchesNewlines(true).firstMatch(in: input) // $ input=input regex=abc modes=DOTALL
|
||||
_ = try Regex("abc").dotMatchesNewlines().ignoresCase().firstMatch(in: input) // $ input=input regex=abc modes="DOTALL | IGNORECASE"
|
||||
_ = try Regex("abc").anchorsMatchLineEndings().firstMatch(in: input) // $ input=input regex=abc modes=MULTILINE
|
||||
|
||||
// parse modes set through NSRegularExpression
|
||||
_ = try NSRegularExpression(pattern: ".*", options: .caseInsensitive).firstMatch(in: input, range: NSMakeRange(0, input.utf16.count)) // $ regex=.* input=input modes=IGNORECASE
|
||||
_ = try NSRegularExpression(pattern: ".*", options: .dotMatchesLineSeparators).firstMatch(in: input, range: NSMakeRange(0, input.utf16.count)) // $ regex=.* input=input modes=DOTALL
|
||||
_ = try NSRegularExpression(pattern: ".*", options: [.caseInsensitive, .dotMatchesLineSeparators]).firstMatch(in: input, range: NSMakeRange(0, input.utf16.count)) // $ regex=.* input=input modes="DOTALL | IGNORECASE"
|
||||
|
||||
let myOptions1 : NSRegularExpression.Options = [.caseInsensitive, .dotMatchesLineSeparators]
|
||||
_ = try NSRegularExpression(pattern: ".*", options: myOptions1).firstMatch(in: input, range: NSMakeRange(0, input.utf16.count)) // $ regex=.* input=input modes="DOTALL | IGNORECASE"
|
||||
|
||||
// parse modes set through other methods
|
||||
|
||||
let myOptions2 : NSString.CompareOptions = [.regularExpression, .caseInsensitive]
|
||||
_ = input.replacingOccurrences(of: ".*", with: "", options: [.regularExpression, .caseInsensitive]) // $ MISSING: regex=.* input=input modes=IGNORECASE
|
||||
_ = input.replacingOccurrences(of: ".*", with: "", options: myOptions2) // $ MISSING: regex=.* input=input modes=IGNORECASE
|
||||
_ = NSString(string: "abc").replacingOccurrences(of: ".*", with: "", options: [.regularExpression, .caseInsensitive], range: NSMakeRange(0, inputNS.length)) // $ MISSING: regex=.* input=inputNS modes=IGNORECASE
|
||||
_ = NSString(string: "abc").replacingOccurrences(of: ".*", with: "", options: myOptions2, range: NSMakeRange(0, inputNS.length)) // $ MISSING: regex=.* input=inputNS modes=IGNORECASE
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user