Skip to content

Commit af5f3e9

Browse files
1030: Adding configuration files support for the UCT feature
1 parent b1dbfbe commit af5f3e9

File tree

12 files changed

+356
-1
lines changed

12 files changed

+356
-1
lines changed

resources/META-INF/plugin.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,21 @@
537537
enabledByDefault="false"
538538
level="WARNING"
539539
implementationClass="com.magento.idea.magento2uct.inspections.php.api.CalledNonInterfaceMethod"/>
540+
541+
<localInspection language="XML" groupPath="UCT"
542+
shortName="UsedNonExistentTypeInConfig"
543+
bundle="uct.bundle.inspection" key="inspection.displayName.UsedNonExistentTypeInConfig"
544+
groupBundle="uct.bundle.inspection" groupKey="inspection.api.group.name"
545+
enabledByDefault="true"
546+
level="ERROR"
547+
implementationClass="com.magento.idea.magento2uct.inspections.xml.UsedNonExistentTypeInConfig"/>
548+
<localInspection language="XML" groupPath="UCT"
549+
shortName="UsedDeprecatedTypeInConfig"
550+
bundle="uct.bundle.inspection" key="inspection.displayName.UsedDeprecatedTypeInConfig"
551+
groupBundle="uct.bundle.inspection" groupKey="inspection.api.group.name"
552+
enabledByDefault="true"
553+
level="ERROR"
554+
implementationClass="com.magento.idea.magento2uct.inspections.xml.UsedDeprecatedTypeInConfig"/>
540555
<!-- \UCT inspection -->
541556

542557
<internalFileTemplate name="Magento Composer JSON"/>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<html>
2+
<body>
3+
<p>[1134] Using Magento 2 @deprecated type: consider using Magento Open Source|Adobe Commerce type marked as @api instead.</p>
4+
<!-- tooltip end -->
5+
</body>
6+
</html>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<html>
2+
<body>
3+
<p>[1110] The used type is no longer present in the codebase.</p>
4+
<!-- tooltip end -->
5+
</body>
6+
</html>

resources/uct/bundle/inspection.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ inspection.displayName.ExtendedNonApiClass=Extended non Magento 2 API class
3838
inspection.displayName.InheritedNonApiInterface=Inherited non Magento 2 API interface
3939
inspection.displayName.PossibleDependencyOnImplDetails=Possible dependency on implementation details
4040
inspection.displayName.CalledNonInterfaceMethod=Called non-interface method
41+
inspection.displayName.UsedNonExistentTypeInConfig=Used non-existent Magento 2 type in the configuration file
42+
inspection.displayName.UsedDeprecatedTypeInConfig=Used deprecated Magento 2 type in the configuration file
4143
customCode.warnings.deprecated.1131=[1131] Extended class ''{0}'' that is @deprecated in the ''{1}''
4244
customCode.warnings.deprecated.1132=[1132] Imported class ''{0}'' that is @deprecated in the ''{1}''
4345
customCode.warnings.deprecated.1134=[1134] Used class ''{0}'' that is @deprecated in the ''{1}''
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright © Magento, Inc. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
6+
package com.magento.idea.magento2uct.inspections.xml;
7+
8+
import com.intellij.codeInspection.InspectionManager;
9+
import com.intellij.codeInspection.ProblemDescriptor;
10+
import com.intellij.codeInspection.XmlSuppressableInspectionTool;
11+
import com.intellij.openapi.project.Project;
12+
import com.intellij.psi.PsiElement;
13+
import com.intellij.psi.PsiFile;
14+
import com.intellij.psi.tree.IElementType;
15+
import com.intellij.psi.util.PsiTreeUtil;
16+
import com.intellij.psi.xml.XmlToken;
17+
import com.intellij.psi.xml.XmlTokenType;
18+
import com.magento.idea.magento2uct.settings.UctSettingsService;
19+
import com.magento.idea.magento2uct.versioning.VersionStateManager;
20+
import java.util.ArrayList;
21+
import java.util.Arrays;
22+
import java.util.List;
23+
import org.jetbrains.annotations.NotNull;
24+
import org.jetbrains.annotations.Nullable;
25+
26+
abstract class ModuleConfigFileInspection extends XmlSuppressableInspectionTool {
27+
28+
private final String[] supportedFiles = new String[]{
29+
"di.xml",
30+
"system.xml",
31+
"events.xml",
32+
"extension_attributes.xml",
33+
"webapi.xml",
34+
"communication.xml",
35+
"queue_consumer.xml",
36+
"crontab.xml",
37+
"indexer.xml",
38+
"mview.xml",
39+
"product_types.xml",
40+
"widget.xml",
41+
};
42+
43+
@Override
44+
public @Nullable ProblemDescriptor[] checkFile(
45+
final @NotNull PsiFile file,
46+
final @NotNull InspectionManager manager,
47+
final boolean isOnTheFly
48+
) {
49+
final Project project = file.getProject();
50+
final UctSettingsService settings = UctSettingsService.getInstance(project);
51+
52+
if (!settings.isEnabled()) {
53+
return getEmptyResult();
54+
}
55+
56+
if (Arrays.stream(supportedFiles).noneMatch(name -> name.equals(file.getName()))) {
57+
return getEmptyResult();
58+
}
59+
final List<IElementType> allowedTokenTypes = new ArrayList<>();
60+
allowedTokenTypes.add(XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN);
61+
allowedTokenTypes.add(XmlTokenType.XML_DATA_CHARACTERS);
62+
63+
final List<ProblemDescriptor> descriptors = new ArrayList<>();
64+
65+
for (final XmlToken token : PsiTreeUtil.findChildrenOfType(file, XmlToken.class)) {
66+
if (!allowedTokenTypes.contains(token.getTokenType())) {
67+
continue;
68+
}
69+
final String fqn = token.getText().trim();
70+
71+
if (!VersionStateManager.getInstance(project).isPresentInCodebase(fqn)) {
72+
continue;
73+
}
74+
// Inspection logic.
75+
doInspection(fqn, token, manager, isOnTheFly, descriptors);
76+
}
77+
78+
return descriptors.toArray(new ProblemDescriptor[0]);
79+
}
80+
81+
/**
82+
* Implement this method to specify inspection logic.
83+
*
84+
* @param fqn String
85+
* @param target PsiElement
86+
* @param manager InspectionManager
87+
* @param isOnTheFly boolean
88+
* @param descriptors List[ProblemDescriptor]
89+
*/
90+
protected abstract void doInspection(
91+
final @NotNull String fqn,
92+
final @NotNull PsiElement target,
93+
final @NotNull InspectionManager manager,
94+
final boolean isOnTheFly,
95+
final @NotNull List<ProblemDescriptor> descriptors
96+
);
97+
98+
/**
99+
* Retrieves an empty result.
100+
*
101+
* @return ProblemDescriptor[]
102+
*/
103+
private ProblemDescriptor[] getEmptyResult() {
104+
return new ProblemDescriptor[0];
105+
}
106+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright © Magento, Inc. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
6+
package com.magento.idea.magento2uct.inspections.xml;
7+
8+
import com.intellij.codeInspection.InspectionManager;
9+
import com.intellij.codeInspection.ProblemDescriptor;
10+
import com.intellij.codeInspection.ProblemHighlightType;
11+
import com.intellij.psi.PsiElement;
12+
import com.magento.idea.magento2uct.packages.SupportedIssue;
13+
import com.magento.idea.magento2uct.versioning.VersionStateManager;
14+
import java.util.List;
15+
import org.jetbrains.annotations.NotNull;
16+
17+
public class UsedDeprecatedTypeInConfig extends ModuleConfigFileInspection {
18+
19+
@Override
20+
protected void doInspection(
21+
final @NotNull String fqn,
22+
final @NotNull PsiElement target,
23+
final @NotNull InspectionManager manager,
24+
final boolean isOnTheFly,
25+
final @NotNull List<ProblemDescriptor> descriptors
26+
) {
27+
if (VersionStateManager.getInstance(manager.getProject()).isDeprecated(fqn)) {
28+
final String message = SupportedIssue.USED_DEPRECATED_TYPE_IN_CONFIG.getMessage(
29+
fqn,
30+
VersionStateManager.getInstance(
31+
manager.getProject()
32+
).getDeprecatedInVersion(fqn)
33+
);
34+
35+
final ProblemDescriptor descriptor = manager.createProblemDescriptor(
36+
target,
37+
message,
38+
null,
39+
ProblemHighlightType.WARNING,
40+
isOnTheFly,
41+
false
42+
);
43+
descriptors.add(descriptor);
44+
}
45+
}
46+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright © Magento, Inc. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
6+
package com.magento.idea.magento2uct.inspections.xml;
7+
8+
import com.intellij.codeInspection.InspectionManager;
9+
import com.intellij.codeInspection.ProblemDescriptor;
10+
import com.intellij.codeInspection.ProblemHighlightType;
11+
import com.intellij.psi.PsiElement;
12+
import com.magento.idea.magento2uct.packages.SupportedIssue;
13+
import com.magento.idea.magento2uct.versioning.VersionStateManager;
14+
import java.util.List;
15+
import org.jetbrains.annotations.NotNull;
16+
17+
public class UsedNonExistentTypeInConfig extends ModuleConfigFileInspection {
18+
19+
@Override
20+
protected void doInspection(
21+
final @NotNull String fqn,
22+
final @NotNull PsiElement target,
23+
final @NotNull InspectionManager manager,
24+
final boolean isOnTheFly,
25+
final @NotNull List<ProblemDescriptor> descriptors
26+
) {
27+
if (!VersionStateManager.getInstance(manager.getProject()).isExists(fqn)) {
28+
final String message = SupportedIssue.USED_NON_EXISTENT_TYPE_IN_CONFIG.getMessage(
29+
fqn,
30+
VersionStateManager.getInstance(
31+
manager.getProject()
32+
).getRemovedInVersion(fqn)
33+
);
34+
35+
final ProblemDescriptor descriptor = manager.createProblemDescriptor(
36+
target,
37+
message,
38+
null,
39+
ProblemHighlightType.ERROR,
40+
isOnTheFly,
41+
false
42+
);
43+
descriptors.add(descriptor);
44+
}
45+
}
46+
}

src/com/magento/idea/magento2uct/packages/SupportedIssue.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import com.intellij.codeInspection.LocalInspectionTool;
99
import com.intellij.psi.PsiElementVisitor;
10+
import com.jetbrains.php.lang.psi.PhpFile;
1011
import com.magento.idea.magento2uct.bundles.UctInspectionBundle;
1112
import com.magento.idea.magento2uct.inspections.UctProblemsHolder;
1213
import com.magento.idea.magento2uct.inspections.php.api.CalledNonApiMethod;
@@ -45,7 +46,10 @@
4546
import com.magento.idea.magento2uct.inspections.php.existence.UsedNonExistentConstant;
4647
import com.magento.idea.magento2uct.inspections.php.existence.UsedNonExistentProperty;
4748
import com.magento.idea.magento2uct.inspections.php.existence.UsedNonExistentType;
49+
import com.magento.idea.magento2uct.inspections.xml.UsedDeprecatedTypeInConfig;
50+
import com.magento.idea.magento2uct.inspections.xml.UsedNonExistentTypeInConfig;
4851
import java.lang.reflect.InvocationTargetException;
52+
import java.util.ArrayList;
4953
import java.util.LinkedList;
5054
import java.util.List;
5155
import org.jetbrains.annotations.Nullable;
@@ -268,6 +272,18 @@ public enum SupportedIssue {
268272
IssueSeverityLevel.ERROR,
269273
"customCode.errors.api.1449",
270274
CalledNonInterfaceMethod.class
275+
),
276+
USED_NON_EXISTENT_TYPE_IN_CONFIG(
277+
1110,
278+
IssueSeverityLevel.CRITICAL,
279+
"customCode.critical.existence.1110",
280+
UsedNonExistentTypeInConfig.class
281+
),
282+
USED_DEPRECATED_TYPE_IN_CONFIG(
283+
1134,
284+
IssueSeverityLevel.WARNING,
285+
"customCode.warnings.deprecated.1134",
286+
UsedDeprecatedTypeInConfig.class
271287
);
272288

273289
private final int code;
@@ -377,6 +393,18 @@ public static List<PsiElementVisitor> getVisitors(
377393
return visitors;
378394
}
379395

396+
/**
397+
* Get supported file types.
398+
*
399+
* @return List
400+
*/
401+
public static List<Class<?>> getSupportedFileTypes() {
402+
final List<Class<?>> types = new ArrayList<>();
403+
types.add(PhpFile.class);
404+
405+
return types;
406+
}
407+
380408
/**
381409
* Build inspection visitor for file.
382410
*
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright © Magento, Inc. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
6+
package com.magento.idea.magento2uct.util.php;
7+
8+
import com.intellij.openapi.project.Project;
9+
import com.jetbrains.php.PhpIndex;
10+
import com.jetbrains.php.lang.psi.elements.PhpClass;
11+
import java.util.Collection;
12+
import java.util.regex.Matcher;
13+
import org.jetbrains.annotations.NotNull;
14+
15+
public final class FqnValidatorUtil {
16+
17+
private FqnValidatorUtil() {}
18+
19+
/**
20+
* Check if provided string is a valid FQN.
21+
*
22+
* @param fqnCandidate String
23+
* @param project Project
24+
*
25+
* @return boolean
26+
*/
27+
public static boolean validate(
28+
final @NotNull String fqnCandidate,
29+
final @NotNull Project project
30+
) {
31+
String safeFqn = MagentoTypeEscapeUtil.escapeProperty(fqnCandidate);
32+
33+
if (isFactoryOrProxy(safeFqn)) {
34+
safeFqn = MagentoTypeEscapeUtil.escape(safeFqn);
35+
}
36+
final Collection<PhpClass> classes = PhpIndex.getInstance(project).getAnyByFQN(safeFqn);
37+
38+
return !classes.isEmpty();
39+
}
40+
41+
/**
42+
* Check if provided FQN is a Factory or Proxy.
43+
*
44+
* @param fqn String
45+
*
46+
* @return boolean
47+
*/
48+
private static boolean isFactoryOrProxy(final @NotNull String fqn) {
49+
final Matcher matcher = MagentoTypeEscapeUtil.FACTORY_PROXY_TYPE_PATTERN.matcher(fqn);
50+
51+
return matcher.find();
52+
}
53+
}

src/com/magento/idea/magento2uct/util/php/MagentoTypeEscapeUtil.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ public final class MagentoTypeEscapeUtil {
1515
= "(Factory|\\\\Proxy|Factory\\\\Proxy)($|\\.)";
1616
public static final Pattern FACTORY_PROXY_TYPE_PATTERN
1717
= Pattern.compile(FACTORY_PROXY_TYPE_REGEX, Pattern.MULTILINE);
18+
public static final String PHP_PROPERTY_REFERENCE_SEPARATOR = "::";
19+
public static final String JAVA_PROPERTY_REFERENCE_SEPARATOR = ".";
1820

1921
private MagentoTypeEscapeUtil() {
2022
}
@@ -43,4 +45,23 @@ private MagentoTypeEscapeUtil() {
4345

4446
return typeFqn.equals(result) ? typeFqn : result;
4547
}
48+
49+
/**
50+
* Replace PHP property reference separator with the Intellij based separator.
51+
*
52+
* @param typeFqn String
53+
*
54+
* @return String
55+
*/
56+
public static @NotNull String escapeProperty(final @NotNull String typeFqn) {
57+
if (typeFqn.contains(PHP_PROPERTY_REFERENCE_SEPARATOR)
58+
&& typeFqn.split(PHP_PROPERTY_REFERENCE_SEPARATOR).length == 1) {
59+
return typeFqn.replace(
60+
PHP_PROPERTY_REFERENCE_SEPARATOR,
61+
JAVA_PROPERTY_REFERENCE_SEPARATOR
62+
);
63+
}
64+
65+
return typeFqn;
66+
}
4667
}

0 commit comments

Comments
 (0)