[K2] isUsedAsExpression analysis

This commit is contained in:
Kristoffer Andersen
2022-08-01 17:46:54 +02:00
committed by teamcity
parent d9701c71b7
commit f765457e51
673 changed files with 11862 additions and 10 deletions
@@ -15,11 +15,9 @@ import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.analysis.utils.printer.parentOfType
import org.jetbrains.kotlin.cfg.WhenChecker
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtReturnExpression
import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
internal class KtFe10ExpressionInfoProvider(
override val analysisSession: KtFe10AnalysisSession
@@ -40,4 +38,9 @@ internal class KtFe10ExpressionInfoProvider(
val bindingContext = analysisContext.analyze(whenExpression)
return WhenChecker.getMissingCases(whenExpression, bindingContext)
}
override fun isUsedAsExpression(expression: KtExpression): Boolean {
val bindingContext = analysisContext.analyze(expression)
return expression.isUsedAsExpression(bindingContext)
}
}
@@ -184,6 +184,12 @@ public class Fe10IdeNormalAnalysisSourceModuleHLExpressionTypeTestGenerated exte
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/returnExpression.kt");
}
@Test
@TestMetadata("singleExpressionLambdaBody.kt")
public void testSingleExpressionLambdaBody() throws Exception {
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/singleExpressionLambdaBody.kt");
}
@Test
@TestMetadata("smartcast_asCallArg.kt")
public void testSmartcast_asCallArg() throws Exception {
@@ -5,18 +5,24 @@
package org.jetbrains.kotlin.analysis.api.fir.components
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.analysis.api.calls.KtSimpleVariableAccessCall
import org.jetbrains.kotlin.analysis.api.calls.KtSuccessCallInfo
import org.jetbrains.kotlin.analysis.api.components.KtExpressionInfoProvider
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirSafe
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.fir.declarations.FirErrorFunction
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
import org.jetbrains.kotlin.fir.resolve.transformers.FirWhenExhaustivenessTransformer
import org.jetbrains.kotlin.psi.KtReturnExpression
import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.unwrapParenthesesLabelsAndAnnotations
internal class KtFirExpressionInfoProvider(
override val analysisSession: KtFirAnalysisSession,
@@ -33,4 +39,398 @@ internal class KtFirExpressionInfoProvider(
val firWhenExpression = whenExpression.getOrBuildFirSafe<FirWhenExpression>(analysisSession.firResolveSession) ?: return emptyList()
return FirWhenExhaustivenessTransformer.computeAllMissingCases(analysisSession.firResolveSession.useSiteFirSession, firWhenExpression)
}
override fun isUsedAsExpression(expression: KtExpression): Boolean =
isUsed(expression)
/**
* [isUsed] and [doesParentUseChild] are defined in mutual recursion,
* climbing up the syntax tree, passing control back and forth between the
* two.
*
* Whether an expression is used is defined by the context in which it
* appears. E.g. a "statement" in a block is considered used if it is the
* last expression in that block AND the block itself is used -- a
* recursive call to `isUsed`, one level higher in the syntax tree.
*
* The methods are _conservative_, erring on the side of answering `true`.
*/
private fun isUsed(psiElement: PsiElement): Boolean {
return when (psiElement) {
/**
* DECLARATIONS
*/
// Inner PSI of KtLambdaExpressions. Used if the containing KtLambdaExpression is.
is KtFunctionLiteral ->
doesParentUseChild(psiElement.parent, psiElement)
// KtNamedFunction includes `fun() { ... }` lambda syntax. No other
// "named" functions can be expressions.
is KtNamedFunction ->
doesParentUseChild(psiElement.parent, psiElement)
// No other declarations are considered expressions
is KtDeclaration ->
false
/**
* EXPRESSIONS
*/
// A handful of expression are never considered used:
// - Everything of type `Nothing`
is KtThrowExpression ->
false
is KtReturnExpression ->
false
is KtBreakExpression ->
false
is KtContinueExpression ->
false
// - Loops
is KtLoopExpression ->
false
// - The `this` in `constructor(x: Int) : this(x)`
is KtConstructorDelegationReferenceExpression ->
false
// - Administrative node for EnumEntries. Never used as expression.
is KtEnumEntrySuperclassReferenceExpression ->
false
// - The "reference" in a constructor call. E.g. `C` in `C()`
is KtConstructorCalleeExpression ->
false
// - Labels themselves: `@label` in return`@label` or `label@`while...
is KtLabelReferenceExpression ->
false
// - The operation symbol itself in binary and unary operations: `!!`, `+`...
is KtOperationReferenceExpression ->
false
// All other expressions are used if their parent expression uses them.
else ->
doesParentUseChild(psiElement.parent, psiElement)
}
}
private fun doesParentUseChild(parent: PsiElement, child: PsiElement): Boolean {
return when (parent) {
/**
* NON-EXPRESSION PARENTS
*/
// KtValueArguments are a container for call-sites, and use exactly the
// argument expression they wrap.
is KtValueArgument ->
parent.getArgumentExpression() == child
// KtContainerNode are containers used in `KtIfExpressions`, and should be regarded
// as parentheses for the purpose of this analysis.
is KtContainerNode ->
// !!!!CAUTION!!!! Not `parentUse(parent.parent, _parent_)`
// Here we assume the parent (e.g. If condition) statement
// ignores the ContainerNode when accessing child
doesParentUseChild(parent.parent, child)
// KtWhenEntry/WhenCondition are containers used in KtWhenExpressions, ard
// should be regarded as parentheses.
is KtWhenEntry ->
(parent.expression == child && isUsed(parent.parent)) || child in parent.conditions
is KtWhenCondition ->
doesParentUseChild(parent.parent, parent)
// Type parameters, return types and other annotations are all contained in KtUserType,
// and are never considered used as expressions
is KtUserType ->
false
// Only top-level named declarations have KtFile/KtScript Parents, and are never considered used
is KtFile ->
false
is KtScript ->
false
// Only class members have KtClassBody parents, and are never considered used
is KtClassBody ->
false
// $_ and ${_} contexts use their inner expression
is KtStringTemplateEntry ->
parent.expression == child
// Catch blocks are used if the parent-try uses the catch block
is KtCatchClause ->
doesParentUseChild(parent.parent, parent)
// Finally blocks are never used
is KtFinallySection ->
false
!is KtExpression ->
error("Unhandled Non-KtExpression parent of KtExpression: ${parent::class}")
/**
* EXPRESSIONS
*/
// Enum entries, type parameters, lamda expressions and script
// initializers never use any child expressions.
is KtEnumEntry ->
false
is KtTypeParameter ->
false
is KtLambdaExpression ->
false
is KtScriptInitializer ->
false
// The last expression of a block is considered used iff the block itself is used.
is KtBlockExpression ->
parent.statements.lastOrNull() == child && isUsed(parent)
// Destructuring declarations use their initializer.
is KtDestructuringDeclaration ->
parent.initializer == child
// Backing field declarations use their initializer.
is KtBackingField ->
parent.initializer == child
// Property accessors can use their bodies if not blocks.
is KtPropertyAccessor ->
parent.bodyExpression == child && doesPropertyAccessorUseBody(parent, child)
// Lambdas do not use their expression-blocks if they are inferred
// to be of unit type
is KtFunctionLiteral ->
parent.bodyBlockExpression == child && !returnsUnit(parent)
// Named functions do not use their bodies if the function itself returns unit
// UNLESS it's an expression body/lambda of type Unit.
/** See [doesNamedFunctionUseBody] */
is KtNamedFunction ->
doesNamedFunctionUseBody(parent, child)
// Function parameter declarations use their default value expressions.
is KtParameter ->
parent.defaultValue == child
// Variable declarations use their initializer.
is KtVariableDeclaration ->
parent.initializer == child
// Binary expressions always use both operands.
is KtBinaryExpression ->
parent.left == child || parent.right == child
// Binary expressions with type RHS always use its operand.
is KtBinaryExpressionWithTypeRHS ->
parent.left == child
// Is expressions always use their LHS.
is KtIsExpression ->
parent.leftHandSide == child
// Unary expressions always use its operand.
is KtUnaryExpression ->
parent.baseExpression == child
// Qualified expressions always use its receiver. The selector is
// used iff the qualified expression is.
is KtQualifiedExpression ->
parent.receiverExpression == child || (parent.selectorExpression == child && isUsed(parent))
// Array accesses use both receiver and index.
is KtArrayAccessExpression ->
child in parent.indexExpressions || parent.arrayExpression == child
// Calls use only the callee directly -- arguments are wrapped in a
// KtValueArgument container
is KtCallExpression ->
parent.calleeExpression == child && doesCallExpressionUseCallee(child)
// Collection literals use each of its constituent expressions.
is KtCollectionLiteralExpression ->
child in parent.getInnerExpressions()
// Annotations are regarded as parentheses. The annotation itself is never used.
is KtAnnotatedExpression ->
parent.baseExpression == child && isUsed(parent)
/** See [doesDoubleColonUseLHS] */
is KtDoubleColonExpression ->
parent.lhs == child && doesDoubleColonUseLHS(child)
// Parentheses are ignored for this analysis.
is KtParenthesizedExpression ->
doesParentUseChild(parent.parent, parent)
// When expressions use the subject expression _unless_ the first branch in the
// when is an `else`.
is KtWhenExpression ->
parent.subjectExpression == child && parent.entries.firstOrNull()?.isElse == false
// Throw expressions use the expression thrown.
is KtThrowExpression ->
parent.thrownExpression == child
// Body and catch blocks of try-catch expressions are used if the try-catch itself
// is used.
is KtTryExpression ->
(parent.tryBlock == child || child in parent.catchClauses) && isUsed(parent)
// If expressions always use their condition, and the branches are used if the
// If itself is used as an expression.
is KtIfExpression ->
parent.condition == child ||
((parent.then == child ||
parent.`else` == child) && isUsed(parent))
// For expressions use their loop range expression.
is KtForExpression ->
parent.loopRange == child
// While, DoWhile loops use their conditions, not their bodies
is KtWhileExpressionBase ->
parent.condition == child
// Return expressions use the return value
is KtReturnExpression ->
parent.returnedExpression == child
// Labels are regarded as parentheses for this analysis. The label itself is never used.
is KtLabeledExpression ->
parent.baseExpression == child && isUsed(parent)
// No children.
is KtConstantExpression ->
false
// no children of class and script initializers are used
is KtAnonymousInitializer ->
false
// no child expressions of primary constructors.
is KtPrimaryConstructor ->
false // error?
// no children of secondary constructs are used.
is KtSecondaryConstructor ->
false
// KtClass, KtObjectDeclaration, KtTypeAlias has no expression children
is KtClassLikeDeclaration ->
false // has no expression children
// Simple names do not have expression children
// Labels, operations, references by name
is KtSimpleNameExpression ->
false
// this/super in constructor delegations. No expression children
is KtConstructorDelegationReferenceExpression ->
false
// Object Literal expressions use none of its children.
is KtObjectLiteralExpression ->
false
// break, continue, super, this do not have children
is KtBreakExpression ->
false
is KtContinueExpression ->
false
is KtSuperExpression ->
false
is KtThisExpression ->
false
// No direct expression children
is KtStringTemplateExpression ->
false
else ->
error("Unhandled KtElement subtype: ${parent::class}")
}
}
}
/**
* The left hand side of a `::` is regarded as used unless it refers to a type.
* We decide that the LHS is a type reference by checking if the left hand
* side is a (qualified) name, and, in case it _is_, resolving that name.
*
* If it resolves to a non-class declaration, it does _not_ refer to a type.
*/
private fun doesDoubleColonUseLHS(lhs: PsiElement): Boolean {
val reference = when (val inner = lhs.unwrapParenthesesLabelsAndAnnotations()) {
is KtReferenceExpression ->
inner.mainReference
is KtDotQualifiedExpression ->
(inner.selectorExpression as? KtReferenceExpression)?.mainReference ?: return true
else ->
return true
}
val resolution = reference.resolve()
return resolution != null && resolution !is KtClass
}
/**
* Invocations of _statically named_ callables is not considered a use. E.g.
* consider
*
* 1) fun f() { 54 }; f()
* 2) val f = { 54 }; f()
*
* in which the `f` in 2) is regarded as used and `f` in 1) is not.
*/
private fun doesCallExpressionUseCallee(callee: PsiElement): Boolean {
return callee !is KtReferenceExpression ||
analyze(callee) {
isSimpleVariableAccessCall(callee)
}
}
/**
* The body of setters are always used. The body of getters are only used if they are expression bodies.
*/
private fun doesPropertyAccessorUseBody(propertyAccessor: KtPropertyAccessor, body: PsiElement): Boolean {
return propertyAccessor.isSetter || (propertyAccessor.isGetter && body !is KtBlockExpression)
}
/**
* Named functions do not consider their bodies used if the function itself
* returns Unit UNLESS the function body is an expression body and the body is
* of type Unit.
*/
private fun doesNamedFunctionUseBody(namedFunction: KtNamedFunction, body: PsiElement): Boolean =
when {
!returnsUnit(namedFunction) ->
true
namedFunction.bodyBlockExpression == body ->
false
namedFunction.bodyExpression == body ->
analyze(namedFunction) {
(body as KtExpression).getKtType()?.isUnit == true
}
else ->
false
}
private fun KtAnalysisSession.isSimpleVariableAccessCall(reference: KtReferenceExpression): Boolean =
when (val resolution = reference.resolveCall()) {
is KtSuccessCallInfo ->
resolution.call is KtSimpleVariableAccessCall
else ->
false
}
private fun returnsUnit(declaration: KtDeclaration): Boolean {
return analyze(declaration) {
declaration.getReturnKtType().isUnit
}
}
@@ -96,7 +96,12 @@ internal class KtFirExpressionTypeProvider(
}
override fun getReturnTypeForKtDeclaration(declaration: KtDeclaration): KtType {
val firDeclaration = declaration.getOrBuildFirOfType<FirCallableDeclaration>(firResolveSession)
val firDeclaration = when {
declaration is KtNamedFunction && declaration.name == null ->
declaration.getOrBuildFirOfType<FirAnonymousFunctionExpression>(firResolveSession).anonymousFunction
else ->
declaration.getOrBuildFirOfType<FirCallableDeclaration>(firResolveSession)
}
return firDeclaration.returnTypeRef.coneType.asKtType()
}
@@ -184,6 +184,12 @@ public class FirIdeDependentAnalysisSourceModuleHLExpressionTypeTestGenerated ex
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/returnExpression.kt");
}
@Test
@TestMetadata("singleExpressionLambdaBody.kt")
public void testSingleExpressionLambdaBody() throws Exception {
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/singleExpressionLambdaBody.kt");
}
@Test
@TestMetadata("smartcast_asCallArg.kt")
public void testSmartcast_asCallArg() throws Exception {
@@ -184,6 +184,12 @@ public class FirIdeNormalAnalysisSourceModuleHLExpressionTypeTestGenerated exten
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/returnExpression.kt");
}
@Test
@TestMetadata("singleExpressionLambdaBody.kt")
public void testSingleExpressionLambdaBody() throws Exception {
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/singleExpressionLambdaBody.kt");
}
@Test
@TestMetadata("smartcast_asCallArg.kt")
public void testSmartcast_asCallArg() throws Exception {
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.expressionInfoProvider
import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiSingleFileTest
import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions
abstract class AbstractIsUsedAsExpressionTest : AbstractAnalysisApiSingleFileTest() {
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
val expression = testServices.expressionMarkerProvider.getSelectedElementOfType<KtExpression>(ktFile).let {
if (it is KtBlockExpression && it.statements.size == 1 && it.textRange == it.statements.single().textRange) {
it.statements.single()
} else {
it
}
}
val actual = StringBuilder();
analyseForTest(expression) {
actual.appendLine("expression: $expression")
actual.appendLine("text: ${expression.text}")
actual.appendLine("isUsedAsExpression: ${expression.isUsedAsExpression()}")
}
testServices.assertions.assertEqualsToTestDataFileSibling(actual.toString())
}
}
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.analysis.api.components.KtTypeRendererOptions
import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiSingleFileTest
import org.jetbrains.kotlin.analysis.test.framework.utils.executeOnPooledThreadInReadAction
import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtValueArgument
@@ -18,7 +19,13 @@ import org.jetbrains.kotlin.test.services.assertions
abstract class AbstractHLExpressionTypeTest : AbstractAnalysisApiSingleFileTest() {
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
val selected = testServices.expressionMarkerProvider.getSelectedElement(ktFile)
val selected = testServices.expressionMarkerProvider.getSelectedElement(ktFile).let {
if (it is KtBlockExpression && it.statements.size == 1 && it.textRange == it.statements.single().textRange) {
it.statements.single()
} else {
it
}
}
val expression = when (selected) {
is KtExpression -> selected
is KtValueArgument -> selected.getArgumentExpression()
@@ -184,6 +184,12 @@ public class FirStandaloneNormalAnalysisSourceModuleHLExpressionTypeTestGenerate
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/returnExpression.kt");
}
@Test
@TestMetadata("singleExpressionLambdaBody.kt")
public void testSingleExpressionLambdaBody() throws Exception {
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/singleExpressionLambdaBody.kt");
}
@Test
@TestMetadata("smartcast_asCallArg.kt")
public void testSmartcast_asCallArg() throws Exception {
@@ -8,12 +8,14 @@ package org.jetbrains.kotlin.analysis.api.components
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtReturnExpression
import org.jetbrains.kotlin.psi.KtWhenExpression
public abstract class KtExpressionInfoProvider : KtAnalysisSessionComponent() {
public abstract fun getReturnExpressionTargetSymbol(returnExpression: KtReturnExpression): KtCallableSymbol?
public abstract fun getWhenMissingCases(whenExpression: KtWhenExpression): List<WhenMissingCase>
public abstract fun isUsedAsExpression(expression: KtExpression): Boolean
}
public interface KtExpressionInfoProviderMixIn : KtAnalysisSessionMixIn {
@@ -22,4 +24,28 @@ public interface KtExpressionInfoProviderMixIn : KtAnalysisSessionMixIn {
public fun KtWhenExpression.getMissingCases(): List<WhenMissingCase> =
withValidityAssertion { analysisSession.expressionInfoProvider.getWhenMissingCases(this) }
/**
* Compute if the value of a given expression is possibly used. Or,
* conversely, compute whether the value of an expression is *not* safe to
* discard.
*
* E.g. `x` in the following examples *are* used (`x.isUsedAsExpression() == true`)
* - `if (x) { ... } else { ... }`
* - `val a = x`
* - `x + 8`
* - `when (x) { 1 -> ...; else -> ... }
*
* E.g. `x` in the following example is definitely *not* used (`x.isUsedAsExpression() == false`)
* - `run { x; println(50) }`
* - `when (x) { else -> ... }`
*
* **Note!** This is a conservative check, not a control-flow analysis.
* E.g. `x` in the following example *is possibly used*, even though the
* value is never consumed at runtime.
* - `x + try { throw Exception() } finally { return }`
*
*/
public fun KtExpression.isUsedAsExpression(): Boolean =
withValidityAssertion { analysisSession.expressionInfoProvider.isUsedAsExpression(this) }
}
@@ -0,0 +1,3 @@
fun main(args: Array<String>) {
val x = args + (<expr>@OptIn(Deprecated::class) args</expr>)
}
@@ -0,0 +1,3 @@
expression: ANNOTATED_EXPRESSION
text: @OptIn(Deprecated::class) args
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun main(args: Array<String>) {
val x = args + (@<expr>OptIn</expr>(Deprecated::class) args)
}
@@ -0,0 +1,3 @@
expression: CONSTRUCTOR_CALLEE
text: OptIn
isUsedAsExpression: false
@@ -0,0 +1,3 @@
fun main(args: Array<String>) {
val x = args + (@OptIn(Deprecated::class) <expr>args</expr>)
}
@@ -0,0 +1,3 @@
expression: REFERENCE_EXPRESSION
text: args
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun main(args: Array<String>) {
(@OptIn(Deprecated::class) <expr>args</expr>)
}
@@ -0,0 +1,3 @@
expression: REFERENCE_EXPRESSION
text: args
isUsedAsExpression: false
@@ -0,0 +1,5 @@
@OptIn(Deprecated::class, <expr>Deprecated::class</expr>)
@SuppressWarnings(["unsued", "foo"])
fun test() {
}
@@ -0,0 +1,3 @@
expression: CLASS_LITERAL_EXPRESSION
text: Deprecated::class
isUsedAsExpression: true
@@ -0,0 +1,5 @@
@OptIn(Deprecated::class, Deprecated::class)
@SuppressWarnings(<expr>["unsued", "foo"]</expr>)
fun test() {
}
@@ -0,0 +1,3 @@
expression: COLLECTION_LITERAL_EXPRESSION
text: ["unsued", "foo"]
isUsedAsExpression: true
@@ -0,0 +1,5 @@
@OptIn(Deprecated::class, Deprecated::class)
@SuppressWarnings(["unsued", <expr>"foo"</expr>])
fun test() {
}
@@ -0,0 +1,3 @@
expression: STRING_TEMPLATE
text: "foo"
isUsedAsExpression: true
@@ -0,0 +1,5 @@
@OptIn(Deprecated::class, Deprecated::class)
@<expr>SuppressWarnings</expr>(["unsued", "foo"])
fun test() {
}
@@ -0,0 +1,3 @@
expression: CONSTRUCTOR_CALLEE
text: SuppressWarnings
isUsedAsExpression: false
@@ -0,0 +1,6 @@
fun println(x: Boolean) {}
fun test(b: Boolean): Int {
println(<expr>b</expr>)
return 54
}
@@ -0,0 +1,3 @@
expression: REFERENCE_EXPRESSION
text: b
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun main(args: Array<String>) {
val s = <expr>args[0]</expr>
}
@@ -0,0 +1,3 @@
expression: ARRAY_ACCESS_EXPRESSION
text: args[0]
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun main(args: Array<String>, i: Int) {
val s = <expr>args[i + i]</expr>
}
@@ -0,0 +1,3 @@
expression: ARRAY_ACCESS_EXPRESSION
text: args[i + i]
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun main(args: Array<String>, i: Int) {
val s = args[<expr>i + i</expr>]
}
@@ -0,0 +1,3 @@
expression: BINARY_EXPRESSION
text: i + i
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun main(args: Array<String>) {
val s = args[<expr>0</expr>]
}
@@ -0,0 +1,3 @@
expression: INTEGER_CONSTANT
text: 0
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun main(args: Array<String>) {
val s = <expr>args</expr>[0]
}
@@ -0,0 +1,3 @@
expression: REFERENCE_EXPRESSION
text: args
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun test(a: Array<Int>) {
<expr>(a + a)[0]++</expr>
}
@@ -0,0 +1,3 @@
expression: POSTFIX_EXPRESSION
text: (a + a)[0]++
isUsedAsExpression: false
@@ -0,0 +1,3 @@
fun test(a: Array<Int>) {
<expr>(a + a)</expr>[0]++
}
@@ -0,0 +1,3 @@
expression: PARENTHESIZED
text: (a + a)
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun test(a: Array<Int>) {
(<expr>a + a</expr>)[0]++
}
@@ -0,0 +1,3 @@
expression: BINARY_EXPRESSION
text: a + a
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun test(a: Array<Int>) {
<expr>a[0]</expr>++
}
@@ -0,0 +1,3 @@
expression: ARRAY_ACCESS_EXPRESSION
text: a[0]
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun main(args: Array<String>) {
<expr>args[0]</expr>
}
@@ -0,0 +1,3 @@
expression: ARRAY_ACCESS_EXPRESSION
text: args[0]
isUsedAsExpression: false
@@ -0,0 +1,3 @@
fun test(v: Any?) {
<expr>(v as String)</expr>.length
}
@@ -0,0 +1,3 @@
expression: PARENTHESIZED
text: (v as String)
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun test(v: Any?) {
<expr>(v as String)?.length</expr>
}
@@ -0,0 +1,3 @@
expression: SAFE_ACCESS_EXPRESSION
text: (v as String)?.length
isUsedAsExpression: false
@@ -0,0 +1,3 @@
fun test(v: Any?) {
<expr>(v as String)</expr>?.length
}
@@ -0,0 +1,3 @@
expression: PARENTHESIZED
text: (v as String)
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun test(v: Any?) {
(v as String)?.<expr>length</expr>
}
@@ -0,0 +1,3 @@
expression: REFERENCE_EXPRESSION
text: length
isUsedAsExpression: false
@@ -0,0 +1,3 @@
fun test(v: Any?) {
val x = (v as String)?.<expr>length</expr>
}
@@ -0,0 +1,3 @@
expression: REFERENCE_EXPRESSION
text: length
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun test(v: Any?) {
val x = <expr>(v as String)?.length</expr>
}
@@ -0,0 +1,3 @@
expression: SAFE_ACCESS_EXPRESSION
text: (v as String)?.length
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun test(v: Any?) {
(<expr>v as String</expr>).length
}
@@ -0,0 +1,3 @@
expression: BINARY_WITH_TYPE
text: v as String
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun test(v: Any?) {
(<expr>v</expr> as String).length
}
@@ -0,0 +1,3 @@
expression: REFERENCE_EXPRESSION
text: v
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun test(v: Any?) {
<expr>(v as? String)</expr>?.length
}
@@ -0,0 +1,3 @@
expression: PARENTHESIZED
text: (v as? String)
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun test(v: Any?) {
(<expr>v as? String</expr>)?.length
}
@@ -0,0 +1,3 @@
expression: BINARY_WITH_TYPE
text: v as? String
isUsedAsExpression: true
@@ -0,0 +1,5 @@
fun test(v: Any?) {
(v as? String)?.<expr>let {
it.length
}</expr>
}
@@ -0,0 +1,5 @@
expression: CALL_EXPRESSION
text: let {
it.length
}
isUsedAsExpression: false
@@ -0,0 +1,5 @@
fun test(v: Any?) {
(v as? String)?.let <expr>{
it.length
}</expr>
}
@@ -0,0 +1,5 @@
expression: LAMBDA_EXPRESSION
text: {
it.length
}
isUsedAsExpression: true
@@ -0,0 +1,5 @@
fun test(v: Any?) {
val x = (v as? String)?.let <expr>{
it.length
}</expr>
}
@@ -0,0 +1,5 @@
expression: LAMBDA_EXPRESSION
text: {
it.length
}
isUsedAsExpression: true
@@ -0,0 +1,5 @@
fun test(v: Any?) {
(v as? String)?.let { <expr>it</expr> ->
it.length
}
}
@@ -0,0 +1,3 @@
expression: VALUE_PARAMETER
text: it
isUsedAsExpression: false
@@ -0,0 +1,5 @@
fun test(v: Any?) {
(v as? String)?.let{
<expr>it</expr>.length
}
}
@@ -0,0 +1,3 @@
expression: REFERENCE_EXPRESSION
text: it
isUsedAsExpression: true
@@ -0,0 +1,5 @@
fun test(v: Any?) {
(v as? String)?.<expr>let</expr> {
it.length
}
}
@@ -0,0 +1,3 @@
expression: REFERENCE_EXPRESSION
text: let
isUsedAsExpression: false
@@ -0,0 +1,5 @@
fun test(v: Any?) {
val x = (v as? String)?.<expr>let {
it.length
}</expr>
}
@@ -0,0 +1,5 @@
expression: CALL_EXPRESSION
text: let {
it.length
}
isUsedAsExpression: true
@@ -0,0 +1,3 @@
fun test(v: Any?) {
(v as <expr>String</expr>).length
}
@@ -0,0 +1,3 @@
expression: REFERENCE_EXPRESSION
text: String
isUsedAsExpression: false
@@ -0,0 +1,3 @@
fun test(v: Any?) {
val x = <expr>(v as String)</expr>.length
}
@@ -0,0 +1,3 @@
expression: PARENTHESIZED
text: (v as String)
isUsedAsExpression: true
@@ -0,0 +1,4 @@
class C {
<expr>val names: List<String> // clearly tells the type of the property upfront
field: MutableList<String> = mutableListOf()</expr>
}
@@ -0,0 +1,4 @@
expression: PROPERTY
text: val names: List<String> // clearly tells the type of the property upfront
field: MutableList<String> = mutableListOf()
isUsedAsExpression: false
@@ -0,0 +1,4 @@
class C {
val names: List<String> // clearly tells the type of the property upfront
<expr>field: MutableList<String> = mutableListOf()</expr>
}
@@ -0,0 +1,3 @@
expression: BACKING_FIELD
text: field: MutableList<String> = mutableListOf()
isUsedAsExpression: false
@@ -0,0 +1,7 @@
class C {
val names: List<String> // clearly tells the type of the property upfront
field: MutableList<String> = <expr>mutableListOf()</expr>
}
// IGNORE_FE10
// RHS of initializer is deemed unused by FE1.0
@@ -0,0 +1,3 @@
expression: CALL_EXPRESSION
text: mutableListOf()
isUsedAsExpression: true
@@ -0,0 +1,5 @@
expression: WHEN
text: when(b) {
else -> {}
}
isUsedAsExpression: false
@@ -0,0 +1,9 @@
fun test(b: Boolean) {
while (true) {
if (<expr>break</expr>) {
"OK"
} else {
continue
}
}
}
@@ -0,0 +1,3 @@
expression: BREAK
text: break
isUsedAsExpression: false
@@ -0,0 +1,4 @@
fun test(b: Boolean): Int {
val n: Int = <expr>b</expr>.hashCode()
return n * 2
}

Some files were not shown because too many files have changed in this diff Show More