diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index 709645aa6..d0a684182 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -153,64 +153,72 @@ + + diff --git a/resources/inspectionDescriptions/InvalidDiTypeInspection.html b/resources/inspectionDescriptions/InvalidDiTypeInspection.html new file mode 100644 index 000000000..ff01cbdcb --- /dev/null +++ b/resources/inspectionDescriptions/InvalidDiTypeInspection.html @@ -0,0 +1,22 @@ + + + +

+ Validates if all types inside the <type/> tag of di.xml files contains valid classes, + interfaces, factories, proxies or virtual type names. +

+

This inspection checks name attribute of the <type/> tag and all arguments recursively which has xsi:type attribute value as object.

+

This inspection supports next types:

+
    +
  • PHP classes
  • +
  • PHP interfaces
  • +
  • PHP classes or interfaces with added Factory or \Proxy suffixes
  • +
  • Magento 2 Virtual Types names
  • +
+ + diff --git a/resources/magento2/inspection.properties b/resources/magento2/inspection.properties index d12e724f1..8ed815dc5 100644 --- a/resources/magento2/inspection.properties +++ b/resources/magento2/inspection.properties @@ -1,3 +1,13 @@ +inspection.group.name=Magento 2 +inspection.displayName.PluginInspection=Inspection for the Plugin declaration +inspection.displayName.ModuleDeclarationInRegistrationPhpInspection=Inspection for the Module declaration in the `registration.php` file +inspection.displayName.ObserverDeclarationInspection=Duplicated Observer Usage in events XML +inspection.displayName.PluginDeclarationInspection=Duplicated Plugin Usage in di XML +inspection.displayName.CacheableFalseInDefaultLayoutInspection=Inspection for disabled cache site-wide +inspection.displayName.ModuleDeclarationInModuleXmlInspection=Inspection for the Module declaration in the `etc/module.xml` file +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.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. inspection.plugin.disabledPluginDoesNotExist=This plugin does not exist to be disabled. diff --git a/src/com/magento/idea/magento2plugin/inspections/validator/InspectionValidator.java b/src/com/magento/idea/magento2plugin/inspections/validator/InspectionValidator.java new file mode 100644 index 000000000..543ec88d8 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/inspections/validator/InspectionValidator.java @@ -0,0 +1,21 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.inspections.validator; + +/** + * All Inspections validators should implement this validator. + */ +public interface InspectionValidator { + + /** + * Validate if provided value acceptable by concrete validator implementation. + * + * @param value String + * + * @return boolean + */ + boolean validate(final String value); +} diff --git a/src/com/magento/idea/magento2plugin/inspections/validator/NotEmptyValidator.java b/src/com/magento/idea/magento2plugin/inspections/validator/NotEmptyValidator.java new file mode 100644 index 000000000..c101772fd --- /dev/null +++ b/src/com/magento/idea/magento2plugin/inspections/validator/NotEmptyValidator.java @@ -0,0 +1,14 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.inspections.validator; + +public class NotEmptyValidator implements InspectionValidator { + + @Override + public boolean validate(final String value) { + return value != null && !value.isEmpty(); + } +} diff --git a/src/com/magento/idea/magento2plugin/inspections/validator/PhpClassExistenceValidator.java b/src/com/magento/idea/magento2plugin/inspections/validator/PhpClassExistenceValidator.java new file mode 100644 index 000000000..b613c0f87 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/inspections/validator/PhpClassExistenceValidator.java @@ -0,0 +1,32 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.inspections.validator; + +import com.intellij.openapi.project.Project; +import com.jetbrains.php.PhpIndex; +import com.jetbrains.php.lang.psi.elements.PhpClass; +import java.util.Collection; +import org.jetbrains.annotations.NotNull; + +public class PhpClassExistenceValidator implements InspectionValidator { + + private final PhpIndex phpIndex; + + public PhpClassExistenceValidator(final @NotNull Project project) { + phpIndex = PhpIndex.getInstance(project); + } + + @Override + public boolean validate(final String value) { + if (value == null) { + return false; + } + final @NotNull Collection classes = phpIndex.getClassesByFQN(value); + final @NotNull Collection interfaces = phpIndex.getInterfacesByFQN(value); + + return !classes.isEmpty() || !interfaces.isEmpty(); + } +} diff --git a/src/com/magento/idea/magento2plugin/inspections/validator/VirtualTypeExistenceValidator.java b/src/com/magento/idea/magento2plugin/inspections/validator/VirtualTypeExistenceValidator.java new file mode 100644 index 000000000..3edfbbfd7 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/inspections/validator/VirtualTypeExistenceValidator.java @@ -0,0 +1,36 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.inspections.validator; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.indexing.FileBasedIndex; +import com.magento.idea.magento2plugin.stubs.indexes.VirtualTypeIndex; +import java.util.Collection; +import org.jetbrains.annotations.NotNull; + +public class VirtualTypeExistenceValidator implements InspectionValidator { + + private final Project project; + + public VirtualTypeExistenceValidator(final @NotNull Project project) { + this.project = project; + } + + @Override + public boolean validate(final String value) { + if (value == null) { + return false; + } + final @NotNull Collection virtualTypes = FileBasedIndex.getInstance().getValues( + VirtualTypeIndex.KEY, + value, + GlobalSearchScope.allScope(project) + ); + + return !virtualTypes.isEmpty(); + } +} diff --git a/src/com/magento/idea/magento2plugin/inspections/xml/InvalidDependencyInjectionTypeInspection.java b/src/com/magento/idea/magento2plugin/inspections/xml/InvalidDependencyInjectionTypeInspection.java new file mode 100644 index 000000000..bf96bf75d --- /dev/null +++ b/src/com/magento/idea/magento2plugin/inspections/xml/InvalidDependencyInjectionTypeInspection.java @@ -0,0 +1,179 @@ +/* + * 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.inspections.validator.VirtualTypeExistenceValidator; +import com.magento.idea.magento2plugin.magento.files.ModuleDiXml; +import com.magento.idea.magento2plugin.util.xml.XmlPsiTreeUtil; +import java.util.List; +import org.jetbrains.annotations.NotNull; + +@SuppressWarnings({"PMD.ExcessiveMethodLength", "PMD.NPathComplexity"}) +public class InvalidDependencyInjectionTypeInspection extends XmlSuppressableInspectionTool { + + @Override + public @NotNull PsiElementVisitor buildVisitor( + final @NotNull ProblemsHolder problemsHolder, + final boolean isOnTheFly + ) { + return new XmlElementVisitor() { + + // Used to show messages for inspection scope. + private final InspectionBundle inspectionBundle = new InspectionBundle(); + // Inspection validators + private final InspectionValidator phpClassExistenceValidator = + new PhpClassExistenceValidator(problemsHolder.getProject()); + private final InspectionValidator virtualTypeExistenceValidator = + new VirtualTypeExistenceValidator(problemsHolder.getProject()); + private final InspectionValidator notEmptyValidator = new NotEmptyValidator(); + + @Override + public void visitXmlTag(final @NotNull XmlTag xmlTag) { + final PsiFile file = xmlTag.getContainingFile(); + + if (!file.getName().equals(ModuleDiXml.FILE_NAME) + || !xmlTag.getName().equals(ModuleDiXml.TYPE_TAG)) { + return; + } + final XmlAttribute nameAttribute = xmlTag.getAttribute(ModuleDiXml.NAME_ATTR); + + if (nameAttribute == null + || nameAttribute.getValue() == null + || nameAttribute.getValueElement() == null) { + return; + } + + //Check whether the name attribute is not empty + if (!notEmptyValidator.validate(nameAttribute.getValue())) { + reportCouldNotBeEmpty( + nameAttribute.getValueElement(), + nameAttribute.getName() + ); + } + + //Check whether the class exists + if (!phpClassExistenceValidator.validate(nameAttribute.getValue())) { + reportClassDoesNotExists( + nameAttribute.getValueElement(), + nameAttribute.getValue() + ); + } + final XmlTag argumentsTag = xmlTag.findFirstSubTag(ModuleDiXml.ARGUMENTS_TAG); + + // Break visiting if there are no arguments + if (argumentsTag == null) { + return; + } + + final List argumentsTags = XmlPsiTreeUtil.findSubTagsOfParent( + argumentsTag, + ModuleDiXml.ARGUMENT_TAG + ); + + for (final XmlTag argumentTag : argumentsTags) { + checkObjectArgumentsRecursively(argumentTag); + } + } + + /** + * Recursively check all xsi-type object attributes. + * + * @param tag XmlTag + */ + private void checkObjectArgumentsRecursively(final @NotNull XmlTag tag) { + final XmlAttribute xsiTypeAttr = tag.getAttribute(ModuleDiXml.XSI_TYPE_ATTR); + + if (xsiTypeAttr == null + || xsiTypeAttr.getValueElement() == null + || xsiTypeAttr.getValue() == null) { + return; + } + final String xsiTypeValue = xsiTypeAttr.getValue(); + + if (xsiTypeValue.equals(ModuleDiXml.XSI_TYPE_ARRAY)) { + final List itemsTags = XmlPsiTreeUtil.findSubTagsOfParent( + tag, + ModuleDiXml.ITEM_TAG + ); + if (itemsTags.isEmpty()) { + return; + } + + for (final XmlTag itemTag : itemsTags) { + checkObjectArgumentsRecursively(itemTag); + } + } else if (xsiTypeValue.equals(ModuleDiXml.XSI_TYPE_OBJECT)) { + final String tagValue = tag.getValue().getText(); + + if (tagValue.isEmpty()) { + return; + } + final String cleanType = tagValue + .replace("Factory", "") + .replace("\\Proxy", ""); + + if (!phpClassExistenceValidator.validate(cleanType) + && !virtualTypeExistenceValidator.validate(tagValue)) { + reportClassDoesNotExists(tag, tagValue); + } + } + } + + /** + * 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/src/com/magento/idea/magento2plugin/inspections/xml/WebApiServiceInspection.java b/src/com/magento/idea/magento2plugin/inspections/xml/WebApiServiceInspection.java index 01cbeaadf..1913ffa18 100644 --- a/src/com/magento/idea/magento2plugin/inspections/xml/WebApiServiceInspection.java +++ b/src/com/magento/idea/magento2plugin/inspections/xml/WebApiServiceInspection.java @@ -17,6 +17,9 @@ import com.jetbrains.php.lang.psi.elements.Method; import com.jetbrains.php.lang.psi.elements.PhpClass; 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.inspections.xml.fix.MethodNotPublicAccessQuickFix; import com.magento.idea.magento2plugin.magento.files.ModuleWebApiXmlFile; import java.util.Collection; @@ -33,7 +36,12 @@ public PsiElementVisitor buildVisitor( final boolean isOnTheFly ) { return new XmlElementVisitor() { + private final InspectionBundle inspectionBundle = new InspectionBundle(); + // Inspection validators + private final InspectionValidator notEmptyValidator = new NotEmptyValidator(); + private final InspectionValidator phpClassExistenceValidator = + new PhpClassExistenceValidator(problemsHolder.getProject()); @Override public void visitXmlTag(final XmlTag xmlTag) { @@ -55,7 +63,8 @@ public void visitXmlTag(final XmlTag xmlTag) { return; } final String classFqn = classAttribute.getValue(); - if (classFqn == null || classFqn.isEmpty()) { + + if (!notEmptyValidator.validate(classFqn)) { problemsHolder.registerProblem( classAttribute, inspectionBundle.message( @@ -68,14 +77,7 @@ public void visitXmlTag(final XmlTag xmlTag) { return; } - //Check whether the class exists - final PhpIndex phpIndex = PhpIndex.getInstance( - problemsHolder.getProject() - ); - @NotNull final Collection classes = phpIndex.getClassesByFQN(classFqn); - @NotNull final Collection interfaces = phpIndex - .getInterfacesByFQN(classFqn); - if (classes.isEmpty() && interfaces.isEmpty()) { + if (!phpClassExistenceValidator.validate(classFqn)) { problemsHolder.registerProblem( classAttribute, inspectionBundle.message( @@ -95,7 +97,8 @@ public void visitXmlTag(final XmlTag xmlTag) { return; } final String methodName = methodAttribute.getValue(); - if (methodName == null || methodName.isEmpty()) { + + if (!notEmptyValidator.validate(methodName)) { problemsHolder.registerProblem( classAttribute, inspectionBundle.message( @@ -109,10 +112,16 @@ public void visitXmlTag(final XmlTag xmlTag) { } //Check whether method exists + final PhpIndex phpIndex = PhpIndex.getInstance(problemsHolder.getProject()); + final @NotNull Collection classes = phpIndex.getClassesByFQN(classFqn); + final @NotNull Collection interfaces = phpIndex + .getInterfacesByFQN(classFqn); + Method targetMethod = findTargetMethod(classes, methodName); if (targetMethod == null) { targetMethod = findTargetMethod(interfaces, methodName); } + if (targetMethod == null && methodAttribute.getValueElement() != null) { problemsHolder.registerProblem( methodAttribute.getValueElement(), @@ -127,7 +136,9 @@ public void visitXmlTag(final XmlTag xmlTag) { } //API method should have public access - if (targetMethod.getAccess() != null && !targetMethod.getAccess().isPublic()) { + if (targetMethod != null + && targetMethod.getAccess() != null + && !targetMethod.getAccess().isPublic()) { problemsHolder.registerProblem( methodAttribute, inspectionBundle.message( @@ -140,8 +151,7 @@ public void visitXmlTag(final XmlTag xmlTag) { } } - @Nullable - private Method findTargetMethod( + private @Nullable Method findTargetMethod( final Collection classes, final String methodName ) { diff --git a/src/com/magento/idea/magento2plugin/magento/files/ModuleDiXml.java b/src/com/magento/idea/magento2plugin/magento/files/ModuleDiXml.java index 36b115643..7db65c0cd 100644 --- a/src/com/magento/idea/magento2plugin/magento/files/ModuleDiXml.java +++ b/src/com/magento/idea/magento2plugin/magento/files/ModuleDiXml.java @@ -54,6 +54,7 @@ public class ModuleDiXml implements ModuleFileInterface { public static String XSI_TYPE_ATTR = "xsi:type"; // attribute values + public static String XSI_TYPE_OBJECT = "object"; public static String XSI_TYPE_STRING = "string"; public static String XSI_TYPE_ARRAY = "array"; public static String COLLECTIONS_ATTR_VALUE = "collections"; diff --git a/testData/inspections/xml/TypeConfigurationTagTypesInspection/argumentFactoryTypeExists/di.xml b/testData/inspections/xml/TypeConfigurationTagTypesInspection/argumentFactoryTypeExists/di.xml new file mode 100644 index 000000000..c18bf23e9 --- /dev/null +++ b/testData/inspections/xml/TypeConfigurationTagTypesInspection/argumentFactoryTypeExists/di.xml @@ -0,0 +1,9 @@ + + + + + Magento\Catalog\Api\ProductRepositoryInterfaceFactory + + + diff --git a/testData/inspections/xml/TypeConfigurationTagTypesInspection/nameAttributeValueTypeDoesNotExist/di.xml b/testData/inspections/xml/TypeConfigurationTagTypesInspection/nameAttributeValueTypeDoesNotExist/di.xml new file mode 100644 index 000000000..8e1497751 --- /dev/null +++ b/testData/inspections/xml/TypeConfigurationTagTypesInspection/nameAttributeValueTypeDoesNotExist/di.xml @@ -0,0 +1,5 @@ + + + + diff --git a/testData/inspections/xml/TypeConfigurationTagTypesInspection/nameAttributeValueTypeExists/di.xml b/testData/inspections/xml/TypeConfigurationTagTypesInspection/nameAttributeValueTypeExists/di.xml new file mode 100644 index 000000000..0e764caa1 --- /dev/null +++ b/testData/inspections/xml/TypeConfigurationTagTypesInspection/nameAttributeValueTypeExists/di.xml @@ -0,0 +1,5 @@ + + + + diff --git a/testData/inspections/xml/TypeConfigurationTagTypesInspection/recursivelyArgumentProxyTypeDoesNotExist/di.xml b/testData/inspections/xml/TypeConfigurationTagTypesInspection/recursivelyArgumentProxyTypeDoesNotExist/di.xml new file mode 100644 index 000000000..b237d29d3 --- /dev/null +++ b/testData/inspections/xml/TypeConfigurationTagTypesInspection/recursivelyArgumentProxyTypeDoesNotExist/di.xml @@ -0,0 +1,13 @@ + + + + + + + Not\Existent\Class\Proxy + + + + + diff --git a/testData/inspections/xml/TypeConfigurationTagTypesInspection/recursivelyArgumentProxyTypeExists/di.xml b/testData/inspections/xml/TypeConfigurationTagTypesInspection/recursivelyArgumentProxyTypeExists/di.xml new file mode 100644 index 000000000..e4c767109 --- /dev/null +++ b/testData/inspections/xml/TypeConfigurationTagTypesInspection/recursivelyArgumentProxyTypeExists/di.xml @@ -0,0 +1,13 @@ + + + + + + + Magento\Catalog\Api\ProductRepositoryInterface\Proxy + + + + + diff --git a/testData/inspections/xml/TypeConfigurationTagTypesInspection/recursivelyArgumentVirtualTypeDoesNotExist/di.xml b/testData/inspections/xml/TypeConfigurationTagTypesInspection/recursivelyArgumentVirtualTypeDoesNotExist/di.xml new file mode 100644 index 000000000..2d8188236 --- /dev/null +++ b/testData/inspections/xml/TypeConfigurationTagTypesInspection/recursivelyArgumentVirtualTypeDoesNotExist/di.xml @@ -0,0 +1,13 @@ + + + + + + + NotExistentVirtualType + + + + + diff --git a/testData/inspections/xml/TypeConfigurationTagTypesInspection/recursivelyArgumentVirtualTypeExists/di.xml b/testData/inspections/xml/TypeConfigurationTagTypesInspection/recursivelyArgumentVirtualTypeExists/di.xml new file mode 100644 index 000000000..f790b54f7 --- /dev/null +++ b/testData/inspections/xml/TypeConfigurationTagTypesInspection/recursivelyArgumentVirtualTypeExists/di.xml @@ -0,0 +1,13 @@ + + + + + + + VirtualProductRepository + + + + + diff --git a/testData/project/magento2/app/code/Foo/Bar2/composer.json b/testData/project/magento2/app/code/Foo/Bar2/composer.json new file mode 100755 index 000000000..d53cc631e --- /dev/null +++ b/testData/project/magento2/app/code/Foo/Bar2/composer.json @@ -0,0 +1,17 @@ +{ + "name": "foo/bar2", + "description": "N/A", + "type": "magento2-module", + "version": "1.0.0", + "require": { + "magento/framework": "*" + }, + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Foo\\Bar2\\": "" + } + } +} diff --git a/testData/project/magento2/app/code/Foo/Bar2/etc/di.xml b/testData/project/magento2/app/code/Foo/Bar2/etc/di.xml new file mode 100644 index 000000000..56852742c --- /dev/null +++ b/testData/project/magento2/app/code/Foo/Bar2/etc/di.xml @@ -0,0 +1,5 @@ + + + + diff --git a/testData/project/magento2/app/code/Foo/Bar2/etc/module.xml b/testData/project/magento2/app/code/Foo/Bar2/etc/module.xml new file mode 100755 index 000000000..103177a10 --- /dev/null +++ b/testData/project/magento2/app/code/Foo/Bar2/etc/module.xml @@ -0,0 +1,5 @@ + + + + diff --git a/testData/project/magento2/app/code/Foo/Bar2/registration.php b/testData/project/magento2/app/code/Foo/Bar2/registration.php new file mode 100755 index 000000000..1ca433f95 --- /dev/null +++ b/testData/project/magento2/app/code/Foo/Bar2/registration.php @@ -0,0 +1,6 @@ +. + */ + public void testNameAttributeValueTypeDoesNotExist() { + configureFixture(); + + final String errorMessage = inspectionBundle.message( + CLASS_DOES_NOT_EXIST, + NOT_EXISTENT_CLASS + ); + + assertHasHighlighting(errorMessage); + } + + /** + * Test type exists: . + */ + public void testNameAttributeValueTypeExists() { + configureFixture(); + + final String errorMessage = inspectionBundle.message( + CLASS_DOES_NOT_EXIST, + "Magento\\Catalog\\Api\\ProductRepositoryInterface" + ); + + assertHasNoHighlighting(errorMessage); + } + + /** + * Test argument factory type exists: TestingTypeFactory. + */ + public void testArgumentFactoryTypeExists() { + configureFixture(); + + final String errorMessage = inspectionBundle.message( + CLASS_DOES_NOT_EXIST, + "Magento\\Catalog\\Api\\ProductRepositoryInterfaceFactory" + ); + + assertHasNoHighlighting(errorMessage); + } + + /** + * Test proxy type doesn't exist highlighting: + * TestType\Proxy. + */ + public void testRecursivelyArgumentProxyTypeDoesNotExist() { + configureFixture(); + + final String errorMessage = inspectionBundle.message( + CLASS_DOES_NOT_EXIST, + NOT_EXISTENT_CLASS.concat("\\Proxy") + ); + + assertHasHighlighting(errorMessage); + } + + /** + * Test proxy type exists. + * TestType\Proxy + */ + public void testRecursivelyArgumentProxyTypeExists() { + configureFixture(); + + final String errorMessage = inspectionBundle.message( + CLASS_DOES_NOT_EXIST, + "Magento\\Catalog\\Api\\ProductRepositoryInterfaceFactory\\Proxy" + ); + + assertHasNoHighlighting(errorMessage); + } + + /** + * Test proxy type doesn't exist highlighting: + * TestType\Proxy. + */ + public void testRecursivelyArgumentVirtualTypeDoesNotExist() { + configureFixture(); + + final String errorMessage = inspectionBundle.message( + CLASS_DOES_NOT_EXIST, + NOT_EXISTENT_VIRTUAL_TYPE + ); + + assertHasHighlighting(errorMessage); + } + + /** + * Test virtual type exists. + * TestVirtualType + */ + public void testRecursivelyArgumentVirtualTypeExists() { + configureFixture(); + + final String errorMessage = inspectionBundle.message( + CLASS_DOES_NOT_EXIST, + EXISTENT_VIRTUAL_TYPE + ); + + assertHasNoHighlighting(errorMessage); + } + + private void configureFixture() { + myFixture.configureByFile(getFixturePath(ModuleDiXml.FILE_NAME)); + } +}