mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[devkit] IJPL-209927 Check that content module used as a dependency is visible
GitOrigin-RevId: 9fd75e092e8e4b247afbd9a42dec5058bc2f8f56
This commit is contained in:
committed by
intellij-monorepo-bot
parent
82d4de3102
commit
600fcb4715
+16
@@ -0,0 +1,16 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports violations of content module visibility when declaring module dependencies.
|
||||
<p>
|
||||
Content modules can specify their visibility as <code>private</code> (default, when missing), <code>internal</code>, or <code>public</code>.
|
||||
</p>
|
||||
<p>
|
||||
When declaring dependencies on other modules, the following rules must be followed:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Private modules can only be accessed within the same plugin.</li>
|
||||
<li>Internal modules can only be accessed when declared within the same namespace (<code>content@namespace</code>).</li>
|
||||
<li>Public modules can be accessed from anywhere.</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
@@ -536,6 +536,13 @@
|
||||
implementationClass="org.jetbrains.idea.devkit.inspections.ModuleNotRegisteredAsPluginContentInspection"
|
||||
key="inspection.module.not.registered.as.plugin.content.name"/>
|
||||
|
||||
<localInspection language="XML" applyToDialects="false"
|
||||
projectType="INTELLIJ_PLUGIN"
|
||||
groupPathKey="inspections.group.path" groupKey="inspections.group.descriptor"
|
||||
enabledByDefault="true" level="ERROR"
|
||||
implementationClass="org.jetbrains.idea.devkit.inspections.ContentModuleVisibilityInspection"
|
||||
key="inspection.content.module.visibility.name"/>
|
||||
|
||||
<localInspection language="XML" applyToDialects="false"
|
||||
projectType="INTELLIJ_PLUGIN"
|
||||
groupPathKey="inspections.group.path" groupKey="inspections.group.descriptor"
|
||||
|
||||
@@ -774,6 +774,12 @@ inspection.module.not.registered.as.plugin.content.name=Module is not declared a
|
||||
inspection.module.not.registered.as.plugin.content.message=The ''{0}'' module is not declared as content in any plugin descriptor
|
||||
inspection.module.not.registered.as.plugin.content.fix.add=Register as content module of the ''{0}'' plugin
|
||||
|
||||
inspection.content.module.visibility.name=Cannot access content module dependency
|
||||
inspection.content.module.visibility.internal=The ''{0}'' module is internal and declared in namespace ''{1}'' in ''{2}'', so it cannot be accessed from module ''{3}'', which is declared in namespace ''{4}'' in ''{5}''
|
||||
inspection.content.module.visibility.internal.dependency.namespace.missing=The ''{0}'' module is internal and declared without namespace in ''{1}'', so it cannot be accessed from module ''{2}'', which is declared in namespace ''{3}'' in ''{4}''
|
||||
inspection.content.module.visibility.internal.current.namespace.missing=The ''{0}'' module is internal and declared in namespace ''{1}'' in ''{2}'', so it cannot be accessed from module ''{3}'', which is declared without namespace in ''{4}''
|
||||
inspection.content.module.visibility.private=The ''{0}'' module is private and declared in plugin ''{1}'', so it cannot be accessed from module ''{2}'' declared in plugin ''{3}''
|
||||
|
||||
inspection.content.module.namespace.name=Content module namespace validation
|
||||
inspection.content.module.namespace.missing=Namespace is required for 'content' with non-private modules
|
||||
inspection.content.module.namespace.mismatch=All 'content' elements must define the same namespace (first namespace: ''{0}'')
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.idea.devkit.inspections
|
||||
|
||||
import com.intellij.ide.highlighter.XmlFileType
|
||||
import com.intellij.openapi.fileEditor.UniqueVFilePathBuilder
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.GlobalSearchScopesCore
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.psi.util.parentOfType
|
||||
import com.intellij.psi.xml.XmlFile
|
||||
import com.intellij.psi.xml.XmlTag
|
||||
import com.intellij.util.xml.DomElement
|
||||
import com.intellij.util.xml.GenericAttributeValue
|
||||
import com.intellij.util.xml.highlighting.DomElementAnnotationHolder
|
||||
import com.intellij.util.xml.highlighting.DomHighlightingHelper
|
||||
import com.intellij.xml.util.XmlUtil
|
||||
import org.jetbrains.annotations.Nls
|
||||
import org.jetbrains.idea.devkit.DevKitBundle.message
|
||||
import org.jetbrains.idea.devkit.dom.ContentModuleVisibility
|
||||
import org.jetbrains.idea.devkit.dom.DependencyDescriptor
|
||||
import org.jetbrains.idea.devkit.dom.IdeaPlugin
|
||||
import org.jetbrains.idea.devkit.dom.index.PluginIdDependenciesIndex
|
||||
import org.jetbrains.idea.devkit.util.DescriptorUtil
|
||||
|
||||
internal class ContentModuleVisibilityInspection : DevKitPluginXmlInspectionBase() {
|
||||
|
||||
override fun checkDomElement(element: DomElement, holder: DomElementAnnotationHolder, helper: DomHighlightingHelper) {
|
||||
val dependencyModule = element as? DependencyDescriptor.ModuleDescriptor ?: return
|
||||
val dependencyName = dependencyModule.name
|
||||
val dependency = dependencyName.value ?: return
|
||||
val dependencyVisibility = dependency.contentModuleVisibility
|
||||
when (dependencyVisibility.value ?: ContentModuleVisibility.PRIVATE) {
|
||||
ContentModuleVisibility.PUBLIC -> return // can be accessed from anywhere
|
||||
ContentModuleVisibility.INTERNAL -> checkInternalVisibility(dependencyName, dependency, holder)
|
||||
ContentModuleVisibility.PRIVATE -> checkPrivateVisibility(dependencyName, dependency, holder)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkInternalVisibility(
|
||||
dependencyValue: GenericAttributeValue<IdeaPlugin?>,
|
||||
moduleDependency: IdeaPlugin,
|
||||
holder: DomElementAnnotationHolder,
|
||||
) {
|
||||
val currentXmlFile = dependencyValue.xmlElement?.containingFile as? XmlFile ?: return
|
||||
val productionXmlFilesScope = getProjectProductionXmlFilesScope(currentXmlFile.project)
|
||||
val currentModuleIncludingPlugins = getPluginXmlFilesIncludingFileAsContentModule(currentXmlFile, productionXmlFilesScope)
|
||||
val dependencyXmlFile = moduleDependency.xmlElement?.containingFile as? XmlFile ?: return
|
||||
val dependencyIncludingPlugins = getPluginXmlFilesIncludingFileAsContentModule(dependencyXmlFile, productionXmlFilesScope)
|
||||
|
||||
for (currentModuleIncludingPlugin in currentModuleIncludingPlugins) {
|
||||
for (dependencyIncludingPlugin in dependencyIncludingPlugins) {
|
||||
if (currentModuleIncludingPlugin == dependencyIncludingPlugin) continue
|
||||
val currentModuleNamespace = currentModuleIncludingPlugin.getNamespace()
|
||||
val dependencyNamespace = dependencyIncludingPlugin.getNamespace()
|
||||
if (currentModuleNamespace != dependencyNamespace) {
|
||||
holder.createProblem(
|
||||
dependencyValue,
|
||||
getInternalVisibilityProblemMessage(dependencyValue, dependencyIncludingPlugin, currentXmlFile, currentModuleIncludingPlugin)
|
||||
)
|
||||
return // report only one problem at once
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInternalVisibilityProblemMessage(
|
||||
dependencyValue: GenericAttributeValue<IdeaPlugin?>,
|
||||
dependencyIncludingPlugin: IdeaPlugin,
|
||||
currentXmlFile: XmlFile,
|
||||
currentModuleIncludingPlugin: IdeaPlugin,
|
||||
): @Nls String? {
|
||||
val dependencyNamespace = dependencyIncludingPlugin.getNamespace()
|
||||
val currentModuleNamespace = currentModuleIncludingPlugin.getNamespace()
|
||||
return when {
|
||||
dependencyNamespace == null -> message(
|
||||
"inspection.content.module.visibility.internal.dependency.namespace.missing",
|
||||
dependencyValue.stringValue, dependencyIncludingPlugin.getUniqueFileName(),
|
||||
getModuleName(currentXmlFile), currentModuleNamespace, currentModuleIncludingPlugin.getUniqueFileName()
|
||||
)
|
||||
currentModuleNamespace == null -> message(
|
||||
"inspection.content.module.visibility.internal.current.namespace.missing",
|
||||
dependencyValue.stringValue, dependencyNamespace, dependencyIncludingPlugin.getUniqueFileName(),
|
||||
getModuleName(currentXmlFile), currentModuleIncludingPlugin.getUniqueFileName()
|
||||
)
|
||||
else -> message(
|
||||
"inspection.content.module.visibility.internal",
|
||||
dependencyValue.stringValue, dependencyNamespace, dependencyIncludingPlugin.getUniqueFileName(),
|
||||
getModuleName(currentXmlFile), currentModuleNamespace, currentModuleIncludingPlugin.getUniqueFileName()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPluginXmlFilesIncludingFileAsContentModule(xmlFile: XmlFile, scope: GlobalSearchScope): Collection<IdeaPlugin> {
|
||||
val moduleVirtualFile = xmlFile.virtualFile ?: return emptyList()
|
||||
val psiManager = xmlFile.manager
|
||||
return findFilesIncludingContentModule(moduleVirtualFile, scope)
|
||||
.mapNotNull { psiManager.findFile(it) as? XmlFile }
|
||||
.mapNotNull { DescriptorUtil.getIdeaPlugin(it) }
|
||||
}
|
||||
|
||||
private fun findFilesIncludingContentModule(moduleVirtualFile: VirtualFile, scope: GlobalSearchScope): Collection<VirtualFile> {
|
||||
return PluginIdDependenciesIndex.findFilesIncludingContentModule(moduleVirtualFile, scope)
|
||||
}
|
||||
|
||||
private fun IdeaPlugin.getNamespace(): String? {
|
||||
// all <content> must have the same namespace, so take it from the first:
|
||||
return this.content.firstOrNull()?.namespace?.value
|
||||
}
|
||||
|
||||
private fun IdeaPlugin.getUniqueFileName(): String {
|
||||
return UniqueVFilePathBuilder.getInstance().getUniqueVirtualFilePath(xmlElement!!.project, xmlElement!!.containingFile.virtualFile)
|
||||
}
|
||||
|
||||
private fun checkPrivateVisibility(
|
||||
dependencyValue: GenericAttributeValue<IdeaPlugin?>,
|
||||
moduleDependency: IdeaPlugin,
|
||||
holder: DomElementAnnotationHolder,
|
||||
) {
|
||||
val currentXmlFile = dependencyValue.xmlElement?.containingFile as? XmlFile ?: return
|
||||
val project = currentXmlFile.project
|
||||
val productionXmlFilesScope = getProjectProductionXmlFilesScope(project)
|
||||
val currentModuleIncludingPlugins = getPluginsIncludingFileAsContentModule(currentXmlFile, productionXmlFilesScope)
|
||||
val dependencyXmlFile = moduleDependency.xmlElement?.containingFile as? XmlFile ?: return
|
||||
val dependencyIncludingPlugins = getPluginsIncludingFileAsContentModule(dependencyXmlFile, productionXmlFilesScope)
|
||||
for (currentModuleIncludingPlugin in currentModuleIncludingPlugins) {
|
||||
if (dependencyIncludingPlugins.contains(currentModuleIncludingPlugin)) continue // are included in the same plugin
|
||||
for (dependencyIncludingPlugin in dependencyIncludingPlugins) {
|
||||
if (currentModuleIncludingPlugin != dependencyIncludingPlugin) {
|
||||
holder.createProblem(
|
||||
dependencyValue,
|
||||
message(
|
||||
"inspection.content.module.visibility.private",
|
||||
getModuleName(dependencyXmlFile), dependencyIncludingPlugin.getIdOrUniqueFileName(),
|
||||
getModuleName(currentXmlFile), currentModuleIncludingPlugin.getIdOrUniqueFileName()
|
||||
)
|
||||
)
|
||||
return // report only one problem at once
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getProjectProductionXmlFilesScope(project: Project): GlobalSearchScope {
|
||||
return GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScopesCore.projectProductionScope(project), XmlFileType.INSTANCE)
|
||||
}
|
||||
|
||||
private fun getPluginsIncludingFileAsContentModule(xmlFile: XmlFile, scope: GlobalSearchScope): Collection<IdeaPlugin> {
|
||||
val moduleVirtualFile = xmlFile.virtualFile ?: return emptyList()
|
||||
val psiManager = xmlFile.manager
|
||||
return findFilesIncludingContentModule(moduleVirtualFile, scope)
|
||||
.mapNotNull { psiManager.findFile(it) as? XmlFile }
|
||||
.flatMap { getActualIncludingPlugins(it, scope) }
|
||||
.distinct()
|
||||
}
|
||||
|
||||
/**
|
||||
* If [xmlFile]:
|
||||
* - has ID or is `META-INF/plugin.xml`, then return it, as it is an actual plugin
|
||||
* - is included via `<xi:include>`, find including plugins (recursively).
|
||||
*/
|
||||
private fun getActualIncludingPlugins(
|
||||
xmlFile: XmlFile,
|
||||
scope: GlobalSearchScope,
|
||||
visited: MutableSet<XmlFile> = mutableSetOf(),
|
||||
): Collection<IdeaPlugin> {
|
||||
if (!visited.add(xmlFile)) return emptyList() // prevent inclusion cycles
|
||||
val ideaPlugin = DescriptorUtil.getIdeaPlugin(xmlFile)
|
||||
if (ideaPlugin != null && isActualPluginDescriptor(ideaPlugin, xmlFile)) {
|
||||
return listOf(ideaPlugin)
|
||||
}
|
||||
return ReferencesSearch.search(xmlFile, scope)
|
||||
.filtering { isXiIncluded(it) }
|
||||
.findAll()
|
||||
.flatMap { getActualIncludingPlugins(it.element.containingFile as XmlFile, scope, visited) }
|
||||
}
|
||||
|
||||
private fun isActualPluginDescriptor(ideaPlugin: IdeaPlugin, xmlFile: XmlFile): Boolean =
|
||||
ideaPlugin.pluginId != null || (xmlFile.name == "plugin.xml" && xmlFile.parent?.name == "META-INF")
|
||||
|
||||
private fun isXiIncluded(reference: PsiReference): Boolean {
|
||||
val xmlTag = reference.element.parentOfType<XmlTag>() ?: return false
|
||||
return xmlTag.namespace == XmlUtil.XINCLUDE_URI && xmlTag.localName == "include"
|
||||
}
|
||||
|
||||
private fun getModuleName(xmlFile: XmlFile): String {
|
||||
return xmlFile.virtualFile.nameWithoutExtension
|
||||
}
|
||||
|
||||
private fun IdeaPlugin.getIdOrUniqueFileName(): String {
|
||||
return pluginId ?: getUniqueFileName()
|
||||
}
|
||||
|
||||
}
|
||||
+629
@@ -0,0 +1,629 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.idea.devkit.inspections
|
||||
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.testFramework.PsiTestUtil
|
||||
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase
|
||||
import org.intellij.lang.annotations.Language
|
||||
import org.jetbrains.idea.devkit.module.PluginModuleType
|
||||
|
||||
class ContentModuleVisibilityInspectionTest : JavaCodeInsightFixtureTestCase() {
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
// it is required for correct recognizing if the project is a plugin project (see PsiUtil.IDE_PROJECT_MARKER_CLASS):
|
||||
myFixture.addClass("package com.intellij.ui.components; public class JBList {}")
|
||||
myFixture.enableInspections(ContentModuleVisibilityInspection())
|
||||
}
|
||||
|
||||
fun `test should report private module dependency from internal module`() {
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.privatemodule",
|
||||
"com.example.plugin.with.privatemodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<id>com.example.plugin.with.privatemodule</id>
|
||||
<content>
|
||||
<module name="com.example.privatemodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.privatemodule",
|
||||
"com.example.privatemodule/com.example.privatemodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="private">
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.internalmodule",
|
||||
"com.example.plugin.with.internalmodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<id>com.example.plugin.with.internalmodule</id>
|
||||
<content>
|
||||
<module name="com.example.internalmodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
val testedFile = addModuleWithPluginDescriptor(
|
||||
"com.example.internalmodule",
|
||||
"com.example.internalmodule/com.example.internalmodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="internal">
|
||||
<dependencies>
|
||||
<module name="<error descr="The 'com.example.privatemodule' module is private and declared in plugin 'com.example.plugin.with.privatemodule', so it cannot be accessed from module 'com.example.internalmodule' declared in plugin 'com.example.plugin.with.internalmodule'">com.example.privatemodule</error>"/>
|
||||
</dependencies>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
testHighlighting(testedFile)
|
||||
}
|
||||
|
||||
fun `test should report private module dependency from public module`() {
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.privatemodule",
|
||||
"com.example.plugin.with.privatemodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<id>com.example.plugin.with.privatemodule</id>
|
||||
<content>
|
||||
<module name="com.example.privatemodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.privatemodule",
|
||||
"com.example.privatemodule/com.example.privatemodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="private">
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.publicmodule",
|
||||
"com.example.plugin.with.publicmodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<id>com.example.plugin.with.publicmodule</id>
|
||||
<content namespace="test-namespace">
|
||||
<module name="com.example.publicmodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
val testedFile = addModuleWithPluginDescriptor(
|
||||
"com.example.publicmodule",
|
||||
"com.example.publicmodule/com.example.publicmodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="public">
|
||||
<dependencies>
|
||||
<module name="<error descr="The 'com.example.privatemodule' module is private and declared in plugin 'com.example.plugin.with.privatemodule', so it cannot be accessed from module 'com.example.publicmodule' declared in plugin 'com.example.plugin.with.publicmodule'">com.example.privatemodule</error>"/>
|
||||
</dependencies>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
testHighlighting(testedFile)
|
||||
}
|
||||
|
||||
fun `test should report internal module with different namespace`() {
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.internalmodule",
|
||||
"com.example.plugin.with.internalmodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<id>com.example.plugin.with.internalmodule</id>
|
||||
<content namespace="test-other-namespace">
|
||||
<module name="com.example.internalmodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.internalmodule",
|
||||
"com.example.internalmodule/com.example.internalmodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="internal">
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.currentmodule",
|
||||
"com.example.plugin.with.currentmodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<id>com.example.plugin.with.currentmodule</id>
|
||||
<content namespace="test-namespace">
|
||||
<module name="com.example.currentmodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
val testedFile = addModuleWithPluginDescriptor(
|
||||
"com.example.currentmodule",
|
||||
"com.example.currentmodule/com.example.currentmodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="public">
|
||||
<dependencies>
|
||||
<module name="<error descr="The 'com.example.internalmodule' module is internal and declared in namespace 'test-other-namespace' in 'com.example.plugin.with.internalmodule/…/plugin.xml', so it cannot be accessed from module 'com.example.currentmodule', which is declared in namespace 'test-namespace' in 'com.example.plugin.with.currentmodule/…/plugin.xml'">com.example.internalmodule</error>"/>
|
||||
</dependencies>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
testHighlighting(testedFile)
|
||||
}
|
||||
|
||||
fun `test should report internal module without namespace`() {
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.internalmodule",
|
||||
"com.example.plugin.with.internalmodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<id>com.example.plugin.with.internalmodule</id>
|
||||
<content>
|
||||
<module name="com.example.internalmodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.internalmodule",
|
||||
"com.example.internalmodule/com.example.internalmodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="internal">
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.currentmodule",
|
||||
"com.example.plugin.with.currentmodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<id>com.example.plugin.with.currentmodule</id>
|
||||
<content namespace="test-namespace">
|
||||
<module name="com.example.currentmodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
val testedFile = addModuleWithPluginDescriptor(
|
||||
"com.example.currentmodule",
|
||||
"com.example.currentmodule/com.example.currentmodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="public">
|
||||
<dependencies>
|
||||
<module name="<error descr="The 'com.example.internalmodule' module is internal and declared without namespace in 'com.example.plugin.with.internalmodule/…/plugin.xml', so it cannot be accessed from module 'com.example.currentmodule', which is declared in namespace 'test-namespace' in 'com.example.plugin.with.currentmodule/…/plugin.xml'">com.example.internalmodule</error>"/>
|
||||
</dependencies>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
testHighlighting(testedFile)
|
||||
}
|
||||
|
||||
fun `test should report internal module when current module has no namespace`() {
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.internalmodule",
|
||||
"com.example.plugin.with.internalmodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<id>com.example.plugin.with.internalmodule</id>
|
||||
<content namespace="test-other-namespace">
|
||||
<module name="com.example.internalmodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.internalmodule",
|
||||
"com.example.internalmodule/com.example.internalmodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="internal">
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.currentmodule",
|
||||
"com.example.plugin.with.currentmodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<id>com.example.plugin.with.currentmodule</id>
|
||||
<content>
|
||||
<module name="com.example.currentmodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
val testedFile = addModuleWithPluginDescriptor(
|
||||
"com.example.currentmodule",
|
||||
"com.example.currentmodule/com.example.currentmodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="public">
|
||||
<dependencies>
|
||||
<module name="<error descr="The 'com.example.internalmodule' module is internal and declared in namespace 'test-other-namespace' in 'com.example.plugin.with.internalmodule/…/plugin.xml', so it cannot be accessed from module 'com.example.currentmodule', which is declared without namespace in 'com.example.plugin.with.currentmodule/…/plugin.xml'">com.example.internalmodule</error>"/>
|
||||
</dependencies>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
testHighlighting(testedFile)
|
||||
}
|
||||
|
||||
fun `test should report private module included via xi-include`() {
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.privatemodule",
|
||||
"com.example.plugin.with.privatemodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<id>com.example.plugin.with.privatemodule</id>
|
||||
<xi:include href="/META-INF/privatemodules.xml"/>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addXmlFile(
|
||||
"com.example.plugin.with.privatemodule/META-INF/privatemodules.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<content>
|
||||
<module name="com.example.privatemodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.privatemodule",
|
||||
"com.example.privatemodule/com.example.privatemodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="private">
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.publicmodule",
|
||||
"com.example.plugin.with.publicmodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<id>com.example.plugin.with.publicmodule</id>
|
||||
<content>
|
||||
<module name="com.example.publicmodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
val testedFile = addModuleWithPluginDescriptor(
|
||||
"com.example.publicmodule",
|
||||
"com.example.publicmodule/com.example.publicmodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="public">
|
||||
<dependencies>
|
||||
<module name="<error descr="The 'com.example.privatemodule' module is private and declared in plugin 'com.example.plugin.with.privatemodule', so it cannot be accessed from module 'com.example.publicmodule' declared in plugin 'com.example.plugin.with.publicmodule'">com.example.privatemodule</error>"/>
|
||||
</dependencies>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
testHighlighting(testedFile)
|
||||
}
|
||||
|
||||
fun `test should report private module included via multiple xi-includes`() {
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.privatemodule",
|
||||
"com.example.plugin.with.privatemodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<id>com.example.plugin.with.privatemodule</id>
|
||||
<xi:include href="/META-INF/modules.xml"/>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addXmlFile(
|
||||
"com.example.plugin.with.privatemodule/META-INF/modules.xml",
|
||||
"""
|
||||
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<xi:include href="/META-INF/privatemodules.xml"/>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addXmlFile(
|
||||
"com.example.plugin.with.privatemodule/META-INF/privatemodules.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<content>
|
||||
<module name="com.example.privatemodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.privatemodule",
|
||||
"com.example.privatemodule/com.example.privatemodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="private">
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.publicmodule",
|
||||
"com.example.plugin.with.publicmodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<id>com.example.plugin.with.publicmodule</id>
|
||||
<content>
|
||||
<module name="com.example.publicmodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
val testedFile = addModuleWithPluginDescriptor(
|
||||
"com.example.publicmodule",
|
||||
"com.example.publicmodule/com.example.publicmodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="public">
|
||||
<dependencies>
|
||||
<module name="<error descr="The 'com.example.privatemodule' module is private and declared in plugin 'com.example.plugin.with.privatemodule', so it cannot be accessed from module 'com.example.publicmodule' declared in plugin 'com.example.plugin.with.publicmodule'">com.example.privatemodule</error>"/>
|
||||
</dependencies>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
testHighlighting(testedFile)
|
||||
}
|
||||
|
||||
fun `test should not report public module`() {
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.publicmodule",
|
||||
"com.example.plugin.with.publicmodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<id>com.example.plugin.with.publicmodule</id>
|
||||
<content>
|
||||
<module name="com.example.publicmodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.publicmodule",
|
||||
"com.example.publicmodule/com.example.publicmodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="public">
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.currentmodule",
|
||||
"com.example.plugin.with.currentmodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<id>com.example.plugin.with.currentmodule</id>
|
||||
<content namespace="test-namespace">
|
||||
<module name="com.example.currentmodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
val testedFile = addModuleWithPluginDescriptor(
|
||||
"com.example.currentmodule",
|
||||
"com.example.currentmodule/com.example.currentmodule.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<dependencies>
|
||||
<module name="com.example.publicmodule"/>
|
||||
</dependencies>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
testHighlighting(testedFile)
|
||||
}
|
||||
|
||||
fun `test should not report internal module with same namespace`() {
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.internalmodule",
|
||||
"com.example.plugin.with.internalmodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<id>com.example.plugin.with.internalmodule</id>
|
||||
<content namespace="test-namespace">
|
||||
<module name="com.example.internalmodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.internalmodule",
|
||||
"com.example.internalmodule/com.example.internalmodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="internal">
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.currentmodule",
|
||||
"com.example.plugin.with.currentmodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<id>com.example.plugin.with.currentmodule</id>
|
||||
<content namespace="test-namespace">
|
||||
<module name="com.example.currentmodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
val testedFile = addModuleWithPluginDescriptor(
|
||||
"com.example.currentmodule",
|
||||
"com.example.currentmodule/com.example.currentmodule.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<dependencies>
|
||||
<module name="com.example.internalmodule"/>
|
||||
</dependencies>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
testHighlighting(testedFile)
|
||||
}
|
||||
|
||||
fun `test should not report private module in same plugin`() {
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin",
|
||||
"com.example.plugin/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<content namespace="test-namespace">
|
||||
<module name="com.example.privatemodule"/>
|
||||
<module name="com.example.anothermodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.privatemodule",
|
||||
"com.example.privatemodule/com.example.privatemodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="private">
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
val testedFile = addModuleWithPluginDescriptor(
|
||||
"com.example.anothermodule",
|
||||
"com.example.anothermodule/com.example.anothermodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="public">
|
||||
<dependencies>
|
||||
<module name="com.example.privatemodule"/>
|
||||
</dependencies>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
testHighlighting(testedFile)
|
||||
}
|
||||
|
||||
fun `test should not report when both modules included in same plugin`() {
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin",
|
||||
"com.example.plugin/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<content>
|
||||
<module name="com.example.internalmodule"/>
|
||||
<module name="com.example.currentmodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.internalmodule",
|
||||
"com.example.internalmodule/com.example.internalmodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="internal">
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
val testedFile = addModuleWithPluginDescriptor(
|
||||
"com.example.currentmodule",
|
||||
"com.example.currentmodule/com.example.currentmodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="public">
|
||||
<dependencies>
|
||||
<module name="com.example.internalmodule"/>
|
||||
</dependencies>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
testHighlighting(testedFile)
|
||||
}
|
||||
|
||||
fun `test should not report private module included via xi-include`() {
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.privatemodule",
|
||||
"com.example.plugin.with.privatemodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<id>com.example.plugin.with.privatemodule</id>
|
||||
<content>
|
||||
<module name="com.example.publicmodule"/>
|
||||
</content>
|
||||
<xi:include href="/META-INF/privatemodules.xml"/>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addXmlFile(
|
||||
"com.example.plugin.with.privatemodule/META-INF/privatemodules.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<content>
|
||||
<module name="com.example.privatemodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.privatemodule",
|
||||
"com.example.privatemodule/com.example.privatemodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="private">
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
val testedFile = addModuleWithPluginDescriptor(
|
||||
"com.example.publicmodule",
|
||||
"com.example.publicmodule/com.example.publicmodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="public">
|
||||
<dependencies>
|
||||
<module name="com.example.privatemodule"/>
|
||||
</dependencies>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
testHighlighting(testedFile)
|
||||
}
|
||||
|
||||
fun `test should not report private module included via multiple xi-includes`() {
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.plugin.with.privatemodule",
|
||||
"com.example.plugin.with.privatemodule/META-INF/plugin.xml",
|
||||
"""
|
||||
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<id>com.example.plugin.with.privatemodule</id>
|
||||
<content>
|
||||
<module name="com.example.publicmodule"/>
|
||||
</content>
|
||||
<xi:include href="/META-INF/modules.xml"/>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addXmlFile(
|
||||
"com.example.plugin.with.privatemodule/META-INF/modules.xml",
|
||||
"""
|
||||
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<xi:include href="/META-INF/privatemodules.xml"/>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addXmlFile(
|
||||
"com.example.plugin.with.privatemodule/META-INF/privatemodules.xml",
|
||||
"""
|
||||
<idea-plugin>
|
||||
<content>
|
||||
<module name="com.example.privatemodule"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
addModuleWithPluginDescriptor(
|
||||
"com.example.privatemodule",
|
||||
"com.example.privatemodule/com.example.privatemodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="private">
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
val testedFile = addModuleWithPluginDescriptor(
|
||||
"com.example.publicmodule",
|
||||
"com.example.publicmodule/com.example.publicmodule.xml",
|
||||
"""
|
||||
<idea-plugin visibility="public">
|
||||
<dependencies>
|
||||
<module name="com.example.privatemodule"/>
|
||||
</dependencies>
|
||||
</idea-plugin>
|
||||
""".trimIndent())
|
||||
|
||||
testHighlighting(testedFile)
|
||||
}
|
||||
|
||||
private fun addModuleWithPluginDescriptor(
|
||||
moduleName: String,
|
||||
pluginDescriptorFilePath: String,
|
||||
@Language("XML") pluginDescriptorContent: String,
|
||||
): PsiFile {
|
||||
PsiTestUtil.addModule(project, PluginModuleType.getInstance(), moduleName, myFixture.tempDirFixture.findOrCreateDir(moduleName))
|
||||
return addXmlFile(pluginDescriptorFilePath, pluginDescriptorContent)
|
||||
}
|
||||
|
||||
private fun addXmlFile(relativePath: String, @Language("XML") fileText: String): PsiFile {
|
||||
return myFixture.addFileToProject(relativePath, fileText)
|
||||
}
|
||||
|
||||
private fun testHighlighting(testedFile: PsiFile) {
|
||||
myFixture.testHighlighting(true, true, true, testedFile.virtualFile)
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -12,7 +12,7 @@ import java.util.stream.Stream;
|
||||
|
||||
public class DevkitInspectionsRegistrationCheckTest extends BasePlatformTestCase {
|
||||
|
||||
private static final int EXPECTED_INSPECTIONS_NUMBER = 85;
|
||||
private static final int EXPECTED_INSPECTIONS_NUMBER = 86;
|
||||
|
||||
/**
|
||||
* Inspections that are finished and intentionally disabled.
|
||||
@@ -59,7 +59,6 @@ public class DevkitInspectionsRegistrationCheckTest extends BasePlatformTestCase
|
||||
List<String> allKnownDisabledInspections = new ArrayList<>(ContainerUtil.concat(DISABLED_INSPECTIONS, WIP_INSPECTIONS));
|
||||
Collections.sort(allKnownDisabledInspections);
|
||||
|
||||
assertSameElements("Mismatch in known WIP inspections", disabledInspectionShortNames,
|
||||
allKnownDisabledInspections);
|
||||
assertSameElements("Mismatch in known WIP inspections", disabledInspectionShortNames, allKnownDisabledInspections);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user