mirror of
https://github.com/github/codeql.git
synced 2026-04-29 18:55:14 +02:00
Merge pull request #7283 from aibaars/ruby-pattern-matching-cfg
Ruby: pattern matching: CFG
This commit is contained in:
@@ -246,8 +246,6 @@ private module Cached {
|
||||
explicitAssignmentNode(g, _)
|
||||
or
|
||||
casePattern(g)
|
||||
or
|
||||
classReferencePattern(g)
|
||||
)
|
||||
} or
|
||||
TScopeResolutionMethodCall(Ruby::ScopeResolution g, Ruby::Identifier i) {
|
||||
@@ -293,8 +291,6 @@ private module Cached {
|
||||
explicitAssignmentNode(g, _)
|
||||
or
|
||||
casePattern(g)
|
||||
or
|
||||
classReferencePattern(g)
|
||||
} or
|
||||
TTokenMethodName(MethodName::Token g) { MethodName::range(g) } or
|
||||
TTokenSuperCall(Ruby::Super g) { vcall(g) } or
|
||||
|
||||
@@ -48,13 +48,7 @@ predicate casePattern(Ruby::AstNode node) {
|
||||
node = any(Ruby::KeywordPattern parent).getValue()
|
||||
or
|
||||
node = any(Ruby::ParenthesizedPattern parent).getChild()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is a class reference used in an
|
||||
* array, find, or hash pattern.
|
||||
*/
|
||||
predicate classReferencePattern(Ruby::AstNode node) {
|
||||
or
|
||||
node = any(Ruby::ArrayPattern p).getClass()
|
||||
or
|
||||
node = any(Ruby::FindPattern p).getClass()
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
private import codeql.ruby.AST
|
||||
private import codeql.ruby.ast.internal.AST
|
||||
private import codeql.ruby.ast.internal.Control
|
||||
private import codeql.ruby.controlflow.ControlFlowGraph
|
||||
private import ControlFlowGraphImpl
|
||||
private import NonReturning
|
||||
@@ -27,6 +28,10 @@ private newtype TCompletion =
|
||||
outer instanceof NonNestedNormalCompletion and
|
||||
nestLevel = 0
|
||||
or
|
||||
inner instanceof TBooleanCompletion and
|
||||
outer instanceof TMatchingCompletion and
|
||||
nestLevel = 0
|
||||
or
|
||||
inner instanceof NormalCompletion and
|
||||
nestedEnsureCompletion(outer, nestLevel)
|
||||
}
|
||||
@@ -81,8 +86,9 @@ private predicate mayRaise(Call c) {
|
||||
|
||||
/** A completion of a statement or an expression. */
|
||||
abstract class Completion extends TCompletion {
|
||||
/** Holds if this completion is valid for node `n`. */
|
||||
predicate isValidFor(AstNode n) {
|
||||
private predicate isValidForSpecific(AstNode n) {
|
||||
exists(AstNode other | n = other.getDesugared() and this.isValidForSpecific(other))
|
||||
or
|
||||
this = n.(NonReturningCall).getACompletion()
|
||||
or
|
||||
completionIsValidForStmt(n, this)
|
||||
@@ -98,15 +104,26 @@ abstract class Completion extends TCompletion {
|
||||
mustHaveMatchingCompletion(n) and
|
||||
this = TMatchingCompletion(_)
|
||||
or
|
||||
n = any(RescueModifierExpr parent).getBody() and this = TRaiseCompletion()
|
||||
n = any(RescueModifierExpr parent).getBody() and
|
||||
this = [TSimpleCompletion().(TCompletion), TRaiseCompletion()]
|
||||
or
|
||||
mayRaise(n) and
|
||||
this = TRaiseCompletion()
|
||||
(
|
||||
mayRaise(n)
|
||||
or
|
||||
n instanceof CaseMatch and not exists(n.(CaseExpr).getElseBranch())
|
||||
) and
|
||||
(
|
||||
this = TRaiseCompletion()
|
||||
or
|
||||
this = TSimpleCompletion() and not n instanceof NonReturningCall
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this completion is valid for node `n`. */
|
||||
predicate isValidFor(AstNode n) {
|
||||
this.isValidForSpecific(n)
|
||||
or
|
||||
not n instanceof NonReturningCall and
|
||||
not completionIsValidForStmt(n, _) and
|
||||
not mustHaveBooleanCompletion(n) and
|
||||
not mustHaveMatchingCompletion(n) and
|
||||
not any(Completion c).isValidForSpecific(n) and
|
||||
this = TSimpleCompletion()
|
||||
}
|
||||
|
||||
@@ -172,6 +189,8 @@ private predicate inBooleanContext(AstNode n) {
|
||||
or
|
||||
n = any(ConditionalLoop parent).getCondition()
|
||||
or
|
||||
n = any(InClause parent).getCondition()
|
||||
or
|
||||
exists(LogicalAndExpr parent |
|
||||
n = parent.getLeftOperand()
|
||||
or
|
||||
@@ -218,6 +237,10 @@ private predicate inMatchingContext(AstNode n) {
|
||||
w.getPattern(_) = n
|
||||
)
|
||||
or
|
||||
n instanceof CasePattern
|
||||
or
|
||||
n = any(VariableReferencePattern p).getVariableAccess()
|
||||
or
|
||||
n.(Trees::DefaultValueParameterTree).hasDefaultValue()
|
||||
}
|
||||
|
||||
@@ -241,7 +264,7 @@ class SimpleCompletion extends NonNestedNormalCompletion, TSimpleCompletion {
|
||||
* the successor. Either a Boolean completion (`BooleanCompletion`), or a matching
|
||||
* completion (`MatchingCompletion`).
|
||||
*/
|
||||
abstract class ConditionalCompletion extends NonNestedNormalCompletion {
|
||||
abstract class ConditionalCompletion extends NormalCompletion {
|
||||
boolean value;
|
||||
|
||||
bindingset[value]
|
||||
@@ -255,7 +278,7 @@ abstract class ConditionalCompletion extends NonNestedNormalCompletion {
|
||||
* A completion that represents evaluation of an expression
|
||||
* with a Boolean value.
|
||||
*/
|
||||
class BooleanCompletion extends ConditionalCompletion, TBooleanCompletion {
|
||||
class BooleanCompletion extends ConditionalCompletion, NonNestedNormalCompletion, TBooleanCompletion {
|
||||
BooleanCompletion() { this = TBooleanCompletion(value) }
|
||||
|
||||
/** Gets the dual Boolean completion. */
|
||||
@@ -280,10 +303,16 @@ class FalseCompletion extends BooleanCompletion {
|
||||
* A completion that represents evaluation of a matching test, for example
|
||||
* a test in a `rescue` statement.
|
||||
*/
|
||||
class MatchingCompletion extends ConditionalCompletion, TMatchingCompletion {
|
||||
MatchingCompletion() { this = TMatchingCompletion(value) }
|
||||
class MatchingCompletion extends ConditionalCompletion {
|
||||
MatchingCompletion() {
|
||||
this = TMatchingCompletion(value)
|
||||
or
|
||||
this = TNestedCompletion(_, TMatchingCompletion(value), _)
|
||||
}
|
||||
|
||||
override MatchingSuccessor getAMatchingSuccessorType() { result.getValue() = value }
|
||||
override ConditionalSuccessor getAMatchingSuccessorType() {
|
||||
this = TMatchingCompletion(result.(MatchingSuccessor).getValue())
|
||||
}
|
||||
|
||||
override string toString() { if value = true then result = "match" else result = "no-match" }
|
||||
}
|
||||
@@ -440,7 +469,9 @@ abstract class NestedCompletion extends Completion, TNestedCompletion {
|
||||
NestedCompletion() { this = TNestedCompletion(inner, outer, nestLevel) }
|
||||
|
||||
/** Gets a completion that is compatible with the inner completion. */
|
||||
abstract Completion getAnInnerCompatibleCompletion();
|
||||
Completion getAnInnerCompatibleCompletion() {
|
||||
result.getOuterCompletion() = this.getInnerCompletion()
|
||||
}
|
||||
|
||||
/** Gets the level of this nested completion. */
|
||||
final int getNestLevel() { result = nestLevel }
|
||||
@@ -483,9 +514,39 @@ class NestedEnsureCompletion extends NestedCompletion {
|
||||
|
||||
override Completion getOuterCompletion() { result = outer }
|
||||
|
||||
override Completion getAnInnerCompatibleCompletion() {
|
||||
result.getOuterCompletion() = this.getInnerCompletion()
|
||||
}
|
||||
|
||||
override SuccessorType getAMatchingSuccessorType() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A completion used for conditions in pattern matching:
|
||||
*
|
||||
* ```rb
|
||||
* in x if x == 5 then puts "five"
|
||||
* in x unless x == 4 then puts "not four"
|
||||
* ```
|
||||
*
|
||||
* The outer (Matching) completion indicates whether there is a match, and
|
||||
* the inner (Boolean) completion indicates what the condition evaluated
|
||||
* to.
|
||||
*
|
||||
* For the condition `x == 5` above, `TNestedCompletion(true, true, 0)` and
|
||||
* `TNestedCompletion(false, false, 0)` are both valid completions, while
|
||||
* `TNestedCompletion(true, false, 0)` and `TNestedCompletion(false, true, 0)`
|
||||
* are valid completions for `x == 4`.
|
||||
*/
|
||||
class NestedMatchingCompletion extends NestedCompletion, MatchingCompletion {
|
||||
NestedMatchingCompletion() {
|
||||
inner instanceof TBooleanCompletion and
|
||||
outer instanceof TMatchingCompletion
|
||||
}
|
||||
|
||||
override BooleanCompletion getInnerCompletion() { result = inner }
|
||||
|
||||
override MatchingCompletion getOuterCompletion() { result = outer }
|
||||
|
||||
override BooleanSuccessor getAMatchingSuccessorType() {
|
||||
result.getValue() = this.getInnerCompletion().getValue()
|
||||
}
|
||||
|
||||
override string toString() { result = NestedCompletion.super.toString() }
|
||||
}
|
||||
|
||||
@@ -419,6 +419,387 @@ module Trees {
|
||||
}
|
||||
}
|
||||
|
||||
private class CaseMatchTree extends PreOrderTree, CaseExpr, ASTInternal::TCaseMatch {
|
||||
final override predicate propagatesAbnormal(AstNode child) {
|
||||
child = this.getValue() or child = this.getABranch()
|
||||
}
|
||||
|
||||
final override predicate last(AstNode last, Completion c) {
|
||||
last(this.getABranch(), last, c) and
|
||||
not c.(MatchingCompletion).getValue() = false
|
||||
or
|
||||
not exists(this.getElseBranch()) and
|
||||
exists(MatchingCompletion lc, Expr lastBranch |
|
||||
lastBranch = max(int i | | this.getBranch(i) order by i) and
|
||||
lc.getValue() = false and
|
||||
last(lastBranch, last, lc) and
|
||||
c instanceof RaiseCompletion and
|
||||
not c instanceof NestedCompletion
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
|
||||
pred = this and
|
||||
first(this.getValue(), succ) and
|
||||
c instanceof SimpleCompletion
|
||||
or
|
||||
last(this.getValue(), pred, c) and
|
||||
first(this.getBranch(0), succ) and
|
||||
c instanceof SimpleCompletion
|
||||
or
|
||||
exists(int i, Expr branch | branch = this.getBranch(i) |
|
||||
last(branch, pred, c) and
|
||||
first(this.getBranch(i + 1), succ) and
|
||||
c.(MatchingCompletion).getValue() = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class PatternVariableAccessTree extends LocalVariableAccessTree, LocalVariableWriteAccess,
|
||||
CasePattern {
|
||||
final override predicate last(AstNode last, Completion c) {
|
||||
super.last(last, c) and
|
||||
c.(MatchingCompletion).getValue() = true
|
||||
}
|
||||
}
|
||||
|
||||
private class ArrayPatternTree extends ControlFlowTree, ArrayPattern {
|
||||
final override predicate propagatesAbnormal(AstNode child) {
|
||||
child = this.getClass() or
|
||||
child = this.getPrefixElement(_) or
|
||||
child = this.getRestVariableAccess() or
|
||||
child = this.getSuffixElement(_)
|
||||
}
|
||||
|
||||
final override predicate first(AstNode first) {
|
||||
first(this.getClass(), first)
|
||||
or
|
||||
not exists(this.getClass()) and first = this
|
||||
}
|
||||
|
||||
final override predicate last(AstNode last, Completion c) {
|
||||
c.(MatchingCompletion).getValue() = false and
|
||||
(
|
||||
last = this and
|
||||
c.isValidFor(this)
|
||||
or
|
||||
exists(AstNode node |
|
||||
node = this.getClass() or
|
||||
node = this.getPrefixElement(_) or
|
||||
node = this.getSuffixElement(_)
|
||||
|
|
||||
last(node, last, c)
|
||||
)
|
||||
)
|
||||
or
|
||||
c.(MatchingCompletion).getValue() = true and
|
||||
last = this and
|
||||
c.isValidFor(this) and
|
||||
not exists(this.getPrefixElement(_)) and
|
||||
not exists(this.getRestVariableAccess())
|
||||
or
|
||||
c.(MatchingCompletion).getValue() = true and
|
||||
last(max(int i | | this.getPrefixElement(i) order by i), last, c) and
|
||||
not exists(this.getRestVariableAccess())
|
||||
or
|
||||
last(this.getRestVariableAccess(), last, c) and
|
||||
not exists(this.getSuffixElement(_))
|
||||
or
|
||||
c.(MatchingCompletion).getValue() = true and
|
||||
last(max(int i | | this.getSuffixElement(i) order by i), last, c)
|
||||
}
|
||||
|
||||
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
|
||||
last(this.getClass(), pred, c) and
|
||||
succ = this and
|
||||
c.(MatchingCompletion).getValue() = true
|
||||
or
|
||||
exists(AstNode next |
|
||||
pred = this and
|
||||
c.(MatchingCompletion).getValue() = true and
|
||||
first(next, succ)
|
||||
|
|
||||
next = this.getPrefixElement(0)
|
||||
or
|
||||
not exists(this.getPrefixElement(_)) and
|
||||
next = this.getRestVariableAccess()
|
||||
)
|
||||
or
|
||||
last(max(int i | | this.getPrefixElement(i) order by i), pred, c) and
|
||||
first(this.getRestVariableAccess(), succ) and
|
||||
c.(MatchingCompletion).getValue() = true
|
||||
or
|
||||
exists(int i |
|
||||
last(this.getPrefixElement(i), pred, c) and
|
||||
first(this.getPrefixElement(i + 1), succ) and
|
||||
c.(MatchingCompletion).getValue() = true
|
||||
)
|
||||
or
|
||||
last(this.getRestVariableAccess(), pred, c) and
|
||||
first(this.getSuffixElement(0), succ) and
|
||||
c instanceof SimpleCompletion
|
||||
or
|
||||
exists(int i |
|
||||
last(this.getSuffixElement(i), pred, c) and
|
||||
first(this.getSuffixElement(i + 1), succ) and
|
||||
c.(MatchingCompletion).getValue() = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class FindPatternTree extends ControlFlowTree, FindPattern {
|
||||
final override predicate propagatesAbnormal(AstNode child) {
|
||||
child = this.getClass() or
|
||||
child = this.getPrefixVariableAccess() or
|
||||
child = this.getElement(_) or
|
||||
child = this.getSuffixVariableAccess()
|
||||
}
|
||||
|
||||
final override predicate first(AstNode first) {
|
||||
first(this.getClass(), first)
|
||||
or
|
||||
not exists(this.getClass()) and first = this
|
||||
}
|
||||
|
||||
final override predicate last(AstNode last, Completion c) {
|
||||
last(this.getSuffixVariableAccess(), last, c)
|
||||
or
|
||||
last(max(int i | | this.getElement(i) order by i), last, c) and
|
||||
not exists(this.getSuffixVariableAccess())
|
||||
or
|
||||
c.(MatchingCompletion).getValue() = false and
|
||||
(
|
||||
last = this and
|
||||
c.isValidFor(this)
|
||||
or
|
||||
exists(AstNode node | node = this.getClass() or node = this.getElement(_) |
|
||||
last(node, last, c)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
|
||||
last(this.getClass(), pred, c) and
|
||||
succ = this and
|
||||
c.(MatchingCompletion).getValue() = true
|
||||
or
|
||||
exists(AstNode next |
|
||||
pred = this and
|
||||
c.(MatchingCompletion).getValue() = true and
|
||||
first(next, succ)
|
||||
|
|
||||
next = this.getPrefixVariableAccess()
|
||||
or
|
||||
not exists(this.getPrefixVariableAccess()) and
|
||||
next = this.getElement(0)
|
||||
)
|
||||
or
|
||||
last(this.getPrefixVariableAccess(), pred, c) and
|
||||
first(this.getElement(0), succ) and
|
||||
c instanceof SimpleCompletion
|
||||
or
|
||||
c.(MatchingCompletion).getValue() = true and
|
||||
exists(int i |
|
||||
last(this.getElement(i), pred, c) and
|
||||
first(this.getElement(i + 1), succ)
|
||||
)
|
||||
or
|
||||
c.(MatchingCompletion).getValue() = true and
|
||||
last(max(int i | | this.getElement(i) order by i), pred, c) and
|
||||
first(this.getSuffixVariableAccess(), succ)
|
||||
}
|
||||
}
|
||||
|
||||
private class HashPatternTree extends ControlFlowTree, HashPattern {
|
||||
final override predicate propagatesAbnormal(AstNode child) {
|
||||
child = this.getClass() or
|
||||
child = this.getValue(_) or
|
||||
child = this.getRestVariableAccess()
|
||||
}
|
||||
|
||||
final override predicate first(AstNode first) {
|
||||
first(this.getClass(), first)
|
||||
or
|
||||
not exists(this.getClass()) and first = this
|
||||
}
|
||||
|
||||
final override predicate last(AstNode last, Completion c) {
|
||||
c.(MatchingCompletion).getValue() = false and
|
||||
(
|
||||
last = this and
|
||||
c.isValidFor(this)
|
||||
or
|
||||
exists(AstNode node |
|
||||
node = this.getClass() or
|
||||
node = this.getValue(_)
|
||||
|
|
||||
last(node, last, c)
|
||||
)
|
||||
)
|
||||
or
|
||||
c.(MatchingCompletion).getValue() = true and
|
||||
last = this and
|
||||
not exists(this.getValue(_)) and
|
||||
not exists(this.getRestVariableAccess())
|
||||
or
|
||||
c.(MatchingCompletion).getValue() = true and
|
||||
last(max(int i | | this.getValue(i) order by i), last, c) and
|
||||
not exists(this.getRestVariableAccess())
|
||||
or
|
||||
last(this.getRestVariableAccess(), last, c)
|
||||
}
|
||||
|
||||
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
|
||||
last(this.getClass(), pred, c) and
|
||||
succ = this and
|
||||
c.(MatchingCompletion).getValue() = true
|
||||
or
|
||||
exists(AstNode next |
|
||||
pred = this and
|
||||
c.(MatchingCompletion).getValue() = true and
|
||||
first(next, succ)
|
||||
|
|
||||
next = this.getValue(0)
|
||||
or
|
||||
not exists(this.getValue(_)) and
|
||||
next = this.getRestVariableAccess()
|
||||
)
|
||||
or
|
||||
last(max(int i | | this.getValue(i) order by i), pred, c) and
|
||||
first(this.getRestVariableAccess(), succ) and
|
||||
c.(MatchingCompletion).getValue() = true
|
||||
or
|
||||
exists(int i |
|
||||
last(this.getValue(i), pred, c) and
|
||||
first(this.getValue(i + 1), succ) and
|
||||
c.(MatchingCompletion).getValue() = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class LineLiteralTree extends LeafTree, LineLiteral { }
|
||||
|
||||
private class FileLiteralTree extends LeafTree, FileLiteral { }
|
||||
|
||||
private class EncodingLiteralTree extends LeafTree, EncodingLiteral { }
|
||||
|
||||
private class AlternativePatternTree extends PreOrderTree, AlternativePattern {
|
||||
final override predicate propagatesAbnormal(AstNode child) { child = this.getAnAlternative() }
|
||||
|
||||
final override predicate last(AstNode last, Completion c) {
|
||||
last(this.getAnAlternative(), last, c) and
|
||||
c.(MatchingCompletion).getValue() = true
|
||||
or
|
||||
last(max(int i | | this.getAlternative(i) order by i), last, c) and
|
||||
c.(MatchingCompletion).getValue() = false
|
||||
}
|
||||
|
||||
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
|
||||
pred = this and
|
||||
first(this.getAlternative(0), succ) and
|
||||
c instanceof SimpleCompletion
|
||||
or
|
||||
exists(int i |
|
||||
last(this.getAlternative(i), pred, c) and
|
||||
first(this.getAlternative(i + 1), succ) and
|
||||
c.(MatchingCompletion).getValue() = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class AsPatternTree extends PreOrderTree, AsPattern {
|
||||
final override predicate propagatesAbnormal(AstNode child) { child = this.getPattern() }
|
||||
|
||||
final override predicate last(AstNode last, Completion c) {
|
||||
last(this.getPattern(), last, c) and
|
||||
c.(MatchingCompletion).getValue() = false
|
||||
or
|
||||
last(this.getVariableAccess(), last, c)
|
||||
}
|
||||
|
||||
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
|
||||
pred = this and
|
||||
first(this.getPattern(), succ) and
|
||||
c instanceof SimpleCompletion
|
||||
or
|
||||
last(this.getPattern(), pred, c) and
|
||||
first(this.getVariableAccess(), succ) and
|
||||
c.(MatchingCompletion).getValue() = true
|
||||
}
|
||||
}
|
||||
|
||||
private class ParenthesizedPatternTree extends StandardPreOrderTree, ParenthesizedPattern {
|
||||
override ControlFlowTree getChildElement(int i) { result = this.getPattern() and i = 0 }
|
||||
}
|
||||
|
||||
private class VariableReferencePatternTree extends StandardPreOrderTree, VariableReferencePattern {
|
||||
override ControlFlowTree getChildElement(int i) { result = this.getVariableAccess() and i = 0 }
|
||||
}
|
||||
|
||||
private class InClauseTree extends PreOrderTree, InClause {
|
||||
final override predicate propagatesAbnormal(AstNode child) {
|
||||
child = this.getPattern() or
|
||||
child = this.getCondition()
|
||||
}
|
||||
|
||||
private predicate lastCondition(AstNode last, BooleanCompletion c, boolean flag) {
|
||||
last(this.getCondition(), last, c) and
|
||||
(
|
||||
flag = true and this.hasIfCondition()
|
||||
or
|
||||
flag = false and this.hasUnlessCondition()
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate last(AstNode last, Completion c) {
|
||||
last(this.getPattern(), last, c) and
|
||||
c.(MatchingCompletion).getValue() = false
|
||||
or
|
||||
exists(BooleanCompletion bc, boolean flag, MatchingCompletion mc |
|
||||
lastCondition(last, bc, flag) and
|
||||
c =
|
||||
any(NestedMatchingCompletion nmc |
|
||||
nmc.getInnerCompletion() = bc and nmc.getOuterCompletion() = mc
|
||||
)
|
||||
|
|
||||
mc.getValue() = false and
|
||||
bc.getValue() = flag.booleanNot()
|
||||
or
|
||||
not exists(this.getBody()) and
|
||||
mc.getValue() = true and
|
||||
bc.getValue() = flag
|
||||
)
|
||||
or
|
||||
last(this.getBody(), last, c)
|
||||
or
|
||||
not exists(this.getBody()) and
|
||||
not exists(this.getCondition()) and
|
||||
last(this.getPattern(), last, c)
|
||||
}
|
||||
|
||||
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
|
||||
pred = this and
|
||||
first(this.getPattern(), succ) and
|
||||
c instanceof SimpleCompletion
|
||||
or
|
||||
exists(Expr next |
|
||||
last(this.getPattern(), pred, c) and
|
||||
c.(MatchingCompletion).getValue() = true and
|
||||
first(next, succ)
|
||||
|
|
||||
next = this.getCondition()
|
||||
or
|
||||
not exists(this.getCondition()) and next = this.getBody()
|
||||
)
|
||||
or
|
||||
exists(boolean flag |
|
||||
lastCondition(pred, c, flag) and
|
||||
c.(BooleanCompletion).getValue() = flag and
|
||||
first(this.getBody(), succ)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class CharacterTree extends LeafTree, CharacterLiteral { }
|
||||
|
||||
private class ClassDeclarationTree extends NamespaceTree, ClassDeclaration {
|
||||
@@ -542,7 +923,8 @@ module Trees {
|
||||
c instanceof SimpleCompletion
|
||||
or
|
||||
this.hasDefaultValue() and
|
||||
c.(MatchingCompletion).getValue() = true
|
||||
c.(MatchingCompletion).getValue() = true and
|
||||
c.isValidFor(this)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user