[java] fixes and unifies 'break'/'continue' highlighting

GitOrigin-RevId: 4472c012bbc690ce96bdc43baa4075c4c14c1c62
This commit is contained in:
Roman Shevchenko
2019-07-02 20:54:53 +02:00
committed by intellij-monorepo-bot
parent 624ff3b0a7
commit 83258b1714
8 changed files with 62 additions and 105 deletions

View File

@@ -515,7 +515,7 @@ public class HighlightUtil extends HighlightUtilBase {
@Nullable
static HighlightInfo checkReturnFromSwitchExpr(@NotNull PsiStatement statement) {
if (PsiImplUtil.findEnclosingSwitchOrLoop(statement) instanceof PsiSwitchExpression) {
if (PsiImplUtil.findEnclosingSwitchExpression(statement) != null) {
String message = JavaErrorMessages.message("return.outside.switch.expr");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message).create();
}
@@ -796,24 +796,9 @@ public class HighlightUtil extends HighlightUtilBase {
@Nullable
static HighlightInfo checkBreakTarget(@NotNull PsiBreakStatement statement, @NotNull LanguageLevel languageLevel) {
if (statement.findExitedStatement() == null) {
if (Feature.ENHANCED_SWITCH.isSufficient(languageLevel) && PsiImplUtil.findEnclosingSwitchOrLoop(statement) instanceof PsiSwitchExpression) {
String message = JavaErrorMessages.message("break.outside.switch.expr");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message).create();
}
PsiIdentifier label = statement.getLabelIdentifier();
if (label != null) {
String message = JavaErrorMessages.message("unresolved.label", label.getText());
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(label).descriptionAndTooltip(message).create();
}
else {
String message = JavaErrorMessages.message("break.outside.switch.or.loop");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message).create();
}
}
return null;
return checkBreakOrContinueTarget(statement, statement.getLabelIdentifier(), statement.findExitedStatement(), languageLevel,
"break.outside.switch.or.loop",
"break.outside.switch.expr");
}
@Nullable
@@ -838,42 +823,45 @@ public class HighlightUtil extends HighlightUtilBase {
}
@Nullable
static HighlightInfo checkContinueOutsideLoop(@NotNull PsiContinueStatement statement, LanguageLevel languageLevel) {
if (PsiImplUtil.findEnclosingLoop(statement) == null) {
String message = JavaErrorMessages.message("continue.outside.loop");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message).create();
}
return checkContinueOutsideOfSwitchExpression(statement, statement.findContinuedStatement(), languageLevel);
}
@Nullable
static HighlightInfo checkContinueTarget(@NotNull PsiContinueStatement statement, @NotNull PsiIdentifier label, @NotNull LanguageLevel level) {
static HighlightInfo checkContinueTarget(@NotNull PsiContinueStatement statement, @NotNull LanguageLevel languageLevel) {
PsiStatement continuedStatement = statement.findContinuedStatement();
PsiIdentifier label = statement.getLabelIdentifier();
if (continuedStatement == null) {
String message = JavaErrorMessages.message("unresolved.label", label.getText());
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(label).descriptionAndTooltip(message).create();
}
if (!(continuedStatement instanceof PsiLoopStatement)) {
if (label != null && continuedStatement != null && !(continuedStatement instanceof PsiLoopStatement)) {
String message = JavaErrorMessages.message("not.loop.label", label.getText());
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message).create();
}
return checkContinueOutsideOfSwitchExpression(statement, continuedStatement, level);
return checkBreakOrContinueTarget(statement, label, continuedStatement, languageLevel,
"continue.outside.loop",
"continue.outside.switch.expr");
}
private static HighlightInfo checkContinueOutsideOfSwitchExpression(PsiContinueStatement statement,
PsiStatement continuedStatement,
LanguageLevel level) {
@Nullable
private static HighlightInfo checkBreakOrContinueTarget(PsiStatement statement,
@Nullable PsiIdentifier label,
@Nullable PsiStatement target,
LanguageLevel level,
@PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String misplacedKey,
@PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String crossingKey) {
if (target == null && label != null) {
String message = JavaErrorMessages.message("unresolved.label", label.getText());
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(label).descriptionAndTooltip(message).create();
}
if (Feature.ENHANCED_SWITCH.isSufficient(level)) {
PsiElement enclosing = PsiImplUtil.findEnclosingSwitchOrLoop(statement);
if (enclosing instanceof PsiSwitchExpression && PsiTreeUtil.isAncestor(continuedStatement, enclosing, true)) {
String message = JavaErrorMessages.message("continue.outside.switch.expr");
PsiSwitchExpression expression = PsiImplUtil.findEnclosingSwitchExpression(statement);
if (expression != null && (target == null || PsiTreeUtil.isAncestor(target, expression, true))) {
String message = JavaErrorMessages.message(crossingKey);
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message).create();
}
}
if (target == null) {
String message = JavaErrorMessages.message(misplacedKey);
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message).create();
}
return null;
}

View File

@@ -461,11 +461,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override
public void visitContinueStatement(PsiContinueStatement statement) {
super.visitContinueStatement(statement);
if (!myHolder.hasErrorResults()) {
PsiIdentifier label = statement.getLabelIdentifier();
myHolder.add(label == null ? HighlightUtil.checkContinueOutsideLoop(statement, myLanguageLevel)
: HighlightUtil.checkContinueTarget(statement, label, myLanguageLevel));
}
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkContinueTarget(statement, myLanguageLevel));
}
@Override

View File

@@ -1,18 +1,4 @@
/*
* 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-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;
@@ -22,19 +8,13 @@ import org.jetbrains.annotations.Nullable;
*/
public interface PsiContinueStatement extends PsiStatement {
/**
* Returns the identifier representing the label specified on the statement.
*
* @return the identifier for the label, or null if the statement has no label.
* Returns an identifier element containing the statement's target label, if any.
*/
@Nullable
PsiIdentifier getLabelIdentifier();
@Nullable PsiIdentifier getLabelIdentifier();
/**
* Returns the statement instance ({@link PsiForStatement}, {@link PsiWhileStatement} etc.) representing
* the statement to the next iteration of which {@code continue} transfers control.
*
* @return the statement instance, or null if the statement is not valid in the context where it is located.
*/
@Nullable
PsiStatement findContinuedStatement();
}
@Nullable PsiStatement findContinuedStatement();
}

View File

@@ -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.impl;
import com.intellij.codeInsight.AnnotationTargetUtil;
@@ -39,7 +39,6 @@ import com.intellij.util.IncorrectOperationException;
import com.intellij.util.PairFunction;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -652,11 +651,6 @@ public class PsiImplUtil {
}
}
@Contract("null -> false")
public static boolean isUnqualifiedReference(@Nullable PsiExpression expression) {
return expression instanceof PsiReferenceExpression && ((PsiReferenceExpression)expression).getQualifierExpression() == null;
}
@Nullable
public static PsiLoopStatement findEnclosingLoop(@NotNull PsiElement start) {
for (PsiElement e = start; !isCodeBoundary(e); e = e.getParent()) {
@@ -666,9 +660,17 @@ public class PsiImplUtil {
}
@Nullable
public static PsiElement findEnclosingSwitchOrLoop(@NotNull PsiElement start) {
public static PsiStatement findEnclosingSwitchOrLoop(@NotNull PsiElement start) {
for (PsiElement e = start; !isCodeBoundary(e); e = e.getParent()) {
if (e instanceof PsiSwitchBlock || e instanceof PsiLoopStatement) return e;
if (e instanceof PsiSwitchStatement || e instanceof PsiLoopStatement) return (PsiStatement)e;
}
return null;
}
@Nullable
public static PsiSwitchExpression findEnclosingSwitchExpression(@NotNull PsiElement start) {
for (PsiElement e = start; !isCodeBoundary(e); e = e.getParent()) {
if (e instanceof PsiSwitchExpression) return (PsiSwitchExpression)e;
}
return null;
}

View File

@@ -27,18 +27,14 @@ public class PsiBreakStatementImpl extends CompositePsiElement implements PsiBre
@Nullable
@Override
public PsiStatement findExitedStatement() {
PsiElement enclosing = PsiImplUtil.findEnclosingSwitchOrLoop(this);
if (enclosing instanceof PsiSwitchExpression) {
return null;
}
PsiIdentifier label = getLabelIdentifier();
if (label != null) {
PsiLabeledStatement labeled = PsiImplUtil.findEnclosingLabeledStatement(this, label.getText());
return labeled != null ? labeled.getStatement() : null;
}
return (PsiStatement)enclosing;
else {
return PsiImplUtil.findEnclosingSwitchOrLoop(this);
}
}
@Override

View File

@@ -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.impl.source.tree.java;
import com.intellij.lang.ASTNode;
@@ -29,16 +29,13 @@ public class PsiContinueStatementImpl extends CompositePsiElement implements Psi
@Override
public PsiStatement findContinuedStatement() {
PsiIdentifier label = getLabelIdentifier();
if (label == null) {
if (label != null) {
PsiLabeledStatement labeled = PsiImplUtil.findEnclosingLabeledStatement(this, label.getText());
return labeled != null ? labeled.getStatement() : null;
}
else {
return PsiImplUtil.findEnclosingLoop(this);
}
PsiLabeledStatement labeled = PsiImplUtil.findEnclosingLabeledStatement(this, label.getText());
if (labeled != null) {
return labeled.getStatement();
}
return null;
}
@Override

View File

@@ -22,12 +22,7 @@ public class PsiYieldStatementImpl extends CompositePsiElement implements PsiYie
@Nullable
@Override
public PsiSwitchExpression findEnclosingExpression() {
PsiElement element = this, enclosing;
while (element != null && (enclosing = PsiImplUtil.findEnclosingSwitchOrLoop(element)) != null) {
if (enclosing instanceof PsiSwitchExpression) return (PsiSwitchExpression)enclosing;
element = enclosing.getParent();
}
return null;
return PsiImplUtil.findEnclosingSwitchExpression(this);
}
@Override

View File

@@ -10,12 +10,12 @@ class YieldStatements {
default: <error descr="Yield outside of switch expression">yield 0;</error>
}
System.out.println(switch (i) {
out: System.out.println(switch (i) {
case 1 -> { while (true) yield ref; }
case 2 -> { while (true) break <error descr="Undefined label: 'wtf'">wtf</error>; }
case 3 -> { yield ref; }
case 4 -> { yield (ref); }
case 5 -> { <error descr="Break outside of enclosing switch expression">break wtf;</error> }
case 5 -> { break <error descr="Undefined label: 'wtf'">wtf</error>; }
case 6 -> {
int a = 0;
a: switch (0) { default: yield a; }
@@ -34,6 +34,9 @@ class YieldStatements {
yield yield;
}
case 11 -> { yield <error descr="Expression type should not be 'void'">m(0)</error>; }
case 12 -> {
switch (i) { default: <error descr="Break outside of enclosing switch expression">break out;</error>; }
}
default -> throw new RuntimeException();
});