diff --git a/java/java-psi-api/src/com/intellij/psi/JavaElementVisitor.java b/java/java-psi-api/src/com/intellij/psi/JavaElementVisitor.java index 10e40a4628b0..cd13b384115e 100644 --- a/java/java-psi-api/src/com/intellij/psi/JavaElementVisitor.java +++ b/java/java-psi-api/src/com/intellij/psi/JavaElementVisitor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.psi; import com.intellij.psi.javadoc.*; @@ -36,6 +36,10 @@ public abstract class JavaElementVisitor extends PsiElementVisitor { visitStatement(statement); } + public void visitYieldStatement(PsiYieldStatement statement) { + visitStatement(statement); + } + public void visitClass(PsiClass aClass) { visitElement(aClass); } diff --git a/java/java-psi-api/src/com/intellij/psi/JavaTokenType.java b/java/java-psi-api/src/com/intellij/psi/JavaTokenType.java index dee1b7efd9ff..a0e9460618de 100644 --- a/java/java-psi-api/src/com/intellij/psi/JavaTokenType.java +++ b/java/java-psi-api/src/com/intellij/psi/JavaTokenType.java @@ -141,4 +141,5 @@ public interface JavaTokenType extends TokenType { IElementType WITH_KEYWORD = new IJavaElementType("WITH"); IElementType VAR_KEYWORD = new IJavaElementType("VAR"); + IElementType YIELD_KEYWORD = new IJavaElementType("YIELD"); } \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/PsiKeyword.java b/java/java-psi-api/src/com/intellij/psi/PsiKeyword.java index 309f3655a948..b4564c003c99 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiKeyword.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiKeyword.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.psi; /** @@ -86,4 +72,5 @@ public interface PsiKeyword extends PsiJavaToken { String WITH = "with"; String VAR = "var"; + String YIELD = "yield"; } \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/PsiYieldStatement.java b/java/java-psi-api/src/com/intellij/psi/PsiYieldStatement.java new file mode 100644 index 000000000000..adc631dd8473 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/psi/PsiYieldStatement.java @@ -0,0 +1,19 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.psi; + +import org.jetbrains.annotations.Nullable; + +/** + * Represents a Java {@code yield} statement. + */ +public interface PsiYieldStatement extends PsiStatement { + /** + * Returns the value expression of the statement, if present. + */ + @Nullable PsiExpression getExpression(); + + /** + * Returns the {@link PsiSwitchExpression switch expression}, out of which the statement transfers control. + */ + @Nullable PsiSwitchExpression findEnclosingExpression(); +} \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/lang/java/parser/StatementParser.java b/java/java-psi-impl/src/com/intellij/lang/java/parser/StatementParser.java index ca5b218c8e2e..77951d607c3f 100644 --- a/java/java-psi-impl/src/com/intellij/lang/java/parser/StatementParser.java +++ b/java/java-psi-impl/src/com/intellij/lang/java/parser/StatementParser.java @@ -1,10 +1,11 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.lang.java.parser; import com.intellij.codeInsight.daemon.JavaErrorMessages; import com.intellij.lang.PsiBuilder; import com.intellij.lang.WhitespacesBinders; import com.intellij.psi.JavaTokenType; +import com.intellij.psi.PsiKeyword; import com.intellij.psi.impl.source.tree.ElementType; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.tree.IElementType; @@ -116,6 +117,9 @@ public class StatementParser { else if (tokenType == JavaTokenType.BREAK_KEYWORD) { return parseBreakStatement(builder); } + else if (tokenType == JavaTokenType.IDENTIFIER && PsiKeyword.YIELD.equals(builder.getTokenText())) { + return parseYieldStatement(builder); + } else if (tokenType == JavaTokenType.CONTINUE_KEYWORD) { return parseContinueStatement(builder); } @@ -472,6 +476,23 @@ public class StatementParser { return statement; } + @NotNull + private PsiBuilder.Marker parseYieldStatement(PsiBuilder builder) { + PsiBuilder.Marker statement = builder.mark(); + builder.remapCurrentToken(JavaTokenType.YIELD_KEYWORD); + builder.advanceLexer(); + + if (myParser.getExpressionParser().parse(builder) == null) { + error(builder, JavaErrorMessages.message("expected.expression")); + } + else { + semicolon(builder); + } + + done(statement, JavaElementType.YIELD_STATEMENT); + return statement; + } + @NotNull private static PsiBuilder.Marker parseContinueStatement(PsiBuilder builder) { PsiBuilder.Marker statement = builder.mark(); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java index 77aa89059622..22476b0d3195 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java @@ -118,6 +118,7 @@ public interface JavaElementType { IElementType SWITCH_LABEL_STATEMENT = new JavaCompositeElementType("SWITCH_LABEL_STATEMENT", PsiSwitchLabelStatementImpl::new); IElementType SWITCH_LABELED_RULE = new JavaCompositeElementType("SWITCH_LABELED_RULE", PsiSwitchLabeledRuleStatementImpl::new); IElementType BREAK_STATEMENT = new JavaCompositeElementType("BREAK_STATEMENT", PsiBreakStatementImpl::new); + IElementType YIELD_STATEMENT = new JavaCompositeElementType("YIELD_STATEMENT", PsiYieldStatementImpl::new); IElementType CONTINUE_STATEMENT = new JavaCompositeElementType("CONTINUE_STATEMENT", PsiContinueStatementImpl::new); IElementType RETURN_STATEMENT = new JavaCompositeElementType("RETURN_STATEMENT", PsiReturnStatementImpl::new); IElementType THROW_STATEMENT = new JavaCompositeElementType("THROW_STATEMENT", PsiThrowStatementImpl::new); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiYieldStatementImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiYieldStatementImpl.java new file mode 100644 index 000000000000..069154f7cc32 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiYieldStatementImpl.java @@ -0,0 +1,47 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.psi.impl.source.tree.java; + +import com.intellij.psi.*; +import com.intellij.psi.impl.PsiImplUtil; +import com.intellij.psi.impl.source.tree.CompositePsiElement; +import com.intellij.psi.impl.source.tree.ElementType; +import com.intellij.psi.impl.source.tree.JavaElementType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class PsiYieldStatementImpl extends CompositePsiElement implements PsiYieldStatement { + public PsiYieldStatementImpl() { + super(JavaElementType.YIELD_STATEMENT); + } + + @Override + public PsiExpression getExpression() { + return (PsiExpression)findPsiChildByType(ElementType.EXPRESSION_BIT_SET); + } + + @Nullable + @Override + public PsiSwitchExpression findEnclosingExpression() { + PsiElement element = this, enclosing; + while ((enclosing = PsiImplUtil.findEnclosingSwitchOrLoop(element)) != null) { + if (enclosing instanceof PsiSwitchExpression) return (PsiSwitchExpression)enclosing; + element = enclosing.getParent(); + } + return null; + } + + @Override + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof JavaElementVisitor) { + ((JavaElementVisitor)visitor).visitYieldStatement(this); + } + else { + visitor.visitElement(this); + } + } + + @Override + public String toString() { + return "PsiYieldStatement"; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/Yield.txt b/java/java-tests/testData/psi/parser-partial/statements/Yield.txt new file mode 100644 index 000000000000..2c0956b6419c --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/Yield.txt @@ -0,0 +1,9 @@ +PsiJavaFile:Yield.java + PsiYieldStatement + PsiJavaToken:YIELD('yield') + PsiWhiteSpace(' ') + PsiReferenceExpression:x + PsiReferenceParameterList + + PsiIdentifier:x('x') + PsiJavaToken:SEMICOLON(';') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/YieldCall.txt b/java/java-tests/testData/psi/parser-partial/statements/YieldCall.txt new file mode 100644 index 000000000000..66c05251a956 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/YieldCall.txt @@ -0,0 +1,16 @@ +PsiJavaFile:YieldCall.java + PsiExpressionStatement + PsiMethodCallExpression:foo.yield() + PsiReferenceExpression:foo.yield + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:yield('yield') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/YieldIncomplete0.txt b/java/java-tests/testData/psi/parser-partial/statements/YieldIncomplete0.txt new file mode 100644 index 000000000000..723843f6cc63 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/YieldIncomplete0.txt @@ -0,0 +1,6 @@ +PsiJavaFile:YieldIncomplete0.java + PsiYieldStatement + PsiJavaToken:YIELD('yield') + PsiErrorElement:Expression expected + + PsiWhiteSpace(' ') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/YieldIncomplete1.txt b/java/java-tests/testData/psi/parser-partial/statements/YieldIncomplete1.txt new file mode 100644 index 000000000000..8885ea82ea33 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/YieldIncomplete1.txt @@ -0,0 +1,10 @@ +PsiJavaFile:YieldIncomplete1.java + PsiYieldStatement + PsiJavaToken:YIELD('yield') + PsiWhiteSpace(' ') + PsiReferenceExpression:x + PsiReferenceParameterList + + PsiIdentifier:x('x') + PsiErrorElement:';' expected + \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/YieldNested.txt b/java/java-tests/testData/psi/parser-partial/statements/YieldNested.txt new file mode 100644 index 000000000000..d13c940fc4f9 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/YieldNested.txt @@ -0,0 +1,28 @@ +PsiJavaFile:YieldNested.java + PsiYieldStatement + PsiJavaToken:YIELD('yield') + PsiWhiteSpace(' ') + PsiSwitchExpression: 0 + PsiKeyword:switch('switch') + PsiWhiteSpace(' ') + PsiJavaToken:LPARENTH('(') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSwitchLabelStatement + PsiKeyword:default('default') + PsiJavaToken:COLON(':') + PsiWhiteSpace(' ') + PsiYieldStatement + PsiJavaToken:YIELD('yield') + PsiWhiteSpace(' ') + PsiLiteralExpression:42 + PsiJavaToken:INTEGER_LITERAL('42') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiJavaToken:SEMICOLON(';') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/parser/partial/StatementParserTest.java b/java/java-tests/testSrc/com/intellij/java/parser/partial/StatementParserTest.java index 6a1a8fcafdde..07475cd1e732 100644 --- a/java/java-tests/testSrc/com/intellij/java/parser/partial/StatementParserTest.java +++ b/java/java-tests/testSrc/com/intellij/java/parser/partial/StatementParserTest.java @@ -1,9 +1,10 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.java.parser.partial; import com.intellij.java.parser.JavaParsingTestCase; import com.intellij.lang.java.parser.JavaParser; +@SuppressWarnings("GraziInspection") public class StatementParserTest extends JavaParsingTestCase { public StatementParserTest() { super("parser-partial/statements"); @@ -28,6 +29,12 @@ public class StatementParserTest extends JavaParsingTestCase { public void testBreakIncomplete() { doParserTest("break"); } public void testBreakExpr() { doParserTest("break boo();"); } + public void testYield() { doParserTest("yield x;"); } + public void testYieldNested() { doParserTest("yield switch (0) { default: yield 42; };"); } + public void testYieldIncomplete0() { doParserTest("yield "); } + public void testYieldIncomplete1() { doParserTest("yield x"); } + public void testYieldCall() { doParserTest("foo.yield();"); } + public void testContinueNormal0() { doParserTest("continue;"); } public void testContinueNormal1() { doParserTest("continue LABEL;"); }