Kotlin highlight passes are reworked
#KT-37702 Fixed
This commit is contained in:
committed by
Space
parent
558338f997
commit
913c298be8
@@ -312,7 +312,7 @@ fun main(args: Array<String>) {
|
|||||||
model("resolve/resolveModeComparison")
|
model("resolve/resolveModeComparison")
|
||||||
}
|
}
|
||||||
|
|
||||||
testClass<AbstractPsiCheckerTest> {
|
testClass<AbstractKotlinHighlightingPassTest> {
|
||||||
model("checker", recursive = false)
|
model("checker", recursive = false)
|
||||||
model("checker/regression")
|
model("checker/regression")
|
||||||
model("checker/recovery")
|
model("checker/recovery")
|
||||||
@@ -1092,7 +1092,7 @@ fun main(args: Array<String>) {
|
|||||||
model("resolve/references", pattern = KT_WITHOUT_DOTS_IN_NAME)
|
model("resolve/references", pattern = KT_WITHOUT_DOTS_IN_NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
testClass<AbstractFirPsiCheckerTest> {
|
testClass<AbstractFirKotlinHighlightingPassTest> {
|
||||||
model("checker", recursive = false)
|
model("checker", recursive = false)
|
||||||
model("checker/regression")
|
model("checker/regression")
|
||||||
model("checker/recovery")
|
model("checker/recovery")
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ fun main(args: Array<String>) {
|
|||||||
model("resolve/partialBodyResolve")
|
model("resolve/partialBodyResolve")
|
||||||
}
|
}
|
||||||
|
|
||||||
testClass<AbstractPsiCheckerTest> {
|
testClass<AbstractKotlinHighlightingPassTest> {
|
||||||
model("checker", recursive = false)
|
model("checker", recursive = false)
|
||||||
model("checker/regression")
|
model("checker/regression")
|
||||||
model("checker/recovery")
|
model("checker/recovery")
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ fun main(args: Array<String>) {
|
|||||||
model("resolve/partialBodyResolve")
|
model("resolve/partialBodyResolve")
|
||||||
}
|
}
|
||||||
|
|
||||||
testClass<AbstractPsiCheckerTest> {
|
testClass<AbstractKotlinHighlightingPassTest> {
|
||||||
model("checker", recursive = false)
|
model("checker", recursive = false)
|
||||||
model("checker/regression")
|
model("checker/regression")
|
||||||
model("checker/recovery")
|
model("checker/recovery")
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ html.type.mismatch.table.tr.td.required.td.td.0.td.tr.tr.td.found.td.td.1.td.tr.
|
|||||||
|
|
||||||
intention.suppress.family=Suppress Warnings
|
intention.suppress.family=Suppress Warnings
|
||||||
intention.suppress.text=Suppress ''{0}'' for {1} {2}
|
intention.suppress.text=Suppress ''{0}'' for {1} {2}
|
||||||
|
intention.calculating.text=Quick fix is being calculated ...
|
||||||
|
|
||||||
special.module.for.files.not.under.source.root=<special module for files not under source root>
|
special.module.for.files.not.under.source.root=<special module for files not under source root>
|
||||||
sdk.0=<sdk {0}>
|
sdk.0=<sdk {0}>
|
||||||
sources.for.library.0=<sources for library {0}>
|
sources.for.library.0=<sources for library {0}>
|
||||||
|
|||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.highlighter
|
||||||
|
|
||||||
|
import com.intellij.lang.annotation.AnnotationHolder
|
||||||
|
import com.intellij.lang.annotation.Annotator
|
||||||
|
import com.intellij.openapi.editor.Document
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiRecursiveElementVisitor
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
|
||||||
|
@Suppress("UnstableApiUsage")
|
||||||
|
abstract class AbstractBindingContextAwareHighlightingPassBase(
|
||||||
|
file: KtFile,
|
||||||
|
document: Document
|
||||||
|
) : AbstractHighlightingPassBase(file, document) {
|
||||||
|
|
||||||
|
private val cachedAnnotator by lazy { annotator }
|
||||||
|
|
||||||
|
protected abstract val annotator: Annotator
|
||||||
|
|
||||||
|
private var bindingContext: BindingContext? = null
|
||||||
|
|
||||||
|
protected fun bindingContext(): BindingContext = bindingContext ?: error("bindingContext has to be acquired")
|
||||||
|
|
||||||
|
protected open fun buildBindingContext(holder: AnnotationHolder): BindingContext =
|
||||||
|
file.analyzeWithAllCompilerChecks().also { it.throwIfError() }.bindingContext
|
||||||
|
|
||||||
|
override fun runAnnotatorWithContext(element: PsiElement, holder: AnnotationHolder) {
|
||||||
|
bindingContext = buildBindingContext(holder)
|
||||||
|
try {
|
||||||
|
element.accept(object : PsiRecursiveElementVisitor() {
|
||||||
|
override fun visitElement(element: PsiElement) {
|
||||||
|
cachedAnnotator.annotate(element, holder)
|
||||||
|
super.visitElement(element)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
bindingContext = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+293
@@ -0,0 +1,293 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.highlighter
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.daemon.impl.Divider
|
||||||
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
|
import com.intellij.lang.annotation.Annotation
|
||||||
|
import com.intellij.lang.annotation.AnnotationHolder
|
||||||
|
import com.intellij.lang.annotation.Annotator
|
||||||
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
|
import com.intellij.openapi.editor.Document
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.util.CommonProcessors
|
||||||
|
import com.intellij.util.containers.MultiMap
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
|
import org.jetbrains.kotlin.diagnostics.rendering.RenderingContext
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.QuickFixes
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtParameter
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import java.lang.reflect.*
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
abstract class AbstractKotlinHighlightingPass(file: KtFile, document: Document) :
|
||||||
|
AbstractBindingContextAwareHighlightingPassBase(file, document) {
|
||||||
|
override val annotator: Annotator
|
||||||
|
get() = KotlinAfterAnalysisAnnotator()
|
||||||
|
|
||||||
|
private inner class KotlinAfterAnalysisAnnotator : Annotator {
|
||||||
|
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
|
||||||
|
val bindingContext = bindingContext()
|
||||||
|
getAfterAnalysisVisitor(holder, bindingContext).forEach { visitor -> element.accept(visitor) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildBindingContext(holder: AnnotationHolder): BindingContext {
|
||||||
|
val dividedElements: List<Divider.DividedElements> = ArrayList()
|
||||||
|
Divider.divideInsideAndOutsideAllRoots(
|
||||||
|
file, file.textRange, file.textRange, { true },
|
||||||
|
CommonProcessors.CollectProcessor(dividedElements)
|
||||||
|
)
|
||||||
|
// TODO: for the sake of check that element belongs to the file
|
||||||
|
// for some reason analyzeWithAllCompilerChecks could return psiElements those do not belong to the file
|
||||||
|
// see [ScriptConfigurationHighlightingTestGenerated$Highlighting.testCustomExtension]
|
||||||
|
val elements = dividedElements.flatMap(Divider.DividedElements::inside).toSet()
|
||||||
|
|
||||||
|
// annotate diagnostics on fly: show diagnostics as soon as front-end reports them
|
||||||
|
// don't create quick fixes as it could require some resolve
|
||||||
|
val annotationByDiagnostic = mutableMapOf<Diagnostic, Annotation>()
|
||||||
|
val annotationByTextRange = mutableMapOf<TextRange, Annotation>()
|
||||||
|
|
||||||
|
// render of on-fly diagnostics with descriptors could lead to recursion
|
||||||
|
fun checkIfDescriptor(candidate: Any?): Boolean =
|
||||||
|
candidate is DeclarationDescriptor || candidate is Collection<*> && candidate.any(::checkIfDescriptor)
|
||||||
|
|
||||||
|
val analysisResult =
|
||||||
|
file.analyzeWithAllCompilerChecks({
|
||||||
|
val element = it.psiElement
|
||||||
|
if (element in elements &&
|
||||||
|
it !in annotationByDiagnostic &&
|
||||||
|
!RenderingContext.parameters(it).any(::checkIfDescriptor)
|
||||||
|
) {
|
||||||
|
annotateDiagnostic(element, holder, it, annotationByDiagnostic, annotationByTextRange)
|
||||||
|
}
|
||||||
|
}).also { it.throwIfError() }
|
||||||
|
// resolve is done!
|
||||||
|
|
||||||
|
val bindingContext = analysisResult.bindingContext
|
||||||
|
|
||||||
|
cleanUpCalculatingAnnotations(annotationByTextRange)
|
||||||
|
// TODO: for some reasons it could be duplicated diagnostics for the same factory
|
||||||
|
// see [PsiCheckerTestGenerated$Checker.testRedeclaration]
|
||||||
|
val diagnostics = bindingContext.diagnostics.asSequence().filter { it.psiElement in elements }.toSet()
|
||||||
|
|
||||||
|
if (diagnostics.isNotEmpty()) {
|
||||||
|
// annotate diagnostics those were not possible to render on fly
|
||||||
|
diagnostics.asSequence().filterNot { it in annotationByDiagnostic }.forEach {
|
||||||
|
annotateDiagnostic(it.psiElement, holder, it, annotationByDiagnostic, calculatingInProgress = false)
|
||||||
|
}
|
||||||
|
// apply quick fixes for all diagnostics grouping by element
|
||||||
|
diagnostics.groupBy(Diagnostic::psiElement).forEach {
|
||||||
|
annotateQuickFixes(it.key, it.value, annotationByDiagnostic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bindingContext
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun annotateDiagnostic(
|
||||||
|
element: PsiElement,
|
||||||
|
holder: AnnotationHolder,
|
||||||
|
diagnostic: Diagnostic,
|
||||||
|
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>? = null,
|
||||||
|
annotationByTextRange: MutableMap<TextRange, Annotation>? = null,
|
||||||
|
calculatingInProgress: Boolean = true
|
||||||
|
) = annotateDiagnostics(element, holder, listOf(diagnostic), annotationByDiagnostic, annotationByTextRange, true, calculatingInProgress)
|
||||||
|
|
||||||
|
private fun cleanUpCalculatingAnnotations(annotationByTextRange: Map<TextRange, Annotation>) {
|
||||||
|
annotationByTextRange.values.forEach { annotation ->
|
||||||
|
annotation.quickFixes?.removeIf {
|
||||||
|
it.quickFix is CalculatingIntentionAction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun annotateDiagnostics(
|
||||||
|
element: PsiElement,
|
||||||
|
holder: AnnotationHolder,
|
||||||
|
diagnostics: List<Diagnostic>,
|
||||||
|
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>? = null,
|
||||||
|
annotationByTextRange: MutableMap<TextRange, Annotation>? = null,
|
||||||
|
noFixes: Boolean = false,
|
||||||
|
calculatingInProgress: Boolean = false
|
||||||
|
) = annotateDiagnostics(
|
||||||
|
file, element, holder, diagnostics, annotationByDiagnostic, annotationByTextRange,
|
||||||
|
::shouldSuppressUnusedParameter,
|
||||||
|
noFixes = noFixes, calculatingInProgress = calculatingInProgress
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [diagnostics] has to belong to the same element
|
||||||
|
*/
|
||||||
|
private fun annotateQuickFixes(
|
||||||
|
element: PsiElement,
|
||||||
|
diagnostics: List<Diagnostic>,
|
||||||
|
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>
|
||||||
|
) {
|
||||||
|
if (diagnostics.isEmpty()) return
|
||||||
|
|
||||||
|
assertBelongsToTheSameElement(element, diagnostics)
|
||||||
|
|
||||||
|
val shouldHighlightErrors =
|
||||||
|
KotlinHighlightingUtil.shouldHighlightErrors(
|
||||||
|
if (element.isPhysical) file else element
|
||||||
|
)
|
||||||
|
|
||||||
|
if (shouldHighlightErrors) {
|
||||||
|
ElementAnnotator(element) { param ->
|
||||||
|
shouldSuppressUnusedParameter(param)
|
||||||
|
}.registerDiagnosticsQuickFixes(diagnostics, annotationByDiagnostic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun shouldSuppressUnusedParameter(parameter: KtParameter): Boolean = false
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun createQuickFixes(diagnostic: Diagnostic): Collection<IntentionAction> =
|
||||||
|
createQuickFixes(listOfNotNull(diagnostic))[diagnostic]
|
||||||
|
|
||||||
|
private val UNRESOLVED_KEY = Key<Unit>("KotlinHighlightingPass.UNRESOLVED_KEY")
|
||||||
|
|
||||||
|
fun wasUnresolved(element: KtNameReferenceExpression) = element.getUserData(UNRESOLVED_KEY) != null
|
||||||
|
|
||||||
|
fun getAfterAnalysisVisitor(holder: AnnotationHolder, bindingContext: BindingContext) = arrayOf(
|
||||||
|
PropertiesHighlightingVisitor(holder, bindingContext),
|
||||||
|
FunctionsHighlightingVisitor(holder, bindingContext),
|
||||||
|
VariablesHighlightingVisitor(holder, bindingContext),
|
||||||
|
TypeKindHighlightingVisitor(holder, bindingContext)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun assertBelongsToTheSameElement(element: PsiElement, diagnostics: Collection<Diagnostic>) {
|
||||||
|
assert(diagnostics.all { it.psiElement == element })
|
||||||
|
}
|
||||||
|
|
||||||
|
fun annotateDiagnostics(
|
||||||
|
file: KtFile,
|
||||||
|
element: PsiElement,
|
||||||
|
holder: AnnotationHolder,
|
||||||
|
diagnostics: Collection<Diagnostic>,
|
||||||
|
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>? = null,
|
||||||
|
annotationByTextRange: MutableMap<TextRange, Annotation>? = null,
|
||||||
|
shouldSuppressUnusedParameter: (KtParameter) -> Boolean = { false },
|
||||||
|
noFixes: Boolean = false,
|
||||||
|
calculatingInProgress: Boolean = false
|
||||||
|
) {
|
||||||
|
if (diagnostics.isEmpty()) return
|
||||||
|
|
||||||
|
assertBelongsToTheSameElement(element, diagnostics)
|
||||||
|
|
||||||
|
if (element is KtNameReferenceExpression) {
|
||||||
|
val unresolved = diagnostics.any { it.factory == Errors.UNRESOLVED_REFERENCE }
|
||||||
|
element.putUserData(UNRESOLVED_KEY, if (unresolved) Unit else null)
|
||||||
|
}
|
||||||
|
|
||||||
|
val shouldHighlightErrors =
|
||||||
|
KotlinHighlightingUtil.shouldHighlightErrors(
|
||||||
|
if (element.isPhysical) file else element
|
||||||
|
)
|
||||||
|
|
||||||
|
if (shouldHighlightErrors) {
|
||||||
|
val elementAnnotator = ElementAnnotator(element) { param ->
|
||||||
|
shouldSuppressUnusedParameter(param)
|
||||||
|
}
|
||||||
|
elementAnnotator.registerDiagnosticsAnnotations(
|
||||||
|
holder, diagnostics, annotationByDiagnostic,
|
||||||
|
annotationByTextRange,
|
||||||
|
noFixes = noFixes, calculatingInProgress = calculatingInProgress
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal fun createQuickFixes(similarDiagnostics: Collection<Diagnostic>): MultiMap<Diagnostic, IntentionAction> {
|
||||||
|
val first = similarDiagnostics.minByOrNull { it.toString() }
|
||||||
|
val factory = similarDiagnostics.first().getRealDiagnosticFactory()
|
||||||
|
|
||||||
|
val actions = MultiMap<Diagnostic, IntentionAction>()
|
||||||
|
|
||||||
|
val intentionActionsFactories = QuickFixes.getInstance().getActionFactories(factory)
|
||||||
|
for (intentionActionsFactory in intentionActionsFactories) {
|
||||||
|
val allProblemsActions = intentionActionsFactory.createActionsForAllProblems(similarDiagnostics)
|
||||||
|
if (allProblemsActions.isNotEmpty()) {
|
||||||
|
actions.putValues(first, allProblemsActions)
|
||||||
|
} else {
|
||||||
|
for (diagnostic in similarDiagnostics) {
|
||||||
|
actions.putValues(diagnostic, intentionActionsFactory.createActions(diagnostic))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (diagnostic in similarDiagnostics) {
|
||||||
|
actions.putValues(diagnostic, QuickFixes.getInstance().getActions(diagnostic.factory))
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.values().forEach { NoDeclarationDescriptorsChecker.check(it::class.java) }
|
||||||
|
|
||||||
|
return actions
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Diagnostic.getRealDiagnosticFactory(): DiagnosticFactory<*> =
|
||||||
|
when (factory) {
|
||||||
|
Errors.PLUGIN_ERROR -> Errors.PLUGIN_ERROR.cast(this).a.factory
|
||||||
|
Errors.PLUGIN_WARNING -> Errors.PLUGIN_WARNING.cast(this).a.factory
|
||||||
|
Errors.PLUGIN_INFO -> Errors.PLUGIN_INFO.cast(this).a.factory
|
||||||
|
else -> factory
|
||||||
|
}
|
||||||
|
|
||||||
|
private object NoDeclarationDescriptorsChecker {
|
||||||
|
private val LOG = Logger.getInstance(NoDeclarationDescriptorsChecker::class.java)
|
||||||
|
|
||||||
|
private val checkedQuickFixClasses = Collections.synchronizedSet(HashSet<Class<*>>())
|
||||||
|
|
||||||
|
fun check(quickFixClass: Class<*>) {
|
||||||
|
if (!checkedQuickFixClasses.add(quickFixClass)) return
|
||||||
|
|
||||||
|
for (field in quickFixClass.declaredFields) {
|
||||||
|
checkType(field.genericType, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
quickFixClass.superclass?.let { check(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkType(type: Type, field: Field) {
|
||||||
|
when (type) {
|
||||||
|
is Class<*> -> {
|
||||||
|
if (DeclarationDescriptor::class.java.isAssignableFrom(type) || KotlinType::class.java.isAssignableFrom(type)) {
|
||||||
|
LOG.error(
|
||||||
|
"QuickFix class ${field.declaringClass.name} contains field ${field.name} that holds ${type.simpleName}. "
|
||||||
|
+ "This leads to holding too much memory through this quick-fix instance. "
|
||||||
|
+ "Possible solution can be wrapping it using KotlinIntentionActionFactoryWithDelegate."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IntentionAction::class.java.isAssignableFrom(type)) {
|
||||||
|
check(type)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
is GenericArrayType -> checkType(type.genericComponentType, field)
|
||||||
|
|
||||||
|
is ParameterizedType -> {
|
||||||
|
if (Collection::class.java.isAssignableFrom(type.rawType as Class<*>)) {
|
||||||
|
type.actualTypeArguments.forEach { checkType(it, field) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is WildcardType -> type.upperBounds.forEach { checkType(it, field) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+61
-24
@@ -8,10 +8,9 @@ package org.jetbrains.kotlin.idea.highlighter
|
|||||||
import com.intellij.codeInsight.intention.EmptyIntentionAction
|
import com.intellij.codeInsight.intention.EmptyIntentionAction
|
||||||
import com.intellij.codeInsight.intention.IntentionAction
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
import com.intellij.codeInspection.ProblemHighlightType
|
import com.intellij.codeInspection.ProblemHighlightType
|
||||||
import com.intellij.lang.annotation.AnnotationBuilder
|
import com.intellij.lang.annotation.Annotation
|
||||||
import com.intellij.lang.annotation.AnnotationHolder
|
import com.intellij.lang.annotation.AnnotationHolder
|
||||||
import com.intellij.lang.annotation.HighlightSeverity
|
import com.intellij.lang.annotation.HighlightSeverity
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
|
||||||
import com.intellij.openapi.editor.colors.TextAttributesKey
|
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.util.containers.MultiMap
|
import com.intellij.util.containers.MultiMap
|
||||||
@@ -20,6 +19,8 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic
|
|||||||
import org.jetbrains.kotlin.diagnostics.Severity
|
import org.jetbrains.kotlin.diagnostics.Severity
|
||||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||||
import org.jetbrains.kotlin.idea.inspections.KotlinUniversalQuickFix
|
import org.jetbrains.kotlin.idea.inspections.KotlinUniversalQuickFix
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.isApplicationInternalMode
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.isUnitTestMode
|
||||||
|
|
||||||
class AnnotationPresentationInfo(
|
class AnnotationPresentationInfo(
|
||||||
val ranges: List<TextRange>,
|
val ranges: List<TextRange>,
|
||||||
@@ -28,32 +29,59 @@ class AnnotationPresentationInfo(
|
|||||||
val textAttributes: TextAttributesKey? = null
|
val textAttributes: TextAttributesKey? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun processDiagnostics(holder: AnnotationHolder, diagnostics: List<Diagnostic>, fixesMap: MultiMap<Diagnostic, IntentionAction>) {
|
fun processDiagnostics(
|
||||||
|
holder: AnnotationHolder,
|
||||||
|
diagnostics: Collection<Diagnostic>,
|
||||||
|
annotationBuilderByDiagnostic: MutableMap<Diagnostic, Annotation>? = null,
|
||||||
|
annotationByTextRange: MutableMap<TextRange, Annotation>?,
|
||||||
|
fixesMap: MultiMap<Diagnostic, IntentionAction>?,
|
||||||
|
calculatingInProgress: Boolean
|
||||||
|
) {
|
||||||
for (range in ranges) {
|
for (range in ranges) {
|
||||||
for (diagnostic in diagnostics) {
|
for (diagnostic in diagnostics) {
|
||||||
val fixes = fixesMap[diagnostic]
|
|
||||||
create(diagnostic, range, holder) { annotation ->
|
create(diagnostic, range, holder) { annotation ->
|
||||||
fixes.forEach {
|
annotationBuilderByDiagnostic?.put(diagnostic, annotation)
|
||||||
when (it) {
|
if (fixesMap != null) {
|
||||||
is KotlinUniversalQuickFix -> annotation.newFix(it).universal().registerFix()
|
applyFixes(fixesMap, diagnostic, annotation)
|
||||||
is IntentionAction -> annotation.newFix(it).registerFix()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (calculatingInProgress && annotationByTextRange?.containsKey(range) == false) {
|
||||||
if (diagnostic.severity == Severity.WARNING) {
|
annotationByTextRange[range] = annotation
|
||||||
annotation.problemGroup(KotlinSuppressableWarningProblemGroup(diagnostic.factory))
|
annotation.registerFix(CalculatingIntentionAction(), range)
|
||||||
|
|
||||||
if (fixes.isEmpty()) {
|
|
||||||
// if there are no quick fixes we need to register an EmptyIntentionAction to enable 'suppress' actions
|
|
||||||
annotation.newFix(EmptyIntentionAction(diagnostic.factory.name!!)).registerFix()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun create(diagnostic: Diagnostic, range: TextRange, holder: AnnotationHolder, consumer: (AnnotationBuilder) -> Unit) {
|
internal fun applyFixes(
|
||||||
|
fixesMap: MultiMap<Diagnostic, IntentionAction>,
|
||||||
|
diagnostic: Diagnostic,
|
||||||
|
annotation: Annotation
|
||||||
|
) {
|
||||||
|
val fixes = fixesMap[diagnostic]
|
||||||
|
val textRange = TextRange(annotation.startOffset, annotation.endOffset)
|
||||||
|
fixes.forEach {
|
||||||
|
when (it) {
|
||||||
|
is KotlinUniversalQuickFix -> {
|
||||||
|
annotation.registerBatchFix(it, textRange, null)
|
||||||
|
annotation.registerFix(it, textRange)
|
||||||
|
}
|
||||||
|
is IntentionAction -> {
|
||||||
|
annotation.registerFix(it, textRange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diagnostic.severity == Severity.WARNING) {
|
||||||
|
if (fixes.isEmpty()) {
|
||||||
|
// if there are no quick fixes we need to register an EmptyIntentionAction to enable 'suppress' actions
|
||||||
|
//annotation.newFix(EmptyIntentionAction(diagnostic.factory.name)).registerFix()
|
||||||
|
annotation.registerFix(EmptyIntentionAction(diagnostic.factory.name), textRange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun create(diagnostic: Diagnostic, range: TextRange, holder: AnnotationHolder, consumer: (Annotation) -> Unit) {
|
||||||
val severity = when (diagnostic.severity) {
|
val severity = when (diagnostic.severity) {
|
||||||
Severity.ERROR -> HighlightSeverity.ERROR
|
Severity.ERROR -> HighlightSeverity.ERROR
|
||||||
Severity.WARNING -> if (highlightType == ProblemHighlightType.WEAK_WARNING) {
|
Severity.WARNING -> if (highlightType == ProblemHighlightType.WEAK_WARNING) {
|
||||||
@@ -62,18 +90,26 @@ class AnnotationPresentationInfo(
|
|||||||
Severity.INFO -> HighlightSeverity.WEAK_WARNING
|
Severity.INFO -> HighlightSeverity.WEAK_WARNING
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.newAnnotation(severity, nonDefaultMessage ?: getDefaultMessage(diagnostic))
|
|
||||||
|
val message = nonDefaultMessage ?: getDefaultMessage(diagnostic)
|
||||||
|
holder.newAnnotation(severity, message)
|
||||||
.range(range)
|
.range(range)
|
||||||
.tooltip(getMessage(diagnostic))
|
.tooltip(getMessage(diagnostic))
|
||||||
.also { builder -> highlightType?.let { builder.highlightType(it) } }
|
.also { builder -> highlightType?.let { builder.highlightType(it) } }
|
||||||
.also { builder -> textAttributes?.let { builder.textAttributes(it) } }
|
.also { builder -> textAttributes?.let { builder.textAttributes(it) } }
|
||||||
.also { consumer(it) }
|
.also {
|
||||||
|
if (diagnostic.severity == Severity.WARNING) {
|
||||||
|
it.problemGroup(KotlinSuppressableWarningProblemGroup(diagnostic.factory))
|
||||||
|
}
|
||||||
|
}
|
||||||
.create()
|
.create()
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
(holder as? List<Annotation>)?.last()?.let(consumer::invoke)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getMessage(diagnostic: Diagnostic): String {
|
private fun getMessage(diagnostic: Diagnostic): String {
|
||||||
var message = IdeErrorMessages.render(diagnostic)
|
var message = IdeErrorMessages.render(diagnostic)
|
||||||
if (ApplicationManager.getApplication().isInternal || ApplicationManager.getApplication().isUnitTestMode) {
|
if (isApplicationInternalMode() || isUnitTestMode()) {
|
||||||
val factoryName = diagnostic.factory.name
|
val factoryName = diagnostic.factory.name
|
||||||
message = if (message.startsWith("<html>")) {
|
message = if (message.startsWith("<html>")) {
|
||||||
"<html>[$factoryName] ${message.substring("<html>".length)}"
|
"<html>[$factoryName] ${message.substring("<html>".length)}"
|
||||||
@@ -89,10 +125,11 @@ class AnnotationPresentationInfo(
|
|||||||
|
|
||||||
private fun getDefaultMessage(diagnostic: Diagnostic): String {
|
private fun getDefaultMessage(diagnostic: Diagnostic): String {
|
||||||
val message = DefaultErrorMessages.render(diagnostic)
|
val message = DefaultErrorMessages.render(diagnostic)
|
||||||
if (ApplicationManager.getApplication().isInternal || ApplicationManager.getApplication().isUnitTestMode) {
|
return if (isApplicationInternalMode() || isUnitTestMode()) {
|
||||||
return "[${diagnostic.factory.name}] $message"
|
"[${diagnostic.factory.name}] $message"
|
||||||
|
} else {
|
||||||
|
message
|
||||||
}
|
}
|
||||||
return message
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.highlighter
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.intention.AbstractEmptyIntentionAction
|
||||||
|
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||||
|
import com.intellij.icons.AllIcons
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.Iconable
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinIdeaAnalysisBundle
|
||||||
|
import javax.swing.Icon
|
||||||
|
|
||||||
|
class CalculatingIntentionAction : AbstractEmptyIntentionAction(), LowPriorityAction, Iconable {
|
||||||
|
override fun getText(): String = KotlinIdeaAnalysisBundle.message("intention.calculating.text")
|
||||||
|
|
||||||
|
override fun getFamilyName(): String = KotlinIdeaAnalysisBundle.message("intention.calculating.text")
|
||||||
|
|
||||||
|
override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean = true
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean = this === other || other is CalculatingIntentionAction
|
||||||
|
|
||||||
|
override fun hashCode(): Int = 42
|
||||||
|
|
||||||
|
override fun getIcon(@Iconable.IconFlags flags: Int): Icon = AllIcons.Actions.Preview
|
||||||
|
}
|
||||||
+100
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.highlighter
|
||||||
|
|
||||||
|
import com.intellij.codeHighlighting.*
|
||||||
|
import com.intellij.lang.annotation.AnnotationHolder
|
||||||
|
import com.intellij.lang.annotation.Annotator
|
||||||
|
import com.intellij.lang.annotation.HighlightSeverity
|
||||||
|
import com.intellij.openapi.editor.Document
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||||
|
import com.intellij.openapi.progress.ProcessCanceledException
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import org.jetbrains.kotlin.checkers.utils.DebugInfoUtil
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinPluginUtil
|
||||||
|
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.isApplicationInternalMode
|
||||||
|
import org.jetbrains.kotlin.psi.KtCodeFragment
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.KtReferenceExpression
|
||||||
|
|
||||||
|
class DebugInfoHighlightingPass(file: KtFile, document: Document) : AbstractBindingContextAwareHighlightingPassBase(file, document) {
|
||||||
|
override val annotator: Annotator
|
||||||
|
get() = DebugInfoAnnotator()
|
||||||
|
|
||||||
|
private inner class DebugInfoAnnotator : Annotator {
|
||||||
|
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
|
||||||
|
if (element is KtFile && element !is KtCodeFragment) {
|
||||||
|
fun errorAnnotation(
|
||||||
|
expression: PsiElement,
|
||||||
|
message: String,
|
||||||
|
textAttributes: TextAttributesKey? = KotlinHighlightingColors.DEBUG_INFO
|
||||||
|
) =
|
||||||
|
holder.newAnnotation(HighlightSeverity.ERROR, "[DEBUG] $message")
|
||||||
|
.range(expression.textRange)
|
||||||
|
.also {
|
||||||
|
textAttributes?.let { ta -> it.textAttributes(ta) }
|
||||||
|
}
|
||||||
|
.create()
|
||||||
|
|
||||||
|
try {
|
||||||
|
DebugInfoUtil.markDebugAnnotations(element, bindingContext(), object : DebugInfoUtil.DebugInfoReporter() {
|
||||||
|
override fun reportElementWithErrorType(expression: KtReferenceExpression) =
|
||||||
|
errorAnnotation(expression, "Resolved to error element", KotlinHighlightingColors.RESOLVED_TO_ERROR)
|
||||||
|
|
||||||
|
override fun reportMissingUnresolved(expression: KtReferenceExpression) =
|
||||||
|
errorAnnotation(
|
||||||
|
expression,
|
||||||
|
"Reference is not resolved to anything, but is not marked unresolved"
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun reportUnresolvedWithTarget(expression: KtReferenceExpression, target: String) =
|
||||||
|
errorAnnotation(
|
||||||
|
expression,
|
||||||
|
"Reference marked as unresolved is actually resolved to $target"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} catch (e: ProcessCanceledException) {
|
||||||
|
throw e
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
// TODO
|
||||||
|
errorAnnotation(element, e.javaClass.canonicalName + ": " + e.message, null)
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Factory : TextEditorHighlightingPassFactory {
|
||||||
|
override fun createHighlightingPass(file: PsiFile, editor: Editor): TextEditorHighlightingPass? {
|
||||||
|
return if (file is KtFile &&
|
||||||
|
(isApplicationInternalMode() && (KotlinPluginUtil.isSnapshotVersion() || KotlinPluginUtil.isDevVersion())) &&
|
||||||
|
ProjectRootsUtil.isInProjectOrLibSource(file)
|
||||||
|
) {
|
||||||
|
DebugInfoHighlightingPass(file, editor.document)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Registrar : TextEditorHighlightingPassFactoryRegistrar {
|
||||||
|
override fun registerHighlightingPassFactory(registrar: TextEditorHighlightingPassRegistrar, project: Project) {
|
||||||
|
registrar.registerTextEditorHighlightingPass(
|
||||||
|
Factory(),
|
||||||
|
/* runAfterCompletionOf = */ intArrayOf(Pass.UPDATE_ALL),
|
||||||
|
/* runAfterStartingOf = */ null,
|
||||||
|
/* runIntentionsPassAfter = */ false,
|
||||||
|
/* forcedPassId = */ -1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
-50
@@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2015 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.idea.highlighter
|
|
||||||
|
|
||||||
import com.intellij.lang.annotation.AnnotationHolder
|
|
||||||
import com.intellij.lang.annotation.Annotator
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import org.jetbrains.kotlin.asJava.*
|
|
||||||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.*
|
|
||||||
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
|
|
||||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
|
||||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
|
||||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
|
||||||
|
|
||||||
class DuplicateJvmSignatureAnnotator : Annotator {
|
|
||||||
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
|
|
||||||
if (element !is KtFile && element !is KtDeclaration) return
|
|
||||||
if (!ProjectRootsUtil.isInProjectSource(element)) return
|
|
||||||
|
|
||||||
val file = element.containingFile
|
|
||||||
if (file !is KtFile || !TargetPlatformDetector.getPlatform(file).isJvm()) return
|
|
||||||
|
|
||||||
val otherDiagnostics = when (element) {
|
|
||||||
is KtDeclaration -> element.analyzeWithContent()
|
|
||||||
is KtFile -> element.analyzeWithContent()
|
|
||||||
else -> throw AssertionError("DuplicateJvmSignatureAnnotator: should not get here! Element: ${element.text}")
|
|
||||||
}.diagnostics
|
|
||||||
|
|
||||||
val moduleScope = element.getModuleInfo().contentScope()
|
|
||||||
val diagnostics = getJvmSignatureDiagnostics(element, otherDiagnostics, moduleScope) ?: return
|
|
||||||
|
|
||||||
KotlinPsiChecker().annotateElement(element, holder, diagnostics)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+74
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.highlighter
|
||||||
|
|
||||||
|
import com.intellij.codeHighlighting.*
|
||||||
|
import com.intellij.lang.annotation.AnnotationHolder
|
||||||
|
import com.intellij.lang.annotation.Annotator
|
||||||
|
import com.intellij.openapi.editor.Document
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import org.jetbrains.kotlin.asJava.getJvmSignatureDiagnostics
|
||||||
|
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
|
||||||
|
import org.jetbrains.kotlin.idea.highlighter.AbstractKotlinHighlightingPass.Companion.annotateDiagnostics
|
||||||
|
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
|
||||||
|
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||||
|
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||||
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
|
||||||
|
class DuplicateJvmSignatureHighlightPass(file: KtFile, document: Document) :
|
||||||
|
AbstractBindingContextAwareHighlightingPassBase(file, document) {
|
||||||
|
override val annotator: Annotator
|
||||||
|
get() = DuplicateJvmSignatureAnnotator()
|
||||||
|
|
||||||
|
inner class DuplicateJvmSignatureAnnotator : Annotator {
|
||||||
|
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
|
||||||
|
if (element !is KtFile && element !is KtDeclaration) return
|
||||||
|
|
||||||
|
val otherDiagnostics = when (element) {
|
||||||
|
is KtDeclaration -> element.analyzeWithContent()
|
||||||
|
is KtFile -> element.analyzeWithContent()
|
||||||
|
else -> throw AssertionError("DuplicateJvmSignatureAnnotator: should not get here! Element: ${element.text}")
|
||||||
|
}.diagnostics
|
||||||
|
|
||||||
|
val moduleScope = element.getModuleInfo().contentScope()
|
||||||
|
val diagnostics = getJvmSignatureDiagnostics(element, otherDiagnostics, moduleScope) ?: return
|
||||||
|
|
||||||
|
val diagnosticsForElement = diagnostics.forElement(element).toSet()
|
||||||
|
|
||||||
|
annotateDiagnostics(file, element, holder, diagnosticsForElement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Factory : TextEditorHighlightingPassFactory {
|
||||||
|
override fun createHighlightingPass(file: PsiFile, editor: Editor): TextEditorHighlightingPass? {
|
||||||
|
return if (file is KtFile &&
|
||||||
|
ProjectRootsUtil.isInProjectSource(file) &&
|
||||||
|
TargetPlatformDetector.getPlatform(file).isJvm()
|
||||||
|
) {
|
||||||
|
DuplicateJvmSignatureHighlightPass(file, editor.document)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Registrar : TextEditorHighlightingPassFactoryRegistrar {
|
||||||
|
override fun registerHighlightingPassFactory(registrar: TextEditorHighlightingPassRegistrar, project: Project) {
|
||||||
|
registrar.registerTextEditorHighlightingPass(
|
||||||
|
Factory(),
|
||||||
|
/* runAfterCompletionOf = */ intArrayOf(Pass.UPDATE_ALL),
|
||||||
|
/* runAfterStartingOf = */ null,
|
||||||
|
/* runIntentionsPassAfter = */ false,
|
||||||
|
/* forcedPassId = */ -1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.highlighter
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
|
import com.intellij.codeInspection.ProblemHighlightType
|
||||||
|
import com.intellij.lang.annotation.Annotation
|
||||||
|
import com.intellij.lang.annotation.AnnotationHolder
|
||||||
|
import com.intellij.openapi.diagnostic.ControlFlowException
|
||||||
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
|
import com.intellij.openapi.editor.colors.CodeInsightColors
|
||||||
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import com.intellij.psi.MultiRangeReference
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.util.containers.MultiMap
|
||||||
|
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||||
|
import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Severity
|
||||||
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
|
import org.jetbrains.kotlin.idea.util.module
|
||||||
|
import org.jetbrains.kotlin.psi.KtParameter
|
||||||
|
import org.jetbrains.kotlin.psi.KtReferenceExpression
|
||||||
|
|
||||||
|
internal class ElementAnnotator(
|
||||||
|
private val element: PsiElement,
|
||||||
|
private val shouldSuppressUnusedParameter: (KtParameter) -> Boolean
|
||||||
|
) {
|
||||||
|
fun registerDiagnosticsAnnotations(
|
||||||
|
holder: AnnotationHolder,
|
||||||
|
diagnostics: Collection<Diagnostic>,
|
||||||
|
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>?,
|
||||||
|
annotationByTextRange: MutableMap<TextRange, Annotation>?,
|
||||||
|
noFixes: Boolean,
|
||||||
|
calculatingInProgress: Boolean
|
||||||
|
) = diagnostics.groupBy { it.factory }
|
||||||
|
.forEach {
|
||||||
|
registerSameFactoryDiagnosticsAnnotations(
|
||||||
|
holder,
|
||||||
|
it.value,
|
||||||
|
annotationByDiagnostic,
|
||||||
|
annotationByTextRange,
|
||||||
|
noFixes,
|
||||||
|
calculatingInProgress
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerSameFactoryDiagnosticsAnnotations(
|
||||||
|
holder: AnnotationHolder,
|
||||||
|
diagnostics: Collection<Diagnostic>,
|
||||||
|
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>?,
|
||||||
|
annotationByTextRange: MutableMap<TextRange, Annotation>?,
|
||||||
|
noFixes: Boolean,
|
||||||
|
calculatingInProgress: Boolean
|
||||||
|
) {
|
||||||
|
val presentationInfo = presentationInfo(diagnostics) ?: return
|
||||||
|
setUpAnnotations(
|
||||||
|
holder,
|
||||||
|
diagnostics,
|
||||||
|
presentationInfo,
|
||||||
|
annotationByDiagnostic,
|
||||||
|
annotationByTextRange,
|
||||||
|
noFixes,
|
||||||
|
calculatingInProgress
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun registerDiagnosticsQuickFixes(
|
||||||
|
diagnostics: List<Diagnostic>,
|
||||||
|
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>
|
||||||
|
) = diagnostics.groupBy { it.factory }
|
||||||
|
.forEach { registerDiagnosticsSameFactoryQuickFixes(it.value, annotationByDiagnostic) }
|
||||||
|
|
||||||
|
private fun registerDiagnosticsSameFactoryQuickFixes(
|
||||||
|
diagnostics: List<Diagnostic>,
|
||||||
|
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>
|
||||||
|
) {
|
||||||
|
val presentationInfo = presentationInfo(diagnostics) ?: return
|
||||||
|
val fixesMap = createFixesMap(diagnostics) ?: return
|
||||||
|
|
||||||
|
diagnostics.forEach {
|
||||||
|
val annotation = annotationByDiagnostic[it] ?: return
|
||||||
|
|
||||||
|
presentationInfo.applyFixes(fixesMap, it, annotation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun presentationInfo(diagnostics: Collection<Diagnostic>): AnnotationPresentationInfo? {
|
||||||
|
if (diagnostics.isEmpty() || !diagnostics.any { it.isValid }) return null
|
||||||
|
|
||||||
|
val diagnostic = diagnostics.first()
|
||||||
|
// hack till the root cause #KT-21246 is fixed
|
||||||
|
if (isUnstableAbiClassDiagnosticForModulesWithEnabledUnstableAbi(diagnostic)) return null
|
||||||
|
|
||||||
|
val factory = diagnostic.factory
|
||||||
|
|
||||||
|
assert(diagnostics.all { it.psiElement == element && it.factory == factory })
|
||||||
|
|
||||||
|
val ranges = diagnostic.textRanges
|
||||||
|
val presentationInfo: AnnotationPresentationInfo = when (factory.severity) {
|
||||||
|
Severity.ERROR -> {
|
||||||
|
when (factory) {
|
||||||
|
in Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS -> {
|
||||||
|
val referenceExpression = element as KtReferenceExpression
|
||||||
|
val reference = referenceExpression.mainReference
|
||||||
|
if (reference is MultiRangeReference) {
|
||||||
|
AnnotationPresentationInfo(
|
||||||
|
ranges = reference.ranges.map { it.shiftRight(referenceExpression.textOffset) },
|
||||||
|
highlightType = ProblemHighlightType.LIKE_UNKNOWN_SYMBOL
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
AnnotationPresentationInfo(ranges, highlightType = ProblemHighlightType.LIKE_UNKNOWN_SYMBOL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Errors.ILLEGAL_ESCAPE -> AnnotationPresentationInfo(
|
||||||
|
ranges, textAttributes = KotlinHighlightingColors.INVALID_STRING_ESCAPE
|
||||||
|
)
|
||||||
|
|
||||||
|
Errors.REDECLARATION -> AnnotationPresentationInfo(
|
||||||
|
ranges = listOf(diagnostic.textRanges.first()), nonDefaultMessage = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
AnnotationPresentationInfo(
|
||||||
|
ranges,
|
||||||
|
highlightType = if (factory == Errors.INVISIBLE_REFERENCE)
|
||||||
|
ProblemHighlightType.LIKE_UNKNOWN_SYMBOL
|
||||||
|
else
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Severity.WARNING -> {
|
||||||
|
if (factory == Errors.UNUSED_PARAMETER && shouldSuppressUnusedParameter(element as KtParameter)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
AnnotationPresentationInfo(
|
||||||
|
ranges,
|
||||||
|
textAttributes = when (factory) {
|
||||||
|
Errors.DEPRECATION -> CodeInsightColors.DEPRECATED_ATTRIBUTES
|
||||||
|
Errors.UNUSED_ANONYMOUS_PARAMETER -> CodeInsightColors.WEAK_WARNING_ATTRIBUTES
|
||||||
|
else -> null
|
||||||
|
},
|
||||||
|
highlightType = when (factory) {
|
||||||
|
in Errors.UNUSED_ELEMENT_DIAGNOSTICS -> ProblemHighlightType.LIKE_UNUSED_SYMBOL
|
||||||
|
Errors.UNUSED_ANONYMOUS_PARAMETER -> ProblemHighlightType.WEAK_WARNING
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Severity.INFO -> AnnotationPresentationInfo(ranges, highlightType = ProblemHighlightType.INFORMATION)
|
||||||
|
}
|
||||||
|
return presentationInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUpAnnotations(
|
||||||
|
holder: AnnotationHolder,
|
||||||
|
diagnostics: Collection<Diagnostic>,
|
||||||
|
data: AnnotationPresentationInfo,
|
||||||
|
annotationByDiagnostic: MutableMap<Diagnostic, Annotation>?,
|
||||||
|
annotationByTextRange: MutableMap<TextRange, Annotation>?,
|
||||||
|
noFixes: Boolean,
|
||||||
|
calculatingInProgress: Boolean
|
||||||
|
) {
|
||||||
|
val fixesMap =
|
||||||
|
createFixesMap(diagnostics, noFixes)
|
||||||
|
|
||||||
|
data.processDiagnostics(holder, diagnostics, annotationByDiagnostic, annotationByTextRange, fixesMap, calculatingInProgress)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createFixesMap(
|
||||||
|
diagnostics: Collection<Diagnostic>,
|
||||||
|
noFixes: Boolean = false
|
||||||
|
): MultiMap<Diagnostic, IntentionAction>? = if (noFixes) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
createQuickFixes(diagnostics)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (e is ControlFlowException) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
LOG.error(e)
|
||||||
|
MultiMap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isUnstableAbiClassDiagnosticForModulesWithEnabledUnstableAbi(diagnostic: Diagnostic): Boolean {
|
||||||
|
val factory = diagnostic.factory
|
||||||
|
if (factory != Errors.IR_WITH_UNSTABLE_ABI_COMPILED_CLASS && factory != Errors.FIR_COMPILED_CLASS) return false
|
||||||
|
|
||||||
|
val module = element.module ?: return false
|
||||||
|
val moduleFacetSettings = KotlinFacetSettingsProvider.getInstance(element.project)?.getSettings(module) ?: return false
|
||||||
|
return when (factory) {
|
||||||
|
Errors.IR_WITH_UNSTABLE_ABI_COMPILED_CLASS ->
|
||||||
|
moduleFacetSettings.isCompilerSettingPresent(K2JVMCompilerArguments::useIR) &&
|
||||||
|
!moduleFacetSettings.isCompilerSettingPresent(K2JVMCompilerArguments::useOldBackend)
|
||||||
|
Errors.FIR_COMPILED_CLASS ->
|
||||||
|
moduleFacetSettings.isCompilerSettingPresent(K2JVMCompilerArguments::useFir)
|
||||||
|
else -> error(factory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val LOG = Logger.getInstance(ElementAnnotator::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
-2
@@ -34,7 +34,10 @@ import kotlin.script.experimental.api.ScriptDiagnostic
|
|||||||
object KotlinHighlightingUtil {
|
object KotlinHighlightingUtil {
|
||||||
fun shouldHighlight(psiElement: PsiElement): Boolean {
|
fun shouldHighlight(psiElement: PsiElement): Boolean {
|
||||||
val ktFile = psiElement.containingFile as? KtFile ?: return false
|
val ktFile = psiElement.containingFile as? KtFile ?: return false
|
||||||
|
return shouldHighlightFile(ktFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shouldHighlightFile(ktFile: KtFile): Boolean {
|
||||||
if (ktFile is KtCodeFragment && ktFile.context != null) {
|
if (ktFile is KtCodeFragment && ktFile.context != null) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -52,8 +55,12 @@ object KotlinHighlightingUtil {
|
|||||||
return ProjectRootsUtil.isInProjectOrLibraryContent(ktFile) && ktFile.getModuleInfo() !is NotUnderContentRootModuleInfo
|
return ProjectRootsUtil.isInProjectOrLibraryContent(ktFile) && ktFile.getModuleInfo() !is NotUnderContentRootModuleInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
fun shouldHighlightErrors(psiElement: PsiElement): Boolean {
|
fun shouldHighlightErrors(psiElement: PsiElement): Boolean =
|
||||||
val ktFile = psiElement.containingFile as? KtFile ?: return false
|
(psiElement.containingFile as? KtFile)?.let {
|
||||||
|
shouldHighlightErrors(it)
|
||||||
|
} ?: false
|
||||||
|
|
||||||
|
fun shouldHighlightErrors(ktFile: KtFile): Boolean {
|
||||||
if (ktFile.isCompiled) {
|
if (ktFile.isCompiled) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,295 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jetbrains.kotlin.idea.highlighter
|
|
||||||
|
|
||||||
import com.intellij.codeInsight.intention.IntentionAction
|
|
||||||
import com.intellij.codeInspection.ProblemHighlightType
|
|
||||||
import com.intellij.lang.annotation.AnnotationHolder
|
|
||||||
import com.intellij.openapi.diagnostic.ControlFlowException
|
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
|
||||||
import com.intellij.openapi.editor.colors.CodeInsightColors
|
|
||||||
import com.intellij.openapi.progress.ProcessCanceledException
|
|
||||||
import com.intellij.openapi.util.Key
|
|
||||||
import com.intellij.psi.MultiRangeReference
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.util.containers.MultiMap
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
|
||||||
import org.jetbrains.kotlin.config.KotlinFacetSettings
|
|
||||||
import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider
|
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
|
||||||
import org.jetbrains.kotlin.diagnostics.Severity
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
|
|
||||||
import org.jetbrains.kotlin.idea.quickfix.QuickFixes
|
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
|
||||||
import org.jetbrains.kotlin.idea.util.module
|
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
|
||||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
|
||||||
import org.jetbrains.kotlin.psi.KtParameter
|
|
||||||
import org.jetbrains.kotlin.psi.KtReferenceExpression
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
|
||||||
import java.lang.reflect.*
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
open class KotlinPsiChecker : AbstractKotlinPsiChecker() {
|
|
||||||
override fun shouldHighlight(file: KtFile): Boolean = KotlinHighlightingUtil.shouldHighlight(file)
|
|
||||||
|
|
||||||
override fun annotateElement(
|
|
||||||
element: PsiElement,
|
|
||||||
containingFile: KtFile,
|
|
||||||
holder: AnnotationHolder
|
|
||||||
) {
|
|
||||||
val analysisResult = containingFile.analyzeWithAllCompilerChecks()
|
|
||||||
if (analysisResult.isError()) {
|
|
||||||
throw ProcessCanceledException(analysisResult.error)
|
|
||||||
}
|
|
||||||
|
|
||||||
val bindingContext = analysisResult.bindingContext
|
|
||||||
|
|
||||||
getAfterAnalysisVisitor(holder, bindingContext).forEach { visitor -> element.accept(visitor) }
|
|
||||||
|
|
||||||
annotateElement(element, holder, bindingContext.diagnostics)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun shouldSuppressUnusedParameter(parameter: KtParameter): Boolean = false
|
|
||||||
|
|
||||||
fun annotateElement(element: PsiElement, holder: AnnotationHolder, diagnostics: Diagnostics) {
|
|
||||||
val diagnosticsForElement = diagnostics.forElement(element).toSet()
|
|
||||||
|
|
||||||
if (element is KtNameReferenceExpression) {
|
|
||||||
val unresolved = diagnostics.any { it.factory == Errors.UNRESOLVED_REFERENCE }
|
|
||||||
element.putUserData(UNRESOLVED_KEY, if (unresolved) Unit else null)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (diagnosticsForElement.isEmpty()) return
|
|
||||||
|
|
||||||
if (KotlinHighlightingUtil.shouldHighlightErrors(element)) {
|
|
||||||
ElementAnnotator(element, holder) { param ->
|
|
||||||
shouldSuppressUnusedParameter(param)
|
|
||||||
}.registerDiagnosticsAnnotations(diagnosticsForElement)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun getAfterAnalysisVisitor(holder: AnnotationHolder, bindingContext: BindingContext) = arrayOf(
|
|
||||||
PropertiesHighlightingVisitor(holder, bindingContext),
|
|
||||||
FunctionsHighlightingVisitor(holder, bindingContext),
|
|
||||||
VariablesHighlightingVisitor(holder, bindingContext),
|
|
||||||
TypeKindHighlightingVisitor(holder, bindingContext)
|
|
||||||
)
|
|
||||||
|
|
||||||
fun createQuickFixes(diagnostic: Diagnostic): Collection<IntentionAction> =
|
|
||||||
createQuickFixes(listOfNotNull(diagnostic))[diagnostic]
|
|
||||||
|
|
||||||
private val UNRESOLVED_KEY = Key<Unit>("KotlinPsiChecker.UNRESOLVED_KEY")
|
|
||||||
|
|
||||||
fun wasUnresolved(element: KtNameReferenceExpression) = element.getUserData(UNRESOLVED_KEY) != null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createQuickFixes(similarDiagnostics: Collection<Diagnostic>): MultiMap<Diagnostic, IntentionAction> {
|
|
||||||
val first = similarDiagnostics.minByOrNull { it.toString() }
|
|
||||||
val factory = similarDiagnostics.first().getRealDiagnosticFactory()
|
|
||||||
|
|
||||||
val actions = MultiMap<Diagnostic, IntentionAction>()
|
|
||||||
|
|
||||||
val intentionActionsFactories = QuickFixes.getInstance().getActionFactories(factory)
|
|
||||||
for (intentionActionsFactory in intentionActionsFactories) {
|
|
||||||
val allProblemsActions = intentionActionsFactory.createActionsForAllProblems(similarDiagnostics)
|
|
||||||
if (allProblemsActions.isNotEmpty()) {
|
|
||||||
actions.putValues(first, allProblemsActions)
|
|
||||||
} else {
|
|
||||||
for (diagnostic in similarDiagnostics) {
|
|
||||||
actions.putValues(diagnostic, intentionActionsFactory.createActions(diagnostic))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (diagnostic in similarDiagnostics) {
|
|
||||||
actions.putValues(diagnostic, QuickFixes.getInstance().getActions(diagnostic.factory))
|
|
||||||
}
|
|
||||||
|
|
||||||
actions.values().forEach { NoDeclarationDescriptorsChecker.check(it::class.java) }
|
|
||||||
|
|
||||||
return actions
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Diagnostic.getRealDiagnosticFactory(): DiagnosticFactory<*> =
|
|
||||||
when (factory) {
|
|
||||||
Errors.PLUGIN_ERROR -> Errors.PLUGIN_ERROR.cast(this).a.factory
|
|
||||||
Errors.PLUGIN_WARNING -> Errors.PLUGIN_WARNING.cast(this).a.factory
|
|
||||||
Errors.PLUGIN_INFO -> Errors.PLUGIN_INFO.cast(this).a.factory
|
|
||||||
else -> factory
|
|
||||||
}
|
|
||||||
|
|
||||||
private object NoDeclarationDescriptorsChecker {
|
|
||||||
private val LOG = Logger.getInstance(NoDeclarationDescriptorsChecker::class.java)
|
|
||||||
|
|
||||||
private val checkedQuickFixClasses = Collections.synchronizedSet(HashSet<Class<*>>())
|
|
||||||
|
|
||||||
fun check(quickFixClass: Class<*>) {
|
|
||||||
if (!checkedQuickFixClasses.add(quickFixClass)) return
|
|
||||||
|
|
||||||
for (field in quickFixClass.declaredFields) {
|
|
||||||
checkType(field.genericType, field)
|
|
||||||
}
|
|
||||||
|
|
||||||
quickFixClass.superclass?.let { check(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkType(type: Type, field: Field) {
|
|
||||||
when (type) {
|
|
||||||
is Class<*> -> {
|
|
||||||
if (DeclarationDescriptor::class.java.isAssignableFrom(type) || KotlinType::class.java.isAssignableFrom(type)) {
|
|
||||||
LOG.error(
|
|
||||||
"QuickFix class ${field.declaringClass.name} contains field ${field.name} that holds ${type.simpleName}. "
|
|
||||||
+ "This leads to holding too much memory through this quick-fix instance. "
|
|
||||||
+ "Possible solution can be wrapping it using KotlinIntentionActionFactoryWithDelegate."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IntentionAction::class.java.isAssignableFrom(type)) {
|
|
||||||
check(type)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
is GenericArrayType -> checkType(type.genericComponentType, field)
|
|
||||||
|
|
||||||
is ParameterizedType -> {
|
|
||||||
if (Collection::class.java.isAssignableFrom(type.rawType as Class<*>)) {
|
|
||||||
type.actualTypeArguments.forEach { checkType(it, field) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is WildcardType -> type.upperBounds.forEach { checkType(it, field) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ElementAnnotator(
|
|
||||||
private val element: PsiElement,
|
|
||||||
private val holder: AnnotationHolder,
|
|
||||||
private val shouldSuppressUnusedParameter: (KtParameter) -> Boolean
|
|
||||||
) {
|
|
||||||
fun registerDiagnosticsAnnotations(diagnostics: Collection<Diagnostic>) {
|
|
||||||
diagnostics.groupBy { it.factory }.forEach { group -> registerDiagnosticAnnotations(group.value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun registerDiagnosticAnnotations(diagnostics: List<Diagnostic>) {
|
|
||||||
assert(diagnostics.isNotEmpty())
|
|
||||||
|
|
||||||
val validDiagnostics = diagnostics.filter { it.isValid }
|
|
||||||
if (validDiagnostics.isEmpty()) return
|
|
||||||
|
|
||||||
val diagnostic = diagnostics.first()
|
|
||||||
val factory = diagnostic.factory
|
|
||||||
|
|
||||||
// hack till the root cause #KT-21246 is fixed
|
|
||||||
if (isUnstableAbiClassDiagnosticForModulesWithEnabledUnstableAbi(diagnostic)) return
|
|
||||||
|
|
||||||
assert(diagnostics.all { it.psiElement == element && it.factory == factory })
|
|
||||||
|
|
||||||
val ranges = diagnostic.textRanges
|
|
||||||
|
|
||||||
val presentationInfo: AnnotationPresentationInfo = when (factory.severity) {
|
|
||||||
Severity.ERROR -> {
|
|
||||||
when (factory) {
|
|
||||||
in Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS -> {
|
|
||||||
val referenceExpression = element as KtReferenceExpression
|
|
||||||
val reference = referenceExpression.mainReference
|
|
||||||
if (reference is MultiRangeReference) {
|
|
||||||
AnnotationPresentationInfo(
|
|
||||||
ranges = reference.ranges.map { it.shiftRight(referenceExpression.textOffset) },
|
|
||||||
highlightType = ProblemHighlightType.LIKE_UNKNOWN_SYMBOL
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
AnnotationPresentationInfo(ranges, highlightType = ProblemHighlightType.LIKE_UNKNOWN_SYMBOL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Errors.ILLEGAL_ESCAPE -> AnnotationPresentationInfo(
|
|
||||||
ranges, textAttributes = KotlinHighlightingColors.INVALID_STRING_ESCAPE
|
|
||||||
)
|
|
||||||
|
|
||||||
Errors.REDECLARATION -> AnnotationPresentationInfo(
|
|
||||||
ranges = listOf(diagnostic.textRanges.first()), nonDefaultMessage = ""
|
|
||||||
)
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
AnnotationPresentationInfo(
|
|
||||||
ranges,
|
|
||||||
highlightType = if (factory == Errors.INVISIBLE_REFERENCE)
|
|
||||||
ProblemHighlightType.LIKE_UNKNOWN_SYMBOL
|
|
||||||
else
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Severity.WARNING -> {
|
|
||||||
if (factory == Errors.UNUSED_PARAMETER && shouldSuppressUnusedParameter(element as KtParameter)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
AnnotationPresentationInfo(
|
|
||||||
ranges,
|
|
||||||
textAttributes = when (factory) {
|
|
||||||
Errors.DEPRECATION -> CodeInsightColors.DEPRECATED_ATTRIBUTES
|
|
||||||
Errors.UNUSED_ANONYMOUS_PARAMETER -> CodeInsightColors.WEAK_WARNING_ATTRIBUTES
|
|
||||||
else -> null
|
|
||||||
},
|
|
||||||
highlightType = when (factory) {
|
|
||||||
in Errors.UNUSED_ELEMENT_DIAGNOSTICS -> ProblemHighlightType.LIKE_UNUSED_SYMBOL
|
|
||||||
Errors.UNUSED_ANONYMOUS_PARAMETER -> ProblemHighlightType.WEAK_WARNING
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Severity.INFO -> AnnotationPresentationInfo(ranges, highlightType = ProblemHighlightType.INFORMATION)
|
|
||||||
}
|
|
||||||
|
|
||||||
setUpAnnotations(diagnostics, presentationInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setUpAnnotations(diagnostics: List<Diagnostic>, data: AnnotationPresentationInfo) {
|
|
||||||
val fixesMap = try {
|
|
||||||
createQuickFixes(diagnostics)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
if (e is ControlFlowException) {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
LOG.error(e)
|
|
||||||
MultiMap<Diagnostic, IntentionAction>()
|
|
||||||
}
|
|
||||||
|
|
||||||
data.processDiagnostics(holder, diagnostics, fixesMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isUnstableAbiClassDiagnosticForModulesWithEnabledUnstableAbi(diagnostic: Diagnostic): Boolean {
|
|
||||||
val factory = diagnostic.factory
|
|
||||||
if (factory != Errors.IR_WITH_UNSTABLE_ABI_COMPILED_CLASS && factory != Errors.FIR_COMPILED_CLASS) return false
|
|
||||||
|
|
||||||
val module = element.module ?: return false
|
|
||||||
val moduleFacetSettings = KotlinFacetSettingsProvider.getInstance(element.project)?.getSettings(module) ?: return false
|
|
||||||
return when (factory) {
|
|
||||||
Errors.IR_WITH_UNSTABLE_ABI_COMPILED_CLASS ->
|
|
||||||
moduleFacetSettings.isCompilerSettingPresent(K2JVMCompilerArguments::useIR) &&
|
|
||||||
!moduleFacetSettings.isCompilerSettingPresent(K2JVMCompilerArguments::useOldBackend)
|
|
||||||
Errors.FIR_COMPILED_CLASS ->
|
|
||||||
moduleFacetSettings.isCompilerSettingPresent(K2JVMCompilerArguments::useFir)
|
|
||||||
else -> error(factory)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val LOG = Logger.getInstance(ElementAnnotator::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+13
-10
@@ -35,8 +35,8 @@ internal class VariablesHighlightingVisitor(holder: AnnotationHolder, bindingCon
|
|||||||
if (target is ValueParameterDescriptor && bindingContext.get(AUTO_CREATED_IT, target) == true) {
|
if (target is ValueParameterDescriptor && bindingContext.get(AUTO_CREATED_IT, target) == true) {
|
||||||
createInfoAnnotation(
|
createInfoAnnotation(
|
||||||
expression,
|
expression,
|
||||||
FUNCTION_LITERAL_DEFAULT_PARAMETER,
|
KotlinIdeaAnalysisBundle.message("automatically.declared.based.on.the.expected.type"),
|
||||||
KotlinIdeaAnalysisBundle.message("automatically.declared.based.on.the.expected.type")
|
FUNCTION_LITERAL_DEFAULT_PARAMETER
|
||||||
)
|
)
|
||||||
} else if (expression.parent !is KtValueArgumentName) { // highlighted separately
|
} else if (expression.parent !is KtValueArgumentName) { // highlighted separately
|
||||||
highlightVariable(expression, target)
|
highlightVariable(expression, target)
|
||||||
@@ -91,14 +91,15 @@ internal class VariablesHighlightingVisitor(holder: AnnotationHolder, bindingCon
|
|||||||
"0.smart.cast.to.1",
|
"0.smart.cast.to.1",
|
||||||
receiverName,
|
receiverName,
|
||||||
DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type)
|
DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type)
|
||||||
)
|
),
|
||||||
).textAttributes = SMART_CAST_RECEIVER
|
SMART_CAST_RECEIVER
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val nullSmartCast = bindingContext.get(SMARTCAST_NULL, expression) == true
|
val nullSmartCast = bindingContext.get(SMARTCAST_NULL, expression) == true
|
||||||
if (nullSmartCast) {
|
if (nullSmartCast) {
|
||||||
createInfoAnnotation(expression, KotlinIdeaAnalysisBundle.message("always.null")).textAttributes = SMART_CONSTANT
|
createInfoAnnotation(expression, KotlinIdeaAnalysisBundle.message("always.null"), SMART_CONSTANT)
|
||||||
}
|
}
|
||||||
|
|
||||||
val smartCast = bindingContext.get(SMARTCAST, expression)
|
val smartCast = bindingContext.get(SMARTCAST, expression)
|
||||||
@@ -107,8 +108,9 @@ internal class VariablesHighlightingVisitor(holder: AnnotationHolder, bindingCon
|
|||||||
if (defaultType != null) {
|
if (defaultType != null) {
|
||||||
createInfoAnnotation(
|
createInfoAnnotation(
|
||||||
getSmartCastTarget(expression),
|
getSmartCastTarget(expression),
|
||||||
KotlinIdeaAnalysisBundle.message("smart.cast.to.0", DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(defaultType))
|
KotlinIdeaAnalysisBundle.message("smart.cast.to.0", DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(defaultType)),
|
||||||
).textAttributes = SMART_CAST_VALUE
|
SMART_CAST_VALUE
|
||||||
|
)
|
||||||
} else if (smartCast is MultipleSmartCasts) {
|
} else if (smartCast is MultipleSmartCasts) {
|
||||||
for ((call, type) in smartCast.map) {
|
for ((call, type) in smartCast.map) {
|
||||||
createInfoAnnotation(
|
createInfoAnnotation(
|
||||||
@@ -117,8 +119,9 @@ internal class VariablesHighlightingVisitor(holder: AnnotationHolder, bindingCon
|
|||||||
"smart.cast.to.0.for.1.call",
|
"smart.cast.to.0.for.1.call",
|
||||||
DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type),
|
DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type),
|
||||||
call.toString()
|
call.toString()
|
||||||
)
|
),
|
||||||
).textAttributes = SMART_CAST_VALUE
|
SMART_CAST_VALUE
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +157,7 @@ internal class VariablesHighlightingVisitor(holder: AnnotationHolder, bindingCon
|
|||||||
|
|
||||||
val parent = elementToHighlight.parent
|
val parent = elementToHighlight.parent
|
||||||
if (!(parent is PsiNameIdentifierOwner && parent.nameIdentifier == elementToHighlight)) {
|
if (!(parent is PsiNameIdentifierOwner && parent.nameIdentifier == elementToHighlight)) {
|
||||||
createInfoAnnotation(elementToHighlight, WRAPPED_INTO_REF, msg)
|
createInfoAnnotation(elementToHighlight, msg, WRAPPED_INTO_REF)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -9,7 +9,8 @@ import com.intellij.codeInsight.completion.PrefixMatcher
|
|||||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||||
import com.intellij.openapi.progress.ProgressManager
|
import com.intellij.openapi.progress.ProgressManager
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.idea.highlighter.KotlinPsiChecker
|
import org.jetbrains.kotlin.idea.highlighter.AbstractKotlinHighlightingPass
|
||||||
|
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingPass
|
||||||
import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors
|
import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors
|
||||||
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
|
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
|
||||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||||
@@ -28,7 +29,7 @@ class FromUnresolvedNamesCompletion(
|
|||||||
scope.forEachDescendantOfType<KtNameReferenceExpression> { refExpr ->
|
scope.forEachDescendantOfType<KtNameReferenceExpression> { refExpr ->
|
||||||
ProgressManager.checkCanceled()
|
ProgressManager.checkCanceled()
|
||||||
|
|
||||||
if (KotlinPsiChecker.wasUnresolved(refExpr)) {
|
if (AbstractKotlinHighlightingPass.wasUnresolved(refExpr)) {
|
||||||
val callTypeAndReceiver = CallTypeAndReceiver.detect(refExpr)
|
val callTypeAndReceiver = CallTypeAndReceiver.detect(refExpr)
|
||||||
if (callTypeAndReceiver.receiver != null) return@forEachDescendantOfType
|
if (callTypeAndReceiver.receiver != null) return@forEachDescendantOfType
|
||||||
if (sampleDescriptor != null) {
|
if (sampleDescriptor != null) {
|
||||||
|
|||||||
+7
-4
@@ -10,6 +10,7 @@ import com.intellij.psi.PsiElement
|
|||||||
import org.jetbrains.kotlin.idea.KotlinIdeaAnalysisBundle
|
import org.jetbrains.kotlin.idea.KotlinIdeaAnalysisBundle
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.ImplicitReceiverSmartcastKind
|
import org.jetbrains.kotlin.idea.frontend.api.ImplicitReceiverSmartcastKind
|
||||||
|
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingColors
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
|
||||||
internal class ExpressionsSmartcastHighlightingVisitor(
|
internal class ExpressionsSmartcastHighlightingVisitor(
|
||||||
@@ -30,8 +31,9 @@ internal class ExpressionsSmartcastHighlightingVisitor(
|
|||||||
"0.smart.cast.to.1",
|
"0.smart.cast.to.1",
|
||||||
receiverName,
|
receiverName,
|
||||||
type.asStringForDebugging()
|
type.asStringForDebugging()
|
||||||
)
|
),
|
||||||
).textAttributes = org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingColors.SMART_CAST_RECEIVER
|
KotlinHighlightingColors.SMART_CAST_RECEIVER
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expression.getSmartCasts()?.forEach { type ->
|
expression.getSmartCasts()?.forEach { type ->
|
||||||
@@ -40,8 +42,9 @@ internal class ExpressionsSmartcastHighlightingVisitor(
|
|||||||
KotlinIdeaAnalysisBundle.message(
|
KotlinIdeaAnalysisBundle.message(
|
||||||
"smart.cast.to.0",
|
"smart.cast.to.0",
|
||||||
type.asStringForDebugging()
|
type.asStringForDebugging()
|
||||||
)
|
),
|
||||||
).textAttributes = org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingColors.SMART_CAST_VALUE
|
KotlinHighlightingColors.SMART_CAST_VALUE
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo smartcast to null
|
//todo smartcast to null
|
||||||
|
|||||||
+8
@@ -6,6 +6,8 @@
|
|||||||
package org.jetbrains.kotlin.idea.fir.highlighter.visitors
|
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.util.TextRange
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.idea.highlighter.HighlightingVisitor
|
import org.jetbrains.kotlin.idea.highlighter.HighlightingVisitor
|
||||||
|
|
||||||
@@ -14,6 +16,12 @@ abstract class FirAfterResolveHighlightingVisitor(
|
|||||||
protected val holder: AnnotationHolder
|
protected val holder: AnnotationHolder
|
||||||
) : HighlightingVisitor(holder) {
|
) : HighlightingVisitor(holder) {
|
||||||
|
|
||||||
|
override fun createInfoAnnotation(textRange: TextRange, message: String?, textAttributes: TextAttributesKey?) {
|
||||||
|
// TODO: Temporary use deprecated for FIR plugin as it is supposes to be rewritten fully
|
||||||
|
holder.createInfoAnnotation(textRange, message)
|
||||||
|
.also { annotation -> textAttributes?.let { annotation.textAttributes = textAttributes } }
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun createListOfVisitors(
|
fun createListOfVisitors(
|
||||||
analysisSession: KtAnalysisSession,
|
analysisSession: KtAnalysisSession,
|
||||||
|
|||||||
+2
-2
@@ -34,8 +34,8 @@ internal class VariableReferenceHighlightingVisitor(
|
|||||||
if (expression.isAutoCreatedItParameter()) {
|
if (expression.isAutoCreatedItParameter()) {
|
||||||
createInfoAnnotation(
|
createInfoAnnotation(
|
||||||
expression,
|
expression,
|
||||||
Colors.FUNCTION_LITERAL_DEFAULT_PARAMETER,
|
KotlinIdeaAnalysisBundle.message("automatically.declared.based.on.the.expected.type"),
|
||||||
KotlinIdeaAnalysisBundle.message("automatically.declared.based.on.the.expected.type")
|
Colors.FUNCTION_LITERAL_DEFAULT_PARAMETER
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.psi.KtFile
|
|||||||
import org.jetbrains.kotlin.test.uitls.IgnoreTests
|
import org.jetbrains.kotlin.test.uitls.IgnoreTests
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
abstract class AbstractFirPsiCheckerTest : AbstractPsiCheckerTest() {
|
abstract class AbstractFirKotlinHighlightingPassTest : AbstractKotlinHighlightingPassTest() {
|
||||||
override val captureExceptions: Boolean = false
|
override val captureExceptions: Boolean = false
|
||||||
|
|
||||||
override fun isFirPlugin(): Boolean = true
|
override fun isFirPlugin(): Boolean = true
|
||||||
+12
-7
@@ -18,11 +18,11 @@ import java.util.regex.Pattern;
|
|||||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public class FirPsiCheckerTestGenerated extends AbstractFirPsiCheckerTest {
|
public class FirKotlinHighlightingPassTestGenerated extends AbstractFirKotlinHighlightingPassTest {
|
||||||
@TestMetadata("idea/testData/checker")
|
@TestMetadata("idea/testData/checker")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class Checker extends AbstractFirPsiCheckerTest {
|
public static class Checker extends AbstractFirKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
@@ -41,6 +41,11 @@ public class FirPsiCheckerTestGenerated extends AbstractFirPsiCheckerTest {
|
|||||||
runTest("idea/testData/checker/AnnotationOnFile.kt");
|
runTest("idea/testData/checker/AnnotationOnFile.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("AnnotationSupressing.kt")
|
||||||
|
public void testAnnotationSupressing() throws Exception {
|
||||||
|
runTest("idea/testData/checker/AnnotationSupressing.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("AnonymousInitializers.kt")
|
@TestMetadata("AnonymousInitializers.kt")
|
||||||
public void testAnonymousInitializers() throws Exception {
|
public void testAnonymousInitializers() throws Exception {
|
||||||
runTest("idea/testData/checker/AnonymousInitializers.kt");
|
runTest("idea/testData/checker/AnonymousInitializers.kt");
|
||||||
@@ -365,7 +370,7 @@ public class FirPsiCheckerTestGenerated extends AbstractFirPsiCheckerTest {
|
|||||||
@TestMetadata("idea/testData/checker/regression")
|
@TestMetadata("idea/testData/checker/regression")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class Regression extends AbstractFirPsiCheckerTest {
|
public static class Regression extends AbstractFirKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
@@ -608,7 +613,7 @@ public class FirPsiCheckerTestGenerated extends AbstractFirPsiCheckerTest {
|
|||||||
@TestMetadata("idea/testData/checker/recovery")
|
@TestMetadata("idea/testData/checker/recovery")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class Recovery extends AbstractFirPsiCheckerTest {
|
public static class Recovery extends AbstractFirKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
@@ -636,7 +641,7 @@ public class FirPsiCheckerTestGenerated extends AbstractFirPsiCheckerTest {
|
|||||||
@TestMetadata("idea/testData/checker/rendering")
|
@TestMetadata("idea/testData/checker/rendering")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class Rendering extends AbstractFirPsiCheckerTest {
|
public static class Rendering extends AbstractFirKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
@@ -654,7 +659,7 @@ public class FirPsiCheckerTestGenerated extends AbstractFirPsiCheckerTest {
|
|||||||
@TestMetadata("idea/testData/checker/infos")
|
@TestMetadata("idea/testData/checker/infos")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class Infos extends AbstractFirPsiCheckerTest {
|
public static class Infos extends AbstractFirKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
@@ -747,7 +752,7 @@ public class FirPsiCheckerTestGenerated extends AbstractFirPsiCheckerTest {
|
|||||||
@TestMetadata("idea/testData/checker/diagnosticsMessage")
|
@TestMetadata("idea/testData/checker/diagnosticsMessage")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class DiagnosticsMessage extends AbstractFirPsiCheckerTest {
|
public static class DiagnosticsMessage extends AbstractFirKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.highlighter
|
||||||
|
|
||||||
|
import com.intellij.codeHighlighting.TextEditorHighlightingPass
|
||||||
|
import com.intellij.codeInsight.daemon.impl.AnnotationHolderImpl
|
||||||
|
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||||
|
import com.intellij.codeInsight.daemon.impl.UpdateHighlightersUtil
|
||||||
|
import com.intellij.lang.annotation.Annotation
|
||||||
|
import com.intellij.lang.annotation.AnnotationHolder
|
||||||
|
import com.intellij.lang.annotation.AnnotationSession
|
||||||
|
import com.intellij.openapi.editor.Document
|
||||||
|
import com.intellij.openapi.progress.ProgressIndicator
|
||||||
|
import com.intellij.openapi.project.DumbAware
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiRecursiveElementVisitor
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single thread model only (as any other [TextEditorHighlightingPass])
|
||||||
|
*/
|
||||||
|
@Suppress("UnstableApiUsage")
|
||||||
|
abstract class AbstractHighlightingPassBase(
|
||||||
|
protected val file: KtFile,
|
||||||
|
document: Document
|
||||||
|
) : TextEditorHighlightingPass(file.project, document), DumbAware {
|
||||||
|
|
||||||
|
private val highlightInfos: MutableList<HighlightInfo> = mutableListOf()
|
||||||
|
protected var annotationCallback: ((Annotation) -> Unit)? = null
|
||||||
|
protected var annotationHolder: AnnotationHolderImpl? = null
|
||||||
|
|
||||||
|
fun annotationCallback(callback: (Annotation) -> Unit) {
|
||||||
|
annotationCallback = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetAnnotationCallback() {
|
||||||
|
annotationCallback = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doCollectInformation(progress: ProgressIndicator) {
|
||||||
|
highlightInfos.clear()
|
||||||
|
|
||||||
|
// TODO: YES, IT USES `@ApiStatus.Internal` AnnotationHolderImpl intentionally:
|
||||||
|
// there is no other way to highlight:
|
||||||
|
// - HighlightInfo could not be highlighted immediately as myHighlightInfoProcessor.infoIsAvailable is not accessible
|
||||||
|
// (HighlightingSessionImpl impl is closed) and/or UpdateHighlightersUtil.addHighlighterToEditorIncrementally is closed as well.
|
||||||
|
// therefore direct usage of AnnotationHolderImpl is the smallest evil
|
||||||
|
|
||||||
|
val annotationHolder = object : AnnotationHolderImpl(AnnotationSession(file)) {
|
||||||
|
override fun add(element: Annotation?): Boolean {
|
||||||
|
element?.let { annotationCallback?.invoke(it) }
|
||||||
|
return super.add(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
annotationHolder.runAnnotatorWithContext(file) { element, holder ->
|
||||||
|
runAnnotatorWithContext(element, holder)
|
||||||
|
}
|
||||||
|
this.annotationHolder = annotationHolder
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun runAnnotatorWithContext(
|
||||||
|
element: PsiElement,
|
||||||
|
holder: AnnotationHolder
|
||||||
|
) {
|
||||||
|
element.accept(object : PsiRecursiveElementVisitor() {})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getInfos(): MutableList<HighlightInfo> = highlightInfos
|
||||||
|
|
||||||
|
override fun doApplyInformationToEditor() {
|
||||||
|
try {
|
||||||
|
val infos = annotationHolder?.map { HighlightInfo.fromAnnotation(it) } ?: return
|
||||||
|
highlightInfos.addAll(infos)
|
||||||
|
// NOTE: keep !! for 201 version
|
||||||
|
UpdateHighlightersUtil.setHighlightersToEditor(myProject, myDocument!!, 0, file.textLength, infos, colorsScheme, id)
|
||||||
|
} finally {
|
||||||
|
annotationHolder = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+8
-6
@@ -38,7 +38,7 @@ internal class BeforeResolveHighlightingVisitor(holder: AnnotationHolder) : High
|
|||||||
else -> return
|
else -> return
|
||||||
}
|
}
|
||||||
|
|
||||||
createInfoAnnotation(element, null).textAttributes = attributes
|
createInfoAnnotation(element, textAttributes = attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun willApplyRainbowHighlight(element: KDocLink): Boolean {
|
private fun willApplyRainbowHighlight(element: KDocLink): Boolean {
|
||||||
@@ -53,27 +53,29 @@ internal class BeforeResolveHighlightingVisitor(holder: AnnotationHolder) : High
|
|||||||
if (ApplicationManager.getApplication().isUnitTestMode) return
|
if (ApplicationManager.getApplication().isUnitTestMode) return
|
||||||
|
|
||||||
val functionLiteral = lambdaExpression.functionLiteral
|
val functionLiteral = lambdaExpression.functionLiteral
|
||||||
createInfoAnnotation(functionLiteral.lBrace, null).textAttributes = KotlinHighlightingColors.FUNCTION_LITERAL_BRACES_AND_ARROW
|
createInfoAnnotation(functionLiteral.lBrace, textAttributes = KotlinHighlightingColors.FUNCTION_LITERAL_BRACES_AND_ARROW)
|
||||||
|
|
||||||
val closingBrace = functionLiteral.rBrace
|
val closingBrace = functionLiteral.rBrace
|
||||||
if (closingBrace != null) {
|
if (closingBrace != null) {
|
||||||
createInfoAnnotation(closingBrace, null).textAttributes = KotlinHighlightingColors.FUNCTION_LITERAL_BRACES_AND_ARROW
|
createInfoAnnotation(closingBrace, textAttributes = KotlinHighlightingColors.FUNCTION_LITERAL_BRACES_AND_ARROW)
|
||||||
}
|
}
|
||||||
|
|
||||||
val arrow = functionLiteral.arrow
|
val arrow = functionLiteral.arrow
|
||||||
if (arrow != null) {
|
if (arrow != null) {
|
||||||
createInfoAnnotation(arrow, null).textAttributes = KotlinHighlightingColors.FUNCTION_LITERAL_BRACES_AND_ARROW
|
createInfoAnnotation(arrow, textAttributes = KotlinHighlightingColors.FUNCTION_LITERAL_BRACES_AND_ARROW)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitArgument(argument: KtValueArgument) {
|
override fun visitArgument(argument: KtValueArgument) {
|
||||||
val argumentName = argument.getArgumentName() ?: return
|
val argumentName = argument.getArgumentName() ?: return
|
||||||
val eq = argument.equalsToken ?: return
|
val eq = argument.equalsToken ?: return
|
||||||
createInfoAnnotation(TextRange(argumentName.startOffset, eq.endOffset), null).textAttributes =
|
createInfoAnnotation(
|
||||||
if (argument.parent.parent is KtAnnotationEntry)
|
TextRange(argumentName.startOffset, eq.endOffset),
|
||||||
|
textAttributes = if (argument.parent.parent is KtAnnotationEntry)
|
||||||
KotlinHighlightingColors.ANNOTATION_ATTRIBUTE_NAME_ATTRIBUTES
|
KotlinHighlightingColors.ANNOTATION_ATTRIBUTE_NAME_ATTRIBUTES
|
||||||
else
|
else
|
||||||
KotlinHighlightingColors.NAMED_ARGUMENT
|
KotlinHighlightingColors.NAMED_ARGUMENT
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitExpressionWithLabel(expression: KtExpressionWithLabel) {
|
override fun visitExpressionWithLabel(expression: KtExpressionWithLabel) {
|
||||||
|
|||||||
+17
-11
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.highlighter
|
|||||||
|
|
||||||
import com.intellij.lang.annotation.Annotation
|
import com.intellij.lang.annotation.Annotation
|
||||||
import com.intellij.lang.annotation.AnnotationHolder
|
import com.intellij.lang.annotation.AnnotationHolder
|
||||||
|
import com.intellij.lang.annotation.HighlightSeverity
|
||||||
import com.intellij.openapi.editor.colors.TextAttributesKey
|
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
@@ -17,29 +18,34 @@ abstract class HighlightingVisitor protected constructor(
|
|||||||
private val holder: AnnotationHolder
|
private val holder: AnnotationHolder
|
||||||
) : KtVisitorVoid() {
|
) : KtVisitorVoid() {
|
||||||
|
|
||||||
protected fun createInfoAnnotation(element: PsiElement, textAttributes: TextAttributesKey, message: String? = null) {
|
protected fun createInfoAnnotation(element: PsiElement, message: String? = null, textAttributes: TextAttributesKey) {
|
||||||
createInfoAnnotation(element.textRange, textAttributes, message)
|
createInfoAnnotation(element.textRange, message, textAttributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun createInfoAnnotation(range: TextRange, textAttributes: TextAttributesKey, message: String? = null) {
|
protected fun createInfoAnnotation(element: PsiElement, message: String? = null) =
|
||||||
createInfoAnnotation(range, message).textAttributes = textAttributes
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun createInfoAnnotation(element: PsiElement, message: String? = null): Annotation =
|
|
||||||
createInfoAnnotation(element.textRange, message)
|
createInfoAnnotation(element.textRange, message)
|
||||||
|
|
||||||
protected fun createInfoAnnotation(textRange: TextRange, message: String? = null): Annotation =
|
protected open fun createInfoAnnotation(textRange: TextRange, message: String? = null, textAttributes: TextAttributesKey? = null) {
|
||||||
holder.createInfoAnnotation(textRange, message)
|
(message?.let { holder.newAnnotation(HighlightSeverity.INFORMATION, it) }
|
||||||
|
?: holder.newSilentAnnotation(HighlightSeverity.INFORMATION))
|
||||||
|
.range(textRange)
|
||||||
|
.also { builder ->
|
||||||
|
textAttributes?.let {
|
||||||
|
builder.textAttributes(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.create()
|
||||||
|
}
|
||||||
|
|
||||||
protected fun highlightName(element: PsiElement, attributesKey: TextAttributesKey, message: String? = null) {
|
protected fun highlightName(element: PsiElement, attributesKey: TextAttributesKey, message: String? = null) {
|
||||||
if (NameHighlighter.namesHighlightingEnabled && !element.textRange.isEmpty) {
|
if (NameHighlighter.namesHighlightingEnabled && !element.textRange.isEmpty) {
|
||||||
createInfoAnnotation(element, attributesKey, message)
|
createInfoAnnotation(element, message, attributesKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun highlightName(textRange: TextRange, attributesKey: TextAttributesKey, message: String? = null) {
|
protected fun highlightName(textRange: TextRange, attributesKey: TextAttributesKey, message: String? = null) {
|
||||||
if (NameHighlighter.namesHighlightingEnabled) {
|
if (NameHighlighter.namesHighlightingEnabled) {
|
||||||
createInfoAnnotation(textRange, attributesKey, message)
|
createInfoAnnotation(textRange, message, attributesKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-32
@@ -6,51 +6,29 @@
|
|||||||
package org.jetbrains.kotlin.idea.highlighter
|
package org.jetbrains.kotlin.idea.highlighter
|
||||||
|
|
||||||
import com.intellij.codeHighlighting.*
|
import com.intellij.codeHighlighting.*
|
||||||
import com.intellij.codeInsight.daemon.impl.AnnotationHolderImpl
|
|
||||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
|
||||||
import com.intellij.codeInsight.daemon.impl.UpdateHighlightersUtil
|
|
||||||
import com.intellij.lang.annotation.AnnotationHolder
|
import com.intellij.lang.annotation.AnnotationHolder
|
||||||
import com.intellij.lang.annotation.AnnotationSession
|
|
||||||
import com.intellij.openapi.editor.Document
|
import com.intellij.openapi.editor.Document
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.extensions.ExtensionPointName
|
import com.intellij.openapi.extensions.ExtensionPointName
|
||||||
import com.intellij.openapi.progress.ProgressIndicator
|
|
||||||
import com.intellij.openapi.project.DumbAware
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.psi.PsiRecursiveElementVisitor
|
import com.intellij.psi.PsiRecursiveElementVisitor
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
|
||||||
class KotlinBeforeResolveHighlightingPass(
|
class KotlinBeforeResolveHighlightingPass(file: KtFile, document: Document) : AbstractHighlightingPassBase(file, document) {
|
||||||
private val file: KtFile,
|
|
||||||
document: Document
|
|
||||||
) : TextEditorHighlightingPass(file.project, document), DumbAware {
|
|
||||||
|
|
||||||
@Volatile
|
override fun runAnnotatorWithContext(element: PsiElement, holder: AnnotationHolder) {
|
||||||
private var annotationHolder: AnnotationHolderImpl? = null
|
val visitor = BeforeResolveHighlightingVisitor(holder)
|
||||||
|
val extensions = EP_NAME.extensionList.map { it.createVisitor(holder) }
|
||||||
|
|
||||||
override fun doCollectInformation(progress: ProgressIndicator) {
|
element.accept(object : PsiRecursiveElementVisitor() {
|
||||||
val annotationHolder = AnnotationHolderImpl(AnnotationSession(file))
|
|
||||||
val visitor = BeforeResolveHighlightingVisitor(annotationHolder)
|
|
||||||
val extensions = EP_NAME.extensionList.map { it.createVisitor(annotationHolder) }
|
|
||||||
file.accept(object : PsiRecursiveElementVisitor() {
|
|
||||||
override fun visitElement(element: PsiElement) {
|
override fun visitElement(element: PsiElement) {
|
||||||
super.visitElement(element)
|
|
||||||
element.accept(visitor)
|
element.accept(visitor)
|
||||||
extensions.forEach(element::accept)
|
extensions.forEach(element::accept)
|
||||||
|
super.visitElement(element)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.annotationHolder = annotationHolder
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun doApplyInformationToEditor() {
|
|
||||||
if (annotationHolder == null) return
|
|
||||||
|
|
||||||
val infos = annotationHolder!!.map { HighlightInfo.fromAnnotation(it) }
|
|
||||||
|
|
||||||
UpdateHighlightersUtil.setHighlightersToEditor(myProject, myDocument!!, 0, file.textLength, infos, colorsScheme, id)
|
|
||||||
annotationHolder = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory : TextEditorHighlightingPassFactory {
|
class Factory : TextEditorHighlightingPassFactory {
|
||||||
@@ -64,10 +42,10 @@ class KotlinBeforeResolveHighlightingPass(
|
|||||||
override fun registerHighlightingPassFactory(registrar: TextEditorHighlightingPassRegistrar, project: Project) {
|
override fun registerHighlightingPassFactory(registrar: TextEditorHighlightingPassRegistrar, project: Project) {
|
||||||
registrar.registerTextEditorHighlightingPass(
|
registrar.registerTextEditorHighlightingPass(
|
||||||
Factory(),
|
Factory(),
|
||||||
TextEditorHighlightingPassRegistrar.Anchor.BEFORE,
|
/* anchor = */ TextEditorHighlightingPassRegistrar.Anchor.BEFORE,
|
||||||
Pass.UPDATE_FOLDING,
|
/* anchorPassId = */ Pass.UPDATE_FOLDING,
|
||||||
false,
|
/* needAdditionalIntentionsPass = */ false,
|
||||||
false
|
/* inPostHighlightingPass = */ false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ inline fun invokeLater(expired: Condition<*>, crossinline action: () -> Unit) =
|
|||||||
|
|
||||||
inline fun isUnitTestMode(): Boolean = ApplicationManager.getApplication().isUnitTestMode
|
inline fun isUnitTestMode(): Boolean = ApplicationManager.getApplication().isUnitTestMode
|
||||||
|
|
||||||
|
inline fun isApplicationInternalMode(): Boolean = ApplicationManager.getApplication().isInternal
|
||||||
|
|
||||||
inline fun <reified T : Any> ComponentManager.getServiceSafe(): T =
|
inline fun <reified T : Any> ComponentManager.getServiceSafe(): T =
|
||||||
this.getService(T::class.java) ?: error("Unable to locate service ${T::class.java.name}")
|
this.getService(T::class.java) ?: error("Unable to locate service ${T::class.java.name}")
|
||||||
|
|
||||||
|
|||||||
+63
-14
@@ -5,15 +5,17 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.perf
|
package org.jetbrains.kotlin.idea.perf
|
||||||
|
|
||||||
|
import com.intellij.codeHighlighting.*
|
||||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||||
import com.intellij.lang.annotation.AnnotationHolder
|
import com.intellij.openapi.editor.Document
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.progress.ProgressIndicator
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.testFramework.RunAll
|
import com.intellij.testFramework.RunAll
|
||||||
import com.intellij.util.ThrowableRunnable
|
import com.intellij.util.ThrowableRunnable
|
||||||
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
|
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
|
||||||
import org.jetbrains.kotlin.idea.highlighter.KotlinPsiChecker
|
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingPass
|
||||||
import org.jetbrains.kotlin.idea.highlighter.KotlinPsiCheckerAndHighlightingUpdater
|
|
||||||
import org.jetbrains.kotlin.idea.perf.Stats.Companion.TEST_KEY
|
import org.jetbrains.kotlin.idea.perf.Stats.Companion.TEST_KEY
|
||||||
import org.jetbrains.kotlin.idea.perf.Stats.Companion.runAndMeasure
|
import org.jetbrains.kotlin.idea.perf.Stats.Companion.runAndMeasure
|
||||||
import org.jetbrains.kotlin.idea.perf.util.Metric
|
import org.jetbrains.kotlin.idea.perf.util.Metric
|
||||||
@@ -22,6 +24,7 @@ import org.jetbrains.kotlin.idea.testFramework.Fixture
|
|||||||
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.cleanupCaches
|
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.cleanupCaches
|
||||||
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.isAKotlinScriptFile
|
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.isAKotlinScriptFile
|
||||||
import org.jetbrains.kotlin.idea.testFramework.ProjectOpenAction.GRADLE_PROJECT
|
import org.jetbrains.kotlin.idea.testFramework.ProjectOpenAction.GRADLE_PROJECT
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import java.util.concurrent.atomic.AtomicLong
|
import java.util.concurrent.atomic.AtomicLong
|
||||||
import kotlin.test.assertNotEquals
|
import kotlin.test.assertNotEquals
|
||||||
|
|
||||||
@@ -38,6 +41,9 @@ class PerformanceProjectsTest : AbstractPerformanceProjectsTest() {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
val timer: AtomicLong = AtomicLong()
|
val timer: AtomicLong = AtomicLong()
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
val diagnosticTimer: AtomicLong = AtomicLong()
|
||||||
|
|
||||||
fun resetTimestamp() {
|
fun resetTimestamp() {
|
||||||
timer.set(0)
|
timer.set(0)
|
||||||
}
|
}
|
||||||
@@ -326,6 +332,7 @@ class PerformanceProjectsTest : AbstractPerformanceProjectsTest() {
|
|||||||
val testName = "fileAnalysis ${notePrefix(note)}${simpleFilename(fileName)}"
|
val testName = "fileAnalysis ${notePrefix(note)}${simpleFilename(fileName)}"
|
||||||
val extraStats = Stats("${stats.name} $testName")
|
val extraStats = Stats("${stats.name} $testName")
|
||||||
val extraTimingsNs = mutableListOf<Map<String, Any>?>()
|
val extraTimingsNs = mutableListOf<Map<String, Any>?>()
|
||||||
|
val diagnosticTimingsNs = mutableListOf<Map<String, Any>?>()
|
||||||
|
|
||||||
val warmUpIterations = 30
|
val warmUpIterations = 30
|
||||||
val iterations = 50
|
val iterations = 50
|
||||||
@@ -337,7 +344,7 @@ class PerformanceProjectsTest : AbstractPerformanceProjectsTest() {
|
|||||||
iterations(iterations)
|
iterations(iterations)
|
||||||
setUp(perfKtsFileAnalysisSetUp(project, fileName))
|
setUp(perfKtsFileAnalysisSetUp(project, fileName))
|
||||||
test(perfKtsFileAnalysisTest())
|
test(perfKtsFileAnalysisTest())
|
||||||
tearDown(perfKtsFileAnalysisTearDown(extraTimingsNs, project))
|
tearDown(perfKtsFileAnalysisTearDown(extraTimingsNs, diagnosticTimingsNs, project))
|
||||||
profilerConfig.enabled = true
|
profilerConfig.enabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,19 +356,31 @@ class PerformanceProjectsTest : AbstractPerformanceProjectsTest() {
|
|||||||
metricChildren
|
metricChildren
|
||||||
)
|
)
|
||||||
|
|
||||||
|
extraStats.printWarmUpTimings(
|
||||||
|
"diagnostic",
|
||||||
|
diagnosticTimingsNs.take(warmUpIterations).toTypedArray(),
|
||||||
|
metricChildren
|
||||||
|
)
|
||||||
|
|
||||||
extraStats.processTimings(
|
extraStats.processTimings(
|
||||||
"annotator",
|
"annotator",
|
||||||
extraTimingsNs.drop(warmUpIterations).toTypedArray(),
|
extraTimingsNs.drop(warmUpIterations).toTypedArray(),
|
||||||
metricChildren
|
metricChildren
|
||||||
)
|
)
|
||||||
|
|
||||||
|
extraStats.processTimings(
|
||||||
|
"diagnostic",
|
||||||
|
diagnosticTimingsNs.drop(warmUpIterations).toTypedArray(),
|
||||||
|
metricChildren
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun replaceWithCustomHighlighter() {
|
private fun replaceWithCustomHighlighter() {
|
||||||
org.jetbrains.kotlin.idea.testFramework.replaceWithCustomHighlighter(
|
org.jetbrains.kotlin.idea.testFramework.replaceWithCustomHighlighter(
|
||||||
testRootDisposable,
|
testRootDisposable,
|
||||||
KotlinPsiCheckerAndHighlightingUpdater::class.java.name,
|
KotlinHighlightingPass.Registrar::class.java.name,
|
||||||
TestKotlinPsiChecker::class.java.name
|
TestKotlinHighlightingPass.Registrar::class.java.name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,18 +404,22 @@ class PerformanceProjectsTest : AbstractPerformanceProjectsTest() {
|
|||||||
fun perfKtsFileAnalysisTest(): (TestData<Fixture, Pair<Long, List<HighlightInfo>>>) -> Unit {
|
fun perfKtsFileAnalysisTest(): (TestData<Fixture, Pair<Long, List<HighlightInfo>>>) -> Unit {
|
||||||
return {
|
return {
|
||||||
it.value = it.setUpValue?.let { fixture ->
|
it.value = it.setUpValue?.let { fixture ->
|
||||||
Pair(System.nanoTime(), fixture.doHighlighting())
|
val nowNs = System.nanoTime()
|
||||||
|
diagnosticTimer.set(-nowNs)
|
||||||
|
Pair(nowNs, fixture.doHighlighting())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun perfKtsFileAnalysisTearDown(
|
fun perfKtsFileAnalysisTearDown(
|
||||||
extraTimingsNs: MutableList<Map<String, Any>?>,
|
extraTimingsNs: MutableList<Map<String, Any>?>,
|
||||||
|
diagnosticTimingsMs: MutableList<Map<String, Any>?>,
|
||||||
project: Project
|
project: Project
|
||||||
): (TestData<Fixture, Pair<Long, List<HighlightInfo>>>) -> Unit {
|
): (TestData<Fixture, Pair<Long, List<HighlightInfo>>>) -> Unit {
|
||||||
return {
|
return {
|
||||||
it.setUpValue?.let { fixture ->
|
it.setUpValue?.let { fixture ->
|
||||||
it.value?.let { v ->
|
it.value?.let { v ->
|
||||||
|
diagnosticTimingsMs.add(mapOf(TEST_KEY to diagnosticTimer.getAndSet(0)))
|
||||||
assertTrue(v.second.isNotEmpty())
|
assertTrue(v.second.isNotEmpty())
|
||||||
assertNotEquals(0, timer.get())
|
assertNotEquals(0, timer.get())
|
||||||
|
|
||||||
@@ -411,12 +434,38 @@ class PerformanceProjectsTest : AbstractPerformanceProjectsTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class TestKotlinPsiChecker : KotlinPsiChecker() {
|
class TestKotlinHighlightingPass(file: KtFile, document: Document) : KotlinHighlightingPass(file, document) {
|
||||||
override fun annotate(
|
override fun doCollectInformation(progress: ProgressIndicator) {
|
||||||
element: PsiElement, holder: AnnotationHolder
|
annotationCallback {
|
||||||
) {
|
val nowNs = System.nanoTime()
|
||||||
super.annotate(element, holder)
|
diagnosticTimer.addAndGet(nowNs)
|
||||||
markTimestamp()
|
resetAnnotationCallback()
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
super.doCollectInformation(progress)
|
||||||
|
} finally {
|
||||||
|
resetAnnotationCallback()
|
||||||
|
markTimestamp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Factory : TextEditorHighlightingPassFactory {
|
||||||
|
override fun createHighlightingPass(file: PsiFile, editor: Editor): TextEditorHighlightingPass? {
|
||||||
|
if (file !is KtFile) return null
|
||||||
|
return TestKotlinHighlightingPass(file, editor.document)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Registrar : TextEditorHighlightingPassFactoryRegistrar {
|
||||||
|
override fun registerHighlightingPassFactory(registrar: TextEditorHighlightingPassRegistrar, project: Project) {
|
||||||
|
registrar.registerTextEditorHighlightingPass(
|
||||||
|
Factory(),
|
||||||
|
null,
|
||||||
|
intArrayOf(Pass.UPDATE_ALL),
|
||||||
|
false,
|
||||||
|
-1
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,6 +123,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
<projectService serviceImplementation="org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectService"/>
|
<projectService serviceImplementation="org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectService"/>
|
||||||
|
|
||||||
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.KotlinBeforeResolveHighlightingPass$Registrar"/>
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.KotlinBeforeResolveHighlightingPass$Registrar"/>
|
||||||
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingPass$Registrar"/>
|
||||||
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.DebugInfoHighlightingPass$Registrar"/>
|
||||||
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.DuplicateJvmSignatureHighlightPass$Registrar"/>
|
||||||
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.ScriptExternalHighlightingPass$Registrar"/>
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.ScriptExternalHighlightingPass$Registrar"/>
|
||||||
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.refactoring.cutPaste.MoveDeclarationsPassFactory$Registrar"/>
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.refactoring.cutPaste.MoveDeclarationsPassFactory$Registrar"/>
|
||||||
|
|
||||||
|
|||||||
@@ -123,6 +123,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
<projectService serviceImplementation="org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectService"/>
|
<projectService serviceImplementation="org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectService"/>
|
||||||
|
|
||||||
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.KotlinBeforeResolveHighlightingPass$Registrar"/>
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.KotlinBeforeResolveHighlightingPass$Registrar"/>
|
||||||
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingPass$Registrar"/>
|
||||||
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.DebugInfoHighlightingPass$Registrar"/>
|
||||||
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.DuplicateJvmSignatureHighlightPass$Registrar"/>
|
||||||
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.ScriptExternalHighlightingPass$Registrar"/>
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.ScriptExternalHighlightingPass$Registrar"/>
|
||||||
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.refactoring.cutPaste.MoveDeclarationsPassFactory$Registrar"/>
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.refactoring.cutPaste.MoveDeclarationsPassFactory$Registrar"/>
|
||||||
|
|
||||||
|
|||||||
@@ -122,6 +122,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
<projectService serviceImplementation="org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectService"/>
|
<projectService serviceImplementation="org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectService"/>
|
||||||
|
|
||||||
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.KotlinBeforeResolveHighlightingPass$Registrar"/>
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.KotlinBeforeResolveHighlightingPass$Registrar"/>
|
||||||
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingPass$Registrar"/>
|
||||||
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.DebugInfoHighlightingPass$Registrar"/>
|
||||||
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.DuplicateJvmSignatureHighlightPass$Registrar"/>
|
||||||
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.ScriptExternalHighlightingPass$Registrar"/>
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.ScriptExternalHighlightingPass$Registrar"/>
|
||||||
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.refactoring.cutPaste.MoveDeclarationsPassFactory$Registrar"/>
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.refactoring.cutPaste.MoveDeclarationsPassFactory$Registrar"/>
|
||||||
|
|
||||||
|
|||||||
@@ -103,6 +103,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
<projectService serviceImplementation="org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectService"/>
|
<projectService serviceImplementation="org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectService"/>
|
||||||
|
|
||||||
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.KotlinBeforeResolveHighlightingPass$Registrar"/>
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.KotlinBeforeResolveHighlightingPass$Registrar"/>
|
||||||
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingPass$Registrar"/>
|
||||||
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.DebugInfoHighlightingPass$Registrar"/>
|
||||||
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.DuplicateJvmSignatureHighlightPass$Registrar"/>
|
||||||
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.ScriptExternalHighlightingPass$Registrar"/>
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.highlighter.ScriptExternalHighlightingPass$Registrar"/>
|
||||||
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.refactoring.cutPaste.MoveDeclarationsPassFactory$Registrar"/>
|
<highlightingPassFactory implementation="org.jetbrains.kotlin.idea.refactoring.cutPaste.MoveDeclarationsPassFactory$Registrar"/>
|
||||||
|
|
||||||
|
|||||||
@@ -491,13 +491,8 @@
|
|||||||
<liveTemplateMacro implementation="org.jetbrains.kotlin.idea.liveTemplates.macro.KotlinMainArgumentsMacro"/>
|
<liveTemplateMacro implementation="org.jetbrains.kotlin.idea.liveTemplates.macro.KotlinMainArgumentsMacro"/>
|
||||||
<liveTemplateOptionalProcessor implementation="org.jetbrains.kotlin.idea.liveTemplates.KotlinShortenFQNamesProcessor"/>
|
<liveTemplateOptionalProcessor implementation="org.jetbrains.kotlin.idea.liveTemplates.KotlinShortenFQNamesProcessor"/>
|
||||||
|
|
||||||
<annotator language="kotlin" implementationClass="org.jetbrains.kotlin.idea.highlighter.KotlinPsiCheckerAndHighlightingUpdater"/>
|
|
||||||
<highlightRangeExtension implementation="org.jetbrains.kotlin.idea.highlighter.KotlinPsiChecker"/>
|
|
||||||
<daemon.changeLocalityDetector implementation="org.jetbrains.kotlin.idea.highlighter.KotlinChangeLocalityDetector"/>
|
<daemon.changeLocalityDetector implementation="org.jetbrains.kotlin.idea.highlighter.KotlinChangeLocalityDetector"/>
|
||||||
|
|
||||||
<annotator language="kotlin" implementationClass="org.jetbrains.kotlin.idea.highlighter.DebugInfoAnnotator"/>
|
|
||||||
<annotator language="kotlin" implementationClass="org.jetbrains.kotlin.idea.highlighter.DuplicateJvmSignatureAnnotator"/>
|
|
||||||
|
|
||||||
<highlightVisitor implementation="org.jetbrains.kotlin.idea.highlighter.KotlinRainbowVisitor"/>
|
<highlightVisitor implementation="org.jetbrains.kotlin.idea.highlighter.KotlinRainbowVisitor"/>
|
||||||
|
|
||||||
<annotator language="JAVA" implementationClass="org.jetbrains.kotlin.idea.java.UnimplementedKotlinInterfaceMemberAnnotator"/>
|
<annotator language="JAVA" implementationClass="org.jetbrains.kotlin.idea.java.UnimplementedKotlinInterfaceMemberAnnotator"/>
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.highlighter
|
||||||
|
|
||||||
|
import com.intellij.codeHighlighting.*
|
||||||
|
import com.intellij.openapi.editor.Document
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import org.jetbrains.kotlin.idea.inspections.UnusedSymbolInspection
|
||||||
|
import org.jetbrains.kotlin.idea.isMainFunction
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||||
|
import org.jetbrains.kotlin.psi.KtParameter
|
||||||
|
|
||||||
|
open class KotlinHighlightingPass(file: KtFile, document: Document) : AbstractKotlinHighlightingPass(file, document) {
|
||||||
|
|
||||||
|
override fun shouldSuppressUnusedParameter(parameter: KtParameter): Boolean {
|
||||||
|
val grandParent = parameter.parent.parent as? KtNamedFunction ?: return false
|
||||||
|
if (!UnusedSymbolInspection.isEntryPoint(grandParent)) return false
|
||||||
|
return !grandParent.isMainFunction()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Factory : TextEditorHighlightingPassFactory {
|
||||||
|
override fun createHighlightingPass(file: PsiFile, editor: Editor): TextEditorHighlightingPass? {
|
||||||
|
if (file !is KtFile) return null
|
||||||
|
return KotlinHighlightingPass(file, editor.document)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Registrar : TextEditorHighlightingPassFactoryRegistrar {
|
||||||
|
override fun registerHighlightingPassFactory(registrar: TextEditorHighlightingPassRegistrar, project: Project) {
|
||||||
|
registrar.registerTextEditorHighlightingPass(
|
||||||
|
Factory(),
|
||||||
|
/* runAfterCompletionOf = */ null,
|
||||||
|
/* runAfterStartingOf = */ intArrayOf(Pass.UPDATE_ALL),
|
||||||
|
/* runIntentionsPassAfter = */false,
|
||||||
|
/* forcedPassId = */-1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
-30
@@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2015 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.idea.highlighter
|
|
||||||
|
|
||||||
import org.jetbrains.kotlin.idea.inspections.UnusedSymbolInspection
|
|
||||||
import org.jetbrains.kotlin.idea.isMainFunction
|
|
||||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
|
||||||
import org.jetbrains.kotlin.psi.KtParameter
|
|
||||||
|
|
||||||
class KotlinPsiCheckerAndHighlightingUpdater : KotlinPsiChecker() {
|
|
||||||
override fun shouldSuppressUnusedParameter(parameter: KtParameter): Boolean {
|
|
||||||
val grandParent = parameter.parent.parent as? KtNamedFunction ?: return false
|
|
||||||
if (!UnusedSymbolInspection.isEntryPoint(grandParent)) return false
|
|
||||||
return !grandParent.isMainFunction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,7 +17,7 @@ import org.jetbrains.kotlin.diagnostics.Errors
|
|||||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
|
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
|
||||||
import org.jetbrains.kotlin.idea.highlighter.KotlinPsiChecker
|
import org.jetbrains.kotlin.idea.highlighter.AbstractKotlinHighlightingPass
|
||||||
import org.jetbrains.kotlin.idea.quickfix.CleanupFix
|
import org.jetbrains.kotlin.idea.quickfix.CleanupFix
|
||||||
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
|
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
|
||||||
import org.jetbrains.kotlin.idea.quickfix.ReplaceObsoleteLabelSyntaxFix
|
import org.jetbrains.kotlin.idea.quickfix.ReplaceObsoleteLabelSyntaxFix
|
||||||
@@ -99,7 +99,7 @@ class KotlinCleanupInspection : LocalInspectionTool(), CleanupLocalInspectionToo
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun Diagnostic.toCleanupFixes(): Collection<CleanupFix> {
|
private fun Diagnostic.toCleanupFixes(): Collection<CleanupFix> {
|
||||||
return KotlinPsiChecker.createQuickFixes(this).filterIsInstance<CleanupFix>()
|
return AbstractKotlinHighlightingPass.createQuickFixes(this).filterIsInstance<CleanupFix>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Wrapper(val intention: IntentionAction, file: KtFile) : IntentionWrapper(intention, file) {
|
private class Wrapper(val intention: IntentionAction, file: KtFile) : IntentionWrapper(intention, file) {
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
annotation class A(val i: Int)
|
||||||
|
annotation class Z(val i: Int)
|
||||||
|
|
||||||
|
@Z("BAD") @Suppress("TYPE_MISMATCH")
|
||||||
|
fun some0() {}
|
||||||
|
|
||||||
|
@Z("BAD") <error descr="[REPEATED_ANNOTATION] This annotation is not repeatable">@Z("BAD")</error> @Suppress("TYPE_MISMATCH")
|
||||||
|
fun some01() {}
|
||||||
|
|
||||||
|
@Suppress("TYPE_MISMATCH") @Z("BAD")
|
||||||
|
fun some1() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("TYPE_MISMATCH") @Z("BAD") <error descr="[REPEATED_ANNOTATION] This annotation is not repeatable">@Z("BAD")</error>
|
||||||
|
fun some11() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@A("BAD") @Suppress("TYPE_MISMATCH")
|
||||||
|
fun some2() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("TYPE_MISMATCH") @A("BAD")
|
||||||
|
fun some3() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@A(<error descr="[TYPE_MISMATCH] Type mismatch: inferred type is String but Int was expected">"BAD"</error>) <error descr="[REPEATED_ANNOTATION] This annotation is not repeatable">@A(<error descr="[TYPE_MISMATCH] Type mismatch: inferred type is String but Int was expected">"BAD"</error>)</error>
|
||||||
|
fun some4() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Z(<error descr="[TYPE_MISMATCH] Type mismatch: inferred type is String but Int was expected">"BAD"</error>)
|
||||||
|
fun someN() {
|
||||||
|
}
|
||||||
+1
-1
@@ -25,7 +25,7 @@ import java.io.File;
|
|||||||
|
|
||||||
import static org.jetbrains.kotlin.resolve.lazy.ResolveSession.areDescriptorsCreatedForDeclaration;
|
import static org.jetbrains.kotlin.resolve.lazy.ResolveSession.areDescriptorsCreatedForDeclaration;
|
||||||
|
|
||||||
public abstract class AbstractPsiCheckerTest extends KotlinLightCodeInsightFixtureTestCase {
|
public abstract class AbstractKotlinHighlightingPassTest extends KotlinLightCodeInsightFixtureTestCase {
|
||||||
public void doTest(@NotNull VirtualFile file) throws Exception {
|
public void doTest(@NotNull VirtualFile file) throws Exception {
|
||||||
myFixture.configureFromExistingVirtualFile(file);
|
myFixture.configureFromExistingVirtualFile(file);
|
||||||
checkHighlighting(true, false, false);
|
checkHighlighting(true, false, false);
|
||||||
+1
-1
@@ -13,7 +13,7 @@ import org.junit.runner.RunWith
|
|||||||
|
|
||||||
@TestMetadata("idea/testData/checker/custom")
|
@TestMetadata("idea/testData/checker/custom")
|
||||||
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||||
class PsiCheckerCustomTest : AbstractPsiCheckerTest() {
|
class KotlinHighlightingPassCustomTest : AbstractKotlinHighlightingPassTest() {
|
||||||
|
|
||||||
@TestMetadata("noUnusedParameterWhenCustom.kt")
|
@TestMetadata("noUnusedParameterWhenCustom.kt")
|
||||||
fun testNoUnusedParameterWhenCustom() {
|
fun testNoUnusedParameterWhenCustom() {
|
||||||
+1
-1
@@ -12,7 +12,7 @@ import org.junit.runner.RunWith
|
|||||||
|
|
||||||
@TestMetadata("idea/testData/checker/sealed")
|
@TestMetadata("idea/testData/checker/sealed")
|
||||||
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||||
class PsiCheckerSealedTest : AbstractPsiCheckerTest() {
|
class KotlinHighlightingPassSealedTest : AbstractKotlinHighlightingPassTest() {
|
||||||
|
|
||||||
fun testOutsideOfPackageInheritors() {
|
fun testOutsideOfPackageInheritors() {
|
||||||
doTest(
|
doTest(
|
||||||
+17
-12
@@ -18,11 +18,11 @@ import java.util.regex.Pattern;
|
|||||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
|
public class KotlinHighlightingPassTestGenerated extends AbstractKotlinHighlightingPassTest {
|
||||||
@TestMetadata("idea/testData/checker")
|
@TestMetadata("idea/testData/checker")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class Checker extends AbstractPsiCheckerTest {
|
public static class Checker extends AbstractKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
@@ -41,6 +41,11 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
|
|||||||
runTest("idea/testData/checker/AnnotationOnFile.kt");
|
runTest("idea/testData/checker/AnnotationOnFile.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("AnnotationSupressing.kt")
|
||||||
|
public void testAnnotationSupressing() throws Exception {
|
||||||
|
runTest("idea/testData/checker/AnnotationSupressing.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("AnonymousInitializers.kt")
|
@TestMetadata("AnonymousInitializers.kt")
|
||||||
public void testAnonymousInitializers() throws Exception {
|
public void testAnonymousInitializers() throws Exception {
|
||||||
runTest("idea/testData/checker/AnonymousInitializers.kt");
|
runTest("idea/testData/checker/AnonymousInitializers.kt");
|
||||||
@@ -365,7 +370,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
|
|||||||
@TestMetadata("idea/testData/checker/regression")
|
@TestMetadata("idea/testData/checker/regression")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class Regression extends AbstractPsiCheckerTest {
|
public static class Regression extends AbstractKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
@@ -608,7 +613,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
|
|||||||
@TestMetadata("idea/testData/checker/recovery")
|
@TestMetadata("idea/testData/checker/recovery")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class Recovery extends AbstractPsiCheckerTest {
|
public static class Recovery extends AbstractKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
@@ -636,7 +641,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
|
|||||||
@TestMetadata("idea/testData/checker/rendering")
|
@TestMetadata("idea/testData/checker/rendering")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class Rendering extends AbstractPsiCheckerTest {
|
public static class Rendering extends AbstractKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
@@ -654,7 +659,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
|
|||||||
@TestMetadata("idea/testData/checker/scripts")
|
@TestMetadata("idea/testData/checker/scripts")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class Scripts extends AbstractPsiCheckerTest {
|
public static class Scripts extends AbstractKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
@@ -687,7 +692,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
|
|||||||
@TestMetadata("idea/testData/checker/duplicateJvmSignature")
|
@TestMetadata("idea/testData/checker/duplicateJvmSignature")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class DuplicateJvmSignature extends AbstractPsiCheckerTest {
|
public static class DuplicateJvmSignature extends AbstractKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
@@ -699,7 +704,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
|
|||||||
@TestMetadata("idea/testData/checker/duplicateJvmSignature/fields")
|
@TestMetadata("idea/testData/checker/duplicateJvmSignature/fields")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class Fields extends AbstractPsiCheckerTest {
|
public static class Fields extends AbstractKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
@@ -717,7 +722,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
|
|||||||
@TestMetadata("idea/testData/checker/duplicateJvmSignature/functionAndProperty")
|
@TestMetadata("idea/testData/checker/duplicateJvmSignature/functionAndProperty")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class FunctionAndProperty extends AbstractPsiCheckerTest {
|
public static class FunctionAndProperty extends AbstractKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
@@ -780,7 +785,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
|
|||||||
@TestMetadata("idea/testData/checker/duplicateJvmSignature/traitImpl")
|
@TestMetadata("idea/testData/checker/duplicateJvmSignature/traitImpl")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class TraitImpl extends AbstractPsiCheckerTest {
|
public static class TraitImpl extends AbstractKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
@@ -799,7 +804,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
|
|||||||
@TestMetadata("idea/testData/checker/infos")
|
@TestMetadata("idea/testData/checker/infos")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class Infos extends AbstractPsiCheckerTest {
|
public static class Infos extends AbstractKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTestWithInfos, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTestWithInfos, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
@@ -892,7 +897,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
|
|||||||
@TestMetadata("idea/testData/checker/diagnosticsMessage")
|
@TestMetadata("idea/testData/checker/diagnosticsMessage")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class DiagnosticsMessage extends AbstractPsiCheckerTest {
|
public static class DiagnosticsMessage extends AbstractKotlinHighlightingPassTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
}
|
}
|
||||||
+2
-2
@@ -5,12 +5,12 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.debugger.evaluate
|
package org.jetbrains.kotlin.idea.debugger.evaluate
|
||||||
|
|
||||||
import org.jetbrains.kotlin.checkers.AbstractPsiCheckerTest
|
import org.jetbrains.kotlin.checkers.AbstractKotlinHighlightingPassTest
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtCodeFragment
|
import org.jetbrains.kotlin.psi.KtCodeFragment
|
||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
|
|
||||||
abstract class AbstractCodeFragmentAutoImportTest : AbstractPsiCheckerTest() {
|
abstract class AbstractCodeFragmentAutoImportTest : AbstractKotlinHighlightingPassTest() {
|
||||||
override fun doTest(filePath: String) {
|
override fun doTest(filePath: String) {
|
||||||
myFixture.configureByCodeFragment(filePath)
|
myFixture.configureByCodeFragment(filePath)
|
||||||
myFixture.doHighlighting()
|
myFixture.doHighlighting()
|
||||||
|
|||||||
+2
-2
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.idea.debugger.evaluate
|
|||||||
|
|
||||||
import com.intellij.codeInspection.InspectionProfileEntry
|
import com.intellij.codeInspection.InspectionProfileEntry
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
import org.jetbrains.kotlin.checkers.AbstractPsiCheckerTest
|
import org.jetbrains.kotlin.checkers.AbstractKotlinHighlightingPassTest
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
||||||
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||||
@@ -16,7 +16,7 @@ import org.jetbrains.kotlin.psi.KtFile
|
|||||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
abstract class AbstractCodeFragmentHighlightingTest : AbstractPsiCheckerTest() {
|
abstract class AbstractCodeFragmentHighlightingTest : AbstractKotlinHighlightingPassTest() {
|
||||||
override fun doTest(filePath: String) {
|
override fun doTest(filePath: String) {
|
||||||
myFixture.configureByCodeFragment(filePath)
|
myFixture.configureByCodeFragment(filePath)
|
||||||
checkHighlighting(filePath)
|
checkHighlighting(filePath)
|
||||||
|
|||||||
@@ -34,10 +34,12 @@ abstract class AbstractDslHighlighterTest : KotlinLightCodeInsightFixtureTestCas
|
|||||||
val styleIdByCall = extension.highlightCall(element, call)?.externalName
|
val styleIdByCall = extension.highlightCall(element, call)?.externalName
|
||||||
if (styleIdByCall != null && styleIdByCall == styleIdByComment) {
|
if (styleIdByCall != null && styleIdByCall == styleIdByComment) {
|
||||||
val annotationHolder = AnnotationHolderImpl(AnnotationSession(psiFile))
|
val annotationHolder = AnnotationHolderImpl(AnnotationSession(psiFile))
|
||||||
val checkers = KotlinPsiChecker.getAfterAnalysisVisitor(annotationHolder, bindingContext)
|
annotationHolder.runAnnotatorWithContext(file) { _, _ ->
|
||||||
checkers.forEach { call.call.callElement.accept(it) }
|
val checkers = AbstractKotlinHighlightingPass.getAfterAnalysisVisitor(annotationHolder, bindingContext)
|
||||||
|
checkers.forEach { call.call.callElement.accept(it) }
|
||||||
|
}
|
||||||
assertTrue(
|
assertTrue(
|
||||||
"KotlinPsiChecker did not contribute an Annotation containing the correct text attribute key at line ${lineNumber + 1}",
|
"KotlinHighlightingPass did not contribute an Annotation containing the correct text attribute key at line ${lineNumber + 1}",
|
||||||
annotationHolder.any {
|
annotationHolder.any {
|
||||||
it.textAttributes.externalName == styleIdByComment
|
it.textAttributes.externalName == styleIdByComment
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ package org.jetbrains.kotlin.idea.inspections
|
|||||||
import com.google.common.collect.Lists
|
import com.google.common.collect.Lists
|
||||||
import com.intellij.codeHighlighting.HighlightDisplayLevel
|
import com.intellij.codeHighlighting.HighlightDisplayLevel
|
||||||
import com.intellij.codeHighlighting.Pass
|
import com.intellij.codeHighlighting.Pass
|
||||||
|
import com.intellij.codeHighlighting.TextEditorHighlightingPass
|
||||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType
|
import com.intellij.codeInsight.daemon.impl.HighlightInfoType
|
||||||
|
import com.intellij.codeInsight.daemon.impl.TextEditorHighlightingPassRegistrarEx
|
||||||
import com.intellij.codeInsight.intention.EmptyIntentionAction
|
import com.intellij.codeInsight.intention.EmptyIntentionAction
|
||||||
import com.intellij.codeInspection.ProblemHighlightType
|
import com.intellij.codeInspection.ProblemHighlightType
|
||||||
import com.intellij.openapi.util.SystemInfo
|
import com.intellij.openapi.util.SystemInfo
|
||||||
@@ -19,6 +21,7 @@ import junit.framework.ComparisonFailure
|
|||||||
import junit.framework.TestCase
|
import junit.framework.TestCase
|
||||||
import org.jdom.Element
|
import org.jdom.Element
|
||||||
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
|
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
|
||||||
|
import org.jetbrains.kotlin.idea.highlighter.AbstractHighlightingPassBase
|
||||||
import org.jetbrains.kotlin.idea.test.DirectiveBasedActionUtils
|
import org.jetbrains.kotlin.idea.test.DirectiveBasedActionUtils
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||||
import org.jetbrains.kotlin.idea.test.withCustomCompilerOptions
|
import org.jetbrains.kotlin.idea.test.withCustomCompilerOptions
|
||||||
@@ -29,6 +32,7 @@ import org.jetbrains.kotlin.test.KotlinTestUtils
|
|||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
abstract class AbstractLocalInspectionTest : KotlinLightCodeInsightFixtureTestCase() {
|
abstract class AbstractLocalInspectionTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||||
private val inspectionFileName: String
|
private val inspectionFileName: String
|
||||||
get() = ".inspection"
|
get() = ".inspection"
|
||||||
@@ -145,16 +149,23 @@ abstract class AbstractLocalInspectionTest : KotlinLightCodeInsightFixtureTestCa
|
|||||||
state.tool.tool.readSettings(inspectionSettings)
|
state.tool.tool.readSettings(inspectionSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val passIdsToIgnore = mutableListOf(
|
||||||
|
Pass.LINE_MARKERS,
|
||||||
|
Pass.EXTERNAL_TOOLS,
|
||||||
|
Pass.POPUP_HINTS,
|
||||||
|
Pass.UPDATE_ALL,
|
||||||
|
Pass.UPDATE_FOLDING,
|
||||||
|
Pass.WOLF
|
||||||
|
)
|
||||||
|
val passRegistrar = TextEditorHighlightingPassRegistrarEx.getInstanceEx(myFixture.project)
|
||||||
|
// to exclude AbstractHighlightingPassBase instances based on their ids
|
||||||
|
passRegistrar.instantiatePasses(
|
||||||
|
file, editor, passIdsToIgnore.toIntArray()
|
||||||
|
).filterIsInstance<AbstractHighlightingPassBase>().map(TextEditorHighlightingPass::getId).forEach(passIdsToIgnore::add)
|
||||||
|
|
||||||
val caretOffset = myFixture.caretOffset
|
val caretOffset = myFixture.caretOffset
|
||||||
val highlightInfos = CodeInsightTestFixtureImpl.instantiateAndRun(
|
val highlightInfos = CodeInsightTestFixtureImpl.instantiateAndRun(
|
||||||
file, editor, intArrayOf(
|
file, editor, passIdsToIgnore.toIntArray(), (file as? KtFile)?.isScript() == true
|
||||||
Pass.LINE_MARKERS,
|
|
||||||
Pass.EXTERNAL_TOOLS,
|
|
||||||
Pass.POPUP_HINTS,
|
|
||||||
Pass.UPDATE_ALL,
|
|
||||||
Pass.UPDATE_FOLDING,
|
|
||||||
Pass.WOLF
|
|
||||||
), (file as? KtFile)?.isScript() == true
|
|
||||||
).filter { it.description != null && caretOffset in it.startOffset..it.endOffset }
|
).filter { it.description != null && caretOffset in it.startOffset..it.endOffset }
|
||||||
|
|
||||||
Assert.assertTrue(
|
Assert.assertTrue(
|
||||||
|
|||||||
+2
-4
@@ -5,11 +5,9 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.pacelize.ide.test
|
package org.jetbrains.kotlin.pacelize.ide.test
|
||||||
|
|
||||||
import org.jetbrains.kotlin.checkers.AbstractPsiCheckerTest
|
import org.jetbrains.kotlin.checkers.AbstractKotlinHighlightingPassTest
|
||||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
|
||||||
|
|
||||||
abstract class AbstractParcelizeCheckerTest : AbstractPsiCheckerTest() {
|
abstract class AbstractParcelizeCheckerTest : AbstractKotlinHighlightingPassTest() {
|
||||||
override fun setUp() {
|
override fun setUp() {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
addParcelizeLibraries(module)
|
addParcelizeLibraries(module)
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ org.jetbrains.kotlin.idea.caches.resolve.IdeLightClassTestGenerated.Facades.test
|
|||||||
org.jetbrains.kotlin.idea.caches.resolve.IdeLightClassTestGenerated.CompilationErrors.testActualTypeAliasCustomJvmPackageName, Invalid behavior of old lightclasses in common tests,,
|
org.jetbrains.kotlin.idea.caches.resolve.IdeLightClassTestGenerated.CompilationErrors.testActualTypeAliasCustomJvmPackageName, Invalid behavior of old lightclasses in common tests,,
|
||||||
org.jetbrains.kotlin.idea.caches.resolve.IdeLightClassTestGenerated.CompilationErrors.testJvmPackageName, Invalid behavior of old lightclasses in common tests,,
|
org.jetbrains.kotlin.idea.caches.resolve.IdeLightClassTestGenerated.CompilationErrors.testJvmPackageName, Invalid behavior of old lightclasses in common tests,,
|
||||||
org.jetbrains.uast.test.kotlin.SimpleKotlinRenderLogTest.testReceiverFun, Analysing of facade annotation with receiver site is broken (connected with KT-40403),,
|
org.jetbrains.uast.test.kotlin.SimpleKotlinRenderLogTest.testReceiverFun, Analysing of facade annotation with receiver site is broken (connected with KT-40403),,
|
||||||
org.jetbrains.kotlin.checkers.FirPsiCheckerTestGenerated.Regression.testJet53,,, FLAKY
|
org.jetbrains.kotlin.checkers.FirKotlinHighlightingPassTestGenerated.Regression.testJet53,,, FLAKY
|
||||||
org.jetbrains.kotlin.idea.caches.resolve.MultiModuleHighlightingTest.testLanguageVersionsViaFacets,,, FLAKY
|
org.jetbrains.kotlin.idea.caches.resolve.MultiModuleHighlightingTest.testLanguageVersionsViaFacets,,, FLAKY
|
||||||
org.jetbrains.kotlin.jps.build.IncrementalJvmJpsTestGenerated.Jvm.testCircular, Temporary muted due to problems with IC and JVM IR backend,,
|
org.jetbrains.kotlin.jps.build.IncrementalJvmJpsTestGenerated.Jvm.testCircular, Temporary muted due to problems with IC and JVM IR backend,,
|
||||||
org.jetbrains.kotlin.jps.build.IncrementalJvmJpsTestGenerated.Jvm.testCircularDependencyClasses, Temporary muted due to problems with IC and JVM IR backend,,
|
org.jetbrains.kotlin.jps.build.IncrementalJvmJpsTestGenerated.Jvm.testCircularDependencyClasses, Temporary muted due to problems with IC and JVM IR backend,,
|
||||||
|
|||||||
|
Reference in New Issue
Block a user