From ff122c3d8388120164c4e96eaf970bc54170696a Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 12 Nov 2018 16:33:02 +0100 Subject: [PATCH] [java] switch expressions: parser/PSI (IDEA-196643) --- .../com/intellij/psi/JavaElementVisitor.java | 4 + .../src/com/intellij/psi/PsiSwitchBlock.java | 25 +++++ .../com/intellij/psi/PsiSwitchExpression.java | 10 ++ .../com/intellij/psi/PsiSwitchStatement.java | 22 +--- .../lang/java/parser/ExpressionParser.java | 3 + .../lang/java/parser/StatementParser.java | 2 +- .../psi/impl/source/tree/ElementType.java | 2 +- .../psi/impl/source/tree/JavaElementType.java | 1 + .../source/tree/java/PsiSwitchBlockImpl.java | 34 ++++++ .../tree/java/PsiSwitchExpressionImpl.java | 47 ++++++++ .../tree/java/PsiSwitchStatementImpl.java | 106 +++++------------- .../parser-partial/expressions/Switch0.txt | 39 +++++++ .../parser/partial/ExpressionParserTest.java | 2 + 13 files changed, 193 insertions(+), 104 deletions(-) create mode 100644 java/java-psi-api/src/com/intellij/psi/PsiSwitchBlock.java create mode 100644 java/java-psi-api/src/com/intellij/psi/PsiSwitchExpression.java create mode 100644 java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiSwitchBlockImpl.java create mode 100644 java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiSwitchExpressionImpl.java create mode 100644 java/java-tests/testData/psi/parser-partial/expressions/Switch0.txt 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 282d6cccbf74..751b29ff15a8 100644 --- a/java/java-psi-api/src/com/intellij/psi/JavaElementVisitor.java +++ b/java/java-psi-api/src/com/intellij/psi/JavaElementVisitor.java @@ -381,6 +381,10 @@ public abstract class JavaElementVisitor extends PsiElementVisitor { visitExpression(expression); } + public void visitSwitchExpression(PsiSwitchExpression expression) { + visitExpression(expression); + } + public void visitModule(PsiJavaModule module) { visitElement(module); } diff --git a/java/java-psi-api/src/com/intellij/psi/PsiSwitchBlock.java b/java/java-psi-api/src/com/intellij/psi/PsiSwitchBlock.java new file mode 100644 index 000000000000..10aede8977a1 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/psi/PsiSwitchBlock.java @@ -0,0 +1,25 @@ +// 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. +package com.intellij.psi; + +import org.jetbrains.annotations.Nullable; + +/** + * Represents a Java {@code switch} statement or {@code switch} expression. + * + * @see PsiSwitchStatement + * @see PsiSwitchExpression + */ +public interface PsiSwitchBlock extends PsiElement { + /** + * Returns the expression on which the switch is performed, or {@code null} if the statement is incomplete. + */ + @Nullable PsiExpression getExpression(); + + /** + * Returns the body of the switch statement, or {@code null} if the statement is incomplete. + */ + @Nullable PsiCodeBlock getBody(); + + @Nullable PsiJavaToken getLParenth(); + @Nullable PsiJavaToken getRParenth(); +} \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/PsiSwitchExpression.java b/java/java-psi-api/src/com/intellij/psi/PsiSwitchExpression.java new file mode 100644 index 000000000000..cd3e190d3769 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/psi/PsiSwitchExpression.java @@ -0,0 +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. +package com.intellij.psi; + +/** + * Represents a Java {@code switch} expression. + * + * @see PsiSwitchLabelStatement + * @see PsiSwitchLabeledRuleStatement + */ +public interface PsiSwitchExpression extends PsiSwitchBlock, PsiExpression { } \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/PsiSwitchStatement.java b/java/java-psi-api/src/com/intellij/psi/PsiSwitchStatement.java index 590e8439b13e..c27276422fb7 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiSwitchStatement.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiSwitchStatement.java @@ -1,30 +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. package com.intellij.psi; -import org.jetbrains.annotations.Nullable; - /** * Represents a Java {@code switch} statement. * * @see PsiSwitchLabelStatement * @see PsiSwitchLabeledRuleStatement */ -public interface PsiSwitchStatement extends PsiStatement { - /** - * Returns the expression on which the switch is performed, or {@code null} if the statement is incomplete. - */ - @Nullable - PsiExpression getExpression(); - - /** - * Returns the body of the switch statement, or {@code null} if the statement is incomplete. - */ - @Nullable - PsiCodeBlock getBody(); - - @Nullable - PsiJavaToken getLParenth(); - - @Nullable - PsiJavaToken getRParenth(); -} \ No newline at end of file +public interface PsiSwitchStatement extends PsiSwitchBlock, PsiStatement { } \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/lang/java/parser/ExpressionParser.java b/java/java-psi-impl/src/com/intellij/lang/java/parser/ExpressionParser.java index f35c7b9fdb44..61a69d64fc7b 100644 --- a/java/java-psi-impl/src/com/intellij/lang/java/parser/ExpressionParser.java +++ b/java/java-psi-impl/src/com/intellij/lang/java/parser/ExpressionParser.java @@ -295,6 +295,9 @@ public class ExpressionParser { typeCast.done(JavaElementType.TYPE_CAST_EXPRESSION); return typeCast; } + else if (tokenType == JavaTokenType.SWITCH_KEYWORD) { + return myParser.getStatementParser().parseExprInParenthWithBlock(builder, JavaElementType.SWITCH_EXPRESSION, true); + } else { return parsePostfix(builder); } 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 3821cd0ff43d..42a8580ce7e8 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 @@ -612,7 +612,7 @@ public class StatementParser { } @NotNull - private PsiBuilder.Marker parseExprInParenthWithBlock(PsiBuilder builder, IElementType type, boolean block) { + public PsiBuilder.Marker parseExprInParenthWithBlock(@NotNull PsiBuilder builder, @NotNull IElementType type, boolean block) { PsiBuilder.Marker statement = builder.mark(); builder.advanceLexer(); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/ElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/ElementType.java index 6698f0b6171c..f5079a4b2669 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/ElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/ElementType.java @@ -39,7 +39,7 @@ public interface ElementType extends JavaTokenType, JavaDocTokenType, JavaElemen REFERENCE_EXPRESSION, LITERAL_EXPRESSION, THIS_EXPRESSION, SUPER_EXPRESSION, PARENTH_EXPRESSION, METHOD_CALL_EXPRESSION, TYPE_CAST_EXPRESSION, PREFIX_EXPRESSION, POSTFIX_EXPRESSION, BINARY_EXPRESSION, POLYADIC_EXPRESSION, CONDITIONAL_EXPRESSION, ASSIGNMENT_EXPRESSION, NEW_EXPRESSION, ARRAY_ACCESS_EXPRESSION, ARRAY_INITIALIZER_EXPRESSION, INSTANCE_OF_EXPRESSION, - CLASS_OBJECT_ACCESS_EXPRESSION, METHOD_REF_EXPRESSION, LAMBDA_EXPRESSION, EMPTY_EXPRESSION); + CLASS_OBJECT_ACCESS_EXPRESSION, METHOD_REF_EXPRESSION, LAMBDA_EXPRESSION, SWITCH_EXPRESSION, EMPTY_EXPRESSION); TokenSet ANNOTATION_MEMBER_VALUE_BIT_SET = TokenSet.orSet(EXPRESSION_BIT_SET, TokenSet.create(ANNOTATION, ANNOTATION_ARRAY_INITIALIZER)); 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 6836aee68bbf..0da222e53634 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 @@ -114,6 +114,7 @@ public interface JavaElementType { IElementType FOREACH_STATEMENT = new JavaCompositeElementType("FOREACH_STATEMENT", PsiForeachStatementImpl::new); IElementType DO_WHILE_STATEMENT = new JavaCompositeElementType("DO_WHILE_STATEMENT", PsiDoWhileStatementImpl::new); IElementType SWITCH_STATEMENT = new JavaCompositeElementType("SWITCH_STATEMENT", PsiSwitchStatementImpl::new); + IElementType SWITCH_EXPRESSION = new JavaCompositeElementType("SWITCH_EXPRESSION", PsiSwitchExpressionImpl::new); 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); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiSwitchBlockImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiSwitchBlockImpl.java new file mode 100644 index 000000000000..a686f7a3b089 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiSwitchBlockImpl.java @@ -0,0 +1,34 @@ +// 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. +package com.intellij.psi.impl.source.tree.java; + +import com.intellij.psi.*; +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 com.intellij.psi.tree.IElementType; + +public abstract class PsiSwitchBlockImpl extends CompositePsiElement implements PsiSwitchBlock { + protected PsiSwitchBlockImpl(IElementType type) { + super(type); + } + + @Override + public PsiExpression getExpression() { + return (PsiExpression)findPsiChildByType(ElementType.EXPRESSION_BIT_SET); + } + + @Override + public PsiCodeBlock getBody() { + return (PsiCodeBlock)findPsiChildByType(JavaElementType.CODE_BLOCK); + } + + @Override + public PsiJavaToken getLParenth() { + return (PsiJavaToken)findPsiChildByType(JavaTokenType.LPARENTH); + } + + @Override + public PsiJavaToken getRParenth() { + return (PsiJavaToken)findPsiChildByType(JavaTokenType.RPARENTH); + } +} \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiSwitchExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiSwitchExpressionImpl.java new file mode 100644 index 000000000000..2d6dc5c86500 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiSwitchExpressionImpl.java @@ -0,0 +1,47 @@ +// 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. +package com.intellij.psi.impl.source.tree.java; + +import com.intellij.lang.ASTNode; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.tree.ElementType; +import com.intellij.psi.impl.source.tree.JavaElementType; +import com.intellij.psi.impl.source.tree.JavaSourceUtil; +import com.intellij.psi.impl.source.tree.TreeElement; +import org.jetbrains.annotations.NotNull; + +public class PsiSwitchExpressionImpl extends PsiSwitchBlockImpl implements PsiSwitchExpression { + public PsiSwitchExpressionImpl() { + super(JavaElementType.SWITCH_EXPRESSION); + } + + @Override + public PsiExpression getExpression() { + return (PsiExpression)findPsiChildByType(ElementType.EXPRESSION_BIT_SET); + } + + @Override + public PsiType getType() { + return null; //tbd + } + + @Override + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof JavaElementVisitor) { + ((JavaElementVisitor)visitor).visitSwitchExpression(this); + } + else { + super.accept(visitor); + } + } + + @Override + public void replaceChildInternal(@NotNull ASTNode child, @NotNull TreeElement newElement) { + super.replaceChildInternal(child, JavaSourceUtil.addParenthToReplacedChild(child, newElement, getManager())); + } + + @Override + public String toString() { + PsiExpression expression = getExpression(); + return "PsiSwitchExpression: " + (expression != null ? expression.getText() : "(incomplete)"); + } +} \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiSwitchStatementImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiSwitchStatementImpl.java index 8314cd81a6ff..02f1c8001b17 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiSwitchStatementImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiSwitchStatementImpl.java @@ -1,78 +1,36 @@ -/* - * Copyright 2000-2009 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-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. package com.intellij.psi.impl.source.tree.java; import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.psi.*; -import com.intellij.psi.impl.source.Constants; +import com.intellij.psi.JavaElementVisitor; +import com.intellij.psi.JavaTokenType; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiSwitchStatement; import com.intellij.psi.impl.source.tree.ChildRole; -import com.intellij.psi.impl.source.tree.CompositePsiElement; -import com.intellij.psi.tree.IElementType; +import com.intellij.psi.impl.source.tree.ElementType; +import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.tree.ChildRoleBase; +import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; -public class PsiSwitchStatementImpl extends CompositePsiElement implements PsiSwitchStatement, Constants { - private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiSwitchStatementImpl"); +public class PsiSwitchStatementImpl extends PsiSwitchBlockImpl implements PsiSwitchStatement { + private static final Logger LOG = Logger.getInstance(PsiSwitchBlockImpl.class); public PsiSwitchStatementImpl() { - super(SWITCH_STATEMENT); - } - - @Override - public PsiExpression getExpression() { - return (PsiExpression)findChildByRoleAsPsiElement(ChildRole.SWITCH_EXPRESSION); - } - - @Override - public PsiCodeBlock getBody() { - return (PsiCodeBlock)findChildByRoleAsPsiElement(ChildRole.SWITCH_BODY); - } - - @Override - public PsiJavaToken getLParenth() { - return (PsiJavaToken)findChildByRoleAsPsiElement(ChildRole.LPARENTH); - } - - @Override - public PsiJavaToken getRParenth() { - return (PsiJavaToken)findChildByRoleAsPsiElement(ChildRole.RPARENTH); + super(JavaElementType.SWITCH_STATEMENT); } @Override public ASTNode findChildByRole(int role) { LOG.assertTrue(ChildRole.isUnique(role)); - switch(role){ - default: - return null; - - case ChildRole.SWITCH_KEYWORD: - return findChildByType(SWITCH_KEYWORD); - - case ChildRole.LPARENTH: - return findChildByType(LPARENTH); - - case ChildRole.SWITCH_EXPRESSION: - return findChildByType(EXPRESSION_BIT_SET); - - case ChildRole.RPARENTH: - return findChildByType(RPARENTH); - - case ChildRole.SWITCH_BODY: - return findChildByType(CODE_BLOCK); + switch (role) { + case ChildRole.SWITCH_KEYWORD: return findChildByType(JavaTokenType.SWITCH_KEYWORD); + case ChildRole.LPARENTH: return findChildByType(JavaTokenType.LPARENTH); + case ChildRole.SWITCH_EXPRESSION: return findChildByType(ElementType.EXPRESSION_BIT_SET); + case ChildRole.RPARENTH: return findChildByType(JavaTokenType.RPARENTH); + case ChildRole.SWITCH_BODY: return findChildByType(JavaElementType.CODE_BLOCK); + default: return null; } } @@ -80,26 +38,12 @@ public class PsiSwitchStatementImpl extends CompositePsiElement implements PsiSw public int getChildRole(@NotNull ASTNode child) { LOG.assertTrue(child.getTreeParent() == this); IElementType i = child.getElementType(); - if (i == SWITCH_KEYWORD) { - return ChildRole.SWITCH_KEYWORD; - } - else if (i == LPARENTH) { - return ChildRole.LPARENTH; - } - else if (i == RPARENTH) { - return ChildRole.RPARENTH; - } - else { - if (EXPRESSION_BIT_SET.contains(child.getElementType())) { - return ChildRole.SWITCH_EXPRESSION; - } - else if (child.getElementType() == CODE_BLOCK) { - return ChildRole.SWITCH_BODY; - } - else { - return ChildRoleBase.NONE; - } - } + if (i == JavaTokenType.SWITCH_KEYWORD) return ChildRole.SWITCH_KEYWORD; + if (i == JavaTokenType.LPARENTH) return ChildRole.LPARENTH; + if (i == JavaTokenType.RPARENTH) return ChildRole.RPARENTH; + if (ElementType.EXPRESSION_BIT_SET.contains(child.getElementType())) return ChildRole.SWITCH_EXPRESSION; + if (child.getElementType() == JavaElementType.CODE_BLOCK) return ChildRole.SWITCH_BODY; + return ChildRoleBase.NONE; } @Override @@ -116,4 +60,4 @@ public class PsiSwitchStatementImpl extends CompositePsiElement implements PsiSw public String toString() { return "PsiSwitchStatement"; } -} +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/expressions/Switch0.txt b/java/java-tests/testData/psi/parser-partial/expressions/Switch0.txt new file mode 100644 index 000000000000..ca891389c405 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/Switch0.txt @@ -0,0 +1,39 @@ +PsiJavaFile:Switch0.java + PsiSwitchExpression: i + PsiKeyword:switch('switch') + PsiWhiteSpace(' ') + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:i + PsiReferenceParameterList + + PsiIdentifier:i('i') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSwitchLabeledRule + PsiKeyword:case('case') + PsiWhiteSpace(' ') + PsiExpressionList + PsiLiteralExpression:1 + PsiJavaToken:INTEGER_LITERAL('1') + PsiWhiteSpace(' ') + PsiJavaToken:ARROW('->') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiLiteralExpression:1 + PsiJavaToken:INTEGER_LITERAL('1') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiSwitchLabeledRule + PsiKeyword:default('default') + PsiWhiteSpace(' ') + PsiJavaToken:ARROW('->') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiLiteralExpression:2 + PsiJavaToken:INTEGER_LITERAL('2') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/parser/partial/ExpressionParserTest.java b/java/java-tests/testSrc/com/intellij/java/parser/partial/ExpressionParserTest.java index f9a6311c86af..57feb88dc43a 100644 --- a/java/java-tests/testSrc/com/intellij/java/parser/partial/ExpressionParserTest.java +++ b/java/java-tests/testSrc/com/intellij/java/parser/partial/ExpressionParserTest.java @@ -146,6 +146,8 @@ public class ExpressionParserTest extends JavaParsingTestCase { public void testRawLiteral0() { doParserTest("`.`"); } + public void testSwitch0() { doParserTest("switch (i) { case 1 -> 1; default -> 2; }"); } + private void doParserTest(String text) { doParserTest(text, builder -> JavaParser.INSTANCE.getExpressionParser().parse(builder)); }