diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index f501b89dc74..9f52ebe9f2b 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -324,6 +324,10 @@ fun main(args: Array) { model("checker/diagnosticsMessage") } + testClass { + model("checker/wolf") + } + testClass { model("kotlinAndJavaChecker/javaAgainstKotlin") model("kotlinAndJavaChecker/javaWithKotlin") diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/AbstractKotlinHighlightingPass.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/AbstractKotlinHighlightingPass.kt index 3e9ae6bd035..f8924edc46c 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/AbstractKotlinHighlightingPass.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/AbstractKotlinHighlightingPass.kt @@ -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 = 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, + file: VirtualFile, + hasErrorElement: Boolean = true + ): List = + infos.filter { it.severity == HighlightSeverity.ERROR }.map { ProblemImpl(file, it, hasErrorElement) } + companion object { fun createQuickFixes(diagnostic: Diagnostic): Collection = createQuickFixes(listOfNotNull(diagnostic))[diagnostic] diff --git a/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/highlighter/HighlightingVisitor.kt b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/highlighter/HighlightingVisitor.kt index df4edf914ab..597a009daf7 100644 --- a/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/highlighter/HighlightingVisitor.kt +++ b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/highlighter/HighlightingVisitor.kt @@ -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 diff --git a/idea/testData/checker/wolf/diagnostic.kt b/idea/testData/checker/wolf/diagnostic.kt new file mode 100644 index 00000000000..bf03ca376fb --- /dev/null +++ b/idea/testData/checker/wolf/diagnostic.kt @@ -0,0 +1,3 @@ +// HAS-WOLF-ERRORS: true +// TYPE: .map +fun diagnostic(): String = "" \ No newline at end of file diff --git a/idea/testData/checker/wolf/diagnosticAfterSyntax.kt b/idea/testData/checker/wolf/diagnosticAfterSyntax.kt new file mode 100644 index 00000000000..ca02a2eab64 --- /dev/null +++ b/idea/testData/checker/wolf/diagnosticAfterSyntax.kt @@ -0,0 +1,5 @@ +// WOLF-ERRORS: true +// HAS-WOLF-ERRORS: true +// TYPE: asd +// ERROR: Overload resolution ambiguity:
public final operator fun plus(other: Byte): Int defined in kotlin.Int
public final operator fun plus(other: Double): Double defined in kotlin.Int
public final operator fun plus(other: Float): Float defined in kotlin.Int
public final operator fun plus(other: Int): Int defined in kotlin.Int
public final operator fun plus(other: Long): Long defined in kotlin.Int
public final operator fun plus(other: Short): Int defined in kotlin.Int +fun diagnosticAfterSyntax(): Int = 42 + \ No newline at end of file diff --git a/idea/testData/checker/wolf/none.kt b/idea/testData/checker/wolf/none.kt new file mode 100644 index 00000000000..02c4a2ad3fb --- /dev/null +++ b/idea/testData/checker/wolf/none.kt @@ -0,0 +1,3 @@ +// HAS-WOLF-ERRORS: false +// TYPE: +1 +fun none(): Int = 42 \ No newline at end of file diff --git a/idea/testData/checker/wolf/syntax.kt b/idea/testData/checker/wolf/syntax.kt new file mode 100644 index 00000000000..ff4b45dee4f --- /dev/null +++ b/idea/testData/checker/wolf/syntax.kt @@ -0,0 +1,3 @@ +// HAS-WOLF-ERRORS: true +// TYPE: + +fun syntax(): Int = 42 \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/checkers/AbstractKotlinHighlightWolfPassTest.kt b/idea/tests/org/jetbrains/kotlin/checkers/AbstractKotlinHighlightWolfPassTest.kt new file mode 100644 index 00000000000..d635c678b83 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/checkers/AbstractKotlinHighlightWolfPassTest.kt @@ -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" + } + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/checkers/KotlinHighlightWolfPassTestGenerated.java b/idea/tests/org/jetbrains/kotlin/checkers/KotlinHighlightWolfPassTestGenerated.java new file mode 100644 index 00000000000..c7e4450c9a8 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/checkers/KotlinHighlightWolfPassTestGenerated.java @@ -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"); + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOutOfBlockModificationTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOutOfBlockModificationTest.kt index 931eed2f9c0..3f2afa66e72 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOutOfBlockModificationTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOutOfBlockModificationTest.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" + } } } \ No newline at end of file