Correct read/write access detection in case of plusAssign etc

This commit is contained in:
Valentin Kipyatkov
2015-08-17 23:01:58 +03:00
parent 97bf6f820a
commit 4876668716
8 changed files with 68 additions and 34 deletions
@@ -308,29 +308,6 @@ public inline fun <reified T : JetElement, R> flatMapDescendantsOfTypeVisitor(ac
return forEachDescendantOfTypeVisitor<T> { accumulator.addAll(map(it)) }
}
// ----------- Read/write access -----------------------------------------------------------------------------------------------------------------------
public enum class ReferenceAccess {
READ, WRITE, READ_WRITE
}
public fun JetExpression.readWriteAccess(): ReferenceAccess {
var expression = getQualifiedExpressionForSelectorOrThis()
while (expression.parent is JetParenthesizedExpression || expression.parent is JetAnnotatedExpression) {
expression = expression.parent as JetExpression
}
val assignment = expression.getAssignmentByLHS()
if (assignment != null) {
return if (assignment.operationToken == JetTokens.EQ) ReferenceAccess.WRITE else ReferenceAccess.READ_WRITE
}
return if ((expression.parent as? JetUnaryExpression)?.operationToken in constant { setOf(JetTokens.PLUSPLUS, JetTokens.MINUSMINUS) })
ReferenceAccess.READ_WRITE
else
ReferenceAccess.READ
}
// ----------- Other -----------------------------------------------------------------------------------------------------------------------
public fun JetClassOrObject.effectiveDeclarations(): List<JetDeclaration> {
@@ -30,10 +30,7 @@ import com.intellij.psi.PsiReferenceExpression
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.findUsages.UsageTypeEnum.*
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.references.JetArrayAccessReference
import org.jetbrains.kotlin.idea.references.JetInvokeFunctionReference
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.references.unwrappedTargets
import org.jetbrains.kotlin.idea.references.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -146,7 +143,7 @@ public object UsageTypeUtils {
}
}
return when (refExpr.readWriteAccess()) {
return when (refExpr.readWriteAccess(useResolveForReadWrite = true)) {
ReferenceAccess.READ -> READ
ReferenceAccess.WRITE, ReferenceAccess.READ_WRITE -> WRITE
}
@@ -23,8 +23,6 @@ import org.jetbrains.kotlin.idea.kdoc.KDocReference
import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.ReferenceAccess
import org.jetbrains.kotlin.psi.psiUtil.readWriteAccess
public class JetReferenceContributor() : PsiReferenceContributor() {
public override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
@@ -36,7 +34,7 @@ public class JetReferenceContributor() : PsiReferenceContributor() {
registerMultiProvider(javaClass<JetNameReferenceExpression>()) {
if (it.getReferencedNameElementType() != JetTokens.IDENTIFIER) return@registerMultiProvider emptyArray()
when (it.readWriteAccess()) {
when (it.readWriteAccess(useResolveForReadWrite = false)) {
ReferenceAccess.READ -> arrayOf(SyntheticPropertyAccessorReference.Getter(it))
ReferenceAccess.WRITE -> arrayOf(SyntheticPropertyAccessorReference.Setter(it))
ReferenceAccess.READ_WRITE -> arrayOf(SyntheticPropertyAccessorReference.Getter(it), SyntheticPropertyAccessorReference.Setter(it))
@@ -18,12 +18,20 @@ package org.jetbrains.kotlin.idea.references
import com.intellij.psi.*
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention
import org.jetbrains.kotlin.idea.kdoc.KDocReference
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.addToStdlib.constant
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.emptyOrSingletonList
@@ -132,3 +140,40 @@ val JetElement.mainReference: JetReference?
else -> getReferences().firstIsInstanceOrNull<JetReference>()
}
}
// ----------- Read/write access -----------------------------------------------------------------------------------------------------------------------
public enum class ReferenceAccess {
READ, WRITE, READ_WRITE
}
public fun JetExpression.readWriteAccess(useResolveForReadWrite: Boolean): ReferenceAccess {
var expression = getQualifiedExpressionForSelectorOrThis()
while (expression.parent is JetParenthesizedExpression || expression.parent is JetAnnotatedExpression) {
expression = expression.parent as JetExpression
}
val assignment = expression.getAssignmentByLHS()
if (assignment != null) {
when (assignment.operationToken) {
JetTokens.EQ -> return ReferenceAccess.WRITE
else -> {
if (!useResolveForReadWrite) return ReferenceAccess.READ_WRITE
val bindingContext = assignment.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = assignment.getResolvedCall(bindingContext) ?: return ReferenceAccess.READ_WRITE
if (!resolvedCall.status.isSuccess) return ReferenceAccess.READ_WRITE
return if (resolvedCall.resultingDescriptor.name.asString().endsWith("Assign"))
ReferenceAccess.READ
else
ReferenceAccess.READ_WRITE
}
}
}
return if ((expression.parent as? JetUnaryExpression)?.operationToken in constant { setOf(JetTokens.PLUSPLUS, JetTokens.MINUSMINUS) })
ReferenceAccess.READ_WRITE
else
ReferenceAccess.READ
}
@@ -20,11 +20,11 @@ import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import org.jetbrains.kotlin.asJava.KotlinLightMethod
import org.jetbrains.kotlin.idea.references.ReferenceAccess
import org.jetbrains.kotlin.idea.references.readWriteAccess
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.ReferenceAccess
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.readWriteAccess
public class KotlinReadWriteAccessDetector : ReadWriteAccessDetector() {
override fun isReadWriteAccessible(element: PsiElement) = element is JetVariableDeclaration || element is JetParameter
@@ -58,7 +58,7 @@ public class KotlinReadWriteAccessDetector : ReadWriteAccessDetector() {
override fun getExpressionAccess(expression: PsiElement): ReadWriteAccessDetector.Access {
if (expression !is JetExpression) return ReadWriteAccessDetector.Access.Read
return when (expression.readWriteAccess()) {
return when (expression.readWriteAccess(useResolveForReadWrite = true)) {
ReferenceAccess.READ -> ReadWriteAccessDetector.Access.Read
ReferenceAccess.WRITE -> ReadWriteAccessDetector.Access.Write
ReferenceAccess.READ_WRITE -> ReadWriteAccessDetector.Access.ReadWrite
@@ -0,0 +1,10 @@
// PSI_ELEMENT: org.jetbrains.kotlin.psi.JetProperty
// OPTIONS: usages
class C
fun C.plusAssign(p: Int) = this
fun foo() {
val <caret>c = C()
c += 10
}
@@ -0,0 +1 @@
Value read (9: 5) c += 10
@@ -1035,6 +1035,12 @@ public class JetFindUsagesTestGenerated extends AbstractJetFindUsagesTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/findUsages/kotlin/variable"), Pattern.compile("^(.+)\\.0\\.kt$"), true);
}
@TestMetadata("plusAssignFun.0.kt")
public void testPlusAssignFun() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/variable/plusAssignFun.0.kt");
doTest(fileName);
}
@TestMetadata("readAccess.0.kt")
public void testReadAccess() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/variable/readAccess.0.kt");