mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[workspace model] IJPL-201515 Workspace Inspections
- split workspace inspections into K1 and K2 - tests - include tests in Kotlin K2 tests Merge-request: IJ-MR-162676 Merged-by: Kirill Bochkarev <kirill.bochkarev@jetbrains.com> GitOrigin-RevId: e161e7c95e330fa478ece69e4c252981349e97bb
This commit is contained in:
committed by
intellij-monorepo-bot
parent
23fd6c469f
commit
738a0877e3
+2
-2
@@ -12,7 +12,7 @@ import java.util.stream.Stream;
|
||||
|
||||
public class DevkitInspectionsRegistrationCheckTest extends BasePlatformTestCase {
|
||||
|
||||
private static final int EXPECTED_INSPECTIONS_NUMBER = 88;
|
||||
private static final int EXPECTED_INSPECTIONS_NUMBER = 87;
|
||||
|
||||
/**
|
||||
* Inspections that are finished and intentionally disabled.
|
||||
@@ -24,7 +24,7 @@ public class DevkitInspectionsRegistrationCheckTest extends BasePlatformTestCase
|
||||
).sorted().toList();
|
||||
|
||||
/**
|
||||
* Inspections which implementation is in
|
||||
* Inspections which implementation is in progress
|
||||
* or are finished but not battle-tested yet and may require improvements/polishing.
|
||||
*/
|
||||
private static final List<String> WIP_INSPECTIONS =
|
||||
|
||||
@@ -66,6 +66,7 @@ jvm_library(
|
||||
"//platform/core-api:core",
|
||||
"//platform/workspace/jps",
|
||||
"//java/java-impl:impl",
|
||||
"//plugins/kotlin/base/psi",
|
||||
]
|
||||
)
|
||||
### auto-generated section `build intellij.devkit.workspaceModel` end
|
||||
@@ -63,5 +63,6 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.core" />
|
||||
<orderEntry type="module" module-name="intellij.platform.workspace.jps" />
|
||||
<orderEntry type="module" module-name="intellij.java.impl" />
|
||||
<orderEntry type="module" module-name="intellij.kotlin.base.psi" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -39,6 +39,7 @@ jvm_library(
|
||||
"//java/java-psi-impl:psi-impl",
|
||||
"//platform/util",
|
||||
"//platform/core-api:core",
|
||||
"//platform/analysis-api:analysis",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -72,6 +73,7 @@ jvm_library(
|
||||
"//platform/testFramework",
|
||||
"//platform/testFramework:testFramework_test_lib",
|
||||
"//plugins/kotlin/base/test:test_test_lib",
|
||||
"//platform/analysis-api:analysis",
|
||||
]
|
||||
)
|
||||
### auto-generated section `build intellij.devkit.workspaceModel.k1` end
|
||||
|
||||
@@ -45,5 +45,6 @@
|
||||
<orderEntry type="module" module-name="kotlin.tests-common" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
|
||||
<orderEntry type="module" module-name="kotlin.base.test" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.platform.analysis" />
|
||||
</component>
|
||||
</module>
|
||||
+19
@@ -5,9 +5,28 @@
|
||||
<module name="intellij.devkit.workspaceModel"/>
|
||||
</dependencies>
|
||||
|
||||
<resource-bundle>messages.DevKitWorkspaceModelBundle</resource-bundle>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<applicationService serviceInterface="com.intellij.devkit.workspaceModel.metaModel.WorkspaceMetaModelProvider"
|
||||
serviceImplementation="com.intellij.devkit.workspaceModel.k1.metaModel.WorkspaceMetaModelProviderImpl"
|
||||
/>
|
||||
<localInspection language="kotlin"
|
||||
shortName="WorkspaceImplAbsent"
|
||||
projectType="INTELLIJ_PLUGIN"
|
||||
groupBundle="messages.DevKitWorkspaceModelBundle"
|
||||
groupPathKey="inspections.group.path" groupKey="inspections.group.workspace.model"
|
||||
runForWholeFile="true"
|
||||
enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.devkit.workspaceModel.k1.inspections.WorkspaceCodeAbsentInspection"
|
||||
key="inspection.workspace.impl.generation.display.name"/>
|
||||
<localInspection language="kotlin"
|
||||
projectType="INTELLIJ_PLUGIN"
|
||||
groupBundle="messages.DevKitWorkspaceModelBundle"
|
||||
groupPathKey="inspections.group.path" groupKey="inspections.group.workspace.model"
|
||||
runForWholeFile="true"
|
||||
enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.devkit.workspaceModel.k1.inspections.WorkspaceImplObsoleteInspection"
|
||||
key="inspection.workspace.obsolete.model.display.name"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.devkit.workspaceModel.k1.inspections
|
||||
|
||||
import com.intellij.devkit.workspaceModel.inspections.WorkspaceCodeAbsentInspectionBase
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.VisibleForTesting
|
||||
import org.jetbrains.kotlin.idea.base.projectStructure.moduleInfo
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
|
||||
@VisibleForTesting
|
||||
@IntellijInternalApi
|
||||
@ApiStatus.Internal
|
||||
class WorkspaceCodeAbsentInspection : WorkspaceCodeAbsentInspectionBase() {
|
||||
override fun belongToSameModule(ktClass: KtClassOrObject, otherKtClass: KtClassOrObject): Boolean =
|
||||
ktClass.moduleInfo == otherKtClass.moduleInfo
|
||||
|
||||
override fun getModuleSearchScope(ktClass: KtClassOrObject): GlobalSearchScope =
|
||||
ktClass.moduleInfo.contentScope
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.devkit.workspaceModel.k1.inspections
|
||||
|
||||
import com.intellij.devkit.workspaceModel.inspections.WorkspaceImplObsoleteInspectionBase
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.VisibleForTesting
|
||||
import org.jetbrains.kotlin.idea.base.projectStructure.moduleInfo
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
|
||||
@VisibleForTesting
|
||||
@IntellijInternalApi
|
||||
@ApiStatus.Internal
|
||||
class WorkspaceImplObsoleteInspection : WorkspaceImplObsoleteInspectionBase() {
|
||||
override fun getModuleSearchScope(ktClass: KtClassOrObject): GlobalSearchScope =
|
||||
ktClass.moduleInfo.contentScope
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:OptIn(IntellijInternalApi::class)
|
||||
|
||||
package com.intellij.devkit.workspaceModel.k1.inspections
|
||||
|
||||
import com.intellij.devkit.workspaceModel.inspections.WorkspaceCodeAbsentInspectionBaseTest
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
|
||||
class WorkspaceCodeAbsentInspectionTest : WorkspaceCodeAbsentInspectionBaseTest() {
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
myFixture.enableInspections(WorkspaceCodeAbsentInspection())
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:OptIn(IntellijInternalApi::class)
|
||||
|
||||
package com.intellij.devkit.workspaceModel.k1.inspections
|
||||
|
||||
import com.intellij.devkit.workspaceModel.inspections.WorkspaceEntityMutableFieldInspectionBaseTest
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
|
||||
class WorkspaceEntityMutableFieldInspectionTest : WorkspaceEntityMutableFieldInspectionBaseTest()
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:OptIn(IntellijInternalApi::class)
|
||||
|
||||
package com.intellij.devkit.workspaceModel.k1.inspections
|
||||
|
||||
import com.intellij.devkit.workspaceModel.inspections.WorkspaceImplObsoleteInspectionBaseTest
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
|
||||
class WorkspaceImplObsoleteInspectionTest : WorkspaceImplObsoleteInspectionBaseTest() {
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
myFixture.enableInspections(WorkspaceImplObsoleteInspection())
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:OptIn(IntellijInternalApi::class)
|
||||
|
||||
package com.intellij.devkit.workspaceModel.k1.inspections
|
||||
|
||||
import com.intellij.devkit.workspaceModel.inspections.WorkspaceInheritanceInspection
|
||||
import com.intellij.devkit.workspaceModel.inspections.WorkspaceInspectionBaseTest
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
|
||||
class WorkspaceInheritanceInspectionTest : WorkspaceInspectionBaseTest() {
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
myFixture.enableInspections(WorkspaceInheritanceInspection())
|
||||
}
|
||||
|
||||
fun testInheritance() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testNotWorkspaceAbstract() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testNotWorkspaceClasses() {
|
||||
doTest()
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@ jvm_library(
|
||||
"//platform/core-impl",
|
||||
"//java/openapi:java",
|
||||
"//plugins/kotlin/base/psi",
|
||||
"//platform/analysis-api:analysis",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -75,6 +76,7 @@ jvm_library(
|
||||
"//platform/testFramework",
|
||||
"//platform/testFramework:testFramework_test_lib",
|
||||
"//plugins/kotlin/base/test:test_test_lib",
|
||||
"//platform/analysis-api:analysis",
|
||||
]
|
||||
)
|
||||
### auto-generated section `build intellij.devkit.workspaceModel.k2` end
|
||||
|
||||
@@ -46,5 +46,6 @@
|
||||
<orderEntry type="module" module-name="kotlin.plugin.k2" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
|
||||
<orderEntry type="module" module-name="kotlin.base.test" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.platform.analysis" />
|
||||
</component>
|
||||
</module>
|
||||
+19
@@ -5,9 +5,28 @@
|
||||
<module name="intellij.devkit.workspaceModel"/>
|
||||
</dependencies>
|
||||
|
||||
<resource-bundle>messages.DevKitWorkspaceModelBundle</resource-bundle>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<applicationService serviceInterface="com.intellij.devkit.workspaceModel.metaModel.WorkspaceMetaModelProvider"
|
||||
serviceImplementation="com.intellij.devkit.workspaceModel.k2.metaModel.WorkspaceMetaModelProviderImpl"
|
||||
/>
|
||||
<localInspection language="kotlin"
|
||||
shortName="WorkspaceImplAbsent"
|
||||
projectType="INTELLIJ_PLUGIN"
|
||||
groupBundle="messages.DevKitWorkspaceModelBundle"
|
||||
groupPathKey="inspections.group.path" groupKey="inspections.group.workspace.model"
|
||||
runForWholeFile="true"
|
||||
enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.devkit.workspaceModel.k2.inspections.WorkspaceCodeAbsentInspection"
|
||||
key="inspection.workspace.impl.generation.display.name"/>
|
||||
<localInspection language="kotlin"
|
||||
projectType="INTELLIJ_PLUGIN"
|
||||
groupBundle="messages.DevKitWorkspaceModelBundle"
|
||||
groupPathKey="inspections.group.path" groupKey="inspections.group.workspace.model"
|
||||
runForWholeFile="true"
|
||||
enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.devkit.workspaceModel.k2.inspections.WorkspaceImplObsoleteInspection"
|
||||
key="inspection.workspace.obsolete.model.display.name"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.devkit.workspaceModel.k2.inspections
|
||||
|
||||
import com.intellij.devkit.workspaceModel.inspections.WorkspaceCodeAbsentInspectionBase
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.VisibleForTesting
|
||||
import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.analysis.api.getModule
|
||||
import org.jetbrains.kotlin.idea.base.projectStructure.getKaModule
|
||||
import org.jetbrains.kotlin.idea.base.projectStructure.moduleInfo
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
|
||||
@VisibleForTesting
|
||||
@IntellijInternalApi
|
||||
@ApiStatus.Internal
|
||||
class WorkspaceCodeAbsentInspection : WorkspaceCodeAbsentInspectionBase() {
|
||||
override fun belongToSameModule(ktClass: KtClassOrObject, otherKtClass: KtClassOrObject): Boolean =
|
||||
analyze(ktClass) {
|
||||
getModule(ktClass) == getModule(otherKtClass)
|
||||
}
|
||||
|
||||
override fun getModuleSearchScope(ktClass: KtClassOrObject): GlobalSearchScope =
|
||||
analyze(ktClass) {
|
||||
getModule(ktClass).contentScope
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.devkit.workspaceModel.k2.inspections
|
||||
|
||||
import com.intellij.devkit.workspaceModel.inspections.WorkspaceImplObsoleteInspectionBase
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.VisibleForTesting
|
||||
import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.analysis.api.getModule
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
|
||||
@VisibleForTesting
|
||||
@IntellijInternalApi
|
||||
@ApiStatus.Internal
|
||||
class WorkspaceImplObsoleteInspection : WorkspaceImplObsoleteInspectionBase() {
|
||||
override fun getModuleSearchScope(ktClass: KtClassOrObject): GlobalSearchScope =
|
||||
analyze(ktClass) {
|
||||
getModule(ktClass).contentScope
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:OptIn(IntellijInternalApi::class)
|
||||
|
||||
package com.intellij.devkit.workspaceModel.k2.inspections
|
||||
|
||||
import com.intellij.devkit.workspaceModel.inspections.WorkspaceCodeAbsentInspectionBaseTest
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
|
||||
class WorkspaceCodeAbsentInspectionTest : WorkspaceCodeAbsentInspectionBaseTest() {
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
myFixture.enableInspections(WorkspaceCodeAbsentInspection())
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:OptIn(IntellijInternalApi::class)
|
||||
|
||||
package com.intellij.devkit.workspaceModel.k2.inspections
|
||||
|
||||
import com.intellij.devkit.workspaceModel.inspections.WorkspaceEntityMutableFieldInspectionBaseTest
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
|
||||
class WorkspaceEntityMutableFieldInspectionTest : WorkspaceEntityMutableFieldInspectionBaseTest()
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:OptIn(IntellijInternalApi::class)
|
||||
|
||||
package com.intellij.devkit.workspaceModel.k2.inspections
|
||||
|
||||
import com.intellij.devkit.workspaceModel.inspections.WorkspaceImplObsoleteInspectionBaseTest
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
|
||||
class WorkspaceImplObsoleteInspectionTest : WorkspaceImplObsoleteInspectionBaseTest() {
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
myFixture.enableInspections(WorkspaceImplObsoleteInspection())
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:OptIn(IntellijInternalApi::class)
|
||||
|
||||
package com.intellij.devkit.workspaceModel.k2.inspections
|
||||
|
||||
import com.intellij.devkit.workspaceModel.inspections.WorkspaceInheritanceInspectionBaseTest
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
|
||||
class WorkspaceInheritanceInspectionTest : WorkspaceInheritanceInspectionBaseTest()
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports existence of the obsolete implementation for the entity.
|
||||
Reports the existence of the obsolete implementation for the entity.
|
||||
<p>
|
||||
Verifies that existing implementation for entities has the same API version as described at <code>com.intellij.platform.workspace.storage.CodeGeneratorVersions</code> from dependencies.
|
||||
</p>
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports problems related to workspace entity inheritance.
|
||||
<p>
|
||||
Verifies that an entity inherits from only one and only @Abstract workspace entity.
|
||||
</p>
|
||||
<p>
|
||||
Verifies that an entity does not inherit fromWorkspaceEntity and EntitySource at the same time.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
+9
-18
@@ -23,25 +23,16 @@
|
||||
groupPathKey="inspections.group.path" groupKey="inspections.group.workspace.model"
|
||||
runForWholeFile="true"
|
||||
enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.devkit.workspaceModel.WorkspaceImplObsoleteInspection"
|
||||
key="inspection.workspace.obsolete.model.display.name"/>
|
||||
<localInspection language="kotlin"
|
||||
shortName="WorkspaceImplAbsent"
|
||||
projectType="INTELLIJ_PLUGIN"
|
||||
groupBundle="messages.DevKitWorkspaceModelBundle"
|
||||
groupPathKey="inspections.group.path" groupKey="inspections.group.workspace.model"
|
||||
runForWholeFile="true"
|
||||
enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.devkit.workspaceModel.WorkspaceImplGenerationInspection"
|
||||
key="inspection.workspace.impl.generation.display.name"/>
|
||||
<localInspection language="kotlin"
|
||||
projectType="INTELLIJ_PLUGIN"
|
||||
groupBundle="messages.DevKitWorkspaceModelBundle"
|
||||
groupPathKey="inspections.group.path" groupKey="inspections.group.workspace.model"
|
||||
runForWholeFile="true"
|
||||
enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.devkit.workspaceModel.WorkspaceEntityMutableFieldInspection"
|
||||
implementationClass="com.intellij.devkit.workspaceModel.inspections.WorkspaceEntityMutableFieldInspection"
|
||||
key="inspection.workspace.mutable.field.display.name"/>
|
||||
<localInspection language="kotlin"
|
||||
projectType="INTELLIJ_PLUGIN"
|
||||
groupBundle="messages.DevKitWorkspaceModelBundle"
|
||||
groupPathKey="inspections.group.path" groupKey="inspections.group.workspace.model"
|
||||
runForWholeFile="true"
|
||||
enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.devkit.workspaceModel.inspections.WorkspaceInheritanceInspection"
|
||||
key="inspection.workspace.inheritance.display.name"/>
|
||||
<notificationGroup id="Incompatible codegen api versions"
|
||||
displayType="BALLOON"
|
||||
key="notification.workspace.incompatible.codegen.api.versions"/>
|
||||
|
||||
+13
-3
@@ -2,13 +2,19 @@ action.WorkspaceModelGeneration.text=Generate Workspace Model Implementation
|
||||
inspections.group.path=Plugin DevKit
|
||||
inspections.group.workspace.model=Workspace model
|
||||
inspection.workspace.obsolete.model.display.name=Obsolete version of entity implementation
|
||||
inspection.workspace.impl.generation.display.name=Generate implementation
|
||||
inspection.workspace.msg.obsolete.implementation=Obsolete entity implementation
|
||||
inspection.workspace.msg.regenerate.implementation=Regenerate implementation
|
||||
inspection.workspace.impl.generation.display.name=Generate implementation
|
||||
inspection.workspace.msg.generate.implementation=Generate implementation
|
||||
inspection.workspace.msg.collect.class.metadata=Collect class metadata
|
||||
inspection.workspace.msg.absent.implementation=Absent entity implementation
|
||||
inspection.workspace.mutable.field.display.name=Unsupported 'var' field in entity
|
||||
inspection.workspace.msg.absent.source.metadata=Absent EntitySource metadata
|
||||
inspection.workspace.msg.absent.parent.source.metadata=Absent parent EntitySource metadata: {0}
|
||||
inspection.workspace.msg.entity.and.source.inheritance=Cannot inherit EntitySource and WorkspaceEntity at the same time
|
||||
inspection.workspace.msg.non.abstract.inheritance=Entities can only inherit '@Abstract' entities
|
||||
inspection.workspace.msg.multiple.inheritance=Multiple inheritance is not supported in workspace entities
|
||||
inspection.workspace.msg.user.implementation=Entity implementation has to be generated with the dedicated action
|
||||
inspection.workspace.mutable.field.display.name=Unsupported 'var' field in an entity
|
||||
inspection.workspace.inheritance.display.name=Workspace inheritance
|
||||
inspection.workspace.msg.change.field.to.val=Change to 'val'
|
||||
|
||||
progress.title.generating.code=Generating code
|
||||
@@ -25,3 +31,7 @@ notification.workspace.code.generation.not.available=Workspace code generation n
|
||||
notification.workspace.code.generation.not.available.message=Entity code generation is not supported for plugins yet
|
||||
action.WorkspaceModelGenerateAllModulesAction.text=Generate Workspace Model Implementation for All Modules
|
||||
action.WorkspaceEntitiesJsonClipboard.text=Dump Workspace Entities to Clipboard in JSON Format
|
||||
action.WorkspaceEntitiesJsonLog.text=Dump Workspace Entities to Log in JSON Format
|
||||
action.WorkspaceEntitiesJsonLogFile.text=Dump Workspace Entities to Log File in JSON Format
|
||||
progress.title.dumping.workspace.entities.json.to.clipboard=Dumping workspace entities JSON to clipboard
|
||||
notification.title.cannot.find.log.directory=Cannot find the log directory
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.devkit.workspaceModel
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionTool
|
||||
import com.intellij.codeInspection.LocalQuickFixOnPsiElement
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ProjectRootManager
|
||||
import com.intellij.platform.workspace.storage.WorkspaceEntity
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.workspaceModel.codegen.engine.SKIPPED_TYPES
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinClassShortNameIndex
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtVisitorVoid
|
||||
|
||||
internal class WorkspaceImplGenerationInspection: LocalInspectionTool() {
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object : KtVisitorVoid() {
|
||||
override fun visitClass(klass: KtClass) {
|
||||
val superTypeFqn = klass.getWorkspaceModelSuperType()
|
||||
if (superTypeFqn == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val highlightType: ProblemHighlightType
|
||||
val descriptionTemplate: String
|
||||
if (superTypeFqn == WorkspaceEntity::class.qualifiedName) { //is WorkspaceEntity implementation
|
||||
if (!klass.isInterface()) return
|
||||
if (klass.name in SKIPPED_TYPES) return
|
||||
if (klass.isAbstractEntity()) return
|
||||
if (klass.name == "Builder") return
|
||||
val foundImplClasses = KotlinClassShortNameIndex["${klass.name}Impl", klass.project, GlobalSearchScope.allScope(klass.project)]
|
||||
if (foundImplClasses.isEmpty()) {
|
||||
descriptionTemplate = DevKitWorkspaceModelBundle.message("inspection.workspace.msg.absent.implementation")
|
||||
highlightType = ProblemHighlightType.GENERIC_ERROR_OR_WARNING
|
||||
} else {
|
||||
descriptionTemplate = DevKitWorkspaceModelBundle.message("inspection.workspace.msg.regenerate.implementation")
|
||||
highlightType = ProblemHighlightType.INFORMATION
|
||||
}
|
||||
} else {
|
||||
descriptionTemplate = DevKitWorkspaceModelBundle.message("inspection.workspace.msg.collect.class.metadata")
|
||||
highlightType = ProblemHighlightType.INFORMATION
|
||||
}
|
||||
|
||||
holder.registerProblem(
|
||||
klass.nameIdentifier!!, descriptionTemplate, highlightType,
|
||||
GenerateWorkspaceModelFix(klass.nameIdentifier!!)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GenerateWorkspaceModelFix(psiElement: PsiElement) : LocalQuickFixOnPsiElement(psiElement) {
|
||||
override fun getText() = DevKitWorkspaceModelBundle.message("inspection.workspace.msg.generate.implementation")
|
||||
|
||||
override fun getFamilyName() = name
|
||||
|
||||
override fun invoke(project: Project, psiFile: PsiFile, startElement: PsiElement, endElement: PsiElement) {
|
||||
if (!isIntellijProjectOrRegistryKeyIsSet(project)) {
|
||||
generationNotAvailableNotification(project)
|
||||
return
|
||||
}
|
||||
val projectFileIndex = ProjectRootManager.getInstance(project).fileIndex
|
||||
val module = projectFileIndex.getModuleForFile(psiFile.virtualFile)
|
||||
WorkspaceModelGenerator.getInstance(project).generate(module!!)
|
||||
}
|
||||
|
||||
override fun startInWriteAction(): Boolean = false
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.devkit.workspaceModel
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionTool
|
||||
import com.intellij.codeInspection.LocalQuickFixOnPsiElement
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ProjectRootManager
|
||||
import com.intellij.platform.workspace.storage.CodeGeneratorVersions
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiLiteralExpression
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinClassShortNameIndex
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtVisitorVoid
|
||||
|
||||
private val LOG = logger<WorkspaceImplObsoleteInspection>()
|
||||
|
||||
internal class WorkspaceImplObsoleteInspection: LocalInspectionTool() {
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object : KtVisitorVoid() {
|
||||
override fun visitClass(klass: KtClass) {
|
||||
if (!klass.isWorkspaceEntity()) return
|
||||
val targetApiVersion = calculateTargetApiVersion(klass.resolveScope, klass.project)
|
||||
if (targetApiVersion == null) {
|
||||
LOG.info("Can't evaluate target API version for ${klass.name}")
|
||||
return
|
||||
}
|
||||
if (klass.name == "Builder") return
|
||||
val foundImplClasses = KotlinClassShortNameIndex.get("${klass.name}Impl", klass.project, GlobalSearchScope.allScope(klass.project))
|
||||
if (foundImplClasses.isEmpty()) return
|
||||
val implClass = foundImplClasses.first()
|
||||
val apiVersion = (implClass as? KtClass)?.getApiVersion()
|
||||
if (apiVersion == targetApiVersion) return
|
||||
holder.registerProblem(klass.nameIdentifier!!, DevKitWorkspaceModelBundle.message("inspection.workspace.msg.obsolete.implementation"),
|
||||
RegenerateWorkspaceModelFix(klass.nameIdentifier!!))
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateTargetApiVersion(scope: GlobalSearchScope, project: Project): Int? {
|
||||
val generatorVersionsClass = JavaPsiFacade.getInstance(project).findClass(CodeGeneratorVersions::class.java.name, scope) ?: return null
|
||||
val versionField = generatorVersionsClass.findFieldByName("API_VERSION_INTERNAL", false) ?: return null
|
||||
return (versionField.initializer as? PsiLiteralExpression)?.value as? Int
|
||||
}
|
||||
}
|
||||
|
||||
private class RegenerateWorkspaceModelFix(psiElement: PsiElement) : LocalQuickFixOnPsiElement(psiElement) {
|
||||
override fun getText() = DevKitWorkspaceModelBundle.message("inspection.workspace.msg.regenerate.implementation")
|
||||
|
||||
override fun getFamilyName() = name
|
||||
|
||||
override fun invoke(project: Project, psiFile: PsiFile, startElement: PsiElement, endElement: PsiElement) {
|
||||
if (!isIntellijProjectOrRegistryKeyIsSet(project)) {
|
||||
generationNotAvailableNotification(project)
|
||||
return
|
||||
}
|
||||
val projectFileIndex = ProjectRootManager.getInstance(project).fileIndex
|
||||
val module = projectFileIndex.getModuleForFile(psiFile.virtualFile)
|
||||
WorkspaceModelGenerator.getInstance(project).generate(module!!)
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.devkit.workspaceModel
|
||||
|
||||
import com.intellij.platform.workspace.storage.*
|
||||
import com.intellij.platform.workspace.storage.annotations.Abstract
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.parsing.parseNumericLiteral
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtConstantExpression
|
||||
import org.jetbrains.kotlin.psi.KtSuperTypeListEntry
|
||||
import org.jetbrains.kotlin.psi.KtUserType
|
||||
import java.util.*
|
||||
|
||||
private val workspaceModelClasses: List<String> = listOfNotNull(
|
||||
WorkspaceEntity::class.qualifiedName,
|
||||
EntitySource::class.qualifiedName,
|
||||
SymbolicEntityId::class.qualifiedName
|
||||
)
|
||||
|
||||
/**
|
||||
* Finds which of the [workspaceModelClasses] inherits this KtClass.
|
||||
*
|
||||
* @return parent class fully qualified name if inherits or null
|
||||
*/
|
||||
internal fun KtClass.getWorkspaceModelSuperType(): String? {
|
||||
val superTypeList = LinkedList<KtSuperTypeListEntry>()
|
||||
superTypeList.addAll(superTypeListEntries)
|
||||
while (!superTypeList.isEmpty()) {
|
||||
val superType = superTypeList.pop()
|
||||
val resolvedKtClass = (superType.typeReference?.typeElement as? KtUserType)?.referenceExpression?.mainReference?.resolve() as? KtClass
|
||||
?: continue
|
||||
val resolvedKtClassFqn = resolvedKtClass.fqName?.asString()
|
||||
if (workspaceModelClasses.contains(resolvedKtClassFqn)) return resolvedKtClassFqn
|
||||
resolvedKtClass.superTypeListEntries.forEach { superTypeList.push(it) }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun KtClass.isWorkspaceEntity(): Boolean {
|
||||
if (!isInterface()) return false
|
||||
val superTypeFqn = getWorkspaceModelSuperType() ?: return false
|
||||
return superTypeFqn == WorkspaceEntity::class.qualifiedName
|
||||
}
|
||||
|
||||
internal fun KtClass.isAbstractEntity(): Boolean {
|
||||
val annotationName = Abstract::class.simpleName
|
||||
return annotationEntries.any { it.shortName?.identifier == annotationName }
|
||||
}
|
||||
|
||||
internal fun KtClass.getApiVersion(): Int? {
|
||||
val annotationName = GeneratedCodeApiVersion::class.simpleName
|
||||
val annotation = annotationEntries.find { it.shortName?.identifier == annotationName }
|
||||
if (annotation == null) {
|
||||
error("$name should contain $annotationName")
|
||||
}
|
||||
if (annotation.valueArguments.size != 1) {
|
||||
error("Annotation $annotationName at $name should contain only one argument")
|
||||
}
|
||||
val argumentExpression = annotation.valueArguments[0].getArgumentExpression() as? KtConstantExpression
|
||||
if (argumentExpression == null) {
|
||||
error("Annotation parameter should be int constant")
|
||||
}
|
||||
val elementType = argumentExpression.node.elementType
|
||||
if (elementType == KtNodeTypes.INTEGER_CONSTANT) {
|
||||
return parseNumericLiteral(argumentExpression.text, elementType)?.toInt()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun KtClass.getImplVersion(): Int? {
|
||||
val annotationName = GeneratedCodeImplVersion::class.simpleName
|
||||
val annotation = annotationEntries.find { it.shortName?.identifier == annotationName }
|
||||
if (annotation == null) {
|
||||
error("$name should contain $annotationName")
|
||||
}
|
||||
if (annotation.valueArguments.size != 1) {
|
||||
error("Annotation $annotationName at $name should contain only one argument")
|
||||
}
|
||||
val argumentExpression = annotation.valueArguments[0].getArgumentExpression() as? KtConstantExpression
|
||||
if (argumentExpression == null) {
|
||||
error("Annotation parameter should be int constant")
|
||||
}
|
||||
val elementType = argumentExpression.node.elementType
|
||||
if (elementType == KtNodeTypes.INTEGER_CONSTANT) {
|
||||
return parseNumericLiteral(argumentExpression.text, elementType)?.toInt()
|
||||
}
|
||||
return null
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.devkit.workspaceModel.inspections
|
||||
|
||||
import com.intellij.codeInspection.LocalQuickFixOnPsiElement
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.devkit.workspaceModel.DevKitWorkspaceModelBundle
|
||||
import com.intellij.devkit.workspaceModel.WorkspaceModelGenerator
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ProjectRootManager
|
||||
import com.intellij.platform.workspace.storage.metadata.impl.MetadataStorageBase
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.workspaceModel.codegen.engine.SKIPPED_TYPES
|
||||
import org.jetbrains.kotlin.idea.base.psi.KotlinPsiHeuristics
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinClassShortNameIndex
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtVisitorVoid
|
||||
|
||||
abstract class WorkspaceCodeAbsentInspectionBase : WorkspaceInspectionBase() {
|
||||
protected abstract fun belongToSameModule(ktClass: KtClassOrObject, otherKtClass: KtClassOrObject): Boolean
|
||||
|
||||
private fun entitySourceIsPresentInMetadata(ktClass: KtClassOrObject): Boolean {
|
||||
val jvmName = KotlinPsiHeuristics.getJvmName(ktClass) ?: return true
|
||||
val searchScope = getModuleSearchScope(ktClass)
|
||||
val moduleMetadata = KotlinClassShortNameIndex["MetadataStorageImpl", ktClass.project, searchScope]
|
||||
val metadataFile: PsiFile = moduleMetadata.firstOrNull { metadataObject ->
|
||||
belongToSameModule(metadataObject, ktClass) && metadataObject.getMatchingSuperTypes { superType ->
|
||||
superType.fqName?.asString() == MetadataStorageBase::class.qualifiedName
|
||||
}.any()
|
||||
}?.containingFile ?: return false
|
||||
// String.escapeDollar codegen/impl/metadata/StringExtensions.kt
|
||||
val workspaceMetaName = jvmName.replace("$", "\\$")
|
||||
// TODO: improve
|
||||
val jvmNameOccurrence = metadataFile.getFileDocument().getImmutableCharSequence().indexOf(workspaceMetaName)
|
||||
return jvmNameOccurrence != -1
|
||||
}
|
||||
|
||||
private fun findSuperSourceAbsentInMetadata(ktClass: KtClassOrObject, visited: MutableSet<KtClass>): String? {
|
||||
for (superType in ktClass.superTypeListEntries) {
|
||||
val resolvedSuper = superType.typeReference?.resolveToKtClass() ?: continue
|
||||
if (!visited.add(resolvedSuper)) continue
|
||||
if (resolvedSuper.isWorkspaceEntitySource()) {
|
||||
if (!entitySourceIsPresentInMetadata(resolvedSuper)) return resolvedSuper.name
|
||||
val absentSuper = findSuperSourceAbsentInMetadata(resolvedSuper, visited)
|
||||
if (absentSuper != null) return absentSuper
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun processWorkspaceEntityDeclaration(klass: KtClass, holder: ProblemsHolder) {
|
||||
val psiElementForHighlighting = klass.getPsiElementForHighlighting() ?: return
|
||||
val highlightType: ProblemHighlightType
|
||||
val descriptionTemplate: String
|
||||
|
||||
if (klass.name in SKIPPED_TYPES) return
|
||||
if (klass.isAbstractEntity()) return
|
||||
val foundImpl = findWorkspaceEntityImplementation(klass, getModuleSearchScope(klass))
|
||||
if (foundImpl == null) {
|
||||
descriptionTemplate = DevKitWorkspaceModelBundle.message("inspection.workspace.msg.absent.implementation")
|
||||
highlightType = ProblemHighlightType.GENERIC_ERROR_OR_WARNING
|
||||
}
|
||||
else {
|
||||
descriptionTemplate = DevKitWorkspaceModelBundle.message("inspection.workspace.msg.regenerate.implementation")
|
||||
highlightType = ProblemHighlightType.INFORMATION
|
||||
}
|
||||
|
||||
holder.registerProblem(
|
||||
psiElementForHighlighting, descriptionTemplate, highlightType,
|
||||
GenerateWorkspaceModelFix(psiElementForHighlighting)
|
||||
)
|
||||
}
|
||||
|
||||
private fun processEntitySource(klass: KtClassOrObject, holder: ProblemsHolder) {
|
||||
val psiElementForHighlighting = klass.getPsiElementForHighlighting() ?: return
|
||||
val highlightType: ProblemHighlightType
|
||||
val descriptionTemplate: String
|
||||
|
||||
if (!entitySourceIsPresentInMetadata(klass)) {
|
||||
descriptionTemplate = DevKitWorkspaceModelBundle.message("inspection.workspace.msg.absent.source.metadata")
|
||||
highlightType = ProblemHighlightType.GENERIC_ERROR_OR_WARNING
|
||||
}
|
||||
else {
|
||||
val absentSuper = findSuperSourceAbsentInMetadata(klass, mutableSetOf()) ?: return
|
||||
descriptionTemplate = DevKitWorkspaceModelBundle.message("inspection.workspace.msg.absent.parent.source.metadata", absentSuper)
|
||||
highlightType = ProblemHighlightType.GENERIC_ERROR_OR_WARNING
|
||||
}
|
||||
|
||||
holder.registerProblem(
|
||||
psiElementForHighlighting, descriptionTemplate, highlightType,
|
||||
GenerateWorkspaceModelFix(psiElementForHighlighting)
|
||||
)
|
||||
}
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): KtVisitorVoid = object : KtVisitorVoid() {
|
||||
override fun visitClassOrObject(klass: KtClassOrObject) {
|
||||
val superTypesFqns = klass.getWorkspaceSupers()
|
||||
if (superTypesFqns.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (klass is KtClass && klass.isWorkspaceEntityDeclaration()) {
|
||||
processWorkspaceEntityDeclaration(klass, holder)
|
||||
}
|
||||
else if (klass.isWorkspaceEntitySource()) {
|
||||
processEntitySource(klass, holder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GenerateWorkspaceModelFix(psiElement: PsiElement) : LocalQuickFixOnPsiElement(psiElement) {
|
||||
override fun getText() = DevKitWorkspaceModelBundle.message("inspection.workspace.msg.generate.implementation")
|
||||
|
||||
override fun getFamilyName() = name
|
||||
|
||||
override fun invoke(project: Project, psiFile: PsiFile, startElement: PsiElement, endElement: PsiElement) {
|
||||
val projectFileIndex = ProjectRootManager.getInstance(project).fileIndex
|
||||
val module = projectFileIndex.getModuleForFile(psiFile.virtualFile)
|
||||
WorkspaceModelGenerator.getInstance(project).generate(module!!)
|
||||
}
|
||||
|
||||
override fun startInWriteAction(): Boolean = false
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -1,7 +1,8 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.devkit.workspaceModel
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.devkit.workspaceModel.inspections
|
||||
|
||||
import com.intellij.codeInspection.*
|
||||
import com.intellij.devkit.workspaceModel.DevKitWorkspaceModelBundle
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
@@ -15,10 +16,9 @@ import org.jetbrains.kotlin.psi.KtVisitorVoid
|
||||
@IntellijInternalApi
|
||||
@ApiStatus.Internal
|
||||
class WorkspaceEntityMutableFieldInspection: LocalInspectionTool() {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object : KtVisitorVoid() {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): KtVisitorVoid = object : KtVisitorVoid() {
|
||||
override fun visitClass(klass: KtClass) {
|
||||
if (!klass.isWorkspaceEntity()) return
|
||||
if (klass.name == "Builder") return
|
||||
if (!klass.isWorkspaceEntityDeclaration()) return
|
||||
klass.getProperties().forEach { property ->
|
||||
if (property.isVar) {
|
||||
holder.registerProblem(property, DevKitWorkspaceModelBundle.message("inspection.workspace.mutable.field.display.name"),
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.devkit.workspaceModel.inspections
|
||||
|
||||
import com.intellij.codeInspection.LocalQuickFixOnPsiElement
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.devkit.workspaceModel.DevKitWorkspaceModelBundle
|
||||
import com.intellij.devkit.workspaceModel.WorkspaceModelGenerator
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ProjectRootManager
|
||||
import com.intellij.platform.workspace.storage.CodeGeneratorVersions
|
||||
import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion
|
||||
import com.intellij.platform.workspace.storage.GeneratedCodeImplVersion
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiLiteralExpression
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.parsing.parseNumericLiteral
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtConstantExpression
|
||||
import org.jetbrains.kotlin.psi.KtVisitorVoid
|
||||
|
||||
private val LOG = logger<WorkspaceImplObsoleteInspectionBase>()
|
||||
|
||||
abstract class WorkspaceImplObsoleteInspectionBase : WorkspaceInspectionBase() {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): KtVisitorVoid = object : KtVisitorVoid() {
|
||||
override fun visitClass(klass: KtClass) {
|
||||
val psiElementForHighlighting = klass.getPsiElementForHighlighting() ?: return
|
||||
if (!klass.isWorkspaceEntityDeclaration()) return
|
||||
val foundImplClass = findWorkspaceEntityImplementation(klass, getModuleSearchScope(klass))
|
||||
if (foundImplClass == null) return
|
||||
|
||||
val targetApiVersion = calculateTargetApiVersion(klass.resolveScope, klass.project)
|
||||
if (targetApiVersion == null) {
|
||||
LOG.info("Can't evaluate target API version for ${klass.name}")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
val implApiVersion = foundImplClass.getApiVersion()
|
||||
if (implApiVersion == targetApiVersion) return
|
||||
holder.registerProblem(psiElementForHighlighting, DevKitWorkspaceModelBundle.message("inspection.workspace.msg.obsolete.implementation"),
|
||||
RegenerateWorkspaceModelFix(psiElementForHighlighting))
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateTargetApiVersion(scope: GlobalSearchScope, project: Project): Int? {
|
||||
val generatorVersionsClass = JavaPsiFacade.getInstance(project).findClass(CodeGeneratorVersions::class.java.name, scope) ?: return null
|
||||
val versionField = generatorVersionsClass.findFieldByName("API_VERSION_INTERNAL", false) ?: return null
|
||||
return (versionField.initializer as? PsiLiteralExpression)?.value as? Int
|
||||
}
|
||||
|
||||
private class RegenerateWorkspaceModelFix(psiElement: PsiElement) : LocalQuickFixOnPsiElement(psiElement) {
|
||||
override fun getText() = DevKitWorkspaceModelBundle.message("inspection.workspace.msg.regenerate.implementation")
|
||||
|
||||
override fun getFamilyName() = name
|
||||
|
||||
override fun invoke(project: Project, psiFile: PsiFile, startElement: PsiElement, endElement: PsiElement) {
|
||||
val projectFileIndex = ProjectRootManager.getInstance(project).fileIndex
|
||||
val module = projectFileIndex.getModuleForFile(psiFile.virtualFile)
|
||||
WorkspaceModelGenerator.getInstance(project).generate(module!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtClass.getGeneratedAnnotationVersion(annotationFqName: String): Int? {
|
||||
val annotation = findAnnotation(annotationFqName) ?: return null
|
||||
if (annotation.valueArguments.size != 1) {
|
||||
LOG.warn("Annotation $annotationFqName at $name should contain exactly one argument")
|
||||
return null
|
||||
}
|
||||
val argumentExpression = annotation.valueArguments[0].getArgumentExpression() as? KtConstantExpression
|
||||
val elementType = argumentExpression?.node?.elementType
|
||||
if (elementType != KtNodeTypes.INTEGER_CONSTANT) {
|
||||
LOG.warn("Annotation parameter of $annotationFqName at $name should be an int constant")
|
||||
return null
|
||||
}
|
||||
return parseNumericLiteral(argumentExpression.text, elementType)?.toInt()
|
||||
}
|
||||
|
||||
private fun KtClass.getApiVersion(): Int? {
|
||||
val annotationFqName = GeneratedCodeApiVersion::class.qualifiedName!!
|
||||
return getGeneratedAnnotationVersion(annotationFqName)
|
||||
}
|
||||
|
||||
private fun KtClass.getImplVersion(): Int? {
|
||||
val annotationFqName = GeneratedCodeImplVersion::class.simpleName!!
|
||||
return getGeneratedAnnotationVersion(annotationFqName)
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.devkit.workspaceModel.inspections
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionTool
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.devkit.workspaceModel.DevKitWorkspaceModelBundle
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.VisibleForTesting
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtVisitorVoid
|
||||
|
||||
@VisibleForTesting
|
||||
@IntellijInternalApi
|
||||
@ApiStatus.Internal
|
||||
class WorkspaceInheritanceInspection : LocalInspectionTool() {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): KtVisitorVoid = object : KtVisitorVoid() {
|
||||
override fun visitClassOrObject(klass: KtClassOrObject) {
|
||||
val psiForHighlighting = klass.getPsiElementForHighlighting() ?: return
|
||||
if (klass !is KtClass) return
|
||||
if (!klass.isWorkspaceEntity()) return
|
||||
if (klass.isWorkspaceEntityImplementation()) return
|
||||
if (klass.isWorkspaceEntitySource()) {
|
||||
holder.registerProblem(psiForHighlighting,
|
||||
DevKitWorkspaceModelBundle.message("inspection.workspace.msg.entity.and.source.inheritance"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
|
||||
return
|
||||
}
|
||||
|
||||
if (!klass.isInterface()) {
|
||||
holder.registerProblem(psiForHighlighting,
|
||||
DevKitWorkspaceModelBundle.message("inspection.workspace.msg.user.implementation"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
|
||||
return
|
||||
}
|
||||
|
||||
val visited = mutableSetOf<KtClass>()
|
||||
val superEntities = mutableSetOf<KtClass>()
|
||||
for (superType in klass.superTypeListEntries) {
|
||||
val resolvedSuper = superType.typeReference?.resolveToKtClass() ?: continue
|
||||
if (!visited.add(resolvedSuper)) continue
|
||||
if (!resolvedSuper.isWorkspaceEntity()) continue
|
||||
if (resolvedSuper.isAbstractEntity()) {
|
||||
superEntities.add(resolvedSuper)
|
||||
continue
|
||||
}
|
||||
holder.registerProblem(psiForHighlighting,
|
||||
DevKitWorkspaceModelBundle.message("inspection.workspace.msg.non.abstract.inheritance"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
|
||||
return
|
||||
}
|
||||
if (superEntities.size > 1) {
|
||||
holder.registerProblem(psiForHighlighting,
|
||||
DevKitWorkspaceModelBundle.message("inspection.workspace.msg.multiple.inheritance"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.devkit.workspaceModel.inspections
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionTool
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
|
||||
abstract class WorkspaceInspectionBase : LocalInspectionTool() {
|
||||
protected abstract fun getModuleSearchScope(ktClass: KtClassOrObject): GlobalSearchScope
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.devkit.workspaceModel.inspections
|
||||
|
||||
import com.intellij.platform.workspace.storage.EntitySource
|
||||
import com.intellij.platform.workspace.storage.SymbolicEntityId
|
||||
import com.intellij.platform.workspace.storage.WorkspaceEntity
|
||||
import com.intellij.platform.workspace.storage.annotations.Abstract
|
||||
import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import com.intellij.psi.util.PsiModificationTracker
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinClassShortNameIndex
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClass
|
||||
import java.util.*
|
||||
import java.util.function.Predicate
|
||||
|
||||
private val workspaceModelClasses: List<String> = listOfNotNull(
|
||||
WorkspaceEntity::class.qualifiedName,
|
||||
EntitySource::class.qualifiedName,
|
||||
SymbolicEntityId::class.qualifiedName,
|
||||
WorkspaceEntity.Builder::class.qualifiedName,
|
||||
WorkspaceEntityBase::class.qualifiedName
|
||||
)
|
||||
|
||||
/**
|
||||
* Finds which [workspaceModelClasses] this KtClassOrObject inherits and caches the result.
|
||||
*
|
||||
* @return set of parent classes' fully qualified names
|
||||
*/
|
||||
internal fun KtClassOrObject.getWorkspaceSupers(): Set<String> {
|
||||
return CachedValuesManager.getCachedValue(this) {
|
||||
val workspaceSupersSequence = getMatchingSuperTypes { ktClass ->
|
||||
workspaceModelClasses.contains(ktClass.fqName?.asString())
|
||||
}
|
||||
val workspaceSupers = workspaceSupersSequence.mapNotNull { it.fqName?.asString() }.toSet()
|
||||
CachedValueProvider.Result(workspaceSupers, PsiModificationTracker.MODIFICATION_COUNT)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun KtTypeReference.resolveToKtClass(): KtClass? {
|
||||
val resolvedReference = (typeElement as? KtUserType)?.referenceExpression?.mainReference?.resolve()
|
||||
return when (resolvedReference) {
|
||||
is KtClass -> resolvedReference
|
||||
is KtConstructor<*> -> resolvedReference.containingClass()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
internal fun KtClassOrObject.getMatchingSuperTypes(predicate: Predicate<KtClass>): Sequence<KtClass> = sequence {
|
||||
val visited = mutableSetOf<KtClass>()
|
||||
val superTypeList = LinkedList<KtSuperTypeListEntry>()
|
||||
superTypeList.addAll(superTypeListEntries)
|
||||
while (!superTypeList.isEmpty()) {
|
||||
val superType = superTypeList.pop()
|
||||
val resolvedKtClass = superType.typeReference?.resolveToKtClass() ?: continue
|
||||
if (!visited.add(resolvedKtClass)) continue
|
||||
if (predicate.test(resolvedKtClass)) yield(resolvedKtClass)
|
||||
resolvedKtClass.superTypeListEntries.forEach { superTypeList.push(it) }
|
||||
}
|
||||
}
|
||||
|
||||
internal fun findWorkspaceEntityImplementation(entityClass: KtClass, searchScope: GlobalSearchScope): KtClass? {
|
||||
val thisFqName = entityClass.fqName?.asString() ?: return null
|
||||
val foundImplClasses = KotlinClassShortNameIndex["${entityClass.name}Impl", entityClass.project, searchScope]
|
||||
val foundEntityImpls = foundImplClasses.filter { it is KtClass && it.isWorkspaceEntityImplementation() }
|
||||
val foundImpl = foundEntityImpls.find { someImpl -> someImpl.getMatchingSuperTypes { it.fqName?.asString() == thisFqName }.any() }
|
||||
return foundImpl as? KtClass
|
||||
}
|
||||
|
||||
internal fun KtClass.isWorkspaceEntity(): Boolean {
|
||||
return getWorkspaceSupers().contains(WorkspaceEntity::class.qualifiedName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a class is an **interface** that extends WorkspaceEntity, but not WorkspaceEntity.Builder, meaning that it is a declaration of
|
||||
* an entity.
|
||||
*/
|
||||
internal fun KtClass.isWorkspaceEntityDeclaration(): Boolean {
|
||||
if (!isInterface()) return false
|
||||
val workspaceSupers = getWorkspaceSupers()
|
||||
return workspaceSupers.contains(WorkspaceEntity::class.qualifiedName) &&
|
||||
!workspaceSupers.contains(WorkspaceEntity.Builder::class.qualifiedName)
|
||||
}
|
||||
|
||||
internal fun KtClass.isWorkspaceEntityImplementation(): Boolean {
|
||||
val workspaceSupers = getWorkspaceSupers()
|
||||
return workspaceSupers.contains(WorkspaceEntity::class.qualifiedName) && workspaceSupers.contains(WorkspaceEntityBase::class.qualifiedName)
|
||||
}
|
||||
|
||||
internal fun KtClassOrObject.isWorkspaceEntitySource(): Boolean {
|
||||
return getWorkspaceSupers().contains(EntitySource::class.qualifiedName)
|
||||
}
|
||||
|
||||
internal fun KtClass.findAnnotation(annotationFqName: String): KtAnnotationEntry? {
|
||||
for (annotationEntry in annotationEntries) {
|
||||
val annotationClass = annotationEntry.typeReference?.resolveToKtClass() ?: continue
|
||||
if (annotationClass.fqName?.asString() == annotationFqName) return annotationEntry
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun KtClass.isAbstractEntity(): Boolean {
|
||||
val annotationFqName = Abstract::class.qualifiedName!!
|
||||
return findAnnotation(annotationFqName) != null
|
||||
}
|
||||
|
||||
internal fun KtClassOrObject.getPsiElementForHighlighting(): PsiElement? = nameIdentifier ?: firstChild
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.intellij.workspaceModel.test.api
|
||||
|
||||
import com.intellij.platform.workspace.storage.WorkspaceEntity
|
||||
import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase
|
||||
|
||||
interface <warning descr="Absent entity implementation">EntityWithoutImplementation</warning> : WorkspaceEntity {
|
||||
val property: String
|
||||
val isValid: Boolean
|
||||
}
|
||||
|
||||
interface <warning descr="Absent entity implementation">EntityWithFakeImplementation</warning> : WorkspaceEntity {
|
||||
val property: String
|
||||
val isValid: Boolean
|
||||
}
|
||||
|
||||
internal class EntityWithFakeImplementationImpl() : EntityWithFakeImplementation {
|
||||
override val property: String = ""
|
||||
override val isValid: Boolean = false
|
||||
}
|
||||
|
||||
interface EntityWithImplementation : WorkspaceEntity {
|
||||
val property: String
|
||||
val isValid: Boolean
|
||||
}
|
||||
|
||||
internal class EntityWithImplementationImpl() : EntityWithImplementation, WorkspaceEntityBase() {
|
||||
override val property: String = ""
|
||||
override val isValid: Boolean = false
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.intellij.workspaceModel.test.api
|
||||
|
||||
import com.intellij.platform.workspace.storage.EntitySource
|
||||
|
||||
// Fake MetadataStorageBase
|
||||
abstract class MetadataStorageBase() {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
protected fun addMetadataHash(typeFqn: String, metadataHash: Int) {
|
||||
}
|
||||
|
||||
protected abstract fun initializeMetadataHash()
|
||||
}
|
||||
|
||||
internal object MetadataStorageImpl : MetadataStorageBase() {
|
||||
override fun initializeMetadataHash() {
|
||||
addMetadataHash(typeFqn = "com.intellij.platform.workspace.storage.EntitySource", metadataHash = 0)
|
||||
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.PresentObjectSource", metadataHash = 0)
|
||||
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.PresentClassSource", metadataHash = 0)
|
||||
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.PresentInterfaceSource", metadataHash = 0)
|
||||
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.PresentInheritedPresentSource", metadataHash = 0)
|
||||
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.OuterClass1\$PresentInnerEntitySource", metadataHash = 0)
|
||||
}
|
||||
}
|
||||
|
||||
object <warning descr="Absent EntitySource metadata">PresentObjectSource</warning> : EntitySource
|
||||
|
||||
class <warning descr="Absent EntitySource metadata">PresentClassSource</warning> : EntitySource
|
||||
|
||||
interface <warning descr="Absent EntitySource metadata">PresentInterfaceSource</warning> : EntitySource
|
||||
|
||||
class <warning descr="Absent EntitySource metadata">PresentInheritedPresentSource</warning> : PresentInterfaceSource
|
||||
|
||||
class OuterClass1 {
|
||||
class <warning descr="Absent EntitySource metadata">PresentInnerEntitySource</warning> : EntitySource
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.intellij.workspaceModel.test.api
|
||||
|
||||
import com.intellij.platform.workspace.storage.EntitySource
|
||||
import com.intellij.platform.workspace.storage.metadata.impl.MetadataStorageBase
|
||||
|
||||
// Present in metadata
|
||||
|
||||
internal object MetadataStorageImpl: MetadataStorageBase() {
|
||||
override fun initializeMetadataHash() {
|
||||
addMetadataHash(typeFqn = "com.intellij.platform.workspace.storage.EntitySource", metadataHash = 0)
|
||||
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.PresentObjectSource", metadataHash = 0)
|
||||
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.PresentClassSource", metadataHash = 0)
|
||||
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.PresentInterfaceSource", metadataHash = 0)
|
||||
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.PresentInheritedPresentSource", metadataHash = 0)
|
||||
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.OuterClass1\$PresentInnerEntitySource", metadataHash = 0)
|
||||
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.PresentInheritedAbsentSource", metadataHash = 0)
|
||||
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.PresentRecursive1", metadataHash = 0)
|
||||
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.PresentRecursive2", metadataHash = 0)
|
||||
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.PresentRecursiveParentAbsent", metadataHash = 0)
|
||||
}
|
||||
}
|
||||
|
||||
object PresentObjectSource : EntitySource
|
||||
|
||||
class PresentClassSource : EntitySource
|
||||
|
||||
interface PresentInterfaceSource : EntitySource
|
||||
|
||||
class PresentInheritedPresentSource : PresentInterfaceSource
|
||||
|
||||
class OuterClass1 {
|
||||
class PresentInnerEntitySource : EntitySource
|
||||
}
|
||||
|
||||
// Absent in metadata
|
||||
|
||||
object <warning descr="Absent EntitySource metadata">AbsentObjectSource</warning> : EntitySource
|
||||
|
||||
class <warning descr="Absent EntitySource metadata">AbsentClassSource</warning> : EntitySource
|
||||
|
||||
interface <warning descr="Absent EntitySource metadata">AbsentInterfaceSource</warning> : EntitySource
|
||||
|
||||
class <warning descr="Absent EntitySource metadata">AbsentInheritedAbsentSource</warning> : AbsentInterfaceSource
|
||||
|
||||
class <warning descr="Absent parent EntitySource metadata: AbsentInterfaceSource">PresentInheritedAbsentSource</warning> : AbsentInterfaceSource
|
||||
|
||||
class OuterClass2 {
|
||||
class <warning descr="Absent EntitySource metadata">AbsentInnerEntitySource</warning> : EntitySource
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package com.intellij.workspaceModel.test.api
|
||||
|
||||
import com.intellij.platform.workspace.storage.WorkspaceEntity
|
||||
import com.intellij.platform.workspace.storage.EntitySource
|
||||
import com.intellij.platform.workspace.storage.annotations.Abstract
|
||||
import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase
|
||||
|
||||
interface EntityWithFakeImplementation : WorkspaceEntity {
|
||||
val property: String
|
||||
val isValid: Boolean
|
||||
}
|
||||
|
||||
internal class <warning descr="Entity implementation has to be generated with the dedicated action">EntityWithFakeImplementationImpl</warning> : EntityWithFakeImplementation {
|
||||
override val property: String = ""
|
||||
override val isValid: Boolean = false
|
||||
}
|
||||
|
||||
interface EntityWithImplementation : WorkspaceEntity {
|
||||
val property: String
|
||||
val isValid: Boolean
|
||||
}
|
||||
|
||||
internal class EntityWithImplementationImpl() : EntityWithImplementation, WorkspaceEntityBase() {
|
||||
override val property: String = ""
|
||||
override val isValid: Boolean = false
|
||||
}
|
||||
|
||||
interface OnlySource : EntitySource
|
||||
|
||||
object AnotherOnlySource : EntitySource
|
||||
|
||||
interface <warning descr="Cannot inherit EntitySource and WorkspaceEntity at the same time">EntityAndSource</warning> : WorkspaceEntity, EntitySource {
|
||||
val property: String
|
||||
}
|
||||
|
||||
interface SomeEntity : WorkspaceEntity
|
||||
|
||||
interface <warning descr="Entities can only inherit '@Abstract' entities">InheritsNonAbstract</warning> : SomeEntity
|
||||
|
||||
<warning descr="Entities can only inherit '@Abstract' entities">interface</warning><error descr="Name expected"> </error>: SomeEntity
|
||||
|
||||
@Abstract
|
||||
interface AbstractEntity1 : WorkspaceEntity
|
||||
|
||||
interface NonAbstractEntity1 : AbstractEntity1
|
||||
|
||||
@Abstract
|
||||
interface AbstractEntity2 : WorkspaceEntity
|
||||
|
||||
interface NonAbstractEntity2 : AbstractEntity2
|
||||
|
||||
interface <warning descr="Multiple inheritance is not supported in workspace entities">NonAbstractEntity3</warning> : AbstractEntity1, AbstractEntity2
|
||||
|
||||
interface <warning descr="Entities can only inherit '@Abstract' entities">NonAbstractEntity4</warning> : AbstractEntity1, NonAbstractEntity2
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.intellij.workspaceModel.test.api
|
||||
|
||||
import com.intellij.platform.workspace.storage.WorkspaceEntity
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
annotation class Abstract
|
||||
|
||||
@Abstract
|
||||
interface NotWorkspaceAbstract : WorkspaceEntity
|
||||
|
||||
interface <warning descr="Entities can only inherit '@Abstract' entities">SomeEntity</warning> : NotWorkspaceAbstract
|
||||
|
||||
@com.intellij.platform.workspace.storage.annotations.Abstract
|
||||
interface WorkspaceAbstract : WorkspaceEntity
|
||||
|
||||
interface AnotherEntity : WorkspaceAbstract
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.intellij.workspaceModel.test.api
|
||||
|
||||
import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase
|
||||
|
||||
interface WorkspaceEntity
|
||||
|
||||
interface NotWorkspaceEntity : WorkspaceEntity {
|
||||
var property: String
|
||||
}
|
||||
|
||||
interface AnotherNotWorkspaceEntity : WorkspaceEntity {
|
||||
var flag: Boolean
|
||||
}
|
||||
|
||||
interface EntitySource
|
||||
|
||||
object NotWorkspaceSource : EntitySource
|
||||
|
||||
class AnotherNotWorkspaceSource : EntitySource
|
||||
|
||||
interface NotWorkspaceMultipleInheritance : NotWorkspaceEntity, AnotherNotWorkspaceEntity, EntitySource
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
annotation class GeneratedCodeApiVersion(val version: Int)
|
||||
|
||||
interface EntityWithOboleteImplementation : WorkspaceEntity
|
||||
|
||||
@GeneratedCodeApiVersion(2)
|
||||
internal class EntityWithOboleteImplementationImpl() : EntityWithOboleteImplementation, WorkspaceEntityBase()
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.intellij.workspaceModel.test.api
|
||||
|
||||
import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion
|
||||
import com.intellij.platform.workspace.storage.WorkspaceEntity
|
||||
import com.intellij.platform.workspace.storage.annotations.Abstract
|
||||
import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase
|
||||
|
||||
interface EntityWithFakeImplementation : WorkspaceEntity
|
||||
|
||||
internal class EntityWithFakeImplementationImpl() : EntityWithFakeImplementation
|
||||
|
||||
interface <warning descr="Obsolete entity implementation">EntityWithOboleteImplementation</warning> : WorkspaceEntity
|
||||
|
||||
@GeneratedCodeApiVersion(2)
|
||||
internal class EntityWithOboleteImplementationImpl() : EntityWithOboleteImplementation, WorkspaceEntityBase()
|
||||
|
||||
interface EntityWithCorrectImplementation : WorkspaceEntity
|
||||
|
||||
@GeneratedCodeApiVersion(3)
|
||||
internal class EntityWithCorrectImplementationImpl() : EntityWithCorrectImplementation, WorkspaceEntityBase()
|
||||
|
||||
@Abstract
|
||||
interface AbstractEntity : WorkspaceEntity
|
||||
|
||||
interface <warning descr="Obsolete entity implementation">AnotherEntityWithOboleteImplementation</warning> : AbstractEntity
|
||||
|
||||
@GeneratedCodeApiVersion(2)
|
||||
internal class AnotherEntityWithOboleteImplementationImpl() : AnotherEntityWithOboleteImplementation, WorkspaceEntityBase()
|
||||
|
||||
interface AnotherEntityWithCorrectImplementation : WorkspaceEntity
|
||||
|
||||
@GeneratedCodeApiVersion(3)
|
||||
internal class AnotherEntityWithCorrectImplementationImpl() : AnotherEntityWithCorrectImplementation, WorkspaceEntityBase()
|
||||
+1
-1
@@ -4,5 +4,5 @@ import com.intellij.platform.workspace.storage.WorkspaceEntity
|
||||
|
||||
interface MainEntity : WorkspaceEntity {
|
||||
val property: String
|
||||
<error descr="Unsupported 'var' field in entity">var <caret>isValid: Boolean</error>
|
||||
<error descr="Unsupported 'var' field in an entity">var isValid: Boolean</error>
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:OptIn(IntellijInternalApi::class)
|
||||
|
||||
package com.intellij.devkit.workspaceModel.inspections
|
||||
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
|
||||
abstract class WorkspaceCodeAbsentInspectionBaseTest : WorkspaceInspectionBaseTest() {
|
||||
fun testEntityImplementation() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testEntitySourceMetadata() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testEntitySourceFakeMetadata() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testNotWorkspaceClasses() {
|
||||
doTest()
|
||||
}
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.devkit.workspaceModel.inspections
|
||||
|
||||
import com.intellij.openapi.application.PluginPathManager
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase
|
||||
|
||||
abstract class WorkspaceEntityInspectionBase: LightJavaCodeInsightFixtureTestCase() {
|
||||
private val TESTDATA_PATH = PluginPathManager.getPluginHomePathRelative("devkit") + "/intellij.devkit.workspaceModel/tests/testData/inspections/"
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
myFixture.createFile("Obj.kt", """
|
||||
package com.intellij.platform.workspace.storage
|
||||
|
||||
interface Obj""".trimIndent())
|
||||
myFixture.createFile("WorkspaceEntity.kt", """
|
||||
package com.intellij.platform.workspace.storage
|
||||
|
||||
import com.intellij.platform.workspace.storage.Obj
|
||||
|
||||
interface WorkspaceEntity : Obj""".trimIndent())
|
||||
}
|
||||
|
||||
override fun getBasePath() = TESTDATA_PATH
|
||||
|
||||
protected open fun doTest(fixName: String) {
|
||||
val testName = getTestName(true)
|
||||
val fileNameBefore = "$testName/entity.kt"
|
||||
val fileNameAfter = "${testName}/entity_after.kt"
|
||||
myFixture.testHighlighting(fileNameBefore)
|
||||
val intention = myFixture.findSingleIntention(fixName)
|
||||
myFixture.checkPreviewAndLaunchAction(intention)
|
||||
myFixture.checkResultByFile(fileNameBefore, fileNameAfter, true)
|
||||
}
|
||||
}
|
||||
+6
-3
@@ -3,16 +3,19 @@
|
||||
|
||||
package com.intellij.devkit.workspaceModel.inspections
|
||||
|
||||
import com.intellij.devkit.workspaceModel.WorkspaceEntityMutableFieldInspection
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
|
||||
class WorkspaceEntityMutableFieldInspectionTest: WorkspaceEntityInspectionBase() {
|
||||
abstract class WorkspaceEntityMutableFieldInspectionBaseTest : WorkspaceInspectionBaseTest() {
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
myFixture.enableInspections(WorkspaceEntityMutableFieldInspection())
|
||||
}
|
||||
|
||||
fun testVarFieldForbidden() {
|
||||
doTest("Change to 'val'")
|
||||
doTestWithQuickFix("Change to 'val'")
|
||||
}
|
||||
|
||||
fun testNotWorkspaceClasses() {
|
||||
doTest()
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:OptIn(IntellijInternalApi::class)
|
||||
|
||||
package com.intellij.devkit.workspaceModel.inspections
|
||||
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
|
||||
abstract class WorkspaceImplObsoleteInspectionBaseTest : WorkspaceInspectionBaseTest() {
|
||||
fun testObsoleteImplementation() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testNotWorkspaceClasses() {
|
||||
doTest()
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:OptIn(IntellijInternalApi::class)
|
||||
|
||||
package com.intellij.devkit.workspaceModel.inspections
|
||||
|
||||
import com.intellij.openapi.util.IntellijInternalApi
|
||||
|
||||
abstract class WorkspaceInheritanceInspectionBaseTest : WorkspaceInspectionBaseTest() {
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
myFixture.enableInspections(WorkspaceInheritanceInspection())
|
||||
}
|
||||
|
||||
fun testInheritance() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testNotWorkspaceAbstract() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testNotWorkspaceClasses() {
|
||||
doTest()
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.devkit.workspaceModel.inspections
|
||||
|
||||
import com.intellij.openapi.application.PluginPathManager
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase
|
||||
import org.intellij.lang.annotations.Language
|
||||
|
||||
abstract class WorkspaceInspectionBaseTest : LightJavaCodeInsightFixtureTestCase() {
|
||||
override fun getBasePath() = TESTDATA_PATH
|
||||
|
||||
private fun getBeforeAndAfterFileNames(): Pair<String, String> {
|
||||
val testName = getTestName(true)
|
||||
val fileNameBefore = "$testName/entity.kt"
|
||||
val fileNameAfter = "${testName}/entity_after.kt"
|
||||
return fileNameBefore to fileNameAfter
|
||||
}
|
||||
|
||||
protected fun addKotlinFile(relativePath: String, @Language("kotlin") fileText: String) {
|
||||
myFixture.addFileToProject(relativePath, fileText)
|
||||
}
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
addKotlinFile("EntitySource.kt", """
|
||||
package com.intellij.platform.workspace.storage
|
||||
interface EntitySource
|
||||
""".trimIndent())
|
||||
addKotlinFile("Abstract.kt", """
|
||||
package com.intellij.platform.workspace.storage.annotations
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
annotation class Abstract
|
||||
""".trimIndent())
|
||||
addKotlinFile("generatedCodeCompatibility.kt", """
|
||||
package com.intellij.platform.workspace.storage
|
||||
object CodeGeneratorVersions {
|
||||
private const val API_VERSION_INTERNAL = 3
|
||||
}
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class GeneratedCodeApiVersion(val version: Int)
|
||||
""".trimIndent())
|
||||
addKotlinFile("WorkspaceEntity.kt", """
|
||||
package com.intellij.platform.workspace.storage
|
||||
import com.intellij.platform.workspace.storage.annotations.Abstract
|
||||
@Abstract
|
||||
interface WorkspaceEntity
|
||||
""".trimIndent())
|
||||
addKotlinFile("MetadataStorageBase.kt", """
|
||||
package com.intellij.platform.workspace.storage.metadata.impl
|
||||
abstract class MetadataStorageBase() {
|
||||
protected fun addMetadataHash(typeFqn: String, metadataHash: Int) {}
|
||||
protected abstract fun initializeMetadataHash()
|
||||
}
|
||||
""".trimIndent())
|
||||
addKotlinFile("WorkspaceEntityBase.kt", """
|
||||
package com.intellij.platform.workspace.storage.impl
|
||||
import com.intellij.platform.workspace.storage.WorkspaceEntity
|
||||
abstract class WorkspaceEntityBase() : WorkspaceEntity
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
protected fun doTestWithQuickFix(fixName: String) {
|
||||
val (fileNameBefore, fileNameAfter) = getBeforeAndAfterFileNames()
|
||||
myFixture.testHighlighting(fileNameBefore)
|
||||
val quickFix = myFixture.getAllQuickFixes().find { it.text == fixName }
|
||||
assertNotNull("Fix $fixName not found", quickFix)
|
||||
myFixture.checkPreviewAndLaunchAction(quickFix!!)
|
||||
myFixture.checkResultByFile(fileNameBefore, fileNameAfter, true)
|
||||
}
|
||||
|
||||
protected fun doTest() {
|
||||
val (fileNameBefore, _) = getBeforeAndAfterFileNames()
|
||||
myFixture.testHighlighting(fileNameBefore)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TESTDATA_PATH = PluginPathManager.getPluginHomePathRelative("devkit") + "/intellij.devkit.workspaceModel/tests/testData/inspections/"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user