[K2] isUsedAsExpression analysis
This commit is contained in:
committed by
teamcity
parent
d9701c71b7
commit
f765457e51
+7
-4
@@ -15,11 +15,9 @@ import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
|
|||||||
import org.jetbrains.kotlin.analysis.utils.printer.parentOfType
|
import org.jetbrains.kotlin.analysis.utils.printer.parentOfType
|
||||||
import org.jetbrains.kotlin.cfg.WhenChecker
|
import org.jetbrains.kotlin.cfg.WhenChecker
|
||||||
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
|
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
|
||||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
|
||||||
import org.jetbrains.kotlin.psi.KtReturnExpression
|
|
||||||
import org.jetbrains.kotlin.psi.KtWhenExpression
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||||
|
|
||||||
internal class KtFe10ExpressionInfoProvider(
|
internal class KtFe10ExpressionInfoProvider(
|
||||||
override val analysisSession: KtFe10AnalysisSession
|
override val analysisSession: KtFe10AnalysisSession
|
||||||
@@ -40,4 +38,9 @@ internal class KtFe10ExpressionInfoProvider(
|
|||||||
val bindingContext = analysisContext.analyze(whenExpression)
|
val bindingContext = analysisContext.analyze(whenExpression)
|
||||||
return WhenChecker.getMissingCases(whenExpression, bindingContext)
|
return WhenChecker.getMissingCases(whenExpression, bindingContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun isUsedAsExpression(expression: KtExpression): Boolean {
|
||||||
|
val bindingContext = analysisContext.analyze(expression)
|
||||||
|
return expression.isUsedAsExpression(bindingContext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+2004
File diff suppressed because it is too large
Load Diff
+6
@@ -184,6 +184,12 @@ public class Fe10IdeNormalAnalysisSourceModuleHLExpressionTypeTestGenerated exte
|
|||||||
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/returnExpression.kt");
|
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
|
@Test
|
||||||
@TestMetadata("smartcast_asCallArg.kt")
|
@TestMetadata("smartcast_asCallArg.kt")
|
||||||
public void testSmartcast_asCallArg() throws Exception {
|
public void testSmartcast_asCallArg() throws Exception {
|
||||||
|
|||||||
+403
-3
@@ -5,18 +5,24 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.analysis.api.fir.components
|
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.components.KtExpressionInfoProvider
|
||||||
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
|
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.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.analysis.low.level.api.fir.api.getOrBuildFirSafe
|
||||||
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
|
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirErrorFunction
|
import org.jetbrains.kotlin.fir.declarations.FirErrorFunction
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
|
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
|
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
|
||||||
import org.jetbrains.kotlin.fir.resolve.transformers.FirWhenExhaustivenessTransformer
|
import org.jetbrains.kotlin.fir.resolve.transformers.FirWhenExhaustivenessTransformer
|
||||||
import org.jetbrains.kotlin.psi.KtReturnExpression
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
import org.jetbrains.kotlin.psi.KtWhenExpression
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.unwrapParenthesesLabelsAndAnnotations
|
||||||
|
|
||||||
internal class KtFirExpressionInfoProvider(
|
internal class KtFirExpressionInfoProvider(
|
||||||
override val analysisSession: KtFirAnalysisSession,
|
override val analysisSession: KtFirAnalysisSession,
|
||||||
@@ -33,4 +39,398 @@ internal class KtFirExpressionInfoProvider(
|
|||||||
val firWhenExpression = whenExpression.getOrBuildFirSafe<FirWhenExpression>(analysisSession.firResolveSession) ?: return emptyList()
|
val firWhenExpression = whenExpression.getOrBuildFirSafe<FirWhenExpression>(analysisSession.firResolveSession) ?: return emptyList()
|
||||||
return FirWhenExhaustivenessTransformer.computeAllMissingCases(analysisSession.firResolveSession.useSiteFirSession, firWhenExpression)
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+6
-1
@@ -96,7 +96,12 @@ internal class KtFirExpressionTypeProvider(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getReturnTypeForKtDeclaration(declaration: KtDeclaration): KtType {
|
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()
|
return firDeclaration.returnTypeRef.coneType.asKtType()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2004
File diff suppressed because it is too large
Load Diff
+2004
File diff suppressed because it is too large
Load Diff
+6
@@ -184,6 +184,12 @@ public class FirIdeDependentAnalysisSourceModuleHLExpressionTypeTestGenerated ex
|
|||||||
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/returnExpression.kt");
|
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
|
@Test
|
||||||
@TestMetadata("smartcast_asCallArg.kt")
|
@TestMetadata("smartcast_asCallArg.kt")
|
||||||
public void testSmartcast_asCallArg() throws Exception {
|
public void testSmartcast_asCallArg() throws Exception {
|
||||||
|
|||||||
+6
@@ -184,6 +184,12 @@ public class FirIdeNormalAnalysisSourceModuleHLExpressionTypeTestGenerated exten
|
|||||||
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/returnExpression.kt");
|
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
|
@Test
|
||||||
@TestMetadata("smartcast_asCallArg.kt")
|
@TestMetadata("smartcast_asCallArg.kt")
|
||||||
public void testSmartcast_asCallArg() throws Exception {
|
public void testSmartcast_asCallArg() throws Exception {
|
||||||
|
|||||||
+35
@@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
-1
@@ -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.services.expressionMarkerProvider
|
||||||
import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiSingleFileTest
|
import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiSingleFileTest
|
||||||
import org.jetbrains.kotlin.analysis.test.framework.utils.executeOnPooledThreadInReadAction
|
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.KtExpression
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.KtValueArgument
|
import org.jetbrains.kotlin.psi.KtValueArgument
|
||||||
@@ -18,7 +19,13 @@ import org.jetbrains.kotlin.test.services.assertions
|
|||||||
|
|
||||||
abstract class AbstractHLExpressionTypeTest : AbstractAnalysisApiSingleFileTest() {
|
abstract class AbstractHLExpressionTypeTest : AbstractAnalysisApiSingleFileTest() {
|
||||||
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
|
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) {
|
val expression = when (selected) {
|
||||||
is KtExpression -> selected
|
is KtExpression -> selected
|
||||||
is KtValueArgument -> selected.getArgumentExpression()
|
is KtValueArgument -> selected.getArgumentExpression()
|
||||||
|
|||||||
+2004
File diff suppressed because it is too large
Load Diff
+6
@@ -184,6 +184,12 @@ public class FirStandaloneNormalAnalysisSourceModuleHLExpressionTypeTestGenerate
|
|||||||
runTest("analysis/analysis-api/testData/components/expressionTypeProvider/expressionType/returnExpression.kt");
|
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
|
@Test
|
||||||
@TestMetadata("smartcast_asCallArg.kt")
|
@TestMetadata("smartcast_asCallArg.kt")
|
||||||
public void testSmartcast_asCallArg() throws Exception {
|
public void testSmartcast_asCallArg() throws Exception {
|
||||||
|
|||||||
+26
@@ -8,12 +8,14 @@ package org.jetbrains.kotlin.analysis.api.components
|
|||||||
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
|
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
|
||||||
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
|
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
|
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.KtReturnExpression
|
||||||
import org.jetbrains.kotlin.psi.KtWhenExpression
|
import org.jetbrains.kotlin.psi.KtWhenExpression
|
||||||
|
|
||||||
public abstract class KtExpressionInfoProvider : KtAnalysisSessionComponent() {
|
public abstract class KtExpressionInfoProvider : KtAnalysisSessionComponent() {
|
||||||
public abstract fun getReturnExpressionTargetSymbol(returnExpression: KtReturnExpression): KtCallableSymbol?
|
public abstract fun getReturnExpressionTargetSymbol(returnExpression: KtReturnExpression): KtCallableSymbol?
|
||||||
public abstract fun getWhenMissingCases(whenExpression: KtWhenExpression): List<WhenMissingCase>
|
public abstract fun getWhenMissingCases(whenExpression: KtWhenExpression): List<WhenMissingCase>
|
||||||
|
public abstract fun isUsedAsExpression(expression: KtExpression): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface KtExpressionInfoProviderMixIn : KtAnalysisSessionMixIn {
|
public interface KtExpressionInfoProviderMixIn : KtAnalysisSessionMixIn {
|
||||||
@@ -22,4 +24,28 @@ public interface KtExpressionInfoProviderMixIn : KtAnalysisSessionMixIn {
|
|||||||
|
|
||||||
public fun KtWhenExpression.getMissingCases(): List<WhenMissingCase> =
|
public fun KtWhenExpression.getMissingCases(): List<WhenMissingCase> =
|
||||||
withValidityAssertion { analysisSession.expressionInfoProvider.getWhenMissingCases(this) }
|
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) }
|
||||||
}
|
}
|
||||||
analysis/analysis-api/testData/components/expressionInfoProvider/isUsedAsExpression/annotatedExpr.kt
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun main(args: Array<String>) {
|
||||||
|
val x = args + (<expr>@OptIn(Deprecated::class) args</expr>)
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: ANNOTATED_EXPRESSION
|
||||||
|
text: @OptIn(Deprecated::class) args
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun main(args: Array<String>) {
|
||||||
|
val x = args + (@<expr>OptIn</expr>(Deprecated::class) args)
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: CONSTRUCTOR_CALLEE
|
||||||
|
text: OptIn
|
||||||
|
isUsedAsExpression: false
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun main(args: Array<String>) {
|
||||||
|
val x = args + (@OptIn(Deprecated::class) <expr>args</expr>)
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: REFERENCE_EXPRESSION
|
||||||
|
text: args
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun main(args: Array<String>) {
|
||||||
|
(@OptIn(Deprecated::class) <expr>args</expr>)
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: REFERENCE_EXPRESSION
|
||||||
|
text: args
|
||||||
|
isUsedAsExpression: false
|
||||||
analysis/analysis-api/testData/components/expressionInfoProvider/isUsedAsExpression/annotationArg.kt
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
@OptIn(Deprecated::class, <expr>Deprecated::class</expr>)
|
||||||
|
@SuppressWarnings(["unsued", "foo"])
|
||||||
|
fun test() {
|
||||||
|
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: CLASS_LITERAL_EXPRESSION
|
||||||
|
text: Deprecated::class
|
||||||
|
isUsedAsExpression: true
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
@OptIn(Deprecated::class, Deprecated::class)
|
||||||
|
@SuppressWarnings(<expr>["unsued", "foo"]</expr>)
|
||||||
|
fun test() {
|
||||||
|
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: COLLECTION_LITERAL_EXPRESSION
|
||||||
|
text: ["unsued", "foo"]
|
||||||
|
isUsedAsExpression: true
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
@OptIn(Deprecated::class, Deprecated::class)
|
||||||
|
@SuppressWarnings(["unsued", <expr>"foo"</expr>])
|
||||||
|
fun test() {
|
||||||
|
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: STRING_TEMPLATE
|
||||||
|
text: "foo"
|
||||||
|
isUsedAsExpression: true
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
@OptIn(Deprecated::class, Deprecated::class)
|
||||||
|
@<expr>SuppressWarnings</expr>(["unsued", "foo"])
|
||||||
|
fun test() {
|
||||||
|
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: CONSTRUCTOR_CALLEE
|
||||||
|
text: SuppressWarnings
|
||||||
|
isUsedAsExpression: false
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
fun println(x: Boolean) {}
|
||||||
|
|
||||||
|
fun test(b: Boolean): Int {
|
||||||
|
println(<expr>b</expr>)
|
||||||
|
return 54
|
||||||
|
}
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: REFERENCE_EXPRESSION
|
||||||
|
text: b
|
||||||
|
isUsedAsExpression: true
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun main(args: Array<String>) {
|
||||||
|
val s = <expr>args[0]</expr>
|
||||||
|
}
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: ARRAY_ACCESS_EXPRESSION
|
||||||
|
text: args[0]
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun main(args: Array<String>, i: Int) {
|
||||||
|
val s = <expr>args[i + i]</expr>
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: ARRAY_ACCESS_EXPRESSION
|
||||||
|
text: args[i + i]
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun main(args: Array<String>, i: Int) {
|
||||||
|
val s = args[<expr>i + i</expr>]
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: BINARY_EXPRESSION
|
||||||
|
text: i + i
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun main(args: Array<String>) {
|
||||||
|
val s = args[<expr>0</expr>]
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: INTEGER_CONSTANT
|
||||||
|
text: 0
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun main(args: Array<String>) {
|
||||||
|
val s = <expr>args</expr>[0]
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: REFERENCE_EXPRESSION
|
||||||
|
text: args
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun test(a: Array<Int>) {
|
||||||
|
<expr>(a + a)[0]++</expr>
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: POSTFIX_EXPRESSION
|
||||||
|
text: (a + a)[0]++
|
||||||
|
isUsedAsExpression: false
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun test(a: Array<Int>) {
|
||||||
|
<expr>(a + a)</expr>[0]++
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: PARENTHESIZED
|
||||||
|
text: (a + a)
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun test(a: Array<Int>) {
|
||||||
|
(<expr>a + a</expr>)[0]++
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: BINARY_EXPRESSION
|
||||||
|
text: a + a
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun test(a: Array<Int>) {
|
||||||
|
<expr>a[0]</expr>++
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: ARRAY_ACCESS_EXPRESSION
|
||||||
|
text: a[0]
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun main(args: Array<String>) {
|
||||||
|
<expr>args[0]</expr>
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: ARRAY_ACCESS_EXPRESSION
|
||||||
|
text: args[0]
|
||||||
|
isUsedAsExpression: false
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
<expr>(v as String)</expr>.length
|
||||||
|
}
|
||||||
analysis/analysis-api/testData/components/expressionInfoProvider/isUsedAsExpression/asExpression.txt
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: PARENTHESIZED
|
||||||
|
text: (v as String)
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
<expr>(v as String)?.length</expr>
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: SAFE_ACCESS_EXPRESSION
|
||||||
|
text: (v as String)?.length
|
||||||
|
isUsedAsExpression: false
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
<expr>(v as String)</expr>?.length
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: PARENTHESIZED
|
||||||
|
text: (v as String)
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
(v as String)?.<expr>length</expr>
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: REFERENCE_EXPRESSION
|
||||||
|
text: length
|
||||||
|
isUsedAsExpression: false
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
val x = (v as String)?.<expr>length</expr>
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: REFERENCE_EXPRESSION
|
||||||
|
text: length
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
val x = <expr>(v as String)?.length</expr>
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: SAFE_ACCESS_EXPRESSION
|
||||||
|
text: (v as String)?.length
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
(<expr>v as String</expr>).length
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: BINARY_WITH_TYPE
|
||||||
|
text: v as String
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
(<expr>v</expr> as String).length
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: REFERENCE_EXPRESSION
|
||||||
|
text: v
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
<expr>(v as? String)</expr>?.length
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: PARENTHESIZED
|
||||||
|
text: (v as? String)
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
(<expr>v as? String</expr>)?.length
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: BINARY_WITH_TYPE
|
||||||
|
text: v as? String
|
||||||
|
isUsedAsExpression: true
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
(v as? String)?.<expr>let {
|
||||||
|
it.length
|
||||||
|
}</expr>
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
expression: CALL_EXPRESSION
|
||||||
|
text: let {
|
||||||
|
it.length
|
||||||
|
}
|
||||||
|
isUsedAsExpression: false
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
(v as? String)?.let <expr>{
|
||||||
|
it.length
|
||||||
|
}</expr>
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
expression: LAMBDA_EXPRESSION
|
||||||
|
text: {
|
||||||
|
it.length
|
||||||
|
}
|
||||||
|
isUsedAsExpression: true
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
val x = (v as? String)?.let <expr>{
|
||||||
|
it.length
|
||||||
|
}</expr>
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
expression: LAMBDA_EXPRESSION
|
||||||
|
text: {
|
||||||
|
it.length
|
||||||
|
}
|
||||||
|
isUsedAsExpression: true
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
(v as? String)?.let { <expr>it</expr> ->
|
||||||
|
it.length
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: VALUE_PARAMETER
|
||||||
|
text: it
|
||||||
|
isUsedAsExpression: false
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
(v as? String)?.let{
|
||||||
|
<expr>it</expr>.length
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: REFERENCE_EXPRESSION
|
||||||
|
text: it
|
||||||
|
isUsedAsExpression: true
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
(v as? String)?.<expr>let</expr> {
|
||||||
|
it.length
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: REFERENCE_EXPRESSION
|
||||||
|
text: let
|
||||||
|
isUsedAsExpression: false
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
val x = (v as? String)?.<expr>let {
|
||||||
|
it.length
|
||||||
|
}</expr>
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
expression: CALL_EXPRESSION
|
||||||
|
text: let {
|
||||||
|
it.length
|
||||||
|
}
|
||||||
|
isUsedAsExpression: true
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
(v as <expr>String</expr>).length
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: REFERENCE_EXPRESSION
|
||||||
|
text: String
|
||||||
|
isUsedAsExpression: false
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun test(v: Any?) {
|
||||||
|
val x = <expr>(v as String)</expr>.length
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: PARENTHESIZED
|
||||||
|
text: (v as String)
|
||||||
|
isUsedAsExpression: true
|
||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
class C {
|
||||||
|
<expr>val names: List<String> // clearly tells the type of the property upfront
|
||||||
|
field: MutableList<String> = mutableListOf()</expr>
|
||||||
|
}
|
||||||
analysis/analysis-api/testData/components/expressionInfoProvider/isUsedAsExpression/backingField.txt
Vendored
+4
@@ -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
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
class C {
|
||||||
|
val names: List<String> // clearly tells the type of the property upfront
|
||||||
|
<expr>field: MutableList<String> = mutableListOf()</expr>
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: BACKING_FIELD
|
||||||
|
text: field: MutableList<String> = mutableListOf()
|
||||||
|
isUsedAsExpression: false
|
||||||
+7
@@ -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
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: CALL_EXPRESSION
|
||||||
|
text: mutableListOf()
|
||||||
|
isUsedAsExpression: true
|
||||||
analysis/analysis-api/testData/components/expressionInfoProvider/isUsedAsExpression/boolean_else.txt
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
expression: WHEN
|
||||||
|
text: when(b) {
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
isUsedAsExpression: false
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
fun test(b: Boolean) {
|
||||||
|
while (true) {
|
||||||
|
if (<expr>break</expr>) {
|
||||||
|
"OK"
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
expression: BREAK
|
||||||
|
text: break
|
||||||
|
isUsedAsExpression: false
|
||||||
Vendored
+4
@@ -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
Reference in New Issue
Block a user