Kotlin highlight passes are reworked

#KT-37702 Fixed
This commit is contained in:
Vladimir Dolzhenko
2020-11-19 13:38:38 +01:00
committed by Space
parent 558338f997
commit 913c298be8
46 changed files with 1206 additions and 536 deletions
@@ -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}>
@@ -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
}
}
}
@@ -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) }
}
}
}
@@ -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
} }
} }
@@ -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
}
@@ -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
)
}
}
}
@@ -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)
}
}
@@ -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)
}
}
@@ -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)
}
}
@@ -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
} }
} }
@@ -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) {
@@ -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
@@ -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,
@@ -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
} }
@@ -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
@@ -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);
} }
@@ -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
}
}
}
@@ -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) {
@@ -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)
} }
} }
@@ -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}")
@@ -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
)
}
}
}
@@ -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
View File
@@ -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() {
}
@@ -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);
@@ -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() {
@@ -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(
@@ -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);
} }
@@ -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()
@@ -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(
@@ -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)
+1 -1
View File
@@ -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,,
1 Test key Issue State (optional: MUTE or FAIL) Status (optional: FLAKY)
79 org.jetbrains.kotlin.idea.caches.resolve.IdeLightClassTestGenerated.CompilationErrors.testActualTypeAliasCustomJvmPackageName Invalid behavior of old lightclasses in common tests
80 org.jetbrains.kotlin.idea.caches.resolve.IdeLightClassTestGenerated.CompilationErrors.testJvmPackageName Invalid behavior of old lightclasses in common tests
81 org.jetbrains.uast.test.kotlin.SimpleKotlinRenderLogTest.testReceiverFun Analysing of facade annotation with receiver site is broken (connected with KT-40403)
82 org.jetbrains.kotlin.checkers.FirPsiCheckerTestGenerated.Regression.testJet53 org.jetbrains.kotlin.checkers.FirKotlinHighlightingPassTestGenerated.Regression.testJet53 FLAKY
83 org.jetbrains.kotlin.idea.caches.resolve.MultiModuleHighlightingTest.testLanguageVersionsViaFacets FLAKY
84 org.jetbrains.kotlin.jps.build.IncrementalJvmJpsTestGenerated.Jvm.testCircular Temporary muted due to problems with IC and JVM IR backend
85 org.jetbrains.kotlin.jps.build.IncrementalJvmJpsTestGenerated.Jvm.testCircularDependencyClasses Temporary muted due to problems with IC and JVM IR backend