[java-inspections] IDEA-360429 Support guards in switch UAST

GitOrigin-RevId: 6679acb5d8380073f75ef7be8a8261c59e6b392b
This commit is contained in:
Mikhail Pyltsin
2024-12-02 11:30:30 +00:00
committed by intellij-monorepo-bot
parent 6b1374f4e3
commit cccb22d78e
18 changed files with 632 additions and 1 deletions
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>Main.java</file>
<line>3</line>
<offset>6</offset>
<length>4</length>
<highlighted_element>Test</highlighted_element>
<description>Class is not instantiated.</description>
</problem>
</problems>
@@ -0,0 +1,17 @@
import java.io.Serializable;
class Test {
@SuppressWarnings("unused")
private <T extends Serializable> Object toConflictDetail(String controlClass, T conflict) {
return switch (conflict) {
case String ruleConflict when hasSameControlClass(controlClass, ruleConflict) -> conflict;
case Number nestingConflict when hasSameControlClass(controlClass, nestingConflict) -> null;
default -> null;
};
}
private static boolean hasSameControlClass(String controlClass, Object ruleConflict) {
return controlClass.equals(ruleConflict);
}
}
@@ -288,6 +288,12 @@ public class UnusedDeclarationInspectionTest extends AbstractUnusedDeclarationTe
});
}
public void testUnusedGuardStatement() {
IdeaTestUtil.withLevel(getModule(), JavaFeature.PATTERN_GUARDS_AND_RECORD_PATTERNS.getMinimumLevel(), () -> {
doTest();
});
}
public void testBrokenClassToImplicitClass() {
IdeaTestUtil.withLevel(getModule(), JavaFeature.IMPLICIT_CLASSES.getMinimumLevel(), () -> {
doTest();
@@ -1,6 +1,7 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.uast
import org.jetbrains.annotations.ApiStatus.Experimental
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
@@ -67,10 +68,19 @@ interface USwitchClauseExpression : UExpression {
*/
val caseValues: List<UExpression>
/**
* Represents the guard expressions for this switch clause or null if there is no guard.
* (for example, expression after `when` in java `switch` statement).
*/
val guard: UExpression?
@Experimental
get() = null
override fun accept(visitor: UastVisitor) {
if (visitor.visitSwitchClauseExpression(this)) return
uAnnotations.acceptList(visitor)
caseValues.acceptList(visitor)
guard?.accept(visitor)
visitor.afterVisitSwitchClauseExpression(this)
}
@@ -98,6 +108,7 @@ interface USwitchClauseExpressionWithBody : USwitchClauseExpression {
if (visitor.visitSwitchClauseExpression(this)) return
uAnnotations.acceptList(visitor)
caseValues.acceptList(visitor)
guard?.accept(visitor)
body.accept(visitor)
visitor.afterVisitSwitchClauseExpression(this)
}
@@ -118,6 +118,7 @@ class JavaUSwitchEntry(
private val caseValuesPart = UastLazyPart<List<UExpression>>()
private val bodyPart = UastLazyPart<UExpressionList>()
private val guardPart = UastLazyPart<UExpression?>()
override val sourcePsi: PsiSwitchLabelStatementBase
get() = labels.first()
@@ -136,6 +137,13 @@ class JavaUSwitchEntry(
}
}
override val guard: UExpression?
get() = guardPart.getOrBuild {
val expression = labels.singleOrNull() ?: return@getOrBuild null
val guard = expression.guardExpression ?: return@getOrBuild null
JavaConverter.convertPsiElement(guard, this, UExpression::class.java) as? UExpression ?: UnknownJavaExpression(guard, this)
}
override val body: UExpressionList
get() = bodyPart.getOrBuild {
object : JavaUExpressionList(sourcePsi, JavaSpecialExpressionKinds.SWITCH_ENTRY, this) {
@@ -163,6 +171,10 @@ class JavaUSwitchEntry(
}
}
}
override fun asRenderString(): String = caseValues.joinToString { it.asRenderString() } +
(guard?.let { " when " + it.asRenderString() } ?: "") +
" -> " + body.asRenderString()
}
internal class DummyYieldExpression(
@@ -0,0 +1,13 @@
public class TypePattern {
static String formatter(Object o) {
String formatted = switch (o) {
case Integer i when i < 0 -> String.format("int %d", i);
case Integer i -> String.format("int %d", i);
case Long l when l < 0 -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> formatted = o.toString();
};
return formatted;
}
}
@@ -0,0 +1,83 @@
UFile (package = )
UClass (name = TypePattern)
UMethod (name = formatter)
UParameter (name = o)
UBlockExpression
UDeclarationsExpression
ULocalVariable (name = formatted)
USwitchExpression
USimpleNameReferenceExpression (identifier = o)
UExpressionList (switch)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = i)
UBinaryExpression (operator = <)
USimpleNameReferenceExpression (identifier = i)
ULiteralExpression (value = 0)
UExpressionList (switch_entry)
UYieldExpression
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "int %d")
USimpleNameReferenceExpression (identifier = i)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = i)
UExpressionList (switch_entry)
UYieldExpression
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "int %d")
USimpleNameReferenceExpression (identifier = i)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = l)
UBinaryExpression (operator = <)
USimpleNameReferenceExpression (identifier = l)
ULiteralExpression (value = 0)
UExpressionList (switch_entry)
UYieldExpression
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "long %d")
USimpleNameReferenceExpression (identifier = l)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = d)
UExpressionList (switch_entry)
UYieldExpression
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "double %f")
USimpleNameReferenceExpression (identifier = d)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = s)
UExpressionList (switch_entry)
UYieldExpression
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "String %s")
USimpleNameReferenceExpression (identifier = s)
USwitchClauseExpressionWithBody
UDefaultCaseExpression
UExpressionList (switch_entry)
UYieldExpression
UBinaryExpression (operator = =)
USimpleNameReferenceExpression (identifier = formatted)
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = o)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0))
UIdentifier (Identifier (toString))
UReturnExpression
USimpleNameReferenceExpression (identifier = formatted)
@@ -0,0 +1,31 @@
public class TypePattern {
static fun formatter(o: java.lang.Object) : java.lang.String {
var formatted: java.lang.String = switch (o)
java.lang.Integer i when i < 0 -> {
yield String.format("int %d", i)
}
java.lang.Integer i -> {
yield String.format("int %d", i)
}
java.lang.Long l when l < 0 -> {
yield String.format("long %d", l)
}
java.lang.Double d -> {
yield String.format("double %f", d)
}
java.lang.String s -> {
yield String.format("String %s", s)
}
else -> {
yield formatted = o.toString()
}
return formatted
}
}
@@ -0,0 +1,20 @@
public class TypePattern {
static String formatter(Object o) {
String formatted;
switch (o) {
case Integer i when i < 0 ->
formatted = String.format("int %d", i);
case Integer i ->
formatted = String.format("int %d", i);
case Long l when l < 0 ->
formatted = String.format("long %d", l);
case Double d ->
formatted = String.format("double %f", d);
case String s ->
formatted = String.format("String %s", s);
default ->
formatted = o.toString();
}
return formatted;
}
}
@@ -0,0 +1,93 @@
UFile (package = )
UClass (name = TypePattern)
UMethod (name = formatter)
UParameter (name = o)
UBlockExpression
UDeclarationsExpression
ULocalVariable (name = formatted)
USwitchExpression
USimpleNameReferenceExpression (identifier = o)
UExpressionList (switch)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = i)
UBinaryExpression (operator = <)
USimpleNameReferenceExpression (identifier = i)
ULiteralExpression (value = 0)
UExpressionList (switch_entry)
UYieldExpression
UBinaryExpression (operator = =)
USimpleNameReferenceExpression (identifier = formatted)
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "int %d")
USimpleNameReferenceExpression (identifier = i)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = i)
UExpressionList (switch_entry)
UYieldExpression
UBinaryExpression (operator = =)
USimpleNameReferenceExpression (identifier = formatted)
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "int %d")
USimpleNameReferenceExpression (identifier = i)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = l)
UBinaryExpression (operator = <)
USimpleNameReferenceExpression (identifier = l)
ULiteralExpression (value = 0)
UExpressionList (switch_entry)
UYieldExpression
UBinaryExpression (operator = =)
USimpleNameReferenceExpression (identifier = formatted)
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "long %d")
USimpleNameReferenceExpression (identifier = l)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = d)
UExpressionList (switch_entry)
UYieldExpression
UBinaryExpression (operator = =)
USimpleNameReferenceExpression (identifier = formatted)
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "double %f")
USimpleNameReferenceExpression (identifier = d)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = s)
UExpressionList (switch_entry)
UYieldExpression
UBinaryExpression (operator = =)
USimpleNameReferenceExpression (identifier = formatted)
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "String %s")
USimpleNameReferenceExpression (identifier = s)
USwitchClauseExpressionWithBody
UDefaultCaseExpression
UExpressionList (switch_entry)
UYieldExpression
UBinaryExpression (operator = =)
USimpleNameReferenceExpression (identifier = formatted)
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = o)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0))
UIdentifier (Identifier (toString))
UReturnExpression
USimpleNameReferenceExpression (identifier = formatted)
@@ -0,0 +1,32 @@
public class TypePattern {
static fun formatter(o: java.lang.Object) : java.lang.String {
var formatted: java.lang.String
switch (o)
java.lang.Integer i when i < 0 -> {
yield formatted = String.format("int %d", i)
}
java.lang.Integer i -> {
yield formatted = String.format("int %d", i)
}
java.lang.Long l when l < 0 -> {
yield formatted = String.format("long %d", l)
}
java.lang.Double d -> {
yield formatted = String.format("double %f", d)
}
java.lang.String s -> {
yield formatted = String.format("String %s", s)
}
else -> {
yield formatted = o.toString()
}
return formatted
}
}
@@ -0,0 +1,19 @@
public class TypePattern {
static String formatter(Object o) {
String formatted = switch (o) {
case Integer i when i < 0:
yield String.format("int %d", i);
case Integer i:
yield String.format("int %d", i);
case Long l when l < 0:
yield String.format("long %d", l);
case Double d:
yield String.format("double %f", d);
case String s:
yield String.format("String %s", s);
default:
yield formatted = o.toString();
};
return formatted;
}
}
@@ -0,0 +1,83 @@
UFile (package = )
UClass (name = TypePattern)
UMethod (name = formatter)
UParameter (name = o)
UBlockExpression
UDeclarationsExpression
ULocalVariable (name = formatted)
USwitchExpression
USimpleNameReferenceExpression (identifier = o)
UExpressionList (switch)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = i)
UBinaryExpression (operator = <)
USimpleNameReferenceExpression (identifier = i)
ULiteralExpression (value = 0)
UExpressionList (switch_entry)
UYieldExpression
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "int %d")
USimpleNameReferenceExpression (identifier = i)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = i)
UExpressionList (switch_entry)
UYieldExpression
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "int %d")
USimpleNameReferenceExpression (identifier = i)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = l)
UBinaryExpression (operator = <)
USimpleNameReferenceExpression (identifier = l)
ULiteralExpression (value = 0)
UExpressionList (switch_entry)
UYieldExpression
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "long %d")
USimpleNameReferenceExpression (identifier = l)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = d)
UExpressionList (switch_entry)
UYieldExpression
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "double %f")
USimpleNameReferenceExpression (identifier = d)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = s)
UExpressionList (switch_entry)
UYieldExpression
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "String %s")
USimpleNameReferenceExpression (identifier = s)
USwitchClauseExpressionWithBody
UDefaultCaseExpression
UExpressionList (switch_entry)
UYieldExpression
UBinaryExpression (operator = =)
USimpleNameReferenceExpression (identifier = formatted)
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = o)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0))
UIdentifier (Identifier (toString))
UReturnExpression
USimpleNameReferenceExpression (identifier = formatted)
@@ -0,0 +1,31 @@
public class TypePattern {
static fun formatter(o: java.lang.Object) : java.lang.String {
var formatted: java.lang.String = switch (o)
java.lang.Integer i when i < 0 -> {
yield String.format("int %d", i)
}
java.lang.Integer i -> {
yield String.format("int %d", i)
}
java.lang.Long l when l < 0 -> {
yield String.format("long %d", l)
}
java.lang.Double d -> {
yield String.format("double %f", d)
}
java.lang.String s -> {
yield String.format("String %s", s)
}
else -> {
yield formatted = o.toString()
}
return formatted
}
}
@@ -0,0 +1,26 @@
public class TypePattern {
static String formatter(Object o) {
String formatted;
switch (o) {
case Integer i when i < 0:
formatted = String.format("int %d", i);
break;
case Integer i:
formatted = String.format("int %d", i);
break;
case Long l when l < 0:
formatted = String.format("long %d", l);
break;
case Double d:
formatted = String.format("double %f", d);
break;
case String s:
formatted = String.format("String %s", s);
break;
default:
formatted = o.toString();
break;
}
return formatted;
}
}
@@ -0,0 +1,93 @@
UFile (package = )
UClass (name = TypePattern)
UMethod (name = formatter)
UParameter (name = o)
UBlockExpression
UDeclarationsExpression
ULocalVariable (name = formatted)
USwitchExpression
USimpleNameReferenceExpression (identifier = o)
UExpressionList (switch)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = i)
UBinaryExpression (operator = <)
USimpleNameReferenceExpression (identifier = i)
ULiteralExpression (value = 0)
UExpressionList (switch_entry)
UBinaryExpression (operator = =)
USimpleNameReferenceExpression (identifier = formatted)
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "int %d")
USimpleNameReferenceExpression (identifier = i)
UBreakExpression (label = null)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = i)
UExpressionList (switch_entry)
UBinaryExpression (operator = =)
USimpleNameReferenceExpression (identifier = formatted)
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "int %d")
USimpleNameReferenceExpression (identifier = i)
UBreakExpression (label = null)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = l)
UBinaryExpression (operator = <)
USimpleNameReferenceExpression (identifier = l)
ULiteralExpression (value = 0)
UExpressionList (switch_entry)
UBinaryExpression (operator = =)
USimpleNameReferenceExpression (identifier = formatted)
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "long %d")
USimpleNameReferenceExpression (identifier = l)
UBreakExpression (label = null)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = d)
UExpressionList (switch_entry)
UBinaryExpression (operator = =)
USimpleNameReferenceExpression (identifier = formatted)
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "double %f")
USimpleNameReferenceExpression (identifier = d)
UBreakExpression (label = null)
USwitchClauseExpressionWithBody
UPatternExpression
UParameter (name = s)
UExpressionList (switch_entry)
UBinaryExpression (operator = =)
USimpleNameReferenceExpression (identifier = formatted)
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = String)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
UIdentifier (Identifier (format))
ULiteralExpression (value = "String %s")
USimpleNameReferenceExpression (identifier = s)
UBreakExpression (label = null)
USwitchClauseExpressionWithBody
UDefaultCaseExpression
UExpressionList (switch_entry)
UBinaryExpression (operator = =)
USimpleNameReferenceExpression (identifier = formatted)
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = o)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0))
UIdentifier (Identifier (toString))
UBreakExpression (label = null)
UReturnExpression
USimpleNameReferenceExpression (identifier = formatted)
@@ -0,0 +1,38 @@
public class TypePattern {
static fun formatter(o: java.lang.Object) : java.lang.String {
var formatted: java.lang.String
switch (o)
java.lang.Integer i when i < 0 -> {
formatted = String.format("int %d", i)
break
}
java.lang.Integer i -> {
formatted = String.format("int %d", i)
break
}
java.lang.Long l when l < 0 -> {
formatted = String.format("long %d", l)
break
}
java.lang.Double d -> {
formatted = String.format("double %f", d)
break
}
java.lang.String s -> {
formatted = String.format("String %s", s)
break
}
else -> {
formatted = o.toString()
break
}
return formatted
}
}
@@ -1,8 +1,8 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.uast.test.java
import org.jetbrains.uast.UFile
import com.intellij.platform.uast.testFramework.common.RenderLogTestBase
import org.jetbrains.uast.UFile
import org.junit.Test
class SimpleJavaRenderLogTest : AbstractJavaRenderLogTest(), RenderLogTestBase {
@@ -94,4 +94,16 @@ class SimpleJavaRenderLogTest : AbstractJavaRenderLogTest(), RenderLogTestBase {
@Test
fun testSwitchCaseRecordPattern() = doTest("Simple/SwitchCaseRecordPattern.java")
@Test
fun testOldStyleSwitchStatementWithGuard() = doTest("Simple/OldStyleSwitchStatementWithGuard.java")
@Test
fun testNewStyleSwitchStatementWithGuard() = doTest("Simple/NewStyleSwitchStatementWithGuard.java")
@Test
fun testOldStyleSwitchExpressionWithGuard() = doTest("Simple/OldStyleSwitchExpressionWithGuard.java")
@Test
fun testNewStyleSwitchExpressionWithGuard() = doTest("Simple/NewStyleSwitchExpressionWithGuard.java")
}