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")
}
testClass<AbstractPsiCheckerTest> {
testClass<AbstractKotlinHighlightingPassTest> {
model("checker", recursive = false)
model("checker/regression")
model("checker/recovery")
@@ -1092,7 +1092,7 @@ fun main(args: Array<String>) {
model("resolve/references", pattern = KT_WITHOUT_DOTS_IN_NAME)
}
testClass<AbstractFirPsiCheckerTest> {
testClass<AbstractFirKotlinHighlightingPassTest> {
model("checker", recursive = false)
model("checker/regression")
model("checker/recovery")
@@ -239,7 +239,7 @@ fun main(args: Array<String>) {
model("resolve/partialBodyResolve")
}
testClass<AbstractPsiCheckerTest> {
testClass<AbstractKotlinHighlightingPassTest> {
model("checker", recursive = false)
model("checker/regression")
model("checker/recovery")
@@ -235,7 +235,7 @@ fun main(args: Array<String>) {
model("resolve/partialBodyResolve")
}
testClass<AbstractPsiCheckerTest> {
testClass<AbstractKotlinHighlightingPassTest> {
model("checker", recursive = false)
model("checker/regression")
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.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>
sdk.0=<sdk {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.IntentionAction
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.HighlightSeverity
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.openapi.util.TextRange
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.rendering.DefaultErrorMessages
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(
val ranges: List<TextRange>,
@@ -28,32 +29,59 @@ class AnnotationPresentationInfo(
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 (diagnostic in diagnostics) {
val fixes = fixesMap[diagnostic]
create(diagnostic, range, holder) { annotation ->
fixes.forEach {
when (it) {
is KotlinUniversalQuickFix -> annotation.newFix(it).universal().registerFix()
is IntentionAction -> annotation.newFix(it).registerFix()
}
annotationBuilderByDiagnostic?.put(diagnostic, annotation)
if (fixesMap != null) {
applyFixes(fixesMap, diagnostic, annotation)
}
if (diagnostic.severity == Severity.WARNING) {
annotation.problemGroup(KotlinSuppressableWarningProblemGroup(diagnostic.factory))
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()
}
if (calculatingInProgress && annotationByTextRange?.containsKey(range) == false) {
annotationByTextRange[range] = annotation
annotation.registerFix(CalculatingIntentionAction(), range)
}
}
}
}
}
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) {
Severity.ERROR -> HighlightSeverity.ERROR
Severity.WARNING -> if (highlightType == ProblemHighlightType.WEAK_WARNING) {
@@ -62,18 +90,26 @@ class AnnotationPresentationInfo(
Severity.INFO -> HighlightSeverity.WEAK_WARNING
}
holder.newAnnotation(severity, nonDefaultMessage ?: getDefaultMessage(diagnostic))
val message = nonDefaultMessage ?: getDefaultMessage(diagnostic)
holder.newAnnotation(severity, message)
.range(range)
.tooltip(getMessage(diagnostic))
.also { builder -> highlightType?.let { builder.highlightType(it) } }
.also { builder -> textAttributes?.let { builder.textAttributes(it) } }
.also { consumer(it) }
.also {
if (diagnostic.severity == Severity.WARNING) {
it.problemGroup(KotlinSuppressableWarningProblemGroup(diagnostic.factory))
}
}
.create()
@Suppress("UNCHECKED_CAST")
(holder as? List<Annotation>)?.last()?.let(consumer::invoke)
}
private fun getMessage(diagnostic: Diagnostic): String {
var message = IdeErrorMessages.render(diagnostic)
if (ApplicationManager.getApplication().isInternal || ApplicationManager.getApplication().isUnitTestMode) {
if (isApplicationInternalMode() || isUnitTestMode()) {
val factoryName = diagnostic.factory.name
message = if (message.startsWith("<html>")) {
"<html>[$factoryName] ${message.substring("<html>".length)}"
@@ -89,10 +125,11 @@ class AnnotationPresentationInfo(
private fun getDefaultMessage(diagnostic: Diagnostic): String {
val message = DefaultErrorMessages.render(diagnostic)
if (ApplicationManager.getApplication().isInternal || ApplicationManager.getApplication().isUnitTestMode) {
return "[${diagnostic.factory.name}] $message"
return if (isApplicationInternalMode() || isUnitTestMode()) {
"[${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 {
fun shouldHighlight(psiElement: PsiElement): Boolean {
val ktFile = psiElement.containingFile as? KtFile ?: return false
return shouldHighlightFile(ktFile)
}
fun shouldHighlightFile(ktFile: KtFile): Boolean {
if (ktFile is KtCodeFragment && ktFile.context != null) {
return true
}
@@ -52,8 +55,12 @@ object KotlinHighlightingUtil {
return ProjectRootsUtil.isInProjectOrLibraryContent(ktFile) && ktFile.getModuleInfo() !is NotUnderContentRootModuleInfo
}
fun shouldHighlightErrors(psiElement: PsiElement): Boolean {
val ktFile = psiElement.containingFile as? KtFile ?: return false
fun shouldHighlightErrors(psiElement: PsiElement): Boolean =
(psiElement.containingFile as? KtFile)?.let {
shouldHighlightErrors(it)
} ?: false
fun shouldHighlightErrors(ktFile: KtFile): Boolean {
if (ktFile.isCompiled) {
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) {
createInfoAnnotation(
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
highlightVariable(expression, target)
@@ -91,14 +91,15 @@ internal class VariablesHighlightingVisitor(holder: AnnotationHolder, bindingCon
"0.smart.cast.to.1",
receiverName,
DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type)
)
).textAttributes = SMART_CAST_RECEIVER
),
SMART_CAST_RECEIVER
)
}
}
val nullSmartCast = bindingContext.get(SMARTCAST_NULL, expression) == true
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)
@@ -107,8 +108,9 @@ internal class VariablesHighlightingVisitor(holder: AnnotationHolder, bindingCon
if (defaultType != null) {
createInfoAnnotation(
getSmartCastTarget(expression),
KotlinIdeaAnalysisBundle.message("smart.cast.to.0", DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(defaultType))
).textAttributes = SMART_CAST_VALUE
KotlinIdeaAnalysisBundle.message("smart.cast.to.0", DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(defaultType)),
SMART_CAST_VALUE
)
} else if (smartCast is MultipleSmartCasts) {
for ((call, type) in smartCast.map) {
createInfoAnnotation(
@@ -117,8 +119,9 @@ internal class VariablesHighlightingVisitor(holder: AnnotationHolder, bindingCon
"smart.cast.to.0.for.1.call",
DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type),
call.toString()
)
).textAttributes = SMART_CAST_VALUE
),
SMART_CAST_VALUE
)
}
}
}
@@ -154,7 +157,7 @@ internal class VariablesHighlightingVisitor(holder: AnnotationHolder, bindingCon
val parent = elementToHighlight.parent
if (!(parent is PsiNameIdentifierOwner && parent.nameIdentifier == elementToHighlight)) {
createInfoAnnotation(elementToHighlight, WRAPPED_INTO_REF, msg)
createInfoAnnotation(elementToHighlight, msg, WRAPPED_INTO_REF)
return
}
}
@@ -9,7 +9,8 @@ import com.intellij.codeInsight.completion.PrefixMatcher
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.openapi.progress.ProgressManager
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.util.CallTypeAndReceiver
import org.jetbrains.kotlin.psi.KtCallExpression
@@ -28,7 +29,7 @@ class FromUnresolvedNamesCompletion(
scope.forEachDescendantOfType<KtNameReferenceExpression> { refExpr ->
ProgressManager.checkCanceled()
if (KotlinPsiChecker.wasUnresolved(refExpr)) {
if (AbstractKotlinHighlightingPass.wasUnresolved(refExpr)) {
val callTypeAndReceiver = CallTypeAndReceiver.detect(refExpr)
if (callTypeAndReceiver.receiver != null) return@forEachDescendantOfType
if (sampleDescriptor != null) {
@@ -10,6 +10,7 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.KotlinIdeaAnalysisBundle
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.ImplicitReceiverSmartcastKind
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingColors
import org.jetbrains.kotlin.psi.*
internal class ExpressionsSmartcastHighlightingVisitor(
@@ -30,8 +31,9 @@ internal class ExpressionsSmartcastHighlightingVisitor(
"0.smart.cast.to.1",
receiverName,
type.asStringForDebugging()
)
).textAttributes = org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingColors.SMART_CAST_RECEIVER
),
KotlinHighlightingColors.SMART_CAST_RECEIVER
)
}
}
expression.getSmartCasts()?.forEach { type ->
@@ -40,8 +42,9 @@ internal class ExpressionsSmartcastHighlightingVisitor(
KotlinIdeaAnalysisBundle.message(
"smart.cast.to.0",
type.asStringForDebugging()
)
).textAttributes = org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingColors.SMART_CAST_VALUE
),
KotlinHighlightingColors.SMART_CAST_VALUE
)
}
//todo smartcast to null
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.idea.fir.highlighter.visitors
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.highlighter.HighlightingVisitor
@@ -14,6 +16,12 @@ abstract class FirAfterResolveHighlightingVisitor(
protected val holder: AnnotationHolder
) : 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 {
fun createListOfVisitors(
analysisSession: KtAnalysisSession,
@@ -34,8 +34,8 @@ internal class VariableReferenceHighlightingVisitor(
if (expression.isAutoCreatedItParameter()) {
createInfoAnnotation(
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
}
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.uitls.IgnoreTests
import java.io.File
abstract class AbstractFirPsiCheckerTest : AbstractPsiCheckerTest() {
abstract class AbstractFirKotlinHighlightingPassTest : AbstractKotlinHighlightingPassTest() {
override val captureExceptions: Boolean = false
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 */
@SuppressWarnings("all")
@RunWith(JUnit3RunnerWithInners.class)
public class FirPsiCheckerTestGenerated extends AbstractFirPsiCheckerTest {
public class FirKotlinHighlightingPassTestGenerated extends AbstractFirKotlinHighlightingPassTest {
@TestMetadata("idea/testData/checker")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Checker extends AbstractFirPsiCheckerTest {
public static class Checker extends AbstractFirKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@@ -41,6 +41,11 @@ public class FirPsiCheckerTestGenerated extends AbstractFirPsiCheckerTest {
runTest("idea/testData/checker/AnnotationOnFile.kt");
}
@TestMetadata("AnnotationSupressing.kt")
public void testAnnotationSupressing() throws Exception {
runTest("idea/testData/checker/AnnotationSupressing.kt");
}
@TestMetadata("AnonymousInitializers.kt")
public void testAnonymousInitializers() throws Exception {
runTest("idea/testData/checker/AnonymousInitializers.kt");
@@ -365,7 +370,7 @@ public class FirPsiCheckerTestGenerated extends AbstractFirPsiCheckerTest {
@TestMetadata("idea/testData/checker/regression")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Regression extends AbstractFirPsiCheckerTest {
public static class Regression extends AbstractFirKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@@ -608,7 +613,7 @@ public class FirPsiCheckerTestGenerated extends AbstractFirPsiCheckerTest {
@TestMetadata("idea/testData/checker/recovery")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Recovery extends AbstractFirPsiCheckerTest {
public static class Recovery extends AbstractFirKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@@ -636,7 +641,7 @@ public class FirPsiCheckerTestGenerated extends AbstractFirPsiCheckerTest {
@TestMetadata("idea/testData/checker/rendering")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Rendering extends AbstractFirPsiCheckerTest {
public static class Rendering extends AbstractFirKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@@ -654,7 +659,7 @@ public class FirPsiCheckerTestGenerated extends AbstractFirPsiCheckerTest {
@TestMetadata("idea/testData/checker/infos")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Infos extends AbstractFirPsiCheckerTest {
public static class Infos extends AbstractFirKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@@ -747,7 +752,7 @@ public class FirPsiCheckerTestGenerated extends AbstractFirPsiCheckerTest {
@TestMetadata("idea/testData/checker/diagnosticsMessage")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class DiagnosticsMessage extends AbstractFirPsiCheckerTest {
public static class DiagnosticsMessage extends AbstractFirKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
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
}
createInfoAnnotation(element, null).textAttributes = attributes
createInfoAnnotation(element, textAttributes = attributes)
}
private fun willApplyRainbowHighlight(element: KDocLink): Boolean {
@@ -53,27 +53,29 @@ internal class BeforeResolveHighlightingVisitor(holder: AnnotationHolder) : High
if (ApplicationManager.getApplication().isUnitTestMode) return
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
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
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) {
val argumentName = argument.getArgumentName() ?: return
val eq = argument.equalsToken ?: return
createInfoAnnotation(TextRange(argumentName.startOffset, eq.endOffset), null).textAttributes =
if (argument.parent.parent is KtAnnotationEntry)
createInfoAnnotation(
TextRange(argumentName.startOffset, eq.endOffset),
textAttributes = if (argument.parent.parent is KtAnnotationEntry)
KotlinHighlightingColors.ANNOTATION_ATTRIBUTE_NAME_ATTRIBUTES
else
KotlinHighlightingColors.NAMED_ARGUMENT
)
}
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.AnnotationHolder
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
@@ -17,29 +18,34 @@ abstract class HighlightingVisitor protected constructor(
private val holder: AnnotationHolder
) : KtVisitorVoid() {
protected fun createInfoAnnotation(element: PsiElement, textAttributes: TextAttributesKey, message: String? = null) {
createInfoAnnotation(element.textRange, textAttributes, message)
protected fun createInfoAnnotation(element: PsiElement, message: String? = null, textAttributes: TextAttributesKey) {
createInfoAnnotation(element.textRange, message, textAttributes)
}
protected fun createInfoAnnotation(range: TextRange, textAttributes: TextAttributesKey, message: String? = null) {
createInfoAnnotation(range, message).textAttributes = textAttributes
}
protected fun createInfoAnnotation(element: PsiElement, message: String? = null): Annotation =
protected fun createInfoAnnotation(element: PsiElement, message: String? = null) =
createInfoAnnotation(element.textRange, message)
protected fun createInfoAnnotation(textRange: TextRange, message: String? = null): Annotation =
holder.createInfoAnnotation(textRange, message)
protected open fun createInfoAnnotation(textRange: TextRange, message: String? = null, textAttributes: TextAttributesKey? = null) {
(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) {
if (NameHighlighter.namesHighlightingEnabled && !element.textRange.isEmpty) {
createInfoAnnotation(element, attributesKey, message)
createInfoAnnotation(element, message, attributesKey)
}
}
protected fun highlightName(textRange: TextRange, attributesKey: TextAttributesKey, message: String? = null) {
if (NameHighlighter.namesHighlightingEnabled) {
createInfoAnnotation(textRange, attributesKey, message)
createInfoAnnotation(textRange, message, attributesKey)
}
}
@@ -6,51 +6,29 @@
package org.jetbrains.kotlin.idea.highlighter
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.AnnotationSession
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor
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.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiRecursiveElementVisitor
import org.jetbrains.kotlin.psi.KtFile
class KotlinBeforeResolveHighlightingPass(
private val file: KtFile,
document: Document
) : TextEditorHighlightingPass(file.project, document), DumbAware {
class KotlinBeforeResolveHighlightingPass(file: KtFile, document: Document) : AbstractHighlightingPassBase(file, document) {
@Volatile
private var annotationHolder: AnnotationHolderImpl? = null
override fun runAnnotatorWithContext(element: PsiElement, holder: AnnotationHolder) {
val visitor = BeforeResolveHighlightingVisitor(holder)
val extensions = EP_NAME.extensionList.map { it.createVisitor(holder) }
override fun doCollectInformation(progress: ProgressIndicator) {
val annotationHolder = AnnotationHolderImpl(AnnotationSession(file))
val visitor = BeforeResolveHighlightingVisitor(annotationHolder)
val extensions = EP_NAME.extensionList.map { it.createVisitor(annotationHolder) }
file.accept(object : PsiRecursiveElementVisitor() {
element.accept(object : PsiRecursiveElementVisitor() {
override fun visitElement(element: PsiElement) {
super.visitElement(element)
element.accept(visitor)
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 {
@@ -64,10 +42,10 @@ class KotlinBeforeResolveHighlightingPass(
override fun registerHighlightingPassFactory(registrar: TextEditorHighlightingPassRegistrar, project: Project) {
registrar.registerTextEditorHighlightingPass(
Factory(),
TextEditorHighlightingPassRegistrar.Anchor.BEFORE,
Pass.UPDATE_FOLDING,
false,
false
/* anchor = */ TextEditorHighlightingPassRegistrar.Anchor.BEFORE,
/* anchorPassId = */ Pass.UPDATE_FOLDING,
/* needAdditionalIntentionsPass = */ false,
/* inPostHighlightingPass = */ false
)
}
}
@@ -58,6 +58,8 @@ inline fun invokeLater(expired: Condition<*>, crossinline action: () -> Unit) =
inline fun isUnitTestMode(): Boolean = ApplicationManager.getApplication().isUnitTestMode
inline fun isApplicationInternalMode(): Boolean = ApplicationManager.getApplication().isInternal
inline fun <reified T : Any> ComponentManager.getServiceSafe(): T =
this.getService(T::class.java) ?: error("Unable to locate service ${T::class.java.name}")
@@ -5,15 +5,17 @@
package org.jetbrains.kotlin.idea.perf
import com.intellij.codeHighlighting.*
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.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.testFramework.RunAll
import com.intellij.util.ThrowableRunnable
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
import org.jetbrains.kotlin.idea.highlighter.KotlinPsiChecker
import org.jetbrains.kotlin.idea.highlighter.KotlinPsiCheckerAndHighlightingUpdater
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingPass
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.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.isAKotlinScriptFile
import org.jetbrains.kotlin.idea.testFramework.ProjectOpenAction.GRADLE_PROJECT
import org.jetbrains.kotlin.psi.KtFile
import java.util.concurrent.atomic.AtomicLong
import kotlin.test.assertNotEquals
@@ -38,6 +41,9 @@ class PerformanceProjectsTest : AbstractPerformanceProjectsTest() {
@JvmStatic
val timer: AtomicLong = AtomicLong()
@JvmStatic
val diagnosticTimer: AtomicLong = AtomicLong()
fun resetTimestamp() {
timer.set(0)
}
@@ -326,6 +332,7 @@ class PerformanceProjectsTest : AbstractPerformanceProjectsTest() {
val testName = "fileAnalysis ${notePrefix(note)}${simpleFilename(fileName)}"
val extraStats = Stats("${stats.name} $testName")
val extraTimingsNs = mutableListOf<Map<String, Any>?>()
val diagnosticTimingsNs = mutableListOf<Map<String, Any>?>()
val warmUpIterations = 30
val iterations = 50
@@ -337,7 +344,7 @@ class PerformanceProjectsTest : AbstractPerformanceProjectsTest() {
iterations(iterations)
setUp(perfKtsFileAnalysisSetUp(project, fileName))
test(perfKtsFileAnalysisTest())
tearDown(perfKtsFileAnalysisTearDown(extraTimingsNs, project))
tearDown(perfKtsFileAnalysisTearDown(extraTimingsNs, diagnosticTimingsNs, project))
profilerConfig.enabled = true
}
@@ -349,19 +356,31 @@ class PerformanceProjectsTest : AbstractPerformanceProjectsTest() {
metricChildren
)
extraStats.printWarmUpTimings(
"diagnostic",
diagnosticTimingsNs.take(warmUpIterations).toTypedArray(),
metricChildren
)
extraStats.processTimings(
"annotator",
extraTimingsNs.drop(warmUpIterations).toTypedArray(),
metricChildren
)
extraStats.processTimings(
"diagnostic",
diagnosticTimingsNs.drop(warmUpIterations).toTypedArray(),
metricChildren
)
}
}
private fun replaceWithCustomHighlighter() {
org.jetbrains.kotlin.idea.testFramework.replaceWithCustomHighlighter(
testRootDisposable,
KotlinPsiCheckerAndHighlightingUpdater::class.java.name,
TestKotlinPsiChecker::class.java.name
KotlinHighlightingPass.Registrar::class.java.name,
TestKotlinHighlightingPass.Registrar::class.java.name
)
}
@@ -385,18 +404,22 @@ class PerformanceProjectsTest : AbstractPerformanceProjectsTest() {
fun perfKtsFileAnalysisTest(): (TestData<Fixture, Pair<Long, List<HighlightInfo>>>) -> Unit {
return {
it.value = it.setUpValue?.let { fixture ->
Pair(System.nanoTime(), fixture.doHighlighting())
val nowNs = System.nanoTime()
diagnosticTimer.set(-nowNs)
Pair(nowNs, fixture.doHighlighting())
}
}
}
fun perfKtsFileAnalysisTearDown(
extraTimingsNs: MutableList<Map<String, Any>?>,
diagnosticTimingsMs: MutableList<Map<String, Any>?>,
project: Project
): (TestData<Fixture, Pair<Long, List<HighlightInfo>>>) -> Unit {
return {
it.setUpValue?.let { fixture ->
it.value?.let { v ->
diagnosticTimingsMs.add(mapOf(TEST_KEY to diagnosticTimer.getAndSet(0)))
assertTrue(v.second.isNotEmpty())
assertNotEquals(0, timer.get())
@@ -411,12 +434,38 @@ class PerformanceProjectsTest : AbstractPerformanceProjectsTest() {
}
class TestKotlinPsiChecker : KotlinPsiChecker() {
override fun annotate(
element: PsiElement, holder: AnnotationHolder
) {
super.annotate(element, holder)
markTimestamp()
class TestKotlinHighlightingPass(file: KtFile, document: Document) : KotlinHighlightingPass(file, document) {
override fun doCollectInformation(progress: ProgressIndicator) {
annotationCallback {
val nowNs = System.nanoTime()
diagnosticTimer.addAndGet(nowNs)
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"/>
<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.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"/>
<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.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"/>
<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.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"/>
<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.refactoring.cutPaste.MoveDeclarationsPassFactory$Registrar"/>
@@ -491,13 +491,8 @@
<liveTemplateMacro implementation="org.jetbrains.kotlin.idea.liveTemplates.macro.KotlinMainArgumentsMacro"/>
<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"/>
<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"/>
<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.idea.KotlinBundle
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.KotlinQuickFixAction
import org.jetbrains.kotlin.idea.quickfix.ReplaceObsoleteLabelSyntaxFix
@@ -99,7 +99,7 @@ class KotlinCleanupInspection : LocalInspectionTool(), CleanupLocalInspectionToo
}
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) {
+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;
public abstract class AbstractPsiCheckerTest extends KotlinLightCodeInsightFixtureTestCase {
public abstract class AbstractKotlinHighlightingPassTest extends KotlinLightCodeInsightFixtureTestCase {
public void doTest(@NotNull VirtualFile file) throws Exception {
myFixture.configureFromExistingVirtualFile(file);
checkHighlighting(true, false, false);
@@ -13,7 +13,7 @@ import org.junit.runner.RunWith
@TestMetadata("idea/testData/checker/custom")
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
class PsiCheckerCustomTest : AbstractPsiCheckerTest() {
class KotlinHighlightingPassCustomTest : AbstractKotlinHighlightingPassTest() {
@TestMetadata("noUnusedParameterWhenCustom.kt")
fun testNoUnusedParameterWhenCustom() {
@@ -12,7 +12,7 @@ import org.junit.runner.RunWith
@TestMetadata("idea/testData/checker/sealed")
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
class PsiCheckerSealedTest : AbstractPsiCheckerTest() {
class KotlinHighlightingPassSealedTest : AbstractKotlinHighlightingPassTest() {
fun testOutsideOfPackageInheritors() {
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 */
@SuppressWarnings("all")
@RunWith(JUnit3RunnerWithInners.class)
public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
public class KotlinHighlightingPassTestGenerated extends AbstractKotlinHighlightingPassTest {
@TestMetadata("idea/testData/checker")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Checker extends AbstractPsiCheckerTest {
public static class Checker extends AbstractKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@@ -41,6 +41,11 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
runTest("idea/testData/checker/AnnotationOnFile.kt");
}
@TestMetadata("AnnotationSupressing.kt")
public void testAnnotationSupressing() throws Exception {
runTest("idea/testData/checker/AnnotationSupressing.kt");
}
@TestMetadata("AnonymousInitializers.kt")
public void testAnonymousInitializers() throws Exception {
runTest("idea/testData/checker/AnonymousInitializers.kt");
@@ -365,7 +370,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
@TestMetadata("idea/testData/checker/regression")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Regression extends AbstractPsiCheckerTest {
public static class Regression extends AbstractKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@@ -608,7 +613,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
@TestMetadata("idea/testData/checker/recovery")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Recovery extends AbstractPsiCheckerTest {
public static class Recovery extends AbstractKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@@ -636,7 +641,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
@TestMetadata("idea/testData/checker/rendering")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Rendering extends AbstractPsiCheckerTest {
public static class Rendering extends AbstractKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@@ -654,7 +659,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
@TestMetadata("idea/testData/checker/scripts")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Scripts extends AbstractPsiCheckerTest {
public static class Scripts extends AbstractKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@@ -687,7 +692,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
@TestMetadata("idea/testData/checker/duplicateJvmSignature")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class DuplicateJvmSignature extends AbstractPsiCheckerTest {
public static class DuplicateJvmSignature extends AbstractKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@@ -699,7 +704,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
@TestMetadata("idea/testData/checker/duplicateJvmSignature/fields")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Fields extends AbstractPsiCheckerTest {
public static class Fields extends AbstractKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@@ -717,7 +722,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
@TestMetadata("idea/testData/checker/duplicateJvmSignature/functionAndProperty")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class FunctionAndProperty extends AbstractPsiCheckerTest {
public static class FunctionAndProperty extends AbstractKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@@ -780,7 +785,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
@TestMetadata("idea/testData/checker/duplicateJvmSignature/traitImpl")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class TraitImpl extends AbstractPsiCheckerTest {
public static class TraitImpl extends AbstractKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@@ -799,7 +804,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
@TestMetadata("idea/testData/checker/infos")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Infos extends AbstractPsiCheckerTest {
public static class Infos extends AbstractKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithInfos, this, testDataFilePath);
}
@@ -892,7 +897,7 @@ public class PsiCheckerTestGenerated extends AbstractPsiCheckerTest {
@TestMetadata("idea/testData/checker/diagnosticsMessage")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class DiagnosticsMessage extends AbstractPsiCheckerTest {
public static class DiagnosticsMessage extends AbstractKotlinHighlightingPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@@ -5,12 +5,12 @@
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.psi.KtCodeFragment
import kotlin.test.assertNull
abstract class AbstractCodeFragmentAutoImportTest : AbstractPsiCheckerTest() {
abstract class AbstractCodeFragmentAutoImportTest : AbstractKotlinHighlightingPassTest() {
override fun doTest(filePath: String) {
myFixture.configureByCodeFragment(filePath)
myFixture.doHighlighting()
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.idea.debugger.evaluate
import com.intellij.codeInspection.InspectionProfileEntry
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.util.ImportInsertHelper
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 java.io.File
abstract class AbstractCodeFragmentHighlightingTest : AbstractPsiCheckerTest() {
abstract class AbstractCodeFragmentHighlightingTest : AbstractKotlinHighlightingPassTest() {
override fun doTest(filePath: String) {
myFixture.configureByCodeFragment(filePath)
checkHighlighting(filePath)
@@ -34,10 +34,12 @@ abstract class AbstractDslHighlighterTest : KotlinLightCodeInsightFixtureTestCas
val styleIdByCall = extension.highlightCall(element, call)?.externalName
if (styleIdByCall != null && styleIdByCall == styleIdByComment) {
val annotationHolder = AnnotationHolderImpl(AnnotationSession(psiFile))
val checkers = KotlinPsiChecker.getAfterAnalysisVisitor(annotationHolder, bindingContext)
checkers.forEach { call.call.callElement.accept(it) }
annotationHolder.runAnnotatorWithContext(file) { _, _ ->
val checkers = AbstractKotlinHighlightingPass.getAfterAnalysisVisitor(annotationHolder, bindingContext)
checkers.forEach { call.call.callElement.accept(it) }
}
assertTrue(
"KotlinPsiChecker did not contribute an Annotation containing the correct text attribute key at line ${lineNumber + 1}",
"KotlinHighlightingPass did not contribute an Annotation containing the correct text attribute key at line ${lineNumber + 1}",
annotationHolder.any {
it.textAttributes.externalName == styleIdByComment
}
@@ -8,7 +8,9 @@ package org.jetbrains.kotlin.idea.inspections
import com.google.common.collect.Lists
import com.intellij.codeHighlighting.HighlightDisplayLevel
import com.intellij.codeHighlighting.Pass
import com.intellij.codeHighlighting.TextEditorHighlightingPass
import com.intellij.codeInsight.daemon.impl.HighlightInfoType
import com.intellij.codeInsight.daemon.impl.TextEditorHighlightingPassRegistrarEx
import com.intellij.codeInsight.intention.EmptyIntentionAction
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.openapi.util.SystemInfo
@@ -19,6 +21,7 @@ import junit.framework.ComparisonFailure
import junit.framework.TestCase
import org.jdom.Element
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.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.withCustomCompilerOptions
@@ -29,6 +32,7 @@ import org.jetbrains.kotlin.test.KotlinTestUtils
import org.junit.Assert
import java.io.File
abstract class AbstractLocalInspectionTest : KotlinLightCodeInsightFixtureTestCase() {
private val inspectionFileName: String
get() = ".inspection"
@@ -145,16 +149,23 @@ abstract class AbstractLocalInspectionTest : KotlinLightCodeInsightFixtureTestCa
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 highlightInfos = CodeInsightTestFixtureImpl.instantiateAndRun(
file, editor, intArrayOf(
Pass.LINE_MARKERS,
Pass.EXTERNAL_TOOLS,
Pass.POPUP_HINTS,
Pass.UPDATE_ALL,
Pass.UPDATE_FOLDING,
Pass.WOLF
), (file as? KtFile)?.isScript() == true
file, editor, passIdsToIgnore.toIntArray(), (file as? KtFile)?.isScript() == true
).filter { it.description != null && caretOffset in it.startOffset..it.endOffset }
Assert.assertTrue(
@@ -5,11 +5,9 @@
package org.jetbrains.kotlin.pacelize.ide.test
import org.jetbrains.kotlin.checkers.AbstractPsiCheckerTest
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.checkers.AbstractKotlinHighlightingPassTest
abstract class AbstractParcelizeCheckerTest : AbstractPsiCheckerTest() {
abstract class AbstractParcelizeCheckerTest : AbstractKotlinHighlightingPassTest() {
override fun setUp() {
super.setUp()
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.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.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.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,,
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