Report highlight errors to WolfTheProblemSolver

Relates to #KT-37702
#KTIJ-1246 Fixed

Original commit: bd222a5255c2fd6f4abfce3115f81733ef9a39f3
This commit is contained in:
Vladimir Dolzhenko
2021-02-19 05:46:04 +00:00
committed by Space
parent afe71f5d59
commit 8783ebc352
10 changed files with 234 additions and 10 deletions
@@ -324,6 +324,10 @@ fun main(args: Array<String>) {
model("checker/diagnosticsMessage")
}
testClass<AbstractKotlinHighlightWolfPassTest> {
model("checker/wolf")
}
testClass<AbstractJavaAgainstKotlinSourceCheckerTest> {
model("kotlinAndJavaChecker/javaAgainstKotlin")
model("kotlinAndJavaChecker/javaWithKotlin")
@@ -6,15 +6,23 @@
package org.jetbrains.kotlin.idea.highlighter
import com.intellij.codeInsight.daemon.impl.Divider
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInsight.problems.ProblemImpl
import com.intellij.lang.annotation.Annotation
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.Annotator
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Document
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.problems.Problem
import com.intellij.problems.WolfTheProblemSolver
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import com.intellij.util.CommonProcessors
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
@@ -44,6 +52,12 @@ abstract class AbstractKotlinHighlightingPass(file: KtFile, document: Document)
}
}
override fun doApplyInformationToEditor() {
super.doApplyInformationToEditor()
reportErrorsToWolf()
}
override fun buildBindingContext(holder: AnnotationHolder): BindingContext {
val dividedElements: List<Divider.DividedElements> = ArrayList()
Divider.divideInsideAndOutsideAllRoots(
@@ -153,6 +167,31 @@ abstract class AbstractKotlinHighlightingPass(file: KtFile, document: Document)
protected open fun shouldSuppressUnusedParameter(parameter: KtParameter): Boolean = false
private fun reportErrorsToWolf() {
if (!file.viewProvider.isPhysical) return // e.g. errors in evaluate expression
val project: Project = file.project
if (!PsiManager.getInstance(project).isInProject(file)) return // do not report problems in libraries
val file: VirtualFile = file.virtualFile ?: return
val wolf = WolfTheProblemSolver.getInstance(project)
val hasSyntaxErrors = wolf.hasSyntaxErrors(file)
val problemFile = wolf.isProblemFile(file)
// do nothing if file has already problems
if (hasSyntaxErrors || problemFile) return
val problems = convertToProblems(infos, file)
wolf.reportProblems(file, problems)
}
private fun convertToProblems(
infos: Collection<HighlightInfo>,
file: VirtualFile,
hasErrorElement: Boolean = true
): List<Problem> =
infos.filter { it.severity == HighlightSeverity.ERROR }.map { ProblemImpl(file, it, hasErrorElement) }
companion object {
fun createQuickFixes(diagnostic: Diagnostic): Collection<IntentionAction> =
createQuickFixes(listOfNotNull(diagnostic))[diagnostic]
@@ -5,7 +5,6 @@
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
+3
View File
@@ -0,0 +1,3 @@
// HAS-WOLF-ERRORS: true
// TYPE: .map
fun diagnostic(): String = ""<caret>
+5
View File
@@ -0,0 +1,5 @@
// WOLF-ERRORS: true
// HAS-WOLF-ERRORS: true
// TYPE: asd
// ERROR: Overload resolution ambiguity: <br>public final operator fun plus(other: Byte): Int defined in kotlin.Int<br>public final operator fun plus(other: Double): Double defined in kotlin.Int<br>public final operator fun plus(other: Float): Float defined in kotlin.Int<br>public final operator fun plus(other: Int): Int defined in kotlin.Int<br>public final operator fun plus(other: Long): Long defined in kotlin.Int<br>public final operator fun plus(other: Short): Int defined in kotlin.Int
fun diagnosticAfterSyntax(): Int = 42 + <caret>
+3
View File
@@ -0,0 +1,3 @@
// HAS-WOLF-ERRORS: false
// TYPE: +1
fun none(): Int = 42<caret>
+3
View File
@@ -0,0 +1,3 @@
// HAS-WOLF-ERRORS: true
// TYPE: +
fun syntax(): Int = 42<caret>
@@ -0,0 +1,117 @@
package org.jetbrains.kotlin.checkers
import com.intellij.codeInsight.problems.MockWolfTheProblemSolver
import com.intellij.codeInsight.problems.WolfTheProblemSolverImpl
import com.intellij.openapi.Disposable
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.problems.ProblemListener
import com.intellij.problems.WolfTheProblemSolver
import com.intellij.psi.PsiDocumentManager
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
import com.intellij.util.ThrowableRunnable
import junit.framework.TestCase
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
import org.jetbrains.kotlin.idea.codeInsight.AbstractOutOfBlockModificationTest
import org.jetbrains.kotlin.idea.test.DirectiveBasedActionUtils.checkForUnexpectedErrors
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.runAll
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.test.InTextDirectivesUtils
abstract class AbstractKotlinHighlightWolfPassTest : KotlinLightCodeInsightFixtureTestCase() {
private val disposable = Disposer.newDisposable("wolfTheProblemSolverParentDisposable")
private var wolfTheProblemSolver: MockWolfTheProblemSolver? = null
override fun setUp() {
super.setUp()
wolfTheProblemSolver = prepareWolf(project, disposable)
}
override fun tearDown() {
runAll(
// TODO: [VD] HAS TO BE UNCOMMENTED with 211
//ThrowableRunnable { wolfTheProblemSolver?.resetDelegate() },
ThrowableRunnable { Disposer.dispose(disposable) },
ThrowableRunnable { super.tearDown() },
)
}
private fun prepareWolf(project: Project, parentDisposable: Disposable): MockWolfTheProblemSolver {
val wolfTheProblemSolver = WolfTheProblemSolver.getInstance(project) as MockWolfTheProblemSolver
// TODO: [VD] HAS TO BE UNCOMMENTED with 211
// val theRealSolver = WolfTheProblemSolverImpl.createInstance(project)
// wolfTheProblemSolver.setDelegate(theRealSolver)
// Disposer.register(parentDisposable, (theRealSolver as Disposable))
return wolfTheProblemSolver
}
open fun doTest(filePath: String) {
myFixture.configureByFile(fileName())
val ktFile = file as KtFile
myFixture.doHighlighting()
// have to analyze file before any change to support incremental analysis
val diagnosticsProvider: (KtFile) -> Diagnostics = { it.analyzeWithAllCompilerChecks().bindingContext.diagnostics }
checkForUnexpectedErrors(ktFile, diagnosticsProvider)
val wolf = WolfTheProblemSolver.getInstance(project)
val virtualFile = ktFile.virtualFile
val initialWolfErrors = wolfErrors(myFixture)
// TODO: [VD] HAS TO BE UNCOMMENTED with 211
//TestCase.assertEquals(initialWolfErrors, wolf.isProblemFile(virtualFile) || wolf.hasSyntaxErrors(virtualFile))
var problemsAppeared = 0
var problemsChanged = 0
var problemsDisappeared = 0
project.messageBus.connect(disposable).subscribe(ProblemListener.TOPIC, object : ProblemListener {
override fun problemsAppeared(file: VirtualFile) {
problemsAppeared++
}
override fun problemsChanged(file: VirtualFile) {
problemsChanged++
}
override fun problemsDisappeared(file: VirtualFile) {
problemsDisappeared++
}
})
myFixture.type(AbstractOutOfBlockModificationTest.stringToType(myFixture))
PsiDocumentManager.getInstance(project).commitDocument(myFixture.getDocument(ktFile))
myFixture.doHighlighting()
// TODO: [VD] HAS TO BE UNCOMMENTED with 211
// val hasWolfErrors = hasWolfErrors(myFixture)
// assertEquals(hasWolfErrors, wolf.isProblemFile(virtualFile) || wolf.hasSyntaxErrors(virtualFile))
// if (hasWolfErrors) {
// TestCase.assertTrue(problemsAppeared > 0)
// } else {
// assertEquals(0, problemsAppeared)
// }
// assertEquals(if (initialWolfErrors) 1 else 0, problemsDisappeared)
}
companion object {
private const val HAS_WOLF_ERRORS_DIRECTIVE = "HAS-WOLF-ERRORS:"
fun hasWolfErrors(fixture: JavaCodeInsightTestFixture): Boolean = findBooleanDirective(fixture, HAS_WOLF_ERRORS_DIRECTIVE)
private const val WOLF_ERRORS_DIRECTIVE = "WOLF-ERRORS:"
fun wolfErrors(fixture: JavaCodeInsightTestFixture): Boolean = findBooleanDirective(fixture, WOLF_ERRORS_DIRECTIVE)
private fun findBooleanDirective(
fixture: JavaCodeInsightTestFixture,
wolfErrorsDirective: String
): Boolean {
val text = fixture.getDocument(fixture.file).text
return InTextDirectivesUtils.findStringWithPrefixes(text, wolfErrorsDirective) == "true"
}
}
}
@@ -0,0 +1,51 @@
/*
* 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.checkers;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("idea/testData/checker/wolf")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class KotlinHighlightWolfPassTestGenerated extends AbstractKotlinHighlightWolfPassTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInWolf() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/checker/wolf"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("diagnostic.kt")
public void testDiagnostic() throws Exception {
runTest("idea/testData/checker/wolf/diagnostic.kt");
}
@TestMetadata("diagnosticAfterSyntax.kt")
public void testDiagnosticAfterSyntax() throws Exception {
runTest("idea/testData/checker/wolf/diagnosticAfterSyntax.kt");
}
@TestMetadata("none.kt")
public void testNone() throws Exception {
runTest("idea/testData/checker/wolf/none.kt");
}
@TestMetadata("syntax.kt")
public void testSyntax() throws Exception {
runTest("idea/testData/checker/wolf/syntax.kt");
}
}
@@ -13,6 +13,7 @@ import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.PsiModificationTrackerImpl
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
import org.jetbrains.kotlin.idea.FrontendInternals
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
@@ -52,7 +53,7 @@ abstract class AbstractOutOfBlockModificationTest : KotlinLightCodeInsightFixtur
// have to analyze file before any change to support incremental analysis
ktFile.analyzeWithAllCompilerChecks()
myFixture.type(stringToType)
myFixture.type(stringToType(myFixture))
PsiDocumentManager.getInstance(myFixture.project).commitDocument(myFixture.getDocument(myFixture.file))
val oobAfterCount = ktFile.outOfBlockModificationCount
val modificationCountAfterType = tracker.modificationCount
@@ -116,14 +117,6 @@ abstract class AbstractOutOfBlockModificationTest : KotlinLightCodeInsightFixtur
}
}
private val stringToType: String
get() {
val text = myFixture.getDocument(myFixture.file).text
val typeDirectives =
InTextDirectivesUtils.findStringWithPrefixes(text, TYPE_DIRECTIVE)
return if (typeDirectives != null) StringUtil.unescapeStringCharacters(typeDirectives) else "a"
}
private val expectedOutOfBlockResult: Boolean
get() {
val text = myFixture.getDocument(myFixture.file).text
@@ -144,5 +137,12 @@ abstract class AbstractOutOfBlockModificationTest : KotlinLightCodeInsightFixtur
const val OUT_OF_CODE_BLOCK_DIRECTIVE = "OUT_OF_CODE_BLOCK:"
const val SKIP_ANALYZE_CHECK_DIRECTIVE = "SKIP_ANALYZE_CHECK"
const val TYPE_DIRECTIVE = "TYPE:"
fun stringToType(fixture: JavaCodeInsightTestFixture): String {
val text = fixture.getDocument(fixture.file).text
val typeDirectives =
InTextDirectivesUtils.findStringWithPrefixes(text, TYPE_DIRECTIVE)
return if (typeDirectives != null) StringUtil.unescapeStringCharacters(typeDirectives) else "a"
}
}
}