diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index c46e50242..8901b735b 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -229,6 +229,13 @@ enabledByDefault="true" level="WARNING" implementationClass="com.magento.idea.magento2plugin.inspections.xml.PreferenceDeclarationInspection"/> + + diff --git a/resources/inspectionDescriptions/PluginAttrTypeInspection.html b/resources/inspectionDescriptions/PluginAttrTypeInspection.html new file mode 100644 index 000000000..a81d6880d --- /dev/null +++ b/resources/inspectionDescriptions/PluginAttrTypeInspection.html @@ -0,0 +1,19 @@ + + + +

+ Validates if attribute `type` in the <plugin/> tag of di.xml files contains valid classes or interfaces + and that it cannot be empty. +

+

This inspection inspects: +

    +
  • The existence of the class on the specified path
  • +
  • Tag `type` is not empty
  • +
+ + diff --git a/resources/magento2/inspection.properties b/resources/magento2/inspection.properties index 15900c813..b41be33d5 100644 --- a/resources/magento2/inspection.properties +++ b/resources/magento2/inspection.properties @@ -8,6 +8,7 @@ inspection.displayName.ModuleDeclarationInModuleXmlInspection=Inspection for the inspection.displayName.AclResourceXmlInspection=Inspection for the Title XML required attribute in the `etc/acl.xml` file inspection.displayName.WebApiServiceInspection=Inspection for the Web API XML service declaration inspection.displayName.InvalidDiTypeInspection=Invalid type configuration in the `etc/di.xml` file +inspection.displayName.PluginAttrTypeInspection=Inspection for the attribute `type` in the `plugin` tag inspection.displayName.PreferenceXmlInspections=Inspection for the Preference declaration inspection.plugin.duplicateInSameFile=The plugin name already used in this file. For more details see Inspection Description. inspection.plugin.duplicateInOtherPlaces=The plugin name "{0}" for targeted "{1}" class is already used in the module "{2}" ({3} scope). For more details see Inspection Description. diff --git a/src/com/magento/idea/magento2plugin/inspections/xml/PluginAttributeTypeInspection.java b/src/com/magento/idea/magento2plugin/inspections/xml/PluginAttributeTypeInspection.java new file mode 100644 index 000000000..54649a79d --- /dev/null +++ b/src/com/magento/idea/magento2plugin/inspections/xml/PluginAttributeTypeInspection.java @@ -0,0 +1,114 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.inspections.xml; + +import com.intellij.codeInspection.ProblemHighlightType; +import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.codeInspection.XmlSuppressableInspectionTool; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiFile; +import com.intellij.psi.XmlElementVisitor; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlTag; +import com.magento.idea.magento2plugin.bundles.InspectionBundle; +import com.magento.idea.magento2plugin.inspections.validator.InspectionValidator; +import com.magento.idea.magento2plugin.inspections.validator.NotEmptyValidator; +import com.magento.idea.magento2plugin.inspections.validator.PhpClassExistenceValidator; +import com.magento.idea.magento2plugin.magento.files.ModuleDiXml; +import org.jetbrains.annotations.NotNull; + +public class PluginAttributeTypeInspection extends XmlSuppressableInspectionTool { + + @Override + public @NotNull + PsiElementVisitor buildVisitor( + final @NotNull ProblemsHolder problemsHolder, + final boolean isOnTheFly + ) { + return new XmlElementVisitor() { + + private final InspectionBundle inspectionBundle = new InspectionBundle(); + private final InspectionValidator phpClassExistenceValidator = + new PhpClassExistenceValidator(problemsHolder.getProject()); + private final InspectionValidator notEmptyValidator = new NotEmptyValidator(); + + @Override + public void visitXmlTag(final XmlTag xmlTag) { + final PsiFile file = xmlTag.getContainingFile(); + + if (!file.getName().equals(ModuleDiXml.FILE_NAME) + || !xmlTag.getName().equals(ModuleDiXml.PLUGIN_TAG_NAME)) { + return; + } + + final XmlAttribute pluginTypeAttribute = + xmlTag.getAttribute(ModuleDiXml.TYPE_ATTR); + + if (pluginTypeAttribute == null + || pluginTypeAttribute.getValue() == null + || pluginTypeAttribute.getValueElement() == null + || pluginTypeAttribute.getValueElement().getText().isEmpty()) { + return; + } + + if (!notEmptyValidator.validate(pluginTypeAttribute.getValue())) { + reportCouldNotBeEmpty( + pluginTypeAttribute.getValueElement(), + pluginTypeAttribute.getName() + ); + } + + if (!phpClassExistenceValidator.validate(pluginTypeAttribute.getValue())) { + reportClassDoesNotExists( + pluginTypeAttribute.getValueElement(), + pluginTypeAttribute.getValue() + ); + } + } + + /** + * Report Attribute Value could not be empty. + * + * @param psiElement PsiElement + * @param messageParams Object... + */ + private void reportCouldNotBeEmpty( + final @NotNull PsiElement psiElement, + final Object... messageParams + ) { + problemsHolder.registerProblem( + psiElement, + inspectionBundle.message( + "inspection.error.idAttributeCanNotBeEmpty", + messageParams + ), + ProblemHighlightType.ERROR + ); + } + + /** + * Report class does not exists. + * + * @param psiElement PsiElement + * @param messageParams Object... + */ + private void reportClassDoesNotExists( + final @NotNull PsiElement psiElement, + final Object... messageParams + ) { + problemsHolder.registerProblem( + psiElement, + inspectionBundle.message( + "inspection.warning.class.does.not.exist", + messageParams + ), + ProblemHighlightType.WARNING + ); + } + }; + } +} diff --git a/testData/inspections/xml/PluginAttributeTypeInspection/attrArgTypeValueIsEmpty/di.xml b/testData/inspections/xml/PluginAttributeTypeInspection/attrArgTypeValueIsEmpty/di.xml new file mode 100644 index 000000000..d59a47dd8 --- /dev/null +++ b/testData/inspections/xml/PluginAttributeTypeInspection/attrArgTypeValueIsEmpty/di.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/testData/inspections/xml/PluginAttributeTypeInspection/attrTypeClassExists/di.xml b/testData/inspections/xml/PluginAttributeTypeInspection/attrTypeClassExists/di.xml new file mode 100644 index 000000000..69ae9c322 --- /dev/null +++ b/testData/inspections/xml/PluginAttributeTypeInspection/attrTypeClassExists/di.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/testData/inspections/xml/PluginAttributeTypeInspection/classAttrTypeDoesNotExists/di.xml b/testData/inspections/xml/PluginAttributeTypeInspection/classAttrTypeDoesNotExists/di.xml new file mode 100644 index 000000000..1cd190a2d --- /dev/null +++ b/testData/inspections/xml/PluginAttributeTypeInspection/classAttrTypeDoesNotExists/di.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/testData/inspections/xml/PluginAttributeTypeInspection/classAttrTypeIsExist/di.xml b/testData/inspections/xml/PluginAttributeTypeInspection/classAttrTypeIsExist/di.xml new file mode 100644 index 000000000..69ae9c322 --- /dev/null +++ b/testData/inspections/xml/PluginAttributeTypeInspection/classAttrTypeIsExist/di.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/tests/com/magento/idea/magento2plugin/inspections/xml/PluginAttributeTypeInspectionTest.java b/tests/com/magento/idea/magento2plugin/inspections/xml/PluginAttributeTypeInspectionTest.java new file mode 100644 index 000000000..712c69233 --- /dev/null +++ b/tests/com/magento/idea/magento2plugin/inspections/xml/PluginAttributeTypeInspectionTest.java @@ -0,0 +1,89 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.inspections.xml; + +import com.magento.idea.magento2plugin.magento.files.ModuleDiXml; + +public class PluginAttributeTypeInspectionTest extends InspectionXmlFixtureTestCase { + + private static final String ARGUMENT_VALUE_IS_EMPTY = + "inspection.error.idAttributeCanNotBeEmpty"; + private static final String CLASS_DOES_NOT_EXIST = + "inspection.warning.class.does.not.exist"; + private static final String EXISTENT_CLASS = + "Magento\\Catalog\\Plugin\\PluginClass"; + private static final String NOT_EXISTENT_CLASS = + "Not\\Existent\\Class"; + + @Override + public void setUp() throws Exception { + super.setUp(); + myFixture.enableInspections(PluginAttributeTypeInspection.class); + } + + /** + * Test for an error for the "type" attribute because it is empty. + * + */ + public void testAttrArgTypeValueIsEmpty() { + configureFixture(); + + final String forAttrIsEmptyMessage = inspectionBundle.message( + ARGUMENT_VALUE_IS_EMPTY, + ModuleDiXml.TYPE_ATTR + ); + + assertHasHighlighting(forAttrIsEmptyMessage); + } + + /** + * Test for no error for the "type" attribute because this class exists. + * + */ + public void testAttrTypeClassExists() { + configureFixture(); + + final String typeAttrIsEmptyMessage = inspectionBundle.message( + ARGUMENT_VALUE_IS_EMPTY, + ModuleDiXml.TYPE_ATTR + ); + + assertHasNoHighlighting(typeAttrIsEmptyMessage); + } + + /** + * Test for throwing an error for a class that does not exist for the "type" attribute. + */ + public void testClassAttrTypeDoesNotExists() { + configureFixture(); + + final String forClassDoesNotExists = inspectionBundle.message( + CLASS_DOES_NOT_EXIST, + NOT_EXISTENT_CLASS + ); + + assertHasHighlighting(forClassDoesNotExists); + } + + /** + * Test for the absence of an error in the presence of + * classes or interfaces specified for plugins. + */ + public void testClassAttrTypeIsExist() { + configureFixture(); + + final String classOneExists = inspectionBundle.message( + CLASS_DOES_NOT_EXIST, + EXISTENT_CLASS + ); + + assertHasNoHighlighting(classOneExists); + } + + private void configureFixture() { + myFixture.configureByFile(getFixturePath(ModuleDiXml.FILE_NAME)); + } +}