Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop plugin declaration inspections #588

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,13 @@
enabledByDefault="true" level="WARNING"
implementationClass="com.magento.idea.magento2plugin.inspections.xml.PreferenceDeclarationInspection"/>

<localInspection language="XML" groupPath="XML"
shortName="PluginAttrTypeInspection"
bundle="magento2.inspection" key="inspection.displayName.PluginAttrTypeInspection"
groupBundle="magento2.inspection" groupKey="inspection.group.name"
enabledByDefault="true" level="WARNING"
implementationClass="com.magento.idea.magento2plugin.inspections.xml.PluginAttributeTypeInspection"/>

<internalFileTemplate name="Magento Composer JSON"/>
<internalFileTemplate name="Magento Registration PHP"/>
<internalFileTemplate name="Magento Module XML"/>
Expand Down
19 changes: 19 additions & 0 deletions resources/inspectionDescriptions/PluginAttrTypeInspection.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!--
/*
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<html>
<body>
<p>
Validates if attribute `type` in the &lt;plugin/&gt; tag of di.xml files contains valid classes or interfaces
and that it cannot be empty.
</p>
<p>This inspection inspects:
<ul>
<li>The existence of the class on the specified path</li>
<li>Tag `type` is not empty</li>
</ul>
</body>
</html>
1 change: 1 addition & 0 deletions resources/magento2/inspection.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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
);
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0"?>
<config>
<type name="Magento\CatalogSearch\Helper\Data">
<plugin name="unique_plugin_name" type=""/>
</type>
</config>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0"?>
<config>
<type name="Magento\CatalogSearch\Helper\Data">
<plugin name="unique_plugin_name" type="Magento\Catalog\Plugin\PluginClass"/>
</type>
</config>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0"?>
<config>
<type name="Magento\CatalogSearch\Helper\Data">
<plugin name="unique_plugin_name" type="Not\Existent\Class"/>
</type>
</config>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0"?>
<config>
<type name="Magento\CatalogSearch\Helper\Data">
<plugin name="unique_plugin_name" type="Magento\Catalog\Plugin\PluginClass"/>
</type>
</config>
Original file line number Diff line number Diff line change
@@ -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.
* <plugin name="unique_plugin_name" type=""/>
*/
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.
* <plugin name="unique_plugin_name" type="Magento\Catalog\Plugin\PluginClass" />
*/
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));
}
}