Skip to content

Commit cb62fc2

Browse files
committed
NoMatchingPatternError should not re-evaluate the case expression
1 parent 64abcbe commit cb62fc2

File tree

3 files changed

+29
-21
lines changed

3 files changed

+29
-21
lines changed

spec/ruby/language/pattern_matching_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,18 @@
251251
}.should raise_error(NoMatchingPatternError, /\[0, 1\]/)
252252
end
253253

254+
it "raises NoMatchingPatternError if no pattern matches and evaluates the expression only once" do
255+
evals = 0
256+
-> {
257+
eval <<~RUBY
258+
case (evals += 1; [0, 1])
259+
in [0]
260+
end
261+
RUBY
262+
}.should raise_error(NoMatchingPatternError, /\[0, 1\]/)
263+
evals.should == 1
264+
end
265+
254266
it "does not allow calculation or method calls in a pattern" do
255267
-> {
256268
eval <<~RUBY

src/main/java/org/truffleruby/parser/BodyTranslator.java

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -837,11 +837,24 @@ public RubyNode visitCaseNode(CaseParseNode node) {
837837
@Override
838838
public RubyNode visitCaseInNode(CaseInParseNode node) {
839839
final SourceIndexLength sourceSection = node.getPosition();
840-
RubyNode caseExprValue = node.getCaseNode().accept(this);
840+
841+
PatternMatchingTranslator translator = new PatternMatchingTranslator(language, source, parserContext,
842+
currentNode, node.getCases(), environment, this);
843+
844+
// Evaluate the case expression and store it in a local
845+
846+
final RubyNode caseExprValue = node.getCaseNode().accept(this);
847+
final int tempSlot = environment.declareLocalTemp("case in value");
848+
final ReadLocalNode readTemp = environment.readNode(tempSlot, sourceSection);
849+
final RubyNode assignTemp = readTemp.makeWriteNode(caseExprValue);
850+
851+
/* Build an if expression from the ins and else. Work backwards because the first if contains all the others in
852+
* its else clause. */
853+
841854
RubyNode elseNode;
842855
if (node.getElseNode() == null) {
843856
RubyCallNodeParameters inspectCallParameters = new RubyCallNodeParameters(
844-
caseExprValue,
857+
NodeUtil.cloneNode(readTemp),
845858
"inspect",
846859
null,
847860
EmptyArgumentsDescriptor.INSTANCE,
@@ -854,20 +867,6 @@ public RubyNode visitCaseInNode(CaseInParseNode node) {
854867
elseNode = translateNodeOrNil(sourceSection, node.getElseNode());
855868
}
856869

857-
PatternMatchingTranslator tr = new PatternMatchingTranslator(language, source, parserContext,
858-
currentNode, node.getCaseNode(), node.getCases(), environment, this);
859-
860-
final RubyNode ret;
861-
862-
// Evaluate the case expression and store it in a local
863-
864-
final int tempSlot = environment.declareLocalTemp("case in value");
865-
final ReadLocalNode readTemp = environment.readNode(tempSlot, sourceSection);
866-
final RubyNode assignTemp = readTemp.makeWriteNode(caseExprValue);
867-
868-
/* Build an if expression from the ins and else. Work backwards because the first if contains all the others in
869-
* its else clause. */
870-
871870
for (int n = node.getCases().size() - 1; n >= 0; n--) {
872871
final InParseNode in = (InParseNode) node.getCases().get(n);
873872

@@ -876,7 +875,7 @@ public RubyNode visitCaseInNode(CaseInParseNode node) {
876875
// us we-using the 'when' parser for 'in' temporarily.
877876
final ParseNode patternNode = in.getExpressionNodes();
878877

879-
final RubyNode conditionNode = tr.translatePatternNode(patternNode, readTemp);
878+
final RubyNode conditionNode = translator.translatePatternNode(patternNode, readTemp);
880879
// Create the if node
881880
final RubyNode thenNode = translateNodeOrNil(sourceSection, in.getBodyNode());
882881
final IfElseNode ifNode = new IfElseNode(conditionNode, thenNode, elseNode);
@@ -888,7 +887,7 @@ public RubyNode visitCaseInNode(CaseInParseNode node) {
888887
final RubyNode ifNode = elseNode;
889888

890889
// A top-level block assigns the temp then runs the if
891-
ret = sequence(sourceSection, Arrays.asList(assignTemp, ifNode));
890+
final RubyNode ret = sequence(sourceSection, Arrays.asList(assignTemp, ifNode));
892891

893892
return addNewlineIfNeeded(node, ret);
894893
}

src/main/java/org/truffleruby/parser/PatternMatchingTranslator.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757

5858
public class PatternMatchingTranslator extends BaseTranslator {
5959

60-
final ParseNode data;
6160
final ListParseNode cases;
6261
final TranslatorEnvironment environment;
6362
final BodyTranslator bodyTranslator;
@@ -69,12 +68,10 @@ public PatternMatchingTranslator(
6968
Source source,
7069
ParserContext parserContext,
7170
Node currentNode,
72-
ParseNode data, // data to match on
7371
ListParseNode cases, // cases to check
7472
TranslatorEnvironment environment,
7573
BodyTranslator bodyTranslator) {
7674
super(language, source, parserContext, currentNode, environment);
77-
this.data = data;
7875
this.cases = cases;
7976
this.environment = environment;
8077
this.bodyTranslator = bodyTranslator;

0 commit comments

Comments
 (0)