Dsl highlighting: support highlighting properties

Fix properties not being highlighted
Fix test infrastructure to verify actual output of highlighting visitors
This commit is contained in:
Pavel V. Talanov
2018-08-01 18:43:54 +02:00
parent c250f7fca4
commit e9a9f2a1aa
8 changed files with 107 additions and 31 deletions
@@ -19,6 +19,6 @@ package org.jetbrains.kotlin.idea.highlighter
import com.intellij.lang.annotation.AnnotationHolder import com.intellij.lang.annotation.AnnotationHolder
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
internal abstract class AfterAnalysisHighlightingVisitor protected constructor( abstract class AfterAnalysisHighlightingVisitor protected constructor(
holder: AnnotationHolder, protected var bindingContext: BindingContext holder: AnnotationHolder, protected var bindingContext: BindingContext
) : HighlightingVisitor(holder) ) : HighlightingVisitor(holder)
@@ -48,16 +48,8 @@ abstract class HighlightingVisitor protected constructor(
} }
} }
protected fun applyHighlighterExtensions(element: PsiElement, descriptor: DeclarationDescriptor): Boolean { protected fun attributeKeyForDeclarationFromExtensions(element: PsiElement, descriptor: DeclarationDescriptor) =
if (!NameHighlighter.namesHighlightingEnabled) return false
Extensions.getExtensions(HighlighterExtension.EP_NAME).firstNotNullResult { extension -> Extensions.getExtensions(HighlighterExtension.EP_NAME).firstNotNullResult { extension ->
extension.highlightDeclaration(element, descriptor) extension.highlightDeclaration(element, descriptor)
}?.let { key ->
highlightName(element, key)
return true
} }
return false
}
} }
@@ -97,7 +97,7 @@ open class KotlinPsiChecker : Annotator, HighlightRangeExtension {
} }
companion object { companion object {
private fun getAfterAnalysisVisitor(holder: AnnotationHolder, bindingContext: BindingContext) = arrayOf( fun getAfterAnalysisVisitor(holder: AnnotationHolder, bindingContext: BindingContext) = arrayOf(
PropertiesHighlightingVisitor(holder, bindingContext), PropertiesHighlightingVisitor(holder, bindingContext),
FunctionsHighlightingVisitor(holder, bindingContext), FunctionsHighlightingVisitor(holder, bindingContext),
VariablesHighlightingVisitor(holder, bindingContext), VariablesHighlightingVisitor(holder, bindingContext),
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.idea.highlighter package org.jetbrains.kotlin.idea.highlighter
import com.intellij.lang.annotation.AnnotationHolder import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.openapi.extensions.Extensions
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
@@ -16,8 +18,10 @@ import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.psi.KtThisExpression import org.jetbrains.kotlin.psi.KtThisExpression
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
import org.jetbrains.kotlin.resolve.calls.tower.isSynthesized import org.jetbrains.kotlin.resolve.calls.tower.isSynthesized
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
internal class PropertiesHighlightingVisitor(holder: AnnotationHolder, bindingContext: BindingContext) internal class PropertiesHighlightingVisitor(holder: AnnotationHolder, bindingContext: BindingContext)
: AfterAnalysisHighlightingVisitor(holder, bindingContext) { : AfterAnalysisHighlightingVisitor(holder, bindingContext) {
@@ -35,14 +39,23 @@ internal class PropertiesHighlightingVisitor(holder: AnnotationHolder, bindingCo
return return
} }
highlightProperty(expression, target) val resolvedCall = expression.getResolvedCall(bindingContext)
val attributesKey = resolvedCall?.let { call ->
Extensions.getExtensions(HighlighterExtension.EP_NAME).firstNotNullResult { extension ->
extension.highlightCall(expression, call)
}
} ?: attributeKeyByPropertyType(target)
highlightName(expression, attributesKey)
} }
override fun visitProperty(property: KtProperty) { override fun visitProperty(property: KtProperty) {
val nameIdentifier = property.nameIdentifier ?: return val nameIdentifier = property.nameIdentifier ?: return
val propertyDescriptor = bindingContext.get(BindingContext.VARIABLE, property) val propertyDescriptor = bindingContext.get(BindingContext.VARIABLE, property)
if (propertyDescriptor is PropertyDescriptor) { if (propertyDescriptor is PropertyDescriptor) {
highlightProperty(nameIdentifier, propertyDescriptor) highlightPropertyDeclaration(nameIdentifier, propertyDescriptor)
} }
super.visitProperty(property) super.visitProperty(property)
@@ -55,19 +68,24 @@ internal class PropertiesHighlightingVisitor(holder: AnnotationHolder, bindingCo
if (propertyDescriptor.isVar) { if (propertyDescriptor.isVar) {
highlightName(nameIdentifier, MUTABLE_VARIABLE) highlightName(nameIdentifier, MUTABLE_VARIABLE)
} }
highlightProperty(nameIdentifier, propertyDescriptor) highlightPropertyDeclaration(nameIdentifier, propertyDescriptor)
} }
super.visitParameter(parameter) super.visitParameter(parameter)
} }
private fun highlightProperty( private fun highlightPropertyDeclaration(
elementToHighlight: PsiElement, elementToHighlight: PsiElement,
descriptor: PropertyDescriptor) { descriptor: PropertyDescriptor
) {
highlightName(
elementToHighlight,
attributeKeyForDeclarationFromExtensions(elementToHighlight, descriptor) ?: attributeKeyByPropertyType(descriptor)
)
}
if (applyHighlighterExtensions(elementToHighlight, descriptor)) return private fun attributeKeyByPropertyType(descriptor: PropertyDescriptor): TextAttributesKey {
return when {
val attributesKey = when {
descriptor.isDynamic() -> descriptor.isDynamic() ->
DYNAMIC_PROPERTY_CALL DYNAMIC_PROPERTY_CALL
@@ -80,6 +98,5 @@ internal class PropertiesHighlightingVisitor(holder: AnnotationHolder, bindingCo
else -> else ->
INSTANCE_PROPERTY INSTANCE_PROPERTY
} }
highlightName(elementToHighlight, attributesKey)
} }
} }
@@ -89,8 +89,11 @@ internal class TypeKindHighlightingVisitor(holder: AnnotationHolder, bindingCont
val identifier = classOrObject.nameIdentifier val identifier = classOrObject.nameIdentifier
val classDescriptor = bindingContext.get(BindingContext.CLASS, classOrObject) val classDescriptor = bindingContext.get(BindingContext.CLASS, classOrObject)
if (identifier != null && classDescriptor != null) { if (identifier != null && classDescriptor != null) {
if (applyHighlighterExtensions(identifier, classDescriptor)) return highlightName(
highlightName(identifier, textAttributesKeyForClass(classDescriptor)) identifier,
attributeKeyForDeclarationFromExtensions(classOrObject, classDescriptor)
?: textAttributesKeyForClass(classDescriptor)
)
} }
super.visitClassOrObject(classOrObject) super.visitClassOrObject(classOrObject)
} }
@@ -99,8 +102,10 @@ internal class TypeKindHighlightingVisitor(holder: AnnotationHolder, bindingCont
val identifier = typeAlias.nameIdentifier val identifier = typeAlias.nameIdentifier
val descriptor = bindingContext.get(BindingContext.TYPE_ALIAS, typeAlias) val descriptor = bindingContext.get(BindingContext.TYPE_ALIAS, typeAlias)
if (identifier != null && descriptor != null) { if (identifier != null && descriptor != null) {
if (applyHighlighterExtensions(identifier, descriptor)) return highlightName(
highlightName(identifier, TYPE_ALIAS) identifier,
attributeKeyForDeclarationFromExtensions(identifier, descriptor) ?: TYPE_ALIAS
)
} }
super.visitTypeAlias(typeAlias) super.visitTypeAlias(typeAlias)
} }
+44
View File
@@ -0,0 +1,44 @@
package p
@DslMarker
annotation class A
@DslMarker
annotation class B
@A
val AC.p1: Int
get() = 3
@A
var p2: Int = 5
@B
val BC.p3
get() = 6
@B
var BC.p7
get() = 3
set(i) {}
@A
class AC
@B
class BC
fun test() {
p2 // 4
p2 = 6 // 4
with(AC()) {
p1 // 4
}
with(BC()) {
p3 // 1
p7 // 1
p7 = 3 // 1
}
}
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.idea.highlighter package org.jetbrains.kotlin.idea.highlighter
import com.intellij.codeInsight.daemon.impl.AnnotationHolderImpl
import com.intellij.lang.annotation.AnnotationSession
import com.intellij.psi.PsiComment import com.intellij.psi.PsiComment
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
@@ -30,15 +32,26 @@ abstract class AbstractDslHighlighterTest : LightCodeInsightFixtureTestCase() {
val commentText = (file.findElementAt(endOffset - 1) as? PsiComment)?.text val commentText = (file.findElementAt(endOffset - 1) as? PsiComment)?.text
val styleIdByComment = commentText?.replace("//", "")?.trim()?.toInt()?.let { DslHighlighterExtension.externalKeyName(it) } val styleIdByComment = commentText?.replace("//", "")?.trim()?.toInt()?.let { DslHighlighterExtension.externalKeyName(it) }
val styleIdByCall = extension.highlightCall(element, call)?.externalName val styleIdByCall = extension.highlightCall(element, call)?.externalName
if (styleIdByCall == styleIdByComment) return if (styleIdByCall != null && styleIdByCall == styleIdByComment) {
val annotationHolder = AnnotationHolderImpl(AnnotationSession(psiFile))
val checkers = KotlinPsiChecker.getAfterAnalysisVisitor(annotationHolder, bindingContext)
checkers.forEach { call.call.callElement.accept(it) }
assertTrue(
"KotlinPsiChecker did not contribute an Annotation containing the correct text attribute key at line ${lineNumber + 1}",
annotationHolder.any {
it.textAttributes.externalName == styleIdByComment
}
)
} else if (styleIdByCall != styleIdByComment) {
val what = element.text
val location = "at line ${editor.document.getLineNumber(element.textOffset) + 1}"
val what = element.text if (styleIdByCall == null) fail("Expected `$what` to be highlighted $location")
val location = "at line ${editor.document.getLineNumber(element.textOffset) + 1}" if (styleIdByComment == null) fail("Unexpected highlighting of `$what` $location")
if (styleIdByCall == null) fail("Expected `$what` to be highlighted $location") fail("Expected: $styleIdByComment, got: $styleIdByCall for $what $location")
if (styleIdByComment == null) fail("Unexpected highlighting of `$what` $location") }
fail("Expected: $styleIdByComment, got: $styleIdByCall for $what $location")
} }
val visitor = object : KtTreeVisitor<Unit?>() { val visitor = object : KtTreeVisitor<Unit?>() {
@@ -33,4 +33,9 @@ public class DslHighlighterTestGenerated extends AbstractDslHighlighterTest {
public void testFunctionCalls() throws Exception { public void testFunctionCalls() throws Exception {
runTest("idea/testData/dslHighlighter/functionCalls.kt"); runTest("idea/testData/dslHighlighter/functionCalls.kt");
} }
@TestMetadata("propertyAccess.kt")
public void testPropertyAccess() throws Exception {
runTest("idea/testData/dslHighlighter/propertyAccess.kt");
}
} }