diff --git a/plugins/devkit/devkit-core/resources/META-INF/plugin.xml b/plugins/devkit/devkit-core/resources/META-INF/plugin.xml
index de193806acde..8bcbfce9123f 100644
--- a/plugins/devkit/devkit-core/resources/META-INF/plugin.xml
+++ b/plugins/devkit/devkit-core/resources/META-INF/plugin.xml
@@ -71,7 +71,7 @@
-
COMPONENT_TYPE_TO_REGISTRATION_TYPE =
+ ContainerUtil.immutableMapBuilder()
+ .put(ComponentType.APPLICATION, RegistrationCheckerUtil.RegistrationType.APPLICATION_COMPONENT)
+ .put(ComponentType.PROJECT, RegistrationCheckerUtil.RegistrationType.PROJECT_COMPONENT)
+ .put(ComponentType.MODULE, RegistrationCheckerUtil.RegistrationType.MODULE_COMPONENT)
+ .build();
@Nullable
public JComponent createOptionsPanel() {
@@ -84,90 +91,87 @@ public class ComponentNotRegisteredInspection extends DevKitInspectionBase {
}
@Nullable
- public ProblemDescriptor[] checkClass(@NotNull PsiClass checkedClass, @NotNull InspectionManager manager, boolean isOnTheFly) {
- PsiIdentifier classIdentifier = checkedClass.getNameIdentifier();
- if (classIdentifier != null &&
- checkedClass.getQualifiedName() != null &&
- checkedClass.getContainingFile().getVirtualFile() != null &&
- !checkedClass.hasModifierProperty(PsiModifier.ABSTRACT) &&
- !checkedClass.isEnum() &&
- !PsiUtil.isInnerClass(checkedClass)) {
-
- GlobalSearchScope scope = checkedClass.getResolveScope();
-
- if (shouldCheckActionClass(checkedClass)) {
- PsiClass actionClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(AnAction.class.getName(), scope);
- if (actionClass == null) {
- // stop if action class cannot be found (non-devkit module/project)
- return null;
- }
- if (checkedClass.isInheritor(actionClass, true)) {
- if (!isActionRegistered(checkedClass) && canFix(checkedClass)) {
- LocalQuickFix fix = new RegisterActionFix(org.jetbrains.idea.devkit.util.PsiUtil.createPointer(checkedClass));
- ProblemDescriptor problem =
- manager.createProblemDescriptor(classIdentifier,
- DevKitBundle.message("inspections.component.not.registered.message",
- DevKitBundle.message("new.menu.action.text")),
- fix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly);
- return new ProblemDescriptor[]{problem};
- }
- // action IS registered, stop here
+ @Override
+ protected JvmElementVisitor buildVisitor(@NotNull Project project, @NotNull HighlightSink sink, boolean isOnTheFly) {
+ return new DefaultJvmElementVisitor() {
+ @Override
+ public Boolean visitClass(@NotNull JvmClass clazz) {
+ PsiElement sourceElement = clazz.getSourceElement();
+ if (!(sourceElement instanceof PsiClass)) {
return null;
}
+ checkClass(project, (PsiClass)sourceElement, sink);
+ return false;
}
-
- PsiClass compClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(BaseComponent.class.getName(), scope);
- if (compClass == null) {
- // stop if component class cannot be found (non-devkit module/project)
- return null;
- }
- if (!checkedClass.isInheritor(compClass, true)) {
- return null;
- }
-
- for (ComponentType componentType : ComponentType.values()) {
- if (!InheritanceUtil.isInheritor(checkedClass, componentType.myClassName)) {
- continue;
- }
-
- if (findRegistrationType(checkedClass, COMPONENT_TYPE_TO_REGISTRATION_TYPE.get(componentType)) != null) {
- return null;
- }
-
- if (!canFix(checkedClass)) {
- return null;
- }
- LocalQuickFix fix = new RegisterComponentFix(componentType, org.jetbrains.idea.devkit.util.PsiUtil.createPointer(checkedClass));
- ProblemDescriptor problem =
- manager.createProblemDescriptor(classIdentifier,
- DevKitBundle.message("inspections.component.not.registered.message",
- DevKitBundle.message(componentType.myPropertyKey)),
- fix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly);
- return new ProblemDescriptor[]{problem};
- }
- }
- return null;
+ };
}
- private static final Map COMPONENT_TYPE_TO_REGISTRATION_TYPE =
- ContainerUtil.immutableMapBuilder()
- .put(ComponentType.APPLICATION, RegistrationCheckerUtil.RegistrationType.APPLICATION_COMPONENT)
- .put(ComponentType.PROJECT, RegistrationCheckerUtil.RegistrationType.PROJECT_COMPONENT)
- .put(ComponentType.MODULE, RegistrationCheckerUtil.RegistrationType.MODULE_COMPONENT)
- .build();
+ private void checkClass(@NotNull Project project, @NotNull PsiClass checkedClass, @NotNull HighlightSink sink) {
+ if (checkedClass.getQualifiedName() == null ||
+ checkedClass.getContainingFile().getVirtualFile() == null ||
+ checkedClass.hasModifierProperty(PsiModifier.ABSTRACT) ||
+ checkedClass.isEnum() ||
+ PsiUtil.isInnerClass(checkedClass) ||
+ !shouldCheckActionClass(checkedClass)) {
+ return;
+ }
- private static PsiClass findRegistrationType(@Nullable PsiClass checkedClass, RegistrationCheckerUtil.RegistrationType type) {
+ GlobalSearchScope scope = checkedClass.getResolveScope();
+ PsiClass actionClass = JavaPsiFacade.getInstance(project).findClass(AnAction.class.getName(), scope);
+ if (actionClass == null) {
+ // stop if action class cannot be found (non-devkit module/project)
+ return;
+ }
+
+ if (checkedClass.isInheritor(actionClass, true)) {
+ if (!isActionRegistered(checkedClass) && canFix(checkedClass)) {
+ LocalQuickFix fix = new RegisterActionFix(org.jetbrains.idea.devkit.util.PsiUtil.createPointer(checkedClass));
+ sink.highlight(DevKitBundle.message("inspections.component.not.registered.message",
+ DevKitBundle.message("new.menu.action.text")), fix);
+ }
+ // action IS registered, stop here
+ return;
+ }
+
+ PsiClass compClass = JavaPsiFacade.getInstance(project).findClass(BaseComponent.class.getName(), scope);
+ if (compClass == null) {
+ // stop if component class cannot be found (non-devkit module/project)
+ return;
+ }
+ if (!checkedClass.isInheritor(compClass, true)) {
+ return;
+ }
+
+ for (ComponentType componentType : ComponentType.values()) {
+ if (!InheritanceUtil.isInheritor(checkedClass, componentType.myClassName)) {
+ continue;
+ }
+
+ if (findRegistrationType(checkedClass, COMPONENT_TYPE_TO_REGISTRATION_TYPE.get(componentType)) != null) {
+ return;
+ }
+ if (!canFix(checkedClass)) {
+ return;
+ }
+
+ LocalQuickFix fix = new RegisterComponentFix(componentType, org.jetbrains.idea.devkit.util.PsiUtil.createPointer(checkedClass));
+ sink.highlight(DevKitBundle.message("inspections.component.not.registered.message",
+ DevKitBundle.message(componentType.myPropertyKey)), fix);
+ }
+ }
+
+ private static PsiClass findRegistrationType(@NotNull PsiClass checkedClass, @NotNull RegistrationCheckerUtil.RegistrationType type) {
final Set types = RegistrationCheckerUtil.getRegistrationTypes(checkedClass, type);
return ContainerUtil.getFirstItem(types);
}
- private boolean shouldCheckActionClass(PsiClass psiClass) {
+ private boolean shouldCheckActionClass(@NotNull PsiClass psiClass) {
if (!CHECK_ACTIONS) return false;
if (IGNORE_NON_PUBLIC && !psiClass.hasModifierProperty(PsiModifier.PUBLIC)) return false;
return true;
}
- private static boolean isActionRegistered(PsiClass actionClass) {
+ private static boolean isActionRegistered(@NotNull PsiClass actionClass) {
final PsiClass registrationType = findRegistrationType(actionClass, RegistrationCheckerUtil.RegistrationType.ACTION);
if (registrationType != null) {
return true;
@@ -197,7 +201,7 @@ public class ComponentNotRegisteredInspection extends DevKitInspectionBase {
return false;
}
- private static boolean canFix(PsiClass psiClass) {
+ private static boolean canFix(@NotNull PsiClass psiClass) {
Project project = psiClass.getProject();
PsiFile psiFile = psiClass.getContainingFile();
LOG.assertTrue(psiFile != null);
diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/ComponentNotRegisteredInspectionTest.java b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/ComponentNotRegisteredInspectionTest.java
index c9b9d5f821d8..9e0fc26d3df7 100644
--- a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/ComponentNotRegisteredInspectionTest.java
+++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/ComponentNotRegisteredInspectionTest.java
@@ -15,159 +15,18 @@
*/
package org.jetbrains.idea.devkit.inspections;
-import com.intellij.codeInsight.intention.IntentionAction;
-import com.intellij.openapi.util.text.StringUtil;
import com.intellij.testFramework.TestDataPath;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.devkit.DevkitJavaTestsUtil;
-import org.jetbrains.idea.devkit.dom.Anchor;
-import org.jetbrains.idea.devkit.inspections.quickfix.RegisterActionFix;
-import org.jetbrains.idea.devkit.util.ActionData;
-import org.jetbrains.idea.devkit.util.PsiUtil;
@TestDataPath("$CONTENT_ROOT/testData/inspections/componentNotRegistered")
-public class ComponentNotRegisteredInspectionTest extends PluginModuleTestCase {
-
+public class ComponentNotRegisteredInspectionTest extends ComponentNotRegisteredInspectionTestBase {
@Override
protected String getBasePath() {
return DevkitJavaTestsUtil.TESTDATA_PATH + "inspections/componentNotRegistered";
}
@Override
- protected void setUp() throws Exception {
- super.setUp();
-
- myFixture.addClass("package com.intellij.openapi.actionSystem; public class AnAction {}");
- myFixture.addClass("package com.intellij.openapi.components; public interface BaseComponent {}");
- myFixture.addClass("package com.intellij.openapi.components; public interface ApplicationComponent extends BaseComponent {}");
-
- myFixture.enableInspections(new ComponentNotRegisteredInspection());
- }
-
- public void testRegisteredAction() {
- setPluginXml("registeredAction-plugin.xml");
- myFixture.testHighlighting("RegisteredAction.java");
- }
-
- public void testRegisteredActionInIDEAProject() {
- PsiUtil.markAsIdeaProject(getProject(), true);
-
- try {
- myFixture.copyFileToProject("registeredAction-plugin.xml", "someOtherPluginXmlName.xml");
- myFixture.testHighlighting("RegisteredAction.java");
- }
- finally {
- PsiUtil.markAsIdeaProject(getProject(), false);
- }
- }
-
- public void testRegisteredActionInOptionalPluginDescriptor() {
- setPluginXml("registeredActionInOptionalPluginDescriptor-plugin.xml");
- myFixture.copyFileToProject("registeredActionInOptionalPluginDescriptor-optional-plugin.xml",
- "META-INF/optional-plugin.xml");
-
- myFixture.testHighlighting("RegisteredAction.java");
- }
-
- public void testRegisteredInIncludedFileAction() {
- setPluginXml("ActionXInclude.xml");
- myFixture.copyFileToProject("ActionXInclude_included.xml", "META-INF/ActionXInclude_included.xml");
- myFixture.testHighlighting("ActionXInclude.java");
- }
-
- public void testUnregisteredAction() {
- setPluginXml("unregisteredAction-plugin.xml");
- myFixture.testHighlighting("UnregisteredAction.java");
-
- RegisterActionFix.ourTestActionData = new MyActionData("UnregisteredAction");
- final IntentionAction registerAction = myFixture.findSingleIntention("Register Action");
- myFixture.launchAction(registerAction);
-
- myFixture.checkResultByFile("META-INF/plugin.xml", "unregisteredAction-plugin_after.xml", true);
- }
-
- public void testUnregisteredActionUsedViaConstructor() {
- myFixture.testHighlighting("UnregisteredActionUsedViaConstructor.java");
- }
-
- public void testRegisteredApplicationComponent() {
- setPluginXml("registeredApplicationComponent-plugin.xml");
- myFixture.testHighlighting("RegisteredApplicationComponent.java");
- }
-
- public void testUnregisteredAbstractApplicationComponent() {
- myFixture.testHighlighting("UnregisteredAbstractApplicationComponent.java");
- }
-
- public void testUnregisteredApplicationComponentWithoutPluginXml() {
- myFixture.testHighlighting("UnregisteredApplicationComponent.java",
- "UnregisteredApplicationComponentInterface.java");
- }
-
- public void testUnregisteredApplicationComponentWithRegisterFix() {
- setPluginXml("unregisteredApplicationComponent-plugin.xml");
-
- myFixture.testHighlighting("UnregisteredApplicationComponent.java",
- "UnregisteredApplicationComponentInterface.java");
- final IntentionAction registerAction = myFixture.findSingleIntention("Register Application Component");
- myFixture.launchAction(registerAction);
-
- myFixture.checkResultByFile("META-INF/plugin.xml", "unregisteredApplicationComponent-plugin_after.xml", true);
- }
-
-
- private static class MyActionData implements ActionData {
- private final String myActionClassFqn;
-
- public MyActionData(String actionClassFqn) {
- myActionClassFqn = actionClassFqn;
- }
-
- @NotNull
- @Override
- public String getActionId() {
- return StringUtil.getShortName(myActionClassFqn);
- }
-
- @NotNull
- @Override
- public String getActionText() {
- return "Action Text " + myActionClassFqn;
- }
-
- @Override
- public String getActionDescription() {
- return "Description " + myActionClassFqn;
- }
-
- @Nullable
- @Override
- public String getSelectedGroupId() {
- return getActionId() + "Group";
- }
-
- @Nullable
- @Override
- public String getSelectedActionId() {
- return getActionId() + "SelectedAction";
- }
-
- @Override
- public String getSelectedAnchor() {
- return Anchor.before.name();
- }
-
- @Nullable
- @Override
- public String getFirstKeyStroke() {
- return "1st Key " + getActionId();
- }
-
- @Nullable
- @Override
- public String getSecondKeyStroke() {
- return "2nd Key " + getActionId();
- }
+ protected String getSourceFileExtension() {
+ return "java";
}
}
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/ActionXInclude.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/ActionXInclude.kt
new file mode 100644
index 000000000000..f12bbe971fe2
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/ActionXInclude.kt
@@ -0,0 +1 @@
+class ActionXInclude : com.intellij.openapi.actionSystem.AnAction()
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/ActionXInclude.xml b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/ActionXInclude.xml
new file mode 100644
index 000000000000..9797654e4ba9
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/ActionXInclude.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/ActionXInclude_included.xml b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/ActionXInclude_included.xml
new file mode 100644
index 000000000000..9b4831cb2b12
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/ActionXInclude_included.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/RegisteredAction.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/RegisteredAction.kt
new file mode 100644
index 000000000000..a3ffa00c573e
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/RegisteredAction.kt
@@ -0,0 +1,7 @@
+import com.intellij.openapi.actionSystem.AnAction
+
+class RegisteredAction : AnAction() {
+
+ class InnerAction : AnAction()
+
+}
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/RegisteredApplicationComponent.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/RegisteredApplicationComponent.kt
new file mode 100644
index 000000000000..50b1becd7231
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/RegisteredApplicationComponent.kt
@@ -0,0 +1,6 @@
+import com.intellij.openapi.components.ApplicationComponent
+
+class RegisteredApplicationComponent : ApplicationComponent {
+
+ class InnerStaticClassApplicationContext : ApplicationComponent
+}
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/UnregisteredAbstractApplicationComponent.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/UnregisteredAbstractApplicationComponent.kt
new file mode 100644
index 000000000000..2cd52da6f36f
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/UnregisteredAbstractApplicationComponent.kt
@@ -0,0 +1,3 @@
+import com.intellij.openapi.components.ApplicationComponent
+
+abstract class UnregisteredAbstractApplicationComponent : ApplicationComponent
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/UnregisteredAction.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/UnregisteredAction.kt
new file mode 100644
index 000000000000..92afa81742cd
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/UnregisteredAction.kt
@@ -0,0 +1,7 @@
+import com.intellij.openapi.actionSystem.AnAction
+
+class UnregisteredAction : AnAction() {
+ class InnerAction : AnAction()
+
+ protected class NonPublicIsIgnored : AnAction()
+}
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/UnregisteredActionUsedViaConstructor.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/UnregisteredActionUsedViaConstructor.kt
new file mode 100644
index 000000000000..2a5c7180fc5b
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/UnregisteredActionUsedViaConstructor.kt
@@ -0,0 +1,9 @@
+@file:Suppress("UNUSED_VARIABLE")
+
+import com.intellij.openapi.actionSystem.AnAction
+
+class UnregisteredActionUsedViaConstructor : AnAction()
+
+fun main(args: Array) {
+ val mine = UnregisteredActionUsedViaConstructor()
+}
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/UnregisteredApplicationComponent.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/UnregisteredApplicationComponent.kt
new file mode 100644
index 000000000000..522b4e6bd207
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/UnregisteredApplicationComponent.kt
@@ -0,0 +1,12 @@
+import com.intellij.openapi.components.ApplicationComponent
+
+class UnregisteredApplicationComponent
+ : ApplicationComponent, UnregisteredApplicationComponentInterface {
+
+ class InnerStaticClassApplicationContext
+ : ApplicationComponent
+
+ inner class InnerClassApplicationContextIsNotChecked : ApplicationComponent
+
+ fun getInstance() : UnregisteredApplicationComponentInterface? = null
+}
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/UnregisteredApplicationComponentInterface.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/UnregisteredApplicationComponentInterface.kt
new file mode 100644
index 000000000000..72bc731fa042
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/UnregisteredApplicationComponentInterface.kt
@@ -0,0 +1 @@
+interface UnregisteredApplicationComponentInterface
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/registeredAction-plugin.xml b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/registeredAction-plugin.xml
new file mode 100644
index 000000000000..1896809e55eb
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/registeredAction-plugin.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/registeredActionInOptionalPluginDescriptor-optional-plugin.xml b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/registeredActionInOptionalPluginDescriptor-optional-plugin.xml
new file mode 100644
index 000000000000..e2216421f271
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/registeredActionInOptionalPluginDescriptor-optional-plugin.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/registeredActionInOptionalPluginDescriptor-plugin.xml b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/registeredActionInOptionalPluginDescriptor-plugin.xml
new file mode 100644
index 000000000000..0ace22f7d835
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/registeredActionInOptionalPluginDescriptor-plugin.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/registeredApplicationComponent-plugin.xml b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/registeredApplicationComponent-plugin.xml
new file mode 100644
index 000000000000..9e8405a71961
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/registeredApplicationComponent-plugin.xml
@@ -0,0 +1,11 @@
+
+
+
+ RegisteredApplicationComponent
+
+
+ RegisteredApplicationComponent$InnerStaticClassApplicationContext
+
+
+
+
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/unregisteredAction-plugin.xml b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/unregisteredAction-plugin.xml
new file mode 100644
index 000000000000..6fb76a66265d
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/unregisteredAction-plugin.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/unregisteredAction-plugin_after.xml b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/unregisteredAction-plugin_after.xml
new file mode 100644
index 000000000000..89977dbe52d4
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/unregisteredAction-plugin_after.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/unregisteredApplicationComponent-plugin.xml b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/unregisteredApplicationComponent-plugin.xml
new file mode 100644
index 000000000000..9d47e45ee4de
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/unregisteredApplicationComponent-plugin.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/unregisteredApplicationComponent-plugin_after.xml b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/unregisteredApplicationComponent-plugin_after.xml
new file mode 100644
index 000000000000..abc6a373be6d
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/componentNotRegistered/unregisteredApplicationComponent-plugin_after.xml
@@ -0,0 +1,9 @@
+
+
+
+
+ UnregisteredApplicationComponent
+ UnregisteredApplicationComponentInterface
+
+
+
\ No newline at end of file
diff --git a/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/inspections/KtComponentNotRegisteredInspectionTest.java b/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/inspections/KtComponentNotRegisteredInspectionTest.java
new file mode 100644
index 000000000000..13d25b44c479
--- /dev/null
+++ b/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/inspections/KtComponentNotRegisteredInspectionTest.java
@@ -0,0 +1,27 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package org.jetbrains.idea.devkit.kotlin.inspections;
+
+import com.intellij.testFramework.TestDataPath;
+import kotlin.KotlinVersion;
+import org.jetbrains.idea.devkit.inspections.ComponentNotRegisteredInspectionTestBase;
+import org.jetbrains.idea.devkit.kotlin.DevkitKtTestsUtil;
+import org.junit.Assume;
+
+@TestDataPath("$CONTENT_ROOT/testData/inspections/componentNotRegistered")
+public class KtComponentNotRegisteredInspectionTest extends ComponentNotRegisteredInspectionTestBase {
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ Assume.assumeTrue(KotlinVersion.CURRENT.isAtLeast(1, 2, 50));
+ }
+
+ @Override
+ protected String getSourceFileExtension() {
+ return "kt";
+ }
+
+ @Override
+ protected String getBasePath() {
+ return DevkitKtTestsUtil.TESTDATA_PATH + "inspections/componentNotRegistered";
+ }
+}
diff --git a/plugins/devkit/devkit-tests-api/src/org/jetbrains/idea/devkit/inspections/ComponentNotRegisteredInspectionTestBase.java b/plugins/devkit/devkit-tests-api/src/org/jetbrains/idea/devkit/inspections/ComponentNotRegisteredInspectionTestBase.java
new file mode 100644
index 000000000000..f88f770d9c9c
--- /dev/null
+++ b/plugins/devkit/devkit-tests-api/src/org/jetbrains/idea/devkit/inspections/ComponentNotRegisteredInspectionTestBase.java
@@ -0,0 +1,158 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package org.jetbrains.idea.devkit.inspections;
+
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.dom.Anchor;
+import org.jetbrains.idea.devkit.inspections.quickfix.RegisterActionFix;
+import org.jetbrains.idea.devkit.util.ActionData;
+import org.jetbrains.idea.devkit.util.PsiUtil;
+
+public abstract class ComponentNotRegisteredInspectionTestBase extends PluginModuleTestCase {
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ myFixture.addClass("package com.intellij.openapi.actionSystem; public class AnAction {}");
+ myFixture.addClass("package com.intellij.openapi.components; public interface BaseComponent {}");
+ myFixture.addClass("package com.intellij.openapi.components; public interface ApplicationComponent extends BaseComponent {}");
+
+ myFixture.enableInspections(new ComponentNotRegisteredInspection());
+ }
+
+ protected abstract String getSourceFileExtension();
+
+
+ public void testRegisteredAction() {
+ setPluginXml("registeredAction-plugin.xml");
+ myFixture.testHighlighting("RegisteredAction." + getSourceFileExtension());
+ }
+
+ public void testRegisteredActionInIDEAProject() {
+ PsiUtil.markAsIdeaProject(getProject(), true);
+
+ try {
+ myFixture.copyFileToProject("registeredAction-plugin.xml", "someOtherPluginXmlName.xml");
+ myFixture.testHighlighting("RegisteredAction." + getSourceFileExtension());
+ }
+ finally {
+ PsiUtil.markAsIdeaProject(getProject(), false);
+ }
+ }
+
+ public void testRegisteredActionInOptionalPluginDescriptor() {
+ setPluginXml("registeredActionInOptionalPluginDescriptor-plugin.xml");
+ myFixture.copyFileToProject("registeredActionInOptionalPluginDescriptor-optional-plugin.xml",
+ "META-INF/optional-plugin.xml");
+
+ myFixture.testHighlighting("RegisteredAction." + getSourceFileExtension());
+ }
+
+ public void testRegisteredInIncludedFileAction() {
+ setPluginXml("ActionXInclude.xml");
+ myFixture.copyFileToProject("ActionXInclude_included.xml", "META-INF/ActionXInclude_included.xml");
+ myFixture.testHighlighting("ActionXInclude." + getSourceFileExtension());
+ }
+
+ @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod")
+ public void testUnregisteredAction() {
+ setPluginXml("unregisteredAction-plugin.xml");
+ myFixture.testHighlighting("UnregisteredAction." + getSourceFileExtension());
+
+ RegisterActionFix.ourTestActionData = new MyActionData("UnregisteredAction");
+ try {
+ final IntentionAction registerAction = myFixture.findSingleIntention("Register Action");
+ myFixture.launchAction(registerAction);
+
+ myFixture.checkResultByFile("META-INF/plugin.xml", "unregisteredAction-plugin_after.xml", true);
+ } finally {
+ RegisterActionFix.ourTestActionData = null;
+ }
+ }
+
+ public void testUnregisteredActionUsedViaConstructor() {
+ myFixture.testHighlighting("UnregisteredActionUsedViaConstructor." + getSourceFileExtension());
+ }
+
+ public void testRegisteredApplicationComponent() {
+ setPluginXml("registeredApplicationComponent-plugin.xml");
+ myFixture.testHighlighting("RegisteredApplicationComponent." + getSourceFileExtension());
+ }
+
+ public void testUnregisteredAbstractApplicationComponent() {
+ myFixture.testHighlighting("UnregisteredAbstractApplicationComponent." + getSourceFileExtension());
+ }
+
+ public void testUnregisteredApplicationComponentWithoutPluginXml() {
+ myFixture.testHighlighting("UnregisteredApplicationComponent." + getSourceFileExtension(),
+ "UnregisteredApplicationComponentInterface." + getSourceFileExtension());
+ }
+
+ public void testUnregisteredApplicationComponentWithRegisterFix() {
+ setPluginXml("unregisteredApplicationComponent-plugin.xml");
+
+ myFixture.testHighlighting("UnregisteredApplicationComponent." + getSourceFileExtension(),
+ "UnregisteredApplicationComponentInterface." + getSourceFileExtension());
+ final IntentionAction registerAction = myFixture.findSingleIntention("Register Application Component");
+ myFixture.launchAction(registerAction);
+
+ myFixture.checkResultByFile("META-INF/plugin.xml", "unregisteredApplicationComponent-plugin_after.xml", true);
+ }
+
+
+ public static class MyActionData implements ActionData {
+ private final String myActionClassFqn;
+
+ public MyActionData(String actionClassFqn) {
+ myActionClassFqn = actionClassFqn;
+ }
+
+ @NotNull
+ @Override
+ public String getActionId() {
+ return StringUtil.getShortName(myActionClassFqn);
+ }
+
+ @NotNull
+ @Override
+ public String getActionText() {
+ return "Action Text " + myActionClassFqn;
+ }
+
+ @Override
+ public String getActionDescription() {
+ return "Description " + myActionClassFqn;
+ }
+
+ @Nullable
+ @Override
+ public String getSelectedGroupId() {
+ return getActionId() + "Group";
+ }
+
+ @Nullable
+ @Override
+ public String getSelectedActionId() {
+ return getActionId() + "SelectedAction";
+ }
+
+ @Override
+ public String getSelectedAnchor() {
+ return Anchor.before.name();
+ }
+
+ @Nullable
+ @Override
+ public String getFirstKeyStroke() {
+ return "1st Key " + getActionId();
+ }
+
+ @Nullable
+ @Override
+ public String getSecondKeyStroke() {
+ return "2nd Key " + getActionId();
+ }
+ }
+}
diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/PluginModuleTestCase.java b/plugins/devkit/devkit-tests-api/src/org/jetbrains/idea/devkit/inspections/PluginModuleTestCase.java
similarity index 100%
rename from plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/PluginModuleTestCase.java
rename to plugins/devkit/devkit-tests-api/src/org/jetbrains/idea/devkit/inspections/PluginModuleTestCase.java