@@ -20,6 +20,19 @@ import com.intellij.psi.PsiComment
20
20
import com.intellij.psi.PsiElement
21
21
import com.intellij.psi.PsiWhiteSpace
22
22
import com.intellij.psi.impl.source.tree.LeafPsiElement
23
+ import com.intellij.psi.util.PsiTreeUtil
24
+ import org.jetbrains.kotlin.analysis.api.resolution.KaCall
25
+ import org.jetbrains.kotlin.analysis.api.resolution.KaExplicitReceiverValue
26
+ import org.jetbrains.kotlin.analysis.api.resolution.KaFunctionCall
27
+ import org.jetbrains.kotlin.analysis.api.resolution.KaImplicitReceiverValue
28
+ import org.jetbrains.kotlin.analysis.api.resolution.successfulCallOrNull
29
+ import org.jetbrains.kotlin.analysis.api.resolution.successfulFunctionCallOrNull
30
+ import org.jetbrains.kotlin.analysis.api.resolution.symbol
31
+ import org.jetbrains.kotlin.analysis.api.symbols.KaAnonymousFunctionSymbol
32
+ import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol
33
+ import org.jetbrains.kotlin.analysis.api.symbols.KaValueParameterSymbol
34
+ import org.jetbrains.kotlin.analysis.api.symbols.KaVariableSymbol
35
+ import org.jetbrains.kotlin.analysis.api.symbols.name
23
36
import org.jetbrains.kotlin.analysis.api.types.KaClassType
24
37
import org.jetbrains.kotlin.analysis.api.types.KaType
25
38
import org.jetbrains.kotlin.coroutines.hasSuspendFunctionType
@@ -32,18 +45,22 @@ import org.jetbrains.kotlin.descriptors.SyntheticPropertyDescriptor
32
45
import org.jetbrains.kotlin.descriptors.ValueDescriptor
33
46
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
34
47
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
48
+ import org.jetbrains.kotlin.idea.references.mainReference
35
49
import org.jetbrains.kotlin.js.descriptorUtils.getKotlinTypeFqName
36
50
import org.jetbrains.kotlin.lexer.KtTokens
37
51
import org.jetbrains.kotlin.psi.Call
38
52
import org.jetbrains.kotlin.psi.KtAnnotated
39
53
import org.jetbrains.kotlin.psi.KtAnnotationEntry
54
+ import org.jetbrains.kotlin.psi.KtArrayAccessExpression
40
55
import org.jetbrains.kotlin.psi.KtBinaryExpression
41
56
import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS
42
57
import org.jetbrains.kotlin.psi.KtBlockExpression
58
+ import org.jetbrains.kotlin.psi.KtCallElement
43
59
import org.jetbrains.kotlin.psi.KtCallExpression
44
60
import org.jetbrains.kotlin.psi.KtClass
45
61
import org.jetbrains.kotlin.psi.KtClassOrObject
46
62
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
63
+ import org.jetbrains.kotlin.psi.KtElement
47
64
import org.jetbrains.kotlin.psi.KtExpression
48
65
import org.jetbrains.kotlin.psi.KtFunction
49
66
import org.jetbrains.kotlin.psi.KtFunctionLiteral
@@ -60,6 +77,7 @@ import org.jetbrains.kotlin.psi.KtStringTemplateExpression
60
77
import org.jetbrains.kotlin.psi.KtThisExpression
61
78
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
62
79
import org.jetbrains.kotlin.psi.KtTypeReference
80
+ import org.jetbrains.kotlin.psi.KtUnaryExpression
63
81
import org.jetbrains.kotlin.psi.KtValueArgument
64
82
import org.jetbrains.kotlin.psi.KtWhenExpression
65
83
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
@@ -114,36 +132,57 @@ private val STRING_TO_BYTE_FUNS = listOf(
114
132
FunMatcher (qualifier = " java.lang.String" , name = " getBytes" )
115
133
)
116
134
135
+ @Deprecated(" use kotlin-analysis-api instead" , ReplaceWith (" this.predictRuntimeStringValue()" ))
117
136
fun KtExpression.predictRuntimeStringValue (bindingContext : BindingContext ) =
118
137
predictRuntimeValueExpression(bindingContext).stringValue(bindingContext)
119
138
139
+ fun KtExpression.predictRuntimeStringValue () =
140
+ predictRuntimeValueExpression().stringValue()
141
+
120
142
fun KtExpression.predictRuntimeStringValueWithSecondaries (bindingContext : BindingContext ) =
121
143
mutableListOf<PsiElement >().let {
122
144
predictRuntimeValueExpression(bindingContext, it)
123
145
.stringValue(bindingContext, it) to it
124
146
}
125
147
148
+ @Deprecated(" use kotlin-analysis-api instead" , ReplaceWith (" this.predictRuntimeIntValue()" ))
126
149
fun KtExpression.predictRuntimeIntValue (bindingContext : BindingContext ) =
127
150
predictRuntimeValueExpression(bindingContext).let { runtimeValueExpression ->
128
151
runtimeValueExpression.getType(bindingContext)?.let {
129
152
bindingContext[BindingContext .COMPILE_TIME_VALUE , runtimeValueExpression]?.getValue(it) as ? Int
130
153
}
131
154
}
132
155
156
+ fun KtExpression.predictRuntimeIntValue (): Int? = withKaSession {
157
+ val valueExpression = predictRuntimeValueExpression()
158
+ if (valueExpression.expressionType?.isIntType == true ) {
159
+ valueExpression.evaluate()?.value as ? Int
160
+ } else null
161
+ }
162
+
163
+ @Deprecated(" use kotlin-analysis-api instead" , ReplaceWith (" this.predictRuntimeBooleanValue()" ))
133
164
fun KtExpression.predictRuntimeBooleanValue (bindingContext : BindingContext ) =
134
165
predictRuntimeValueExpression(bindingContext).let { runtimeValueExpression ->
135
166
runtimeValueExpression.getType(bindingContext)?.let {
136
167
bindingContext[BindingContext .COMPILE_TIME_VALUE , runtimeValueExpression]?.getValue(it) as ? Boolean
137
168
}
138
169
}
139
170
171
+ fun KtExpression.predictRuntimeBooleanValue () = withKaSession {
172
+ val valueExpression = predictRuntimeValueExpression()
173
+ if (valueExpression.expressionType?.isBooleanType == true ) {
174
+ valueExpression.evaluate()?.value as ? Boolean
175
+ } else null
176
+ }
177
+
140
178
/* *
141
179
* In Kotlin, we may often be dealing with expressions that can already statically be resolved to prior and more accurate expressions that
142
180
* they will alias at runtime. A good example of this are `it` within `let` and `also` scopes, as well as `this` within `with`, `apply`
143
181
* and `run` scopes. Other examples include constants assigned to a property elsewhere.
144
182
*
145
183
* This function will try to resolve the current expression as far as it statically can, including deparenthesizing the expression.
146
184
*/
185
+ @Deprecated(" use kotlin-analysis-api instead" , ReplaceWith (" this.predictRuntimeValueExpression(declarations)" ))
147
186
fun KtExpression.predictRuntimeValueExpression (
148
187
bindingContext : BindingContext ,
149
188
declarations : MutableList <PsiElement > = mutableListOf(),
@@ -165,6 +204,35 @@ fun KtExpression.predictRuntimeValueExpression(
165
204
} ? : deparenthesized as ? KtExpression
166
205
} ? : this
167
206
207
+ fun KtExpression.predictRuntimeValueExpression (
208
+ declarations : MutableList <PsiElement > = mutableListOf(),
209
+ ): KtExpression = this .deparenthesize().let { deparenthesized ->
210
+ when (deparenthesized) {
211
+ is KtReferenceExpression -> run {
212
+ val referenceTarget = deparenthesized.extractLetAlsoTargetExpression()
213
+ ? : deparenthesized.extractFromInitializer(declarations)
214
+
215
+ referenceTarget?.predictRuntimeValueExpression(declarations)
216
+ }
217
+
218
+ is KtParenthesizedExpression -> deparenthesized.expression?.predictRuntimeValueExpression(declarations)
219
+
220
+ is KtBinaryExpressionWithTypeRHS -> deparenthesized.left.predictRuntimeValueExpression(declarations)
221
+
222
+ is KtThisExpression -> withKaSession {
223
+ var symbol = deparenthesized.instanceReference.mainReference.resolveToSymbol()
224
+ // TODO investigate: in K1 the symbol is anonymous function symbol, in K2 it is a parameter
225
+ if (symbol !is KaAnonymousFunctionSymbol ) symbol = symbol?.containingSymbol
226
+ symbol?.findFunctionLiteral(deparenthesized)?.findLetAlsoRunWithTargetExpression()
227
+ }
228
+
229
+ else -> withKaSession {
230
+ deparenthesized.resolveToCall()?.successfulFunctionCallOrNull()?.predictValueExpression()
231
+ }
232
+ } ? : deparenthesized as ? KtExpression
233
+ } ? : this
234
+
235
+ @Deprecated(" use kotlin-analysis-api instead" , ReplaceWith (" this.predictReceiverExpression()" ))
168
236
fun KtCallExpression.predictReceiverExpression (
169
237
bindingContext : BindingContext ,
170
238
precomputedResolvedCall : ResolvedCall <* >? = null,
@@ -180,6 +248,18 @@ fun KtCallExpression.predictReceiverExpression(
180
248
return resolvedCall?.getImplicitReceiverValue()?.extractWithRunApplyTargetExpression(this , bindingContext)
181
249
}
182
250
251
+ fun KtExpression.predictReceiverExpression (): KtExpression ? = withKaSession {
252
+ val resolvedCall = this @predictReceiverExpression.resolveToCall()?.successfulFunctionCallOrNull()
253
+ val symbol = resolvedCall?.partiallyAppliedSymbol
254
+ val receiver = symbol?.extensionReceiver ? : symbol?.dispatchReceiver
255
+ when (receiver) {
256
+ is KaExplicitReceiverValue -> receiver.expression.predictRuntimeValueExpression()
257
+ is KaImplicitReceiverValue -> receiver.symbol.containingSymbol
258
+ ?.findFunctionLiteral(this @predictReceiverExpression)?.findLetAlsoRunWithTargetExpression()
259
+ else -> null
260
+ }
261
+ }
262
+
183
263
fun KtStringTemplateExpression.asString () = entries.joinToString(" " ) { it.text }
184
264
185
265
fun PsiElement.linesOfCode (): Set <Int > {
@@ -209,6 +289,7 @@ fun PsiComment.getContent() =
209
289
/* *
210
290
* @param declarations is used to collect all visited declaration for reporting secondary locations
211
291
*/
292
+ @Deprecated(" use kotlin-analysis-api instead" , ReplaceWith (" this.stringValue(declarations)" ))
212
293
private fun KtExpression.stringValue (
213
294
bindingContext : BindingContext ,
214
295
declarations : MutableList <PsiElement > = mutableListOf(),
@@ -240,11 +321,50 @@ private fun KtExpression.stringValue(
240
321
else -> null
241
322
}
242
323
324
+ fun KtExpression.stringValue (
325
+ declarations : MutableList <PsiElement > = mutableListOf(),
326
+ ): String? = withKaSession {
327
+ when (this @stringValue) {
328
+ is KtStringTemplateExpression -> {
329
+ val entries = entries.map {
330
+ if (it.expression != null ) it.expression!! .stringValue(declarations) else it.text
331
+ }
332
+ if (entries.all { it != null }) entries.joinToString(" " ) else null
333
+ }
334
+
335
+ is KtNameReferenceExpression -> {
336
+ (this @stringValue.mainReference.resolveToSymbol() as ? KaVariableSymbol )
337
+ ?.let {
338
+ if (it.isVal) {
339
+ (it.psi as ? KtProperty )
340
+ ?.apply { declarations.add(this ) }
341
+ ?.delegateExpressionOrInitializer?.stringValue(declarations)
342
+ } else null
343
+ }
344
+ }
345
+
346
+ is KtDotQualifiedExpression -> selectorExpression?.stringValue(declarations)
347
+ is KtBinaryExpression ->
348
+ if (operationToken == KtTokens .PLUS )
349
+ left?.stringValue(declarations)?.plus(right?.stringValue(declarations))
350
+ else null
351
+
352
+ else -> null
353
+ }
354
+ }
355
+
356
+ @Deprecated(" use kotlin-analysis-api instead" , ReplaceWith (" this.predictValueExpression()" ))
243
357
private fun Call.predictValueExpression (bindingContext : BindingContext ) =
244
358
if (GET_PROP_WITH_DEFAULT_MATCHER .matches(this , bindingContext)) {
245
359
valueArguments[1 ].getArgumentExpression()
246
360
} else null
247
361
362
+ private fun KaFunctionCall <* >.predictValueExpression (): KtExpression ? =
363
+ if (GET_PROP_WITH_DEFAULT_MATCHER .matches(this )) {
364
+ argumentMapping.keys.elementAt(1 )
365
+ } else null
366
+
367
+ @Deprecated(" use kotlin-analysis-api instead" , ReplaceWith (" this.extractFromInitializer()" ))
248
368
private fun KtReferenceExpression.extractFromInitializer (
249
369
bindingContext : BindingContext ,
250
370
declarations : MutableList <PsiElement > = mutableListOf(),
@@ -258,18 +378,46 @@ private fun KtReferenceExpression.extractFromInitializer(
258
378
} else null
259
379
}
260
380
381
+
382
+ private fun KtReferenceExpression.extractFromInitializer (
383
+ declarations : MutableList <PsiElement > = mutableListOf(),
384
+ ) = withKaSession {
385
+ (this @extractFromInitializer.mainReference.resolveToSymbol() as ? KaVariableSymbol )
386
+ ?.let {
387
+ if (it.isVal) {
388
+ (it.psi as ? KtProperty )
389
+ ?.apply { declarations.add(this ) }
390
+ ?.delegateExpressionOrInitializer?.predictRuntimeValueExpression(declarations)
391
+ } else null
392
+ }
393
+ }
394
+
261
395
/* *
262
396
* Will try to resolve what `it` is an alias for inside of a `let` or `also` scope.
263
397
*/
398
+ @Deprecated(" use kotlin-analysis-api instead" , ReplaceWith (" this.extractLetAlsoTargetExpression()" ))
264
399
private fun KtReferenceExpression.extractLetAlsoTargetExpression (bindingContext : BindingContext ) =
265
400
findReceiverScopeFunctionLiteral(bindingContext)?.findLetAlsoRunWithTargetExpression(bindingContext)
266
401
402
+ private fun KtReferenceExpression.extractLetAlsoTargetExpression () =
403
+ findReceiverScopeFunctionLiteral()?.findLetAlsoRunWithTargetExpression()
404
+
267
405
/* *
268
406
* Will try to resolve what `this` is an alias for inside a `with`, `run` or `apply` scope.
269
407
*/
270
- private fun ImplicitReceiver.extractWithRunApplyTargetExpression (startNode : PsiElement , bindingContext : BindingContext ) =
408
+ @Deprecated(" use kotlin-analysis-api instead" , ReplaceWith (" this.extractWithRunApplyTargetExpression()" ))
409
+ private fun ImplicitReceiver.extractWithRunApplyTargetExpression (
410
+ startNode : PsiElement ,
411
+ bindingContext : BindingContext
412
+ ) =
271
413
findReceiverScopeFunctionLiteral(startNode, bindingContext)?.findLetAlsoRunWithTargetExpression(bindingContext)
272
414
415
+ private fun KaImplicitReceiverValue.extractWithRunApplyTargetExpression (
416
+ startNode : PsiElement
417
+ ) =
418
+ findReceiverScopeFunctionLiteral(startNode)?.findLetAlsoRunWithTargetExpression()
419
+
420
+ @Deprecated(" use kotlin-analysis-api instead" , ReplaceWith (" this.findLetAlsoRunWithTargetExpression()" ))
273
421
private fun KtFunctionLiteral.findLetAlsoRunWithTargetExpression (bindingContext : BindingContext ): KtExpression ? =
274
422
getParentCall(bindingContext)?.let { larwCallCandidate ->
275
423
when (larwCallCandidate.callElement.getCalleeExpressionIfAny()?.text) {
@@ -286,13 +434,66 @@ private fun KtFunctionLiteral.findLetAlsoRunWithTargetExpression(bindingContext:
286
434
}
287
435
}
288
436
437
+ private fun KtFunctionLiteral.findLetAlsoRunWithTargetExpression (): KtExpression ? = withKaSession {
438
+ (getParentCall() as ? KaFunctionCall <* >)?.let { larwCallCandidate ->
439
+ withKaSession {
440
+ when (larwCallCandidate.partiallyAppliedSymbol.symbol.name?.asString()) {
441
+ in KOTLIN_CHAIN_CALL_CONSTRUCTS -> {
442
+ (larwCallCandidate.partiallyAppliedSymbol.extensionReceiver as ? KaExplicitReceiverValue )?.expression?.predictRuntimeValueExpression()
443
+ }
444
+
445
+ " with" -> {
446
+ larwCallCandidate.getFirstArgumentExpression()
447
+ ?.predictRuntimeValueExpression()
448
+ }
449
+
450
+ else -> null
451
+ }
452
+ }
453
+ }
454
+ }
455
+
456
+ fun KtElement.getParentCall (): KaCall ? {
457
+ val callExpressionTypes = arrayOf(
458
+ KtSimpleNameExpression ::class .java,
459
+ KtCallElement ::class .java,
460
+ KtBinaryExpression ::class .java,
461
+ KtUnaryExpression ::class .java,
462
+ KtArrayAccessExpression ::class .java
463
+ )
464
+ val parentOfType = PsiTreeUtil .getParentOfType(this , * callExpressionTypes)
465
+ return withKaSession { parentOfType?.resolveToCall()?.successfulCallOrNull() }
466
+ }
467
+
468
+ fun KaFunctionCall <* >.getFirstArgumentExpression () =
469
+ argumentMapping.keys.elementAtOrNull(0 )
470
+
471
+ @Deprecated(" use kotlin-analysis-api instead" , ReplaceWith (" this.findReceiverScopeFunctionLiteral()" ))
289
472
private fun KtReferenceExpression.findReceiverScopeFunctionLiteral (bindingContext : BindingContext ): KtFunctionLiteral ? =
290
473
(bindingContext[BindingContext .REFERENCE_TARGET , this ] as ? ValueParameterDescriptor )?.containingDeclaration
291
474
?.findFunctionLiteral(this , bindingContext)
292
475
293
- private fun ImplicitReceiver.findReceiverScopeFunctionLiteral (startNode : PsiElement , bindingContext : BindingContext ): KtFunctionLiteral ? =
476
+ private fun KtReferenceExpression.findReceiverScopeFunctionLiteral (): KtFunctionLiteral ? = withKaSession {
477
+ when (val resolvedSymbol = this @findReceiverScopeFunctionLiteral.mainReference.resolveToSymbol()) {
478
+ is KaValueParameterSymbol -> resolvedSymbol.containingSymbol
479
+ ?.findFunctionLiteral(this @findReceiverScopeFunctionLiteral)
480
+ else -> null
481
+ }
482
+ }
483
+
484
+ @Deprecated(" use kotlin-analysis-api instead" , ReplaceWith (" this.findReceiverScopeFunctionLiteral(startNode)" ))
485
+ private fun ImplicitReceiver.findReceiverScopeFunctionLiteral (
486
+ startNode : PsiElement ,
487
+ bindingContext : BindingContext
488
+ ): KtFunctionLiteral ? =
294
489
declarationDescriptor.findFunctionLiteral(startNode, bindingContext)
295
490
491
+ private fun KaImplicitReceiverValue.findReceiverScopeFunctionLiteral (
492
+ startNode : PsiElement ,
493
+ ): KtFunctionLiteral ? =
494
+ symbol.findFunctionLiteral(startNode)
495
+
496
+ @Deprecated(" use kotlin-analysis-api instead" , ReplaceWith (" this.findFunctionLiteral(startNode)" ))
296
497
private fun DeclarationDescriptor.findFunctionLiteral (
297
498
startNode : PsiElement ,
298
499
bindingContext : BindingContext ,
@@ -307,6 +508,18 @@ private fun DeclarationDescriptor.findFunctionLiteral(
307
508
return null
308
509
}
309
510
511
+ private fun KaSymbol.findFunctionLiteral (
512
+ startNode : PsiElement ,
513
+ ): KtFunctionLiteral ? = withKaSession {
514
+ var curNode: PsiElement ? = startNode
515
+ for (i in 0 until MAX_AST_PARENT_TRAVERSALS ) {
516
+ curNode = curNode?.parent ? : break
517
+ if (curNode is KtFunctionLiteral && curNode.symbol == this @findFunctionLiteral)
518
+ return curNode
519
+ }
520
+ return null
521
+ }
522
+
310
523
fun KtNamedFunction.overrides () = modifierList?.hasModifier(KtTokens .OVERRIDE_KEYWORD ) ? : false
311
524
312
525
fun KtNamedFunction.isAbstract () = modifierList?.hasModifier(KtTokens .ABSTRACT_KEYWORD ) ? : false
0 commit comments