From f8aed5ba57498afa21c12bb97ee4a3655717c738 Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Fri, 30 Nov 2018 17:17:07 +0300 Subject: [PATCH] [uast] `UBreakWithValueExpression` for evaluating Java 12 "enhanced" switch (IDEA-202555) --- .../uast/evaluation/TreeBasedEvaluator.kt | 37 +++- .../uast/evaluation/UEvaluationInfo.kt | 5 + .../uast/expressions/UBreakExpression.kt | 21 +++ .../jetbrains/uast/values/UNothingValue.kt | 15 +- .../org/jetbrains/uast/values/UPhiValue.kt | 3 + .../uast/java/JavaAbstractUElement.kt | 9 +- .../uast/java/JavaUastLanguagePlugin.kt | 4 +- .../JavaUSwitchExpression.kt | 53 +++++- .../java/expressions/JavaUBreakExpression.kt | 15 ++ .../java/Simple/EnhancedSwitch.java | 2 +- .../java/Simple/EnhancedSwitch.log.txt | 26 ++- .../java/Simple/EnhancedSwitch.render.txt | 18 +- .../java/Simple/EnhancedSwitch.values.txt | 77 ++++++++ .../EnhancedSwitchComplexEvaluations.java | 85 +++++++++ ...nhancedSwitchComplexEvaluations.values.txt | 172 ++++++++++++++++++ .../uast/test/java/JavaValuesTest.kt | 6 + 16 files changed, 514 insertions(+), 34 deletions(-) create mode 100644 uast/uast-tests/java/Simple/EnhancedSwitch.values.txt create mode 100644 uast/uast-tests/java/Simple/EnhancedSwitchComplexEvaluations.java create mode 100644 uast/uast-tests/java/Simple/EnhancedSwitchComplexEvaluations.values.txt diff --git a/uast/uast-common/src/org/jetbrains/uast/evaluation/TreeBasedEvaluator.kt b/uast/uast-common/src/org/jetbrains/uast/evaluation/TreeBasedEvaluator.kt index 3a1d5a0a627f..62817c458848 100644 --- a/uast/uast-common/src/org/jetbrains/uast/evaluation/TreeBasedEvaluator.kt +++ b/uast/uast-common/src/org/jetbrains/uast/evaluation/TreeBasedEvaluator.kt @@ -113,7 +113,12 @@ class TreeBasedEvaluator( override fun visitBreakExpression(node: UBreakExpression, data: UEvaluationState): UEvaluationInfo { storeState(node, data) - return UNothingValue(node) to data storeResultFor node + return when (node) { + is UBreakWithValueExpression -> + node.valueExpression?.accept(chain, data)?.let { UBreakResult(it.value, node) } ?: UUndeterminedValue + else -> + UNothingValue(node) + } to data storeResultFor node } override fun visitContinueExpression(node: UContinueExpression, data: UEvaluationState): UEvaluationInfo { @@ -523,9 +528,9 @@ class TreeBasedEvaluator( if (!clauseInfo.reachable) break } val clauseValue = clauseInfo.value - if (clauseValue is UNothingValue && clauseValue.containingLoopOrSwitch == node) { + if (exitingNode(clauseValue) == node) { // break from switch - resultInfo = resultInfo?.merge(clauseInfo) ?: clauseInfo + resultInfo = resultInfo merge getBreakResult(clauseInfo) if (caseCondition == UBooleanConstant.True) break@clausesLoop clauseInfo = subjectInfo fallThroughCondition = UBooleanConstant.False @@ -546,6 +551,32 @@ class TreeBasedEvaluator( return resultInfo storeResultFor node } + private fun exitingNode(uValue: UValue): UExpression? { + when (uValue) { + is UNothingValue -> return uValue.containingLoopOrSwitch + is UPhiValue -> { + for (value in uValue.values) if (value is UBreakResult) return value.containingLoopOrSwitch + for (value in uValue.values) if (value is UNothingValue) value.containingLoopOrSwitch?.let { return it } + return null + } + else -> return null + } + } + + private fun getBreakResult(clauseInfo: UEvaluationInfo): UEvaluationInfo { + val clauseValue = clauseInfo.value + return when (clauseValue) { + is UBreakResult -> clauseValue.value to clauseInfo.state + is UPhiValue -> UPhiValue.create(clauseValue.values.map { + when (it) { + is UBreakResult -> it.value + else -> it + } + }) to clauseInfo.state + else -> clauseInfo + } + } + private fun evaluateLoop( loop: ULoopExpression, inputState: UEvaluationState, diff --git a/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluationInfo.kt b/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluationInfo.kt index e555023fdd02..7de39a4139b3 100644 --- a/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluationInfo.kt +++ b/uast/uast-common/src/org/jetbrains/uast/evaluation/UEvaluationInfo.kt @@ -34,4 +34,9 @@ data class UEvaluationInfo(val value: UValue, val state: UEvaluationState) { get() = value.reachable } +infix fun UEvaluationInfo?.merge(otherInfo: UEvaluationInfo?): UEvaluationInfo? = + if (this != null) + if (otherInfo != null) this.merge(otherInfo) else this + else otherInfo + infix fun UValue.to(state: UEvaluationState): UEvaluationInfo = UEvaluationInfo(this, state) \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/expressions/UBreakExpression.kt b/uast/uast-common/src/org/jetbrains/uast/expressions/UBreakExpression.kt index d98b5e13a94c..935fe204f433 100644 --- a/uast/uast-common/src/org/jetbrains/uast/expressions/UBreakExpression.kt +++ b/uast/uast-common/src/org/jetbrains/uast/expressions/UBreakExpression.kt @@ -38,4 +38,25 @@ interface UBreakExpression : UJumpExpression { override fun asLogString(): String = log("label = $label") override fun asRenderString(): String = label?.let { "break@$it" } ?: "break" +} + +interface UBreakWithValueExpression : UBreakExpression { + + val valueExpression: UExpression? + + override fun accept(visitor: UastVisitor) { + if (visitor.visitBreakExpression(this)) return + annotations.acceptList(visitor) + valueExpression?.accept(visitor) + visitor.afterVisitBreakExpression(this) + } + + override fun asLogString(): String = log("label = $label, hasValue = ${valueExpression != null}") + + override fun asRenderString(): String = buildString { + append("break") + label?.let { append("@$it") } + valueExpression?.let { append(" ${it.asRenderString()}") } + } + } \ No newline at end of file diff --git a/uast/uast-common/src/org/jetbrains/uast/values/UNothingValue.kt b/uast/uast-common/src/org/jetbrains/uast/values/UNothingValue.kt index cb63ca352bb5..38afb096e846 100644 --- a/uast/uast-common/src/org/jetbrains/uast/values/UNothingValue.kt +++ b/uast/uast-common/src/org/jetbrains/uast/values/UNothingValue.kt @@ -18,7 +18,7 @@ package org.jetbrains.uast.values import org.jetbrains.uast.* // Something that never can be reached / created -internal class UNothingValue private constructor( +internal open class UNothingValue private constructor( val containingLoopOrSwitch: UExpression?, val kind: JumpKind ) : UValueBase() { @@ -36,6 +36,7 @@ internal class UNothingValue private constructor( override val reachable = false override fun merge(other: UValue) = when (other) { + is UBreakResult -> other is UNothingValue -> { val mergedLoopOrSwitch = if (containingLoopOrSwitch == other.containingLoopOrSwitch) containingLoopOrSwitch @@ -43,7 +44,7 @@ internal class UNothingValue private constructor( val mergedKind = if (mergedLoopOrSwitch == null || kind != other.kind) JumpKind.OTHER else kind UNothingValue(mergedLoopOrSwitch, mergedKind) } - else -> super.merge(other) + else -> other } override fun toString() = "Nothing" + when (kind) { @@ -77,3 +78,13 @@ internal class UNothingValue private constructor( } } } + +internal class UBreakResult(val value: UValue, jump: UBreakWithValueExpression) : UNothingValue(jump) { + override fun toString(): String = "UBreakResult($value)" + + override fun merge(other: UValue): UValue = when (other) { + is UBreakResult -> UPhiValue.create(this, other) + is UNothingValue -> this + else -> other.merge(this) + } +} diff --git a/uast/uast-common/src/org/jetbrains/uast/values/UPhiValue.kt b/uast/uast-common/src/org/jetbrains/uast/values/UPhiValue.kt index 0eada83a7f1e..48d332ac1503 100644 --- a/uast/uast-common/src/org/jetbrains/uast/values/UPhiValue.kt +++ b/uast/uast-common/src/org/jetbrains/uast/values/UPhiValue.kt @@ -11,6 +11,9 @@ class UPhiValue private constructor(val values: Set) : UValueBase() { override fun toString(): String = values.joinToString(prefix = "Phi(", postfix = ")", separator = ", ") + override val reachable: Boolean + get() = values.any { it.reachable } + companion object { private const val PHI_LIMIT = 4 diff --git a/uast/uast-java/src/org/jetbrains/uast/java/JavaAbstractUElement.kt b/uast/uast-java/src/org/jetbrains/uast/java/JavaAbstractUElement.kt index 0af7c1f1fe99..ca953e1664e8 100644 --- a/uast/uast-java/src/org/jetbrains/uast/java/JavaAbstractUElement.kt +++ b/uast/uast-java/src/org/jetbrains/uast/java/JavaAbstractUElement.kt @@ -66,7 +66,6 @@ abstract class JavaAbstractUElement(givenParent: UElement?) : JavaUElementWithCo private fun JavaAbstractUElement.unwrapSwitch(uParent: UElement): UElement { when (uParent) { - is UBreakExpression -> return uParent.uastParent ?: uParent is UBlockExpression -> { val codeBlockParent = uParent.uastParent when (codeBlockParent) { @@ -92,8 +91,12 @@ private fun JavaAbstractUElement.unwrapSwitch(uParent: UElement): UElement { is JavaUSwitchEntry -> { val parentSourcePsi = uParent.sourcePsi - if (parentSourcePsi is PsiSwitchLabeledRuleStatement && parentSourcePsi.body?.children?.contains(psi) == true) - return uParent.body + if (parentSourcePsi is PsiSwitchLabeledRuleStatement && parentSourcePsi.body?.children?.contains(psi) == true) { + val psi = psi + return if (psi is PsiExpression && uParent.body.expressions.size == 1) + DummyUBreakExpression(psi, uParent.body) + else uParent.body + } else return uParent } diff --git a/uast/uast-java/src/org/jetbrains/uast/java/JavaUastLanguagePlugin.kt b/uast/uast-java/src/org/jetbrains/uast/java/JavaUastLanguagePlugin.kt index 051e25496269..90031cefece8 100644 --- a/uast/uast-java/src/org/jetbrains/uast/java/JavaUastLanguagePlugin.kt +++ b/uast/uast-java/src/org/jetbrains/uast/java/JavaUastLanguagePlugin.kt @@ -288,7 +288,9 @@ internal object JavaConverter { is PsiDoWhileStatement -> expr(build(::JavaUDoWhileExpression)) is PsiForStatement -> expr(build(::JavaUForExpression)) is PsiForeachStatement -> expr(build(::JavaUForEachExpression)) - is PsiBreakStatement -> expr(build(::JavaUBreakExpression)) + is PsiBreakStatement -> expr { + el.valueExpression?.let { JavaUBreakWithValueExpression(el, it, givenParent) } ?: JavaUBreakExpression(el, givenParent) + } is PsiContinueStatement -> expr(build(::JavaUContinueExpression)) is PsiReturnStatement -> expr(build(::JavaUReturnExpression)) is PsiAssertStatement -> expr(build(::JavaUAssertExpression)) diff --git a/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUSwitchExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUSwitchExpression.kt index c8e90149081d..f6e32d10c456 100644 --- a/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUSwitchExpression.kt +++ b/uast/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUSwitchExpression.kt @@ -54,9 +54,9 @@ class JavaUSwitchEntryList(override val psi: PsiSwitchBlock, override val uastPa val body = statement.body result += when (body) { is PsiBlockStatement -> - JavaUSwitchEntry(listOf(statement), body.codeBlock.statements.toList(), this) + JavaUSwitchEntry(listOf(statement), body.codeBlock.statements.toList(), this, false) else -> - JavaUSwitchEntry(listOf(statement), listOfNotNull(body), this) + JavaUSwitchEntry(listOf(statement), listOfNotNull(body), this, true) } } @@ -87,7 +87,7 @@ class JavaUSwitchEntryList(override val psi: PsiSwitchBlock, override val uastPa if (switchEntries.isInitialized()) return switchEntries.value.find { it.labels.contains(switchLabelStatement) } if (switchLabelStatement is PsiSwitchLabeledRuleStatement) { - return JavaUSwitchEntry(listOf(switchLabelStatement), listOfNotNull(switchLabelStatement.body), this) + return JavaUSwitchEntry(listOf(switchLabelStatement), listOfNotNull(switchLabelStatement.body), this, true) } val bodyStart = switchLabelStatement.nextSiblings.find { it !is PsiSwitchLabelStatement } ?: return null @@ -116,7 +116,8 @@ private val PsiElement.prevSiblings: Sequence get() = generateSequen class JavaUSwitchEntry( val labels: List, val statements: List, - givenParent: UElement? + givenParent: UElement?, + private val addDummyBreak: Boolean = false ) : JavaAbstractUExpression(givenParent), USwitchClauseExpressionWithBody { override val psi: PsiSwitchLabelStatementBase = labels.first() @@ -134,8 +135,21 @@ class JavaUSwitchEntry( override val body: UExpressionList by lz { object : JavaUExpressionList(psi, JavaSpecialExpressionKinds.SWITCH_ENTRY, this) { - override val expressions: List = - this@JavaUSwitchEntry.statements.map { JavaConverter.convertOrEmpty(it, this) } + override val expressions: List + + init { + val expressions = ArrayList(this@JavaUSwitchEntry.statements.size) + for (statement in this@JavaUSwitchEntry.statements) { + expressions.add(JavaConverter.convertOrEmpty(statement, this)) + } + if (addDummyBreak) { + val lastValueExpressionPsi = expressions.lastOrNull()?.sourcePsi as? PsiExpression + if (lastValueExpressionPsi != null) + expressions[expressions.size - 1] = DummyUBreakExpression(lastValueExpressionPsi, this) + } + + this.expressions = expressions + } override fun asRenderString() = buildString { appendln("{") @@ -144,6 +158,33 @@ class JavaUSwitchEntry( } } } + + +} + +internal class DummyUBreakExpression(val valueExpressionPsi: PsiExpression, + override val uastParent: UElement?) : UBreakWithValueExpression { + override val javaPsi: PsiElement? = null + override val sourcePsi: PsiElement? = null + override val psi: PsiElement? + get() = null + override val label: String? + get() = null + override val annotations: List + get() = emptyList() + + override val valueExpression: UExpression? by lazy { JavaConverter.convertExpression(valueExpressionPsi, this) } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as DummyUBreakExpression + return valueExpressionPsi == other.valueExpressionPsi + } + + override fun hashCode(): Int = valueExpressionPsi.hashCode() + } class JavaUDefaultCaseExpression(override val psi: PsiElement?, givenParent: UElement?) diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUBreakExpression.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUBreakExpression.kt index 230e1569ee7b..ba7c3123ce6d 100644 --- a/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUBreakExpression.kt +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/JavaUBreakExpression.kt @@ -17,8 +17,11 @@ package org.jetbrains.uast.java import com.intellij.psi.PsiBreakStatement +import com.intellij.psi.PsiExpression import org.jetbrains.uast.UBreakExpression +import org.jetbrains.uast.UBreakWithValueExpression import org.jetbrains.uast.UElement +import org.jetbrains.uast.UExpression class JavaUBreakExpression( override val psi: PsiBreakStatement, @@ -26,4 +29,16 @@ class JavaUBreakExpression( ) : JavaAbstractUExpression(givenParent), UBreakExpression { override val label: String? get() = psi.labelIdentifier?.text +} + +class JavaUBreakWithValueExpression( + override val psi: PsiBreakStatement, + val psiExpression: PsiExpression, + givenParent: UElement? +) : JavaAbstractUExpression(givenParent), UBreakWithValueExpression { + override val valueExpression: UExpression? by lazy { + JavaConverter.convertExpression(psiExpression, this) + } + override val label: String? + get() = psi.labelIdentifier?.text } \ No newline at end of file diff --git a/uast/uast-tests/java/Simple/EnhancedSwitch.java b/uast/uast-tests/java/Simple/EnhancedSwitch.java index 8ce9f6ec2a0e..ee903a9330a4 100644 --- a/uast/uast-tests/java/Simple/EnhancedSwitch.java +++ b/uast/uast-tests/java/Simple/EnhancedSwitch.java @@ -1,7 +1,7 @@ public class Main { private static String getString() { - var str = "abc"; + var str = "baz"; final String numericString = switch (str) { diff --git a/uast/uast-tests/java/Simple/EnhancedSwitch.log.txt b/uast/uast-tests/java/Simple/EnhancedSwitch.log.txt index f49fb0a8c941..bd5817b5a985 100644 --- a/uast/uast-tests/java/Simple/EnhancedSwitch.log.txt +++ b/uast/uast-tests/java/Simple/EnhancedSwitch.log.txt @@ -4,7 +4,7 @@ UFile (package = ) UBlockExpression UDeclarationsExpression ULocalVariable (name = str) - ULiteralExpression (value = "abc") + ULiteralExpression (value = "baz") UDeclarationsExpression ULocalVariable (name = numericString) USwitchExpression @@ -20,20 +20,24 @@ UFile (package = ) UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) UIdentifier (Identifier (println)) ULiteralExpression (value = "here") - UBreakExpression (label = null) + UBreakWithValueExpression (label = null, hasValue = true) + ULiteralExpression (value = "FOO") USwitchClauseExpressionWithBody ULiteralExpression (value = "bar") ULiteralExpression (value = "beer") UExpressionList (switch_entry) - ULiteralExpression (value = "BAR") + UBreakWithValueExpression (label = null, hasValue = true) + ULiteralExpression (value = "BAR") USwitchClauseExpressionWithBody ULiteralExpression (value = "baz") UExpressionList (switch_entry) - ULiteralExpression (value = "bAz") + UBreakWithValueExpression (label = null, hasValue = true) + ULiteralExpression (value = "bAz") USwitchClauseExpressionWithBody UDefaultCaseExpression UExpressionList (switch_entry) - ULiteralExpression (value = "default") + UBreakWithValueExpression (label = null, hasValue = true) + ULiteralExpression (value = "default") UDeclarationsExpression ULocalVariable (name = numericString2) USwitchExpression @@ -42,11 +46,13 @@ UFile (package = ) USwitchClauseExpressionWithBody ULiteralExpression (value = "foo") UExpressionList (switch_entry) - UBreakExpression (label = null) + UBreakWithValueExpression (label = null, hasValue = true) + ULiteralExpression (value = "FOO") USwitchClauseExpressionWithBody ULiteralExpression (value = "bar") UExpressionList (switch_entry) - UBreakExpression (label = null) + UBreakWithValueExpression (label = null, hasValue = true) + ULiteralExpression (value = "BAR") USwitchClauseExpressionWithBody ULiteralExpression (value = "baz") ULiteralExpression (value = "zub") @@ -58,11 +64,13 @@ UFile (package = ) UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) UIdentifier (Identifier (println)) ULiteralExpression (value = "here") - UBreakExpression (label = null) + UBreakWithValueExpression (label = null, hasValue = true) + ULiteralExpression (value = "bAz") USwitchClauseExpressionWithBody UDefaultCaseExpression UExpressionList (switch_entry) - UBreakExpression (label = null) + UBreakWithValueExpression (label = null, hasValue = true) + ULiteralExpression (value = "default") UReturnExpression UBinaryExpression (operator = +) USimpleNameReferenceExpression (identifier = numericString) diff --git a/uast/uast-tests/java/Simple/EnhancedSwitch.render.txt b/uast/uast-tests/java/Simple/EnhancedSwitch.render.txt index c9f69d782218..fe60471aac44 100644 --- a/uast/uast-tests/java/Simple/EnhancedSwitch.render.txt +++ b/uast/uast-tests/java/Simple/EnhancedSwitch.render.txt @@ -1,41 +1,41 @@ public class Main { private static fun getString() : java.lang.String { - var str: var = "abc" + var str: var = "baz" final var numericString: java.lang.String = switch (str) "foo" -> { System.out.println("here") - break + break "FOO" } "bar", "beer" -> { - "BAR" + break "BAR" } "baz" -> { - "bAz" + break "bAz" } else -> { - "default" + break "default" } final var numericString2: java.lang.String = switch (str) "foo" -> { - break + break "FOO" } "bar" -> { - break + break "BAR" } "baz", "zub" -> { System.out.println("here") - break + break "bAz" } else -> { - break + break "default" } diff --git a/uast/uast-tests/java/Simple/EnhancedSwitch.values.txt b/uast/uast-tests/java/Simple/EnhancedSwitch.values.txt new file mode 100644 index 000000000000..46c249236aea --- /dev/null +++ b/uast/uast-tests/java/Simple/EnhancedSwitch.values.txt @@ -0,0 +1,77 @@ +UFile (package = ) [public class Main {...] + UClass (name = Main) [public class Main {...}] + UMethod (name = getString) [private static fun getString() : java.lang.String {...}] + UBlockExpression [{...}] = Nothing + UDeclarationsExpression [var str: var = "baz"] = Undetermined + ULocalVariable (name = str) [var str: var = "baz"] + ULiteralExpression (value = "baz") ["baz"] = "baz" + UDeclarationsExpression [final var numericString: java.lang.String = switch (str) ...] = Undetermined + ULocalVariable (name = numericString) [final var numericString: java.lang.String = switch (str) ...] + USwitchExpression [switch (str) ...] = "bAz" + USimpleNameReferenceExpression (identifier = str) [str] = (var str = "baz") + UExpressionList (switch) [ "foo" -> {... ] = Undetermined + USwitchClauseExpressionWithBody ["foo" -> {...] = Undetermined + ULiteralExpression (value = "foo") ["foo"] = "foo" + UExpressionList (switch_entry) [{...] = Undetermined + UQualifiedReferenceExpression [System.out.println("here")] = external println("here")("here") + UQualifiedReferenceExpression [System.out] = Undetermined + USimpleNameReferenceExpression (identifier = System) [System] = external System() + USimpleNameReferenceExpression (identifier = out) [out] = Undetermined + UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) [println("here")] = external println("here")("here") + UIdentifier (Identifier (println)) [UIdentifier (Identifier (println))] + ULiteralExpression (value = "here") ["here"] = "here" + UBreakWithValueExpression (label = null, hasValue = true) [break "FOO"] = UBreakResult("FOO") + ULiteralExpression (value = "FOO") ["FOO"] = "FOO" + USwitchClauseExpressionWithBody ["bar", "beer" -> {...] = Undetermined + ULiteralExpression (value = "bar") ["bar"] = "bar" + ULiteralExpression (value = "beer") ["beer"] = "beer" + UExpressionList (switch_entry) [{...] = Undetermined + UBreakWithValueExpression (label = null, hasValue = true) [break "BAR"] = UBreakResult("BAR") + ULiteralExpression (value = "BAR") ["BAR"] = "BAR" + USwitchClauseExpressionWithBody ["baz" -> {...] = Undetermined + ULiteralExpression (value = "baz") ["baz"] = "baz" + UExpressionList (switch_entry) [{...] = Undetermined + UBreakWithValueExpression (label = null, hasValue = true) [break "bAz"] = UBreakResult("bAz") + ULiteralExpression (value = "bAz") ["bAz"] = "bAz" + USwitchClauseExpressionWithBody [else -> {...] = Undetermined + UDefaultCaseExpression [else] = Undetermined + UExpressionList (switch_entry) [{...] = Undetermined + UBreakWithValueExpression (label = null, hasValue = true) [break "default"] = UBreakResult("default") + ULiteralExpression (value = "default") ["default"] = "default" + UDeclarationsExpression [final var numericString2: java.lang.String = switch (str) ...] = Undetermined + ULocalVariable (name = numericString2) [final var numericString2: java.lang.String = switch (str) ...] + USwitchExpression [switch (str) ...] = "bAz" + USimpleNameReferenceExpression (identifier = str) [str] = (var str = "baz") + UExpressionList (switch) [ "foo" -> {... ] = Undetermined + USwitchClauseExpressionWithBody ["foo" -> {...] = Undetermined + ULiteralExpression (value = "foo") ["foo"] = "foo" + UExpressionList (switch_entry) [{...] = Undetermined + UBreakWithValueExpression (label = null, hasValue = true) [break "FOO"] = UBreakResult("FOO") + ULiteralExpression (value = "FOO") ["FOO"] = "FOO" + USwitchClauseExpressionWithBody ["bar" -> {...] = Undetermined + ULiteralExpression (value = "bar") ["bar"] = "bar" + UExpressionList (switch_entry) [{...] = Undetermined + UBreakWithValueExpression (label = null, hasValue = true) [break "BAR"] = UBreakResult("BAR") + ULiteralExpression (value = "BAR") ["BAR"] = "BAR" + USwitchClauseExpressionWithBody ["baz", "zub" -> {...] = Undetermined + ULiteralExpression (value = "baz") ["baz"] = "baz" + ULiteralExpression (value = "zub") ["zub"] = "zub" + UExpressionList (switch_entry) [{...] = Undetermined + UQualifiedReferenceExpression [System.out.println("here")] = external println("here")("here") + UQualifiedReferenceExpression [System.out] = Undetermined + USimpleNameReferenceExpression (identifier = System) [System] = external System() + USimpleNameReferenceExpression (identifier = out) [out] = Undetermined + UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) [println("here")] = external println("here")("here") + UIdentifier (Identifier (println)) [UIdentifier (Identifier (println))] + ULiteralExpression (value = "here") ["here"] = "here" + UBreakWithValueExpression (label = null, hasValue = true) [break "bAz"] = UBreakResult("bAz") + ULiteralExpression (value = "bAz") ["bAz"] = "bAz" + USwitchClauseExpressionWithBody [else -> {...] = Undetermined + UDefaultCaseExpression [else] = Undetermined + UExpressionList (switch_entry) [{...] = Undetermined + UBreakWithValueExpression (label = null, hasValue = true) [break "default"] = UBreakResult("default") + ULiteralExpression (value = "default") ["default"] = "default" + UReturnExpression [return numericString + numericString2] = Nothing + UBinaryExpression (operator = +) [numericString + numericString2] = "bAzbAz" (depending on: (var numericString = "bAz"), (var numericString2 = "bAz")) + USimpleNameReferenceExpression (identifier = numericString) [numericString] = (var numericString = "bAz") + USimpleNameReferenceExpression (identifier = numericString2) [numericString2] = (var numericString2 = "bAz") diff --git a/uast/uast-tests/java/Simple/EnhancedSwitchComplexEvaluations.java b/uast/uast-tests/java/Simple/EnhancedSwitchComplexEvaluations.java new file mode 100644 index 000000000000..e4a1eef39eaf --- /dev/null +++ b/uast/uast-tests/java/Simple/EnhancedSwitchComplexEvaluations.java @@ -0,0 +1,85 @@ +public class Main { + + private static String getOneOrTwoString(String unknown) { + final String switchResult = + switch (unknown) { + case "foo", "bar" -> "foobar"; + default -> "default"; + }; + + return switchResult; + } + + private static String getOneOrThrow(String unknown) { + final String switchResult = + switch (unknown) { + case "foo", "bar" -> "foobar"; + default -> throw new IllegalArgumentException(); + }; + + return switchResult; + } + + private static String getThrowOrOne(String unknown) { + final String switchResult = + switch (unknown) { + case "foo", "bar" -> throw new IllegalArgumentException(); + default -> "foobar"; + }; + + return switchResult; + } + + private static String getUnknownString(String unknown) { + final String switchResult = + switch (unknown) { + case "foo", "bar" -> "foobar" + unknown; + default -> "default" + unknown; + }; + + return switchResult; + } + + private static int getOneInsideOrThrow(String unknown) { + String known = "abc"; + + final int switchResult = + switch (known) { + case "foo": + case "bar": + break 0; + default: + if (unknown.length() > 0) { + break 12; + } else + throw new IllegalArgumentException(); + }; + + return switchResult; + } + + + private static int getThrowOrOneInside(String unknown) { + String known = "abc"; + + final int switchResult = + switch (known) { + case "foo": + case "bar": + break 0; + default: + if (unknown.length() <= 0) { + throw new IllegalArgumentException(); + } + else if (unknown.equals("true")) { + break 12; + } + else { + break 18; + } + }; + + return switchResult; + } + +} \ No newline at end of file diff --git a/uast/uast-tests/java/Simple/EnhancedSwitchComplexEvaluations.values.txt b/uast/uast-tests/java/Simple/EnhancedSwitchComplexEvaluations.values.txt new file mode 100644 index 000000000000..c5f3ccaabede --- /dev/null +++ b/uast/uast-tests/java/Simple/EnhancedSwitchComplexEvaluations.values.txt @@ -0,0 +1,172 @@ +UFile (package = ) [public class Main {...] + UClass (name = Main) [public class Main {...}] + UMethod (name = getOneOrTwoString) [private static fun getOneOrTwoString(unknown: java.lang.String) : java.lang.String {...}] + UParameter (name = unknown) [var unknown: java.lang.String] + UBlockExpression [{...}] = Nothing + UDeclarationsExpression [final var switchResult: java.lang.String = switch (unknown) ...] = Undetermined + ULocalVariable (name = switchResult) [final var switchResult: java.lang.String = switch (unknown) ...] + USwitchExpression [switch (unknown) ...] = Phi("foobar", "default") + USimpleNameReferenceExpression (identifier = unknown) [unknown] = Undetermined + UExpressionList (switch) [ "foo", "bar" -> {... ] = Undetermined + USwitchClauseExpressionWithBody ["foo", "bar" -> {...] = Undetermined + ULiteralExpression (value = "foo") ["foo"] = "foo" + ULiteralExpression (value = "bar") ["bar"] = "bar" + UExpressionList (switch_entry) [{...] = Undetermined + UBreakWithValueExpression (label = null, hasValue = true) [break "foobar"] = UBreakResult("foobar") + ULiteralExpression (value = "foobar") ["foobar"] = "foobar" + USwitchClauseExpressionWithBody [else -> {...] = Undetermined + UDefaultCaseExpression [else] = Undetermined + UExpressionList (switch_entry) [{...] = Undetermined + UBreakWithValueExpression (label = null, hasValue = true) [break "default"] = UBreakResult("default") + ULiteralExpression (value = "default") ["default"] = "default" + UReturnExpression [return switchResult] = Nothing + USimpleNameReferenceExpression (identifier = switchResult) [switchResult] = (var switchResult = Phi("foobar", "default")) + UMethod (name = getOneOrThrow) [private static fun getOneOrThrow(unknown: java.lang.String) : java.lang.String {...}] + UParameter (name = unknown) [var unknown: java.lang.String] + UBlockExpression [{...}] = Nothing + UDeclarationsExpression [final var switchResult: java.lang.String = switch (unknown) ...] = Undetermined + ULocalVariable (name = switchResult) [final var switchResult: java.lang.String = switch (unknown) ...] + USwitchExpression [switch (unknown) ...] = "foobar" + USimpleNameReferenceExpression (identifier = unknown) [unknown] = Undetermined + UExpressionList (switch) [ "foo", "bar" -> {... ] = Undetermined + USwitchClauseExpressionWithBody ["foo", "bar" -> {...] = Undetermined + ULiteralExpression (value = "foo") ["foo"] = "foo" + ULiteralExpression (value = "bar") ["bar"] = "bar" + UExpressionList (switch_entry) [{...] = Undetermined + UBreakWithValueExpression (label = null, hasValue = true) [break "foobar"] = UBreakResult("foobar") + ULiteralExpression (value = "foobar") ["foobar"] = "foobar" + USwitchClauseExpressionWithBody [else -> {...] = Undetermined + UDefaultCaseExpression [else] = Undetermined + UExpressionList (switch_entry) [{...] = Undetermined + UThrowExpression [throw IllegalArgumentException()] = Nothing + UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) [IllegalArgumentException()] = external IllegalArgumentException()() + USimpleNameReferenceExpression (identifier = IllegalArgumentException) [IllegalArgumentException] = external IllegalArgumentException() + UReturnExpression [return switchResult] = Nothing + USimpleNameReferenceExpression (identifier = switchResult) [switchResult] = (var switchResult = "foobar") + UMethod (name = getThrowOrOne) [private static fun getThrowOrOne(unknown: java.lang.String) : java.lang.String {...}] + UParameter (name = unknown) [var unknown: java.lang.String] + UBlockExpression [{...}] = Nothing + UDeclarationsExpression [final var switchResult: java.lang.String = switch (unknown) ...] = Undetermined + ULocalVariable (name = switchResult) [final var switchResult: java.lang.String = switch (unknown) ...] + USwitchExpression [switch (unknown) ...] = "foobar" + USimpleNameReferenceExpression (identifier = unknown) [unknown] = Undetermined + UExpressionList (switch) [ "foo", "bar" -> {... ] = Undetermined + USwitchClauseExpressionWithBody ["foo", "bar" -> {...] = Undetermined + ULiteralExpression (value = "foo") ["foo"] = "foo" + ULiteralExpression (value = "bar") ["bar"] = "bar" + UExpressionList (switch_entry) [{...] = Undetermined + UThrowExpression [throw IllegalArgumentException()] = Nothing + UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) [IllegalArgumentException()] = external IllegalArgumentException()() + USimpleNameReferenceExpression (identifier = IllegalArgumentException) [IllegalArgumentException] = external IllegalArgumentException() + USwitchClauseExpressionWithBody [else -> {...] = Undetermined + UDefaultCaseExpression [else] = Undetermined + UExpressionList (switch_entry) [{...] = Undetermined + UBreakWithValueExpression (label = null, hasValue = true) [break "foobar"] = UBreakResult("foobar") + ULiteralExpression (value = "foobar") ["foobar"] = "foobar" + UReturnExpression [return switchResult] = Nothing + USimpleNameReferenceExpression (identifier = switchResult) [switchResult] = (var switchResult = "foobar") + UMethod (name = getUnknownString) [private static fun getUnknownString(unknown: java.lang.String) : java.lang.String {...}] + UParameter (name = unknown) [var unknown: java.lang.String] + UBlockExpression [{...}] = Nothing + UDeclarationsExpression [final var switchResult: java.lang.String = switch (unknown) ...] = Undetermined + ULocalVariable (name = switchResult) [final var switchResult: java.lang.String = switch (unknown) ...] + USwitchExpression [switch (unknown) ...] = Undetermined + USimpleNameReferenceExpression (identifier = unknown) [unknown] = Undetermined + UExpressionList (switch) [ "foo", "bar" -> {... ] = Undetermined + USwitchClauseExpressionWithBody ["foo", "bar" -> {...] = Undetermined + ULiteralExpression (value = "foo") ["foo"] = "foo" + ULiteralExpression (value = "bar") ["bar"] = "bar" + UExpressionList (switch_entry) [{...] = Undetermined + UBreakWithValueExpression (label = null, hasValue = true) [break "foobar" + unknown] = UBreakResult(Undetermined) + UBinaryExpression (operator = +) ["foobar" + unknown] = Undetermined + ULiteralExpression (value = "foobar") ["foobar"] = "foobar" + USimpleNameReferenceExpression (identifier = unknown) [unknown] = Undetermined + USwitchClauseExpressionWithBody [else -> {...] = Undetermined + UDefaultCaseExpression [else] = Undetermined + UExpressionList (switch_entry) [{...] = Undetermined + UBreakWithValueExpression (label = null, hasValue = true) [break "default" + unknown] = UBreakResult(Undetermined) + UBinaryExpression (operator = +) ["default" + unknown] = Undetermined + ULiteralExpression (value = "default") ["default"] = "default" + USimpleNameReferenceExpression (identifier = unknown) [unknown] = Undetermined + UReturnExpression [return switchResult] = Nothing + USimpleNameReferenceExpression (identifier = switchResult) [switchResult] = (var switchResult = Undetermined) + UMethod (name = getOneInsideOrThrow) [private static fun getOneInsideOrThrow(unknown: java.lang.String) : int {...}] + UParameter (name = unknown) [var unknown: java.lang.String] + UBlockExpression [{...}] = Nothing + UDeclarationsExpression [var known: java.lang.String = "abc"] = Undetermined + ULocalVariable (name = known) [var known: java.lang.String = "abc"] + ULiteralExpression (value = "abc") ["abc"] = "abc" + UDeclarationsExpression [final var switchResult: int = switch (known) ...] = Undetermined + ULocalVariable (name = switchResult) [final var switchResult: int = switch (known) ...] + USwitchExpression [switch (known) ...] = 12 + USimpleNameReferenceExpression (identifier = known) [known] = (var known = "abc") + UExpressionList (switch) [ "foo", "bar" -> {... ] = Undetermined + USwitchClauseExpressionWithBody ["foo", "bar" -> {...] = Undetermined + ULiteralExpression (value = "foo") ["foo"] = "foo" + ULiteralExpression (value = "bar") ["bar"] = "bar" + UExpressionList (switch_entry) [{...] = Undetermined + UBreakWithValueExpression (label = null, hasValue = true) [break 0] = UBreakResult(0) + ULiteralExpression (value = 0) [0] = 0 + USwitchClauseExpressionWithBody [else -> {...] = Undetermined + UDefaultCaseExpression [else] = Undetermined + UExpressionList (switch_entry) [{...] = Undetermined + UIfExpression [if (unknown.length() > 0) {...}else throw IllegalArgumentException()] = UBreakResult(12) + UBinaryExpression (operator = >) [unknown.length() > 0] = Undetermined + UQualifiedReferenceExpression [unknown.length()] = external length()() + USimpleNameReferenceExpression (identifier = unknown) [unknown] = Undetermined + UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) [length()] = external length()() + UIdentifier (Identifier (length)) [UIdentifier (Identifier (length))] + ULiteralExpression (value = 0) [0] = 0 + UBlockExpression [{...}] = UBreakResult(12) + UBreakWithValueExpression (label = null, hasValue = true) [break 12] = UBreakResult(12) + ULiteralExpression (value = 12) [12] = 12 + UThrowExpression [throw IllegalArgumentException()] = Nothing + UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) [IllegalArgumentException()] = external IllegalArgumentException()() + USimpleNameReferenceExpression (identifier = IllegalArgumentException) [IllegalArgumentException] = external IllegalArgumentException() + UReturnExpression [return switchResult] = Nothing + USimpleNameReferenceExpression (identifier = switchResult) [switchResult] = (var switchResult = 12) + UMethod (name = getThrowOrOneInside) [private static fun getThrowOrOneInside(unknown: java.lang.String) : int {...}] + UParameter (name = unknown) [var unknown: java.lang.String] + UBlockExpression [{...}] = Nothing + UDeclarationsExpression [var known: java.lang.String = "abc"] = Undetermined + ULocalVariable (name = known) [var known: java.lang.String = "abc"] + ULiteralExpression (value = "abc") ["abc"] = "abc" + UDeclarationsExpression [final var switchResult: int = switch (known) ...] = Undetermined + ULocalVariable (name = switchResult) [final var switchResult: int = switch (known) ...] + USwitchExpression [switch (known) ...] = Phi(12, 18) + USimpleNameReferenceExpression (identifier = known) [known] = (var known = "abc") + UExpressionList (switch) [ "foo", "bar" -> {... ] = Undetermined + USwitchClauseExpressionWithBody ["foo", "bar" -> {...] = Undetermined + ULiteralExpression (value = "foo") ["foo"] = "foo" + ULiteralExpression (value = "bar") ["bar"] = "bar" + UExpressionList (switch_entry) [{...] = Undetermined + UBreakWithValueExpression (label = null, hasValue = true) [break 0] = UBreakResult(0) + ULiteralExpression (value = 0) [0] = 0 + USwitchClauseExpressionWithBody [else -> {...] = Undetermined + UDefaultCaseExpression [else] = Undetermined + UExpressionList (switch_entry) [{...] = Undetermined + UIfExpression [if (unknown.length() <= 0) {...}] = Phi(UBreakResult(12), UBreakResult(18)) + UBinaryExpression (operator = <=) [unknown.length() <= 0] = Undetermined + UQualifiedReferenceExpression [unknown.length()] = external length()() + USimpleNameReferenceExpression (identifier = unknown) [unknown] = Undetermined + UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) [length()] = external length()() + UIdentifier (Identifier (length)) [UIdentifier (Identifier (length))] + ULiteralExpression (value = 0) [0] = 0 + UBlockExpression [{...}] = Nothing + UThrowExpression [throw IllegalArgumentException()] = Nothing + UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) [IllegalArgumentException()] = external IllegalArgumentException()() + USimpleNameReferenceExpression (identifier = IllegalArgumentException) [IllegalArgumentException] = external IllegalArgumentException() + UIfExpression [if (unknown.equals("true")) {...}] = Phi(UBreakResult(12), UBreakResult(18)) + UQualifiedReferenceExpression [unknown.equals("true")] = external equals("true")("true") + USimpleNameReferenceExpression (identifier = unknown) [unknown] = Undetermined + UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) [equals("true")] = external equals("true")("true") + UIdentifier (Identifier (equals)) [UIdentifier (Identifier (equals))] + ULiteralExpression (value = "true") ["true"] = "true" + UBlockExpression [{...}] = UBreakResult(12) + UBreakWithValueExpression (label = null, hasValue = true) [break 12] = UBreakResult(12) + ULiteralExpression (value = 12) [12] = 12 + UBlockExpression [{...}] = UBreakResult(18) + UBreakWithValueExpression (label = null, hasValue = true) [break 18] = UBreakResult(18) + ULiteralExpression (value = 18) [18] = 18 + UReturnExpression [return switchResult] = Nothing + USimpleNameReferenceExpression (identifier = switchResult) [switchResult] = (var switchResult = Phi(12, 18)) diff --git a/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaValuesTest.kt b/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaValuesTest.kt index 3ed663e587a9..d042e81d8cfc 100644 --- a/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaValuesTest.kt +++ b/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaValuesTest.kt @@ -81,6 +81,12 @@ class JavaValuesTest : AbstractJavaValuesTest() { @Test fun testEnumSwitch() = doTest("Simple/EnumSwitch.java") + @Test + fun testEnhancedSwitch() = doTest("Simple/EnhancedSwitch.java") + + @Test + fun testEnhancedSwitchComplexEvaluations() = doTest("Simple/EnhancedSwitchComplexEvaluations.java") + @Test fun testEnumSwitchConditionalBreak() = doTest("Simple/EnumSwitchConditionalBreak.java")