diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUFunctionCallExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUFunctionCallExpression.kt index 91aea7f479f..d3f37a1b407 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUFunctionCallExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUFunctionCallExpression.kt @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.uast import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde @@ -44,7 +43,7 @@ class KotlinUFunctionCallExpression( override val functionReference by lz { val calleeExpression = psi.calleeExpression ?: return@lz null val name = (calleeExpression as? KtSimpleNameExpression)?.getReferencedName() ?: return@lz null - KotlinUSimpleReferenceExpression(calleeExpression, name, this) + KotlinNameUSimpleReferenceExpression(calleeExpression, name, this) } override val valueArgumentCount: Int diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUSimpleReferenceExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUSimpleReferenceExpression.kt index 57d2b8d3d5d..52687d1f817 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUSimpleReferenceExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUSimpleReferenceExpression.kt @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.uast import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde @@ -27,15 +26,21 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.uast.* import org.jetbrains.uast.psi.PsiElementBacked -class KotlinUSimpleReferenceExpression( +open class KotlinUSimpleReferenceExpression( override val psi: PsiElement, override val identifier: String, override val parent: UElement -) : USimpleReferenceExpression, PsiElementBacked, KotlinTypeHelper, NoEvaluate { +) : USimpleReferenceExpression, PsiElementBacked, KotlinTypeHelper, KotlinEvaluateHelper { override fun resolve(context: UastContext) = context.convert( psi.references.firstOrNull()?.resolve()) as? UDeclaration } +class KotlinNameUSimpleReferenceExpression( + psi: PsiElement, + identifier: String, + parent: UElement +) : KotlinUSimpleReferenceExpression(psi, identifier, parent) + class KotlinClassViaConstructorUSimpleReferenceExpression( override val psi: KtCallExpression, override val identifier: String, diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/lint/PropertyAsCallAndroidUastAdditionalChecker.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/lint/PropertyAsCallAndroidUastAdditionalChecker.kt new file mode 100644 index 00000000000..848257562d5 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/lint/PropertyAsCallAndroidUastAdditionalChecker.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.uast.lint + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.references.SyntheticPropertyAccessorReference +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.uast.* +import org.jetbrains.uast.* +import org.jetbrains.uast.check.AndroidUastAdditionalChecker +import org.jetbrains.uast.psi.PsiElementBacked + +class PropertyAsCallAndroidUastAdditionalChecker : AndroidUastAdditionalChecker { + override fun invoke(element: UElement, handler: UastHandler, context: UastContext) { + val expr = element as? KotlinUSimpleReferenceExpression ?: return + if (expr is KotlinNameUSimpleReferenceExpression) return + + val ktElement = expr.psi as? KtElement ?: return + val bindingContext = ktElement.analyze(BodyResolveMode.PARTIAL) + + val referenceToAccessor = ktElement.references.firstOrNull { it is SyntheticPropertyAccessorReference } ?: return + val accessorDescriptor = (referenceToAccessor as SyntheticPropertyAccessorReference) + .resolveToDescriptors(bindingContext).firstOrNull() ?: return + + val resolvedCall = ktElement.getResolvedCall(bindingContext) ?: return + + val setterValue = if (referenceToAccessor is SyntheticPropertyAccessorReference.Setter) + findAssignment(ktElement, ktElement.parent)?.right ?: return + else + null + + val callExpression: UCallExpression = object : UCallExpression, NoEvaluate, PsiElementBacked { + override val parent = element.parent + override val psi = ktElement + + override val functionReference = KotlinNameUSimpleReferenceExpression(expr.psi, expr.identifier, expr.parent) + override val classReference = null + override val functionName = accessorDescriptor.name.asString() + override val functionNameElement by lz { KotlinPsiElementStub(ktElement, this) } + + override val valueArgumentCount: Int + get() = if (setterValue != null) 1 else 0 + + override val valueArguments by lz { + if (setterValue != null) + listOf(KotlinConverter.convert(setterValue, this)) + else + emptyList() + } + + override val typeArgumentCount: Int + get() = resolvedCall.typeArguments.size + + override val typeArguments by lz { + resolvedCall.typeArguments.map { KotlinUType(it.value, ktElement.project, this) } + } + + override val kind = UastCallKind.FUNCTION_CALL + + override fun resolve(context: UastContext) = element.resolveIfCan(context) as? UFunction + } + + handler(callExpression) + } + + private tailrec fun findAssignment(prev: PsiElement?, element: PsiElement?): KtBinaryExpression? = when (element) { + is KtBinaryExpression -> if (element.left == prev && element.operationToken == KtTokens.EQ) element else null + is KtQualifiedExpression -> findAssignment(element, element.parent) + is KtSimpleNameExpression -> findAssignment(element, element.parent) + else -> null + } +} \ No newline at end of file