mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[kotlin] Fix incremental analysis for inner class bodies
#KTIJ-18388 Fixed GitOrigin-RevId: 635fbea4ce7ed1cf6cc535aaca04c2c3331eb35f
This commit is contained in:
committed by
intellij-monorepo-bot
parent
3c27b879a4
commit
f169366478
+44
-8
@@ -20,7 +20,6 @@ import com.intellij.pom.tree.events.impl.ChangeInfoImpl
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.psi.impl.source.tree.SharedImplUtil
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.util.findTopmostParentInFile
|
||||
import com.intellij.psi.util.findTopmostParentOfType
|
||||
@@ -111,6 +110,19 @@ class PureKotlinCodeBlockModificationListener(project: Project) : Disposable {
|
||||
if (KtPsiUtil.isLocal(blockDeclaration))
|
||||
return null
|
||||
|
||||
val directParentClassOrObject = PsiTreeUtil.getParentOfType(blockDeclaration, KtClassOrObject::class.java)
|
||||
val parentClassOrObject = directParentClassOrObject
|
||||
?.takeIf { !it.isTopLevel() && it.hasModifier(KtTokens.INNER_KEYWORD) }?.let {
|
||||
var e: KtClassOrObject? = it
|
||||
while (e != null) {
|
||||
e = PsiTreeUtil.getParentOfType(e, KtClassOrObject::class.java)
|
||||
if (e?.hasModifier(KtTokens.INNER_KEYWORD) == false) {
|
||||
break
|
||||
}
|
||||
}
|
||||
e
|
||||
} ?: directParentClassOrObject
|
||||
|
||||
when (blockDeclaration) {
|
||||
is KtNamedFunction -> {
|
||||
// if (blockDeclaration.visibilityModifierType()?.toVisibility() == Visibilities.PRIVATE) {
|
||||
@@ -122,12 +134,24 @@ class PureKotlinCodeBlockModificationListener(project: Project) : Disposable {
|
||||
// case like `fun foo(): String {...<caret>...}`
|
||||
return blockDeclaration.bodyExpression
|
||||
?.takeIf { it.isAncestor(element) }
|
||||
?.let { BlockModificationScopeElement(blockDeclaration, it) }
|
||||
?.let {
|
||||
if (parentClassOrObject == directParentClassOrObject) {
|
||||
BlockModificationScopeElement(blockDeclaration, it)
|
||||
} else if (parentClassOrObject != null) {
|
||||
BlockModificationScopeElement(parentClassOrObject, it)
|
||||
} else null
|
||||
}
|
||||
} else if (blockDeclaration.hasDeclaredReturnType()) {
|
||||
// case like `fun foo(): String = b<caret>labla`
|
||||
return blockDeclaration.initializer
|
||||
?.takeIf { it.isAncestor(element) }
|
||||
?.let { BlockModificationScopeElement(blockDeclaration, it) }
|
||||
?.let {
|
||||
if (parentClassOrObject == directParentClassOrObject) {
|
||||
BlockModificationScopeElement(blockDeclaration, it)
|
||||
} else if (parentClassOrObject != null) {
|
||||
BlockModificationScopeElement(parentClassOrObject, it)
|
||||
} else null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +179,11 @@ class PureKotlinCodeBlockModificationListener(project: Project) : Disposable {
|
||||
blockDeclaration.findTopmostParentOfType<KtClassOrObject>() as? KtElement
|
||||
|
||||
if (declaration != null) {
|
||||
return BlockModificationScopeElement(declaration, properExpression)
|
||||
return if (parentClassOrObject == directParentClassOrObject) {
|
||||
BlockModificationScopeElement(declaration, properExpression)
|
||||
} else if (parentClassOrObject != null) {
|
||||
BlockModificationScopeElement(parentClassOrObject, properExpression)
|
||||
} else null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,8 +203,12 @@ class PureKotlinCodeBlockModificationListener(project: Project) : Disposable {
|
||||
blockDeclaration
|
||||
.takeIf { it.isAncestor(element) }
|
||||
?.let { ktClassInitializer ->
|
||||
(PsiTreeUtil.getParentOfType(blockDeclaration, KtClassOrObject::class.java))?.let {
|
||||
return BlockModificationScopeElement(it, ktClassInitializer)
|
||||
parentClassOrObject?.let {
|
||||
return if (parentClassOrObject == directParentClassOrObject) {
|
||||
BlockModificationScopeElement(it, ktClassInitializer)
|
||||
} else {
|
||||
BlockModificationScopeElement(parentClassOrObject, ktClassInitializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,8 +217,12 @@ class PureKotlinCodeBlockModificationListener(project: Project) : Disposable {
|
||||
blockDeclaration.takeIf {
|
||||
it.bodyExpression?.isAncestor(element) ?: false || it.getDelegationCallOrNull()?.isAncestor(element) ?: false
|
||||
}?.let { ktConstructor ->
|
||||
PsiTreeUtil.getParentOfType(blockDeclaration, KtClassOrObject::class.java)?.let {
|
||||
return BlockModificationScopeElement(it, ktConstructor)
|
||||
parentClassOrObject?.let {
|
||||
return if (parentClassOrObject == directParentClassOrObject) {
|
||||
BlockModificationScopeElement(it, ktConstructor)
|
||||
} else {
|
||||
BlockModificationScopeElement(parentClassOrObject, ktConstructor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -36,8 +36,9 @@ abstract class AbstractOutOfBlockModificationTest : KotlinLightCodeInsightFixtur
|
||||
ktFile.text,
|
||||
SKIP_ANALYZE_CHECK_DIRECTIVE
|
||||
)
|
||||
val project = myFixture.project
|
||||
val tracker =
|
||||
PsiManager.getInstance(myFixture.project).modificationTracker as PsiModificationTrackerImpl
|
||||
PsiManager.getInstance(project).modificationTracker as PsiModificationTrackerImpl
|
||||
val element = ktFile.findElementAt(myFixture.caretOffset)
|
||||
assertNotNull("Should be valid element", element)
|
||||
val oobBeforeType = ktFile.outOfBlockModificationCount
|
||||
@@ -47,7 +48,7 @@ abstract class AbstractOutOfBlockModificationTest : KotlinLightCodeInsightFixtur
|
||||
ktFile.analyzeWithAllCompilerChecks()
|
||||
|
||||
myFixture.type(stringToType)
|
||||
PsiDocumentManager.getInstance(myFixture.project).commitDocument(myFixture.getDocument(myFixture.file))
|
||||
PsiDocumentManager.getInstance(project).commitDocument(myFixture.getDocument(myFixture.file))
|
||||
val oobAfterCount = ktFile.outOfBlockModificationCount
|
||||
val modificationCountAfterType = tracker.modificationCount
|
||||
assertTrue(
|
||||
@@ -60,6 +61,7 @@ abstract class AbstractOutOfBlockModificationTest : KotlinLightCodeInsightFixtur
|
||||
expectedOutOfBlock, oobBeforeType != oobAfterCount
|
||||
)
|
||||
checkForUnexpectedErrors(ktFile)
|
||||
DirectiveBasedActionUtils.inspectionChecks(name, ktFile)
|
||||
|
||||
if (!isSkipCheckDefined) {
|
||||
checkOOBWithDescriptorsResolve(expectedOutOfBlock)
|
||||
|
||||
+5
@@ -368,6 +368,11 @@ public class OutOfBlockModificationTestGenerated extends AbstractOutOfBlockModif
|
||||
runTest("testData/codeInsight/outOfBlock/InitBlockInObject.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("InnerClassInnerMethod.kt")
|
||||
public void testInnerClassInnerMethod() throws Exception {
|
||||
runTest("testData/codeInsight/outOfBlock/InnerClassInnerMethod.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("LocalFunWithBody.kt")
|
||||
public void testLocalFunWithBody() throws Exception {
|
||||
runTest("testData/codeInsight/outOfBlock/LocalFunWithBody.kt");
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// OUT_OF_CODE_BLOCK: FALSE
|
||||
// TYPE: '\b"a"'
|
||||
// INSPECTION-CLASS: org.jetbrains.kotlin.idea.inspections.RedundantInnerClassModifierInspection
|
||||
// INSPECTION: [LIKE_UNUSED_SYMBOL:6] Redundant 'inner' modifier
|
||||
class RedundantInner(private val s: String) {
|
||||
private inner class D {
|
||||
fun f(): Int {
|
||||
return s<caret>.length
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -2,12 +2,17 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.test
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.codeInspection.InspectionManager
|
||||
import com.intellij.codeInspection.ProblemDescriptor
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.testFramework.UsefulTestCase
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
|
||||
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
@@ -66,6 +71,43 @@ object DirectiveBasedActionUtils {
|
||||
)
|
||||
}
|
||||
|
||||
fun inspectionChecks(name: String, file: PsiFile) {
|
||||
InTextDirectivesUtils.findLinesWithPrefixesRemoved(file.text, "// INSPECTION-CLASS:").takeIf { it.isNotEmpty() }?.let { inspectionNames ->
|
||||
val inspectionManager = InspectionManager.getInstance(file.project)
|
||||
val inspections = inspectionNames.map { Class.forName(it).getDeclaredConstructor().newInstance() as AbstractKotlinInspection }
|
||||
|
||||
val problems = mutableListOf<ProblemDescriptor>()
|
||||
ProgressManager.getInstance().executeProcessUnderProgress(
|
||||
{
|
||||
for (inspection in inspections) {
|
||||
problems += inspection.processFile(
|
||||
file,
|
||||
inspectionManager
|
||||
)
|
||||
}
|
||||
}, DaemonProgressIndicator()
|
||||
)
|
||||
val directive = "// INSPECTION:"
|
||||
val expected = InTextDirectivesUtils.findLinesWithPrefixesRemoved(file.text, directive)
|
||||
.sorted()
|
||||
.map { "$directive $it" }
|
||||
|
||||
val actual = problems
|
||||
// lineNumber is 0-based
|
||||
.map { "$directive [${it.highlightType.name}:${it.lineNumber + 1}] $it" }
|
||||
.sorted()
|
||||
|
||||
if (actual.isEmpty() && expected.isEmpty()) return
|
||||
|
||||
KotlinLightCodeInsightFixtureTestCaseBase.assertOrderedEquals(
|
||||
"All actual $name should be mentioned in test data with '$directive' directive. " +
|
||||
"But no unnecessary $name should be me mentioned, file:\n${file.text}",
|
||||
actual,
|
||||
expected
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun checkAvailableActionsAreExpected(file: PsiFile, availableActions: Collection<IntentionAction>) {
|
||||
val expectedActions = InTextDirectivesUtils.findLinesWithPrefixesRemoved(file.text, "// ACTION:").sorted()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user