CFG: correct flow for lambda bodies

Lambda bodies are parsed as nested do-blocks or normal blocks.
This is actually incorrect, as the body of a lambda can't have
parameters. However, we can "inline" such blocks to get the
desired control flow.
This commit is contained in:
Arthur Baars
2020-12-16 11:17:09 +01:00
parent eafec4331b
commit dd954ea943
3 changed files with 32 additions and 20 deletions

View File

@@ -76,6 +76,8 @@ private module CfgScope {
}
private class DoBlockScope extends Range, Generated::DoBlock {
DoBlockScope() { not this.getParent() instanceof Generated::Lambda }
final override string getName() { result = "do block" }
final override predicate entry(Generated::AstNode first) {
@@ -88,6 +90,8 @@ private module CfgScope {
}
private class BlockScope extends Range, Generated::Block {
BlockScope() { not this.getParent() instanceof Generated::Lambda }
final override string getName() { result = "block" }
final override predicate entry(Generated::AstNode first) {
@@ -103,11 +107,22 @@ private module CfgScope {
final override string getName() { result = "lambda" }
final override predicate entry(Generated::AstNode first) {
first(this.(Trees::LambdaTree).getFirstChildNode(), first)
first(this.getParameters(), first)
or
not exists(this.getParameters()) and
(
first(this.getBody().(Trees::DoBlockTree).firstBody(), first)
or
first(this.getBody().(Trees::BlockTree).getFirstChildNode(), first)
)
}
final override predicate exit(Generated::AstNode last, Completion c) {
last(this.(Trees::LambdaTree).getLastChildNode(), last, c)
last(this.getBody().(Trees::BlockTree).getLastChildNode(), last, c)
or
this.getBody().(Trees::RescueEnsureBlockTree).lastBody(last, c)
or
not exists(this.getBody()) and last(this.getParameters(), last, c)
}
}
}

View File

@@ -431,7 +431,7 @@ module Trees {
override predicate isHidden() { any() }
}
private class DoBlockTree extends RescueEnsureBlockTree, PostOrderTree, DoBlock {
class DoBlockTree extends RescueEnsureBlockTree, PostOrderTree, DoBlock {
final override predicate first(AstNode first) { first = this }
final override AstNode getChildNode(int i, boolean rescuable) {
@@ -679,11 +679,19 @@ module Trees {
final override AstNode getDefaultValue() { result = this.getValue() }
}
class LambdaTree extends StandardNode, PreOrderTree, PostOrderTree, Lambda {
final override AstNode getChildNode(int i) {
result = this.getParameters() and i = 0
class LambdaTree extends PreOrderTree, PostOrderTree, Lambda {
final override predicate propagatesAbnormal(AstNode child) { child = this.getParameters() }
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
this.getBody().(ControlFlowTree).succ(pred, succ, c)
or
result = this.getBody() and i = 1
last(this.getParameters(), pred, c) and
c instanceof NormalCompletion and
(
first(this.getBody().(DoBlockTree).firstBody(), succ)
or
first(this.getBody().(BlockTree).getFirstChildNode(), succ)
)
}
}

View File

@@ -49,9 +49,6 @@ cfg.rb:
# 120| enter lambda
#-----| -> x
# 120| enter block
#-----| -> y
# 142| enter print
#-----| -> puts
@@ -1375,7 +1372,7 @@ cfg.rb:
#-----| -> swap
# 120| DestructuredParameter
#-----| -> Block
#-----| -> y
# 120| x
#-----| -> y
@@ -1383,11 +1380,8 @@ cfg.rb:
# 120| y
#-----| -> DestructuredParameter
# 120| Block
#-----| -> exit lambda (normal)
# 120| Array
#-----| -> exit block (normal)
#-----| -> exit lambda (normal)
# 120| y
#-----| -> x
@@ -3690,8 +3684,6 @@ cfg.rb:
# 120| exit lambda
# 120| exit block
# 142| exit print
# 149| exit method
@@ -3821,9 +3813,6 @@ cfg.rb:
# 120| exit lambda (normal)
#-----| -> exit lambda
# 120| exit block (normal)
#-----| -> exit block
# 142| exit print (normal)
#-----| -> exit print