[uast-java] Add support for pattern matching

#IDEA-310333 Fixed

GitOrigin-RevId: 31fa57a0fdc5efcdfc419cd56ccc783f682234ce
This commit is contained in:
Bart van Helvert
2024-02-13 12:58:38 +00:00
committed by intellij-monorepo-bot
parent 992ce073b4
commit 6d55bf058d
18 changed files with 410 additions and 11 deletions
@@ -20,10 +20,8 @@ import org.jetbrains.uast.*
import org.jetbrains.uast.analysis.UastAnalysisPlugin
import org.jetbrains.uast.java.JavaConverter.convertPsiElement
import org.jetbrains.uast.java.declarations.JavaLazyParentUIdentifier
import org.jetbrains.uast.java.expressions.JavaUAnnotationCallExpression
import org.jetbrains.uast.java.expressions.*
import org.jetbrains.uast.java.expressions.JavaUModuleReferenceExpression
import org.jetbrains.uast.java.expressions.JavaUNamedExpression
import org.jetbrains.uast.java.expressions.JavaUSynchronizedExpression
import org.jetbrains.uast.util.ClassSet
import org.jetbrains.uast.util.ClassSetsWrapper
@@ -319,6 +317,14 @@ internal object JavaConverter {
override fun visitResourceList(resourceList: PsiResourceList) {
result = true
}
override fun visitDeconstructionList(deconstructionList: PsiDeconstructionList) {
result = true
}
override fun visitPatternVariable(variable: PsiPatternVariable) {
result = true
}
}
element.accept(visitor)
@@ -411,6 +417,19 @@ internal object JavaConverter {
override fun visitComment(comment: PsiComment) {
result = requiredType.el<UComment,PsiComment>(comment, givenParent, ::UComment)
}
override fun visitTypeTestPattern(pattern: PsiTypeTestPattern) {
result = requiredType.expr<UPatternExpression, PsiTypeTestPattern>(pattern, givenParent, ::JavaUTypePatternExpression)
}
override fun visitUnnamedPattern(pattern: PsiUnnamedPattern) {
result = requiredType.expr<UPatternExpression, PsiUnnamedPattern>(pattern, givenParent, ::JavaUUnamedPatternExpression)
}
override fun visitDeconstructionPattern(deconstructionPattern: PsiDeconstructionPattern) {
result = requiredType.expr<UPatternExpression, PsiDeconstructionPattern>(deconstructionPattern, givenParent,
::JavaUDeconstructionPatternPattern)
}
}
el.accept(visitor)
@@ -464,8 +483,12 @@ internal object JavaConverter {
}
override fun visitInstanceOfExpression(expression: PsiInstanceOfExpression) {
result = requiredType.expr<UBinaryExpressionWithType, PsiInstanceOfExpression>(expression, givenParent,
::JavaUInstanceCheckExpression)
result = if (expression.pattern != null) {
requiredType.expr<UBinaryExpressionWithType, PsiInstanceOfExpression>(expression, givenParent,
::JavaUInstanceWithPatternExpression)
} else {
requiredType.expr<UBinaryExpressionWithPattern, PsiInstanceOfExpression>(expression, givenParent, ::JavaUInstanceCheckExpression)
}
}
override fun visitLambdaExpression(expression: PsiLambdaExpression) {
@@ -130,8 +130,7 @@ class JavaUSwitchEntry(
}
else {
it.caseLabelElementList?.elements.orEmpty().map { element ->
if (element is PsiExpression) JavaConverter.convertOrEmpty(element, this)
else UnknownJavaExpression(element, this)
JavaConverter.convertPsiElement(element, this, UExpression::class.java) as? UExpression ?: UnknownJavaExpression(element, this)
}
}
}
@@ -0,0 +1,30 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.uast.java.expressions
import com.intellij.psi.PsiInstanceOfExpression
import com.intellij.psi.PsiType
import com.intellij.psi.PsiTypes
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.uast.*
import org.jetbrains.uast.java.JavaAbstractUExpression
import org.jetbrains.uast.java.JavaConverter
@ApiStatus.Internal
class JavaUInstanceWithPatternExpression(
override val sourcePsi: PsiInstanceOfExpression,
givenParent: UElement?
) : JavaAbstractUExpression(givenParent), UBinaryExpressionWithPattern {
private val operandPart = UastLazyPart<UExpression>()
private val patternPart = UastLazyPart<UPatternExpression?>()
override val operand: UExpression get() = operandPart.getOrBuild { JavaConverter.convertOrEmpty(sourcePsi.operand, this) }
override val patternExpression: UPatternExpression? get() = patternPart.getOrBuild {
sourcePsi.pattern?.let { pattern ->
JavaConverter.convertPsiElement(pattern, this, UPatternExpression::class.java) as? UPatternExpression
}
}
override fun getExpressionType(): PsiType = PsiTypes.booleanType()
}
@@ -0,0 +1,62 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.uast.java.expressions
import com.intellij.psi.PsiDeconstructionPattern
import com.intellij.psi.PsiTypeTestPattern
import com.intellij.psi.PsiUnnamedPattern
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.uast.*
import org.jetbrains.uast.java.JavaAbstractUExpression
import org.jetbrains.uast.java.JavaConverter
import org.jetbrains.uast.java.JavaUTypeReferenceExpression
@ApiStatus.Internal
class JavaUUnamedPatternExpression(
override val sourcePsi: PsiUnnamedPattern,
givenParent: UElement?
) : JavaAbstractUExpression(givenParent), UPatternExpression {
override val name: String? = null
override val typeReference: UTypeReferenceExpression? = null
override val deconstructedPatterns: List<UPatternExpression> = emptyList()
}
@ApiStatus.Internal
class JavaUTypePatternExpression(
override val sourcePsi: PsiTypeTestPattern,
givenParent: UElement?
) : JavaAbstractUExpression(givenParent), UPatternExpression {
private val typeReferencePart = UastLazyPart<UTypeReferenceExpression?>()
override val name: String? = sourcePsi.patternVariable?.name
override val deconstructedPatterns: List<UPatternExpression> = emptyList()
override val typeReference: UTypeReferenceExpression? = typeReferencePart.getOrBuild {
sourcePsi.checkType?.let { typeElem -> JavaUTypeReferenceExpression(typeElem, this) }
}
}
@ApiStatus.Internal
class JavaUDeconstructionPatternPattern(
override val sourcePsi: PsiDeconstructionPattern,
givenParent: UElement?
) : JavaAbstractUExpression(givenParent), UPatternExpression {
private val typeReferencePart = UastLazyPart<UTypeReferenceExpression>()
private val patternsPart = UastLazyPart<List<UPatternExpression>>()
override val name: String? = null
override val deconstructedPatterns: List<UPatternExpression>
get() = patternsPart.getOrBuild {
sourcePsi.deconstructionList.deconstructionComponents.mapNotNull { component ->
JavaConverter.convertPsiElement(component, this, UPatternExpression::class.java) as? UPatternExpression
}
}
override val typeReference: UTypeReferenceExpression = typeReferencePart.getOrBuild {
JavaUTypeReferenceExpression(sourcePsi.typeElement, this)
}
}
@@ -133,7 +133,10 @@ private val possibleSourceTypes = mapOf<Class<*>, ClassSet<PsiElement>>(
PsiWhileStatement::class.java,
PsiYieldStatement::class.java,
PsiRecordComponent::class.java,
PsiRecordHeader::class.java
PsiRecordHeader::class.java,
PsiUnnamedPattern::class.java,
PsiTypeTestPattern::class.java,
PsiDeconstructionPattern::class.java
),
UAnnotation::class.java to classSetOf<PsiElement>(
PsiAnnotation::class.java
@@ -332,7 +335,10 @@ private val possibleSourceTypes = mapOf<Class<*>, ClassSet<PsiElement>>(
PsiWhileStatement::class.java,
PsiYieldStatement::class.java,
PsiRecordComponent::class.java,
PsiRecordHeader::class.java
PsiRecordHeader::class.java,
PsiUnnamedPattern::class.java,
PsiTypeTestPattern::class.java,
PsiDeconstructionPattern::class.java
),
UElementWithLocation::class.java to classSetOf<PsiElement>(
//PsiExpressionStatement::class.java,
@@ -344,6 +350,11 @@ private val possibleSourceTypes = mapOf<Class<*>, ClassSet<PsiElement>>(
UEnumConstantEx::class.java to classSetOf<PsiElement>(
PsiEnumConstant::class.java
),
UPatternExpression::class.java to classSetOf<PsiElement>(
PsiUnnamedPattern::class.java,
PsiTypeTestPattern::class.java,
PsiDeconstructionPattern::class.java
),
UExpression::class.java to classSetOf<PsiElement>(
PsiAnnotatedJavaCodeReferenceElement::class.java,
PsiAnnotation::class.java,
@@ -397,7 +408,10 @@ private val possibleSourceTypes = mapOf<Class<*>, ClassSet<PsiElement>>(
PsiTypeCastExpression::class.java,
PsiTypeElement::class.java,
PsiWhileStatement::class.java,
PsiYieldStatement::class.java
PsiYieldStatement::class.java,
PsiUnnamedPattern::class.java,
PsiTypeTestPattern::class.java,
PsiDeconstructionPattern::class.java
),
UExpressionList::class.java to classSetOf<PsiElement>(
),
@@ -0,0 +1,15 @@
public class RecordPattern {
public static void main(String[] args) {
Box<Ball> b = new Box<>(new RedBall());
if (b instanceof Box(RedBall a)) { }
if (b instanceof Box(GreenBall _)) { }
if (b instanceof Box(_)) { }
}
}
record Box<T extends Ball>(T content) { }
sealed abstract class Ball permits RedBall, BlueBall, GreenBall { }
final class RedBall extends Ball { }
final class BlueBall extends Ball { }
final class GreenBall extends Ball { }
@@ -0,0 +1,46 @@
UFile (package = )
UClass (name = RecordPattern)
UMethod (name = main)
UParameter (name = args)
UBlockExpression
UDeclarationsExpression
ULocalVariable (name = b)
UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 1))
USimpleNameReferenceExpression (identifier = Box)
UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0))
USimpleNameReferenceExpression (identifier = RedBall)
UIfExpression
UBinaryExpressionWithPattern
USimpleNameReferenceExpression (identifier = b)
UPatternExpression
UTypeReferenceExpression (name = Box)
UPatternExpression
UTypeReferenceExpression (name = RedBall)
UBlockExpression
UastEmptyExpression
UIfExpression
UBinaryExpressionWithPattern
USimpleNameReferenceExpression (identifier = b)
UPatternExpression
UTypeReferenceExpression (name = Box)
UPatternExpression
UTypeReferenceExpression (name = GreenBall)
UBlockExpression
UastEmptyExpression
UIfExpression
UBinaryExpressionWithPattern
USimpleNameReferenceExpression (identifier = b)
UPatternExpression
UTypeReferenceExpression (name = Box)
UPatternExpression
UBlockExpression
UastEmptyExpression
UClass (name = Box)
UField (name = content)
UMethod (name = content)
UMethod (name = Box)
UParameter (name = content)
UClass (name = Ball)
UClass (name = RedBall)
UClass (name = BlueBall)
UClass (name = GreenBall)
@@ -0,0 +1,29 @@
public class RecordPattern {
public static fun main(args: java.lang.String[]) : void {
var b: Box<Ball> = Box(RedBall())
if (b is Box(RedBall a) _) {
}
if (b is Box(GreenBall _) _) {
}
if (b is Box(_) _) {
}
}
}
final class Box {
private final var content: T
public fun content() : T = UastEmptyExpression
fun Box(content: T) = UastEmptyExpression
}
abstract sealed class Ball {
}
final class RedBall : Ball {
}
final class BlueBall : Ball {
}
final class GreenBall : Ball {
}
@@ -0,0 +1,5 @@
public class TypePattern {
public static int foo(Number x) {
if (x instanceof Integer y) { }
}
}
@@ -0,0 +1,12 @@
UFile (package = )
UClass (name = TypePattern)
UMethod (name = foo)
UParameter (name = x)
UBlockExpression
UIfExpression
UBinaryExpressionWithPattern
USimpleNameReferenceExpression (identifier = x)
UPatternExpression
UTypeReferenceExpression (name = java.lang.Integer)
UBlockExpression
UastEmptyExpression
@@ -0,0 +1,6 @@
public class TypePattern {
public static fun foo(x: java.lang.Number) : int {
if (x is java.lang.Integer y) {
}
}
}
@@ -0,0 +1,17 @@
public class TypePattern {
public static int foo(Number x) {
Box<Ball> b = new Box<>(null);
switch (b) {
case Box(RedBall _), Box(BlueBall _) -> System.out.println("red or blue");
case Box(GreenBall a) -> System.out.println("green");
case Box(_) -> System.out.println("null");
}
}
}
record Box<T extends Ball>(T content) { }
sealed abstract class Ball permits RedBall, BlueBall, GreenBall { }
final class RedBall extends Ball { }
final class BlueBall extends Ball { }
final class GreenBall extends Ball { }
@@ -0,0 +1,67 @@
UFile (package = )
UClass (name = TypePattern)
UMethod (name = foo)
UParameter (name = x)
UBlockExpression
UDeclarationsExpression
ULocalVariable (name = b)
UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 1))
USimpleNameReferenceExpression (identifier = Box)
ULiteralExpression (value = null)
USwitchExpression
USimpleNameReferenceExpression (identifier = b)
UExpressionList (switch)
USwitchClauseExpressionWithBody
UPatternExpression
UTypeReferenceExpression (name = Box)
UPatternExpression
UTypeReferenceExpression (name = RedBall)
UPatternExpression
UTypeReferenceExpression (name = Box)
UPatternExpression
UTypeReferenceExpression (name = BlueBall)
UExpressionList (switch_entry)
UYieldExpression
UQualifiedReferenceExpression
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = System)
USimpleNameReferenceExpression (identifier = out)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
UIdentifier (Identifier (println))
ULiteralExpression (value = "red or blue")
USwitchClauseExpressionWithBody
UPatternExpression
UTypeReferenceExpression (name = Box)
UPatternExpression
UTypeReferenceExpression (name = GreenBall)
UExpressionList (switch_entry)
UYieldExpression
UQualifiedReferenceExpression
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = System)
USimpleNameReferenceExpression (identifier = out)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
UIdentifier (Identifier (println))
ULiteralExpression (value = "green")
USwitchClauseExpressionWithBody
UPatternExpression
UTypeReferenceExpression (name = Box)
UPatternExpression
UExpressionList (switch_entry)
UYieldExpression
UQualifiedReferenceExpression
UQualifiedReferenceExpression
USimpleNameReferenceExpression (identifier = System)
USimpleNameReferenceExpression (identifier = out)
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
UIdentifier (Identifier (println))
ULiteralExpression (value = "null")
UClass (name = Box)
UField (name = content)
UMethod (name = content)
UMethod (name = Box)
UParameter (name = content)
UClass (name = Ball)
UClass (name = RedBall)
UClass (name = BlueBall)
UClass (name = GreenBall)
@@ -0,0 +1,37 @@
public class TypePattern {
public static fun foo(x: java.lang.Number) : int {
var b: Box<Ball> = Box(null)
switch (b)
Box(RedBall _) _, Box(BlueBall _) _ -> {
yield System.out.println("red or blue")
}
Box(GreenBall a) _ -> {
yield System.out.println("green")
}
Box(_) _ -> {
yield System.out.println("null")
}
}
}
final class Box {
private final var content: T
public fun content() : T = UastEmptyExpression
fun Box(content: T) = UastEmptyExpression
}
abstract sealed class Ball {
}
final class RedBall : Ball {
}
final class BlueBall : Ball {
}
final class GreenBall : Ball {
}
@@ -0,0 +1,7 @@
public class TypePattern {
public static int foo(Number x) {
switch (x) {
case Integer i -> { }
}
}
}
@@ -0,0 +1,12 @@
UFile (package = )
UClass (name = TypePattern)
UMethod (name = foo)
UParameter (name = x)
UBlockExpression
USwitchExpression
USimpleNameReferenceExpression (identifier = x)
UExpressionList (switch)
USwitchClauseExpressionWithBody
UPatternExpression
UTypeReferenceExpression (name = java.lang.Integer)
UExpressionList (switch_entry)
@@ -0,0 +1,9 @@
public class TypePattern {
public static fun foo(x: java.lang.Number) : int {
switch (x)
java.lang.Integer i -> {
}
}
}
@@ -78,5 +78,14 @@ class SimpleJavaRenderLogTest : AbstractJavaRenderLogTest(), RenderLogTestBase {
fun testRecord() = doTest("Simple/Record.java")
@Test
fun testTypeAnnotations() = doTest("Simple/TypeAnnotations.java")
fun testInstanceOfTypeTestPattern() = doTest("Simple/InstanceOfTypeTestPattern.java")
@Test
fun testInstanceOfRecordPattern() = doTest("Simple/InstanceOfRecordPattern.java")
@Test
fun testSwitchCaseTypeTestPattern() = doTest("Simple/SwitchCaseTypeTestPattern.java")
@Test
fun testSwitchCaseRecordPattern() = doTest("Simple/SwitchCaseRecordPattern.java")
}