FIR IDE: use symbols for call resolve
This commit is contained in:
+11
-8
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.idea.fir.highlighter.visitors
|
|||||||
import com.intellij.lang.annotation.AnnotationHolder
|
import com.intellij.lang.annotation.AnnotationHolder
|
||||||
import com.intellij.openapi.editor.colors.TextAttributesKey
|
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.*
|
import org.jetbrains.kotlin.idea.frontend.api.*
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtAnonymousFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtConstructorSymbol
|
||||||
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -55,15 +57,16 @@ internal class FunctionCallHighlightingVisitor(
|
|||||||
|
|
||||||
private fun getTextAttributesForCal(callInfo: CallInfo): TextAttributesKey? = when {
|
private fun getTextAttributesForCal(callInfo: CallInfo): TextAttributesKey? = when {
|
||||||
callInfo.isSuspendCall -> Colors.SUSPEND_FUNCTION_CALL
|
callInfo.isSuspendCall -> Colors.SUSPEND_FUNCTION_CALL
|
||||||
callInfo is ConstructorCallInfo ->
|
callInfo is FunctionCallInfo -> when (val function = callInfo.targetFunction) {
|
||||||
Colors.CONSTRUCTOR_CALL
|
is KtConstructorSymbol -> Colors.CONSTRUCTOR_CALL
|
||||||
callInfo is SimpleKtFunctionCallInfo -> when {
|
is KtAnonymousFunctionSymbol -> null
|
||||||
callInfo.targetFunction.getKotlinFqName() == KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME ->Colors.KEYWORD
|
is KtFunctionSymbol -> when {
|
||||||
callInfo.targetFunction.isExtensionDeclaration() -> Colors.EXTENSION_FUNCTION_CALL
|
function.fqName == KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME -> Colors.KEYWORD
|
||||||
callInfo.targetFunction.parent is KtFile -> Colors.PACKAGE_FUNCTION_CALL
|
function.isExtension -> Colors.EXTENSION_FUNCTION_CALL
|
||||||
else -> Colors.FUNCTION_CALL
|
function.symbolKind == KtSymbolKind.TOP_LEVEL -> Colors.PACKAGE_FUNCTION_CALL
|
||||||
|
else -> Colors.FUNCTION_CALL
|
||||||
|
}
|
||||||
}
|
}
|
||||||
callInfo is SimpleJavaFunctionCallInfo -> Colors.FUNCTION_CALL
|
|
||||||
callInfo is VariableAsFunctionCallInfo -> Colors.VARIABLE_AS_FUNCTION_CALL
|
callInfo is VariableAsFunctionCallInfo -> Colors.VARIABLE_AS_FUNCTION_CALL
|
||||||
callInfo is VariableAsFunctionLikeCallInfo -> Colors.VARIABLE_AS_FUNCTION_LIKE_CALL
|
callInfo is VariableAsFunctionLikeCallInfo -> Colors.VARIABLE_AS_FUNCTION_LIKE_CALL
|
||||||
else -> null
|
else -> null
|
||||||
|
|||||||
@@ -5,78 +5,26 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.frontend.api
|
package org.jetbrains.kotlin.idea.frontend.api
|
||||||
|
|
||||||
import com.intellij.psi.PsiClass
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.PsiMethod
|
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
|
||||||
import org.jetbrains.kotlin.psi.KtClass
|
|
||||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
|
||||||
import org.jetbrains.kotlin.psi.KtConstructor
|
|
||||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
|
||||||
|
|
||||||
sealed class CallInfo {
|
sealed class CallInfo {
|
||||||
abstract val isSuspendCall: Boolean
|
abstract val isSuspendCall: Boolean
|
||||||
abstract val targetFunction: PsiElement?
|
abstract val targetFunction: KtFunctionLikeSymbol?
|
||||||
abstract val isJavaFunctionCall: Boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class VariableAsFunctionCallInfo(val target: PsiElement, override val isSuspendCall: Boolean) : CallInfo() {
|
data class VariableAsFunctionCallInfo(val target: KtVariableLikeSymbol, override val isSuspendCall: Boolean) : CallInfo() {
|
||||||
override val targetFunction: PsiElement? = null
|
override val targetFunction: KtFunctionLikeSymbol? = null
|
||||||
override val isJavaFunctionCall: Boolean = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class VariableAsFunctionLikeCallInfo(val target: PsiElement, val invokeFunction: KtNamedFunction) : CallInfo() {
|
data class VariableAsFunctionLikeCallInfo(val target: KtVariableLikeSymbol, val invokeFunction: KtFunctionSymbol) : CallInfo() {
|
||||||
override val isSuspendCall: Boolean get() = invokeFunction.hasModifier(KtTokens.SUSPEND_KEYWORD)
|
override val isSuspendCall: Boolean get() = invokeFunction.isSuspend
|
||||||
override val targetFunction: PsiElement? get() = invokeFunction
|
override val targetFunction get() = invokeFunction
|
||||||
override val isJavaFunctionCall: Boolean = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SimpleFunctionCallInfo
|
data class FunctionCallInfo(override val targetFunction: KtFunctionLikeSymbol) : CallInfo() {
|
||||||
|
override val isSuspendCall: Boolean
|
||||||
sealed class SimpleFunctionCallInfo : CallInfo() {
|
get() = when (targetFunction) {
|
||||||
abstract override val targetFunction: PsiElement
|
is KtFunctionSymbol -> targetFunction.isSuspend
|
||||||
}
|
else -> false
|
||||||
|
}
|
||||||
data class SimpleKtFunctionCallInfo(override val targetFunction: KtNamedFunction) : SimpleFunctionCallInfo() {
|
}
|
||||||
override val isSuspendCall: Boolean get() = targetFunction.hasModifier(KtTokens.SUSPEND_KEYWORD)
|
|
||||||
override val isJavaFunctionCall: Boolean = true
|
|
||||||
}
|
|
||||||
|
|
||||||
data class SimpleJavaFunctionCallInfo(override val targetFunction: PsiMethod) : SimpleFunctionCallInfo() {
|
|
||||||
override val isSuspendCall: Boolean = false
|
|
||||||
override val isJavaFunctionCall: Boolean = false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ConstructorCallInfo
|
|
||||||
|
|
||||||
sealed class ConstructorCallInfo : CallInfo() {
|
|
||||||
final override val isSuspendCall: Boolean = false
|
|
||||||
abstract val isPrimary: Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
data class KtExplicitConstructorCallInfo(
|
|
||||||
override val targetFunction: KtConstructor<*>,
|
|
||||||
override val isPrimary: Boolean
|
|
||||||
) : ConstructorCallInfo() {
|
|
||||||
override val isJavaFunctionCall: Boolean = false
|
|
||||||
}
|
|
||||||
|
|
||||||
data class KtImplicitPrimaryConstructorCallInfo(val owner: KtClass) : ConstructorCallInfo() {
|
|
||||||
override val targetFunction: PsiElement? = null
|
|
||||||
override val isJavaFunctionCall: Boolean = false
|
|
||||||
override val isPrimary: Boolean = true
|
|
||||||
}
|
|
||||||
|
|
||||||
data class JavaExplicitConstructorCallInfo(
|
|
||||||
override val targetFunction: PsiMethod,
|
|
||||||
override val isPrimary: Boolean
|
|
||||||
) : ConstructorCallInfo() {
|
|
||||||
override val isJavaFunctionCall: Boolean = true
|
|
||||||
}
|
|
||||||
|
|
||||||
data class JavaImplicitPrimaryConstructorCallInfo(val owner: PsiClass) : ConstructorCallInfo() {
|
|
||||||
override val targetFunction: PsiElement? = null
|
|
||||||
override val isJavaFunctionCall: Boolean = true
|
|
||||||
override val isPrimary: Boolean = true
|
|
||||||
}
|
|
||||||
+13
-31
@@ -6,10 +6,6 @@
|
|||||||
package org.jetbrains.kotlin.idea.frontend.api.fir
|
package org.jetbrains.kotlin.idea.frontend.api.fir
|
||||||
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiClass
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.PsiField
|
|
||||||
import com.intellij.psi.PsiMethod
|
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
@@ -25,7 +21,7 @@ import org.jetbrains.kotlin.idea.fir.*
|
|||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.LowLevelFirApiFacade
|
import org.jetbrains.kotlin.idea.fir.low.level.api.LowLevelFirApiFacade
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.*
|
import org.jetbrains.kotlin.idea.frontend.api.*
|
||||||
import org.jetbrains.kotlin.idea.references.FirReferenceResolveHelper
|
import org.jetbrains.kotlin.idea.references.FirReferenceResolveHelper
|
||||||
import org.jetbrains.kotlin.idea.references.FirReferenceResolveHelper.toTargetPsi
|
import org.jetbrains.kotlin.idea.references.FirReferenceResolveHelper.toTargetSymbol
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -35,6 +31,8 @@ class FirAnalysisSession(
|
|||||||
) : FrontendAnalysisSession(project) {
|
) : FrontendAnalysisSession(project) {
|
||||||
constructor(element: KtElement) : this(element.project)
|
constructor(element: KtElement) : this(element.project)
|
||||||
|
|
||||||
|
internal val symbolBuilder = KtSymbolByFirBuilder(validityToken)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
assertIsValid()
|
assertIsValid()
|
||||||
}
|
}
|
||||||
@@ -114,55 +112,39 @@ class FirAnalysisSession(
|
|||||||
|
|
||||||
private fun resolveCall(firCall: FirFunctionCall, callExpression: KtExpression): CallInfo? {
|
private fun resolveCall(firCall: FirFunctionCall, callExpression: KtExpression): CallInfo? {
|
||||||
val session = callExpression.session
|
val session = callExpression.session
|
||||||
val resolvedFunctionPsi = firCall.calleeReference.toTargetPsi(session)
|
val resolvedFunctionSymbol = firCall.calleeReference.toTargetSymbol(session, symbolBuilder)
|
||||||
val resolvedCalleeSymbol = (firCall.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol
|
val resolvedCalleeSymbol = (firCall.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol
|
||||||
return when {
|
return when {
|
||||||
resolvedCalleeSymbol is FirConstructorSymbol -> {
|
resolvedCalleeSymbol is FirConstructorSymbol -> {
|
||||||
val fir = resolvedCalleeSymbol.fir
|
val fir = resolvedCalleeSymbol.fir
|
||||||
when (resolvedFunctionPsi) {
|
FunctionCallInfo(symbolBuilder.buildFirConstructorSymbol(fir))
|
||||||
is KtClass -> KtImplicitPrimaryConstructorCallInfo(resolvedFunctionPsi)
|
|
||||||
is PsiClass -> JavaImplicitPrimaryConstructorCallInfo(resolvedFunctionPsi)
|
|
||||||
is KtConstructor<*> -> KtExplicitConstructorCallInfo(resolvedFunctionPsi, fir.isPrimary)
|
|
||||||
is PsiMethod -> JavaExplicitConstructorCallInfo(resolvedFunctionPsi, fir.isPrimary)
|
|
||||||
else -> {
|
|
||||||
val classId = resolvedCalleeSymbol.callableId.classId
|
|
||||||
val firClass = classId?.let(session.firSymbolProvider::getClassLikeSymbolByFqName)
|
|
||||||
when (val psiClass = firClass?.fir?.findPsi(callExpression.project)) {
|
|
||||||
is PsiClass -> JavaImplicitPrimaryConstructorCallInfo(psiClass)
|
|
||||||
is KtClass -> KtImplicitPrimaryConstructorCallInfo(psiClass)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
firCall.dispatchReceiver is FirQualifiedAccessExpression && firCall.isImplicitFunctionCall() -> {
|
firCall.dispatchReceiver is FirQualifiedAccessExpression && firCall.isImplicitFunctionCall() -> {
|
||||||
val target = with(FirReferenceResolveHelper) {
|
val target = with(FirReferenceResolveHelper) {
|
||||||
val calleeReference = (firCall.dispatchReceiver as FirQualifiedAccessExpression).calleeReference
|
val calleeReference = (firCall.dispatchReceiver as FirQualifiedAccessExpression).calleeReference
|
||||||
calleeReference.toTargetPsi(session)
|
calleeReference.toTargetSymbol(session, symbolBuilder)
|
||||||
}
|
}
|
||||||
when (target) {
|
when (target) {
|
||||||
null -> null
|
null -> null
|
||||||
is KtValVarKeywordOwner, is PsiField -> {
|
is KtVariableLikeSymbol -> {
|
||||||
val functionSymbol =
|
val functionSymbol =
|
||||||
(firCall.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol as? FirNamedFunctionSymbol
|
(firCall.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol as? FirNamedFunctionSymbol
|
||||||
when (functionSymbol?.callableId) {
|
when (functionSymbol?.callableId) {
|
||||||
null -> null
|
null -> null
|
||||||
in kotlinFunctionInvokeCallableIds -> VariableAsFunctionCallInfo(target, functionSymbol.fir.isSuspend)
|
in kotlinFunctionInvokeCallableIds -> VariableAsFunctionCallInfo(target, functionSymbol.fir.isSuspend)
|
||||||
else -> (resolvedFunctionPsi as? KtNamedFunction)?.let { VariableAsFunctionLikeCallInfo(target, it) }
|
else -> (resolvedFunctionSymbol as? KtSimpleFunctionSymbol)
|
||||||
|
?.let { VariableAsFunctionLikeCallInfo(target, it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> resolvedFunctionPsi?.asSimpleFunctionCall()
|
else -> resolvedFunctionSymbol?.asSimpleFunctionCall()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> resolvedFunctionPsi?.asSimpleFunctionCall()
|
else -> resolvedFunctionSymbol?.asSimpleFunctionCall()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun PsiElement.asSimpleFunctionCall() = when (this) {
|
private fun KtSymbol.asSimpleFunctionCall() =
|
||||||
is KtNamedFunction -> SimpleKtFunctionCallInfo(this)
|
(this as? KtSimpleFunctionSymbol)?.let(::FunctionCallInfo)
|
||||||
is PsiMethod -> SimpleJavaFunctionCallInfo(this)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun forEachSuperClass(firClass: FirClass<*>, action: (FirResolvedTypeRef) -> Unit) {
|
private fun forEachSuperClass(firClass: FirClass<*>, action: (FirResolvedTypeRef) -> Unit) {
|
||||||
firClass.superTypeRefs.forEach { superType ->
|
firClass.superTypeRefs.forEach { superType ->
|
||||||
|
|||||||
+1
-1
@@ -4,4 +4,4 @@ fun call() {
|
|||||||
<selection>function(1)</selection>
|
<selection>function(1)</selection>
|
||||||
}
|
}
|
||||||
|
|
||||||
// CALL: SimpleKtFunctionCallInfo: targetFunction = function(a: Int): IMPLICIT_TYPE
|
// CALL: FunctionCallInfo: targetFunction = function(a: kotlin.Int): kotlin.Unit
|
||||||
+1
-1
@@ -4,4 +4,4 @@ fun call() {
|
|||||||
"str".<selection>function(1)</selection>
|
"str".<selection>function(1)</selection>
|
||||||
}
|
}
|
||||||
|
|
||||||
// CALL: SimpleKtFunctionCallInfo: targetFunction = function(<receiver> : String, a: Int): IMPLICIT_TYPE
|
// CALL: FunctionCallInfo: targetFunction = function(<receiver>: kotlin.String, a: kotlin.Int): kotlin.Unit
|
||||||
Vendored
+1
-1
@@ -4,4 +4,4 @@ fun call() {
|
|||||||
"str"?.<selection>function(1)</selection>
|
"str"?.<selection>function(1)</selection>
|
||||||
}
|
}
|
||||||
|
|
||||||
// CALL: SimpleKtFunctionCallInfo: targetFunction = function(<receiver> : String, a: Int): IMPLICIT_TYPE
|
// CALL: FunctionCallInfo: targetFunction = function(<receiver>: kotlin.String, a: kotlin.Int): kotlin.Unit
|
||||||
+1
-1
@@ -4,4 +4,4 @@ fun call() {
|
|||||||
val a = <selection>A()</selection>
|
val a = <selection>A()</selection>
|
||||||
}
|
}
|
||||||
|
|
||||||
// CALL: KtImplicitPrimaryConstructorCallInfo: owner = Implicit constructor of A
|
// CALL: FunctionCallInfo: targetFunction = <constructor>(): A
|
||||||
+1
-1
@@ -3,4 +3,4 @@ fun call() {
|
|||||||
val a = <selection>A()</selection>
|
val a = <selection>A()</selection>
|
||||||
}
|
}
|
||||||
|
|
||||||
// CALL: JavaImplicitPrimaryConstructorCallInfo: owner = Implicit constructor of A
|
// CALL: FunctionCallInfo: targetFunction = <constructor>(): A
|
||||||
+1
-1
@@ -3,4 +3,4 @@ fun call() {
|
|||||||
javaClass.<selection>javaMethod()</selection>
|
javaClass.<selection>javaMethod()</selection>
|
||||||
}
|
}
|
||||||
|
|
||||||
// CALL: SimpleJavaFunctionCallInfo: targetFunction = JavaClass.javaMethod(): void
|
// CALL: FunctionCallInfo: targetFunction = JavaClass.javaMethod(): kotlin.Unit
|
||||||
+1
-1
@@ -2,4 +2,4 @@ fun call(x: (Int) -> String) {
|
|||||||
<selection>x(1)</selection>
|
<selection>x(1)</selection>
|
||||||
}
|
}
|
||||||
|
|
||||||
// CALL: VariableAsFunctionCallInfo: target = x, isSuspendCall = false
|
// CALL: VariableAsFunctionCallInfo: target = x: kotlin.Function1<kotlin.Int, kotlin.String>, isSuspendCall = false
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
operator fun Int.invoke(): String {}
|
operator fun Int.invoke(): String {}
|
||||||
|
|
||||||
fun call(x: Int) {
|
fun call(x: kotlin.int) {
|
||||||
<selection>x()</selection>
|
<selection>x()</selection>
|
||||||
}
|
}
|
||||||
|
|
||||||
// CALL: SimpleKtFunctionCallInfo: targetFunction = invoke(<receiver> : Int): String
|
// CALL: FunctionCallInfo: targetFunction = invoke(<receiver>: kotlin.Int): kotlin.String
|
||||||
+28
-33
@@ -18,6 +18,8 @@ import com.intellij.psi.PsiParameter
|
|||||||
import com.intellij.testFramework.LightPlatformTestCase
|
import com.intellij.testFramework.LightPlatformTestCase
|
||||||
import org.intellij.plugins.relaxNG.compact.psi.util.PsiFunction
|
import org.intellij.plugins.relaxNG.compact.psi.util.PsiFunction
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.CallInfo
|
import org.jetbrains.kotlin.idea.frontend.api.CallInfo
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.TypeInfo
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||||
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightTestCase
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightTestCase
|
||||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||||
@@ -42,29 +44,31 @@ abstract class AbstractResolveCallTest : @Suppress("DEPRECATION") KotlinLightCod
|
|||||||
val elements = editor.caretModel.caretsAndSelections.map { selection ->
|
val elements = editor.caretModel.caretsAndSelections.map { selection ->
|
||||||
getSingleSelectedElement(selection)
|
getSingleSelectedElement(selection)
|
||||||
}
|
}
|
||||||
val callInfos = executeOnPooledThreadInReadAction {
|
|
||||||
|
val actualText = executeOnPooledThreadInReadAction {
|
||||||
val analysisSession = FirAnalysisSession(file as KtFile)
|
val analysisSession = FirAnalysisSession(file as KtFile)
|
||||||
elements.map { element ->
|
val callInfos = elements.map { element ->
|
||||||
when (element) {
|
when (element) {
|
||||||
is KtCallExpression -> analysisSession.resolveCall(element)
|
is KtCallExpression -> analysisSession.resolveCall(element)
|
||||||
is KtBinaryExpression -> analysisSession.resolveCall(element)
|
is KtBinaryExpression -> analysisSession.resolveCall(element)
|
||||||
else -> error("Selected should be either KtCallExpression or KtBinaryExpression but was $element")
|
else -> error("Selected should be either KtCallExpression or KtBinaryExpression but was $element")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (callInfos.isEmpty()) {
|
|
||||||
error("There are should be at least one call selected")
|
|
||||||
}
|
|
||||||
|
|
||||||
val textWithoutLatestComments = run {
|
if (callInfos.isEmpty()) {
|
||||||
val rawText = File(path).readText()
|
error("There are should be at least one call selected")
|
||||||
"""(?m)^// CALL:\s.*$""".toRegex().replace(rawText, "").trimEnd()
|
}
|
||||||
}
|
|
||||||
val actualText = buildString {
|
val textWithoutLatestComments = run {
|
||||||
append(textWithoutLatestComments)
|
val rawText = File(path).readText()
|
||||||
append("\n\n")
|
"""(?m)^// CALL:\s.*$""".toRegex().replace(rawText, "").trimEnd()
|
||||||
callInfos.joinTo(this, separator = "\n") { info ->
|
}
|
||||||
"// CALL: ${info?.stringRepresentation()}"
|
buildString {
|
||||||
|
append(textWithoutLatestComments)
|
||||||
|
append("\n\n")
|
||||||
|
callInfos.joinTo(this, separator = "\n") { info ->
|
||||||
|
"// CALL: ${info?.stringRepresentation()}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KotlinTestUtils.assertEqualsToFile(File(path), actualText)
|
KotlinTestUtils.assertEqualsToFile(File(path), actualText)
|
||||||
@@ -111,31 +115,22 @@ private fun File.getExternalFiles(): List<File> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun CallInfo.stringRepresentation(): String {
|
private fun CallInfo.stringRepresentation(): String {
|
||||||
|
fun TypeInfo.render() = asDenotableTypeStringRepresentation().replace('/', '.')
|
||||||
fun Any.stringValue(): String? = when (this) {
|
fun Any.stringValue(): String? = when (this) {
|
||||||
is PsiMethod -> buildString {
|
is KtFunctionLikeSymbol -> buildString {
|
||||||
append(getKotlinFqName()!!)
|
append(if (this@stringValue is KtFunctionSymbol) fqName else "<constructor>")
|
||||||
@Suppress("UnstableApiUsage")
|
|
||||||
parameters.joinTo(this, prefix = "(", postfix = ")") { parameter ->
|
|
||||||
"${parameter.name}: ${(parameter as PsiParameter).typeElement!!.text}"
|
|
||||||
}
|
|
||||||
append(": ${returnTypeElement!!.text}")
|
|
||||||
}
|
|
||||||
is KtFunction -> buildString {
|
|
||||||
append(getKotlinFqName()!!)
|
|
||||||
append("(")
|
append("(")
|
||||||
receiverTypeReference?.let { receiver ->
|
(this@stringValue as? KtFunctionSymbol)?.receiverType?.let { receiver ->
|
||||||
append("<receiver> : ${receiver.text}")
|
append("<receiver>: ${receiver.render()}")
|
||||||
if (valueParameters.isNotEmpty()) append(", ")
|
if (valueParameters.isNotEmpty()) append(", ")
|
||||||
}
|
}
|
||||||
valueParameters.joinTo(this,) { parameter ->
|
valueParameters.joinTo(this) { parameter ->
|
||||||
"${parameter.name}: ${parameter.typeReference!!.text}"
|
"${parameter.name}: ${parameter.type.render()}"
|
||||||
}
|
}
|
||||||
append(")")
|
append(")")
|
||||||
append(": ${typeReference?.text ?: "IMPLICIT_TYPE"}")
|
append(": ${type.render()}")
|
||||||
}
|
}
|
||||||
is KtClass -> "Implicit constructor of ${getKotlinFqName()!!}"
|
is KtParameterSymbol -> "$name: ${type.render()}"
|
||||||
is PsiClass -> "Implicit constructor of ${getKotlinFqName()!!}"
|
|
||||||
is KtParameter -> name!!
|
|
||||||
is Boolean -> toString()
|
is Boolean -> toString()
|
||||||
else -> error("unexpected parameter type ${this::class}")
|
else -> error("unexpected parameter type ${this::class}")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user