Skip to content

Commit dceb27b

Browse files
authored
Merge pull request #792 from bohdan-harniuk/uct-inspection-used-non-api-reference
UCT-725: Developed used non api reference inspection, added factory and proxy types support
2 parents 164bdc0 + 87be924 commit dceb27b

File tree

9 files changed

+199
-6
lines changed

9 files changed

+199
-6
lines changed

resources/META-INF/plugin.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,13 @@
466466
enabledByDefault="false"
467467
level="WARNING"
468468
implementationClass="com.magento.idea.magento2uct.inspections.php.api.UsedNonApiProperty"/>
469+
<localInspection language="PHP" groupPath="UCT"
470+
shortName="UsedNonApiType"
471+
bundle="uct.bundle.inspection" key="inspection.displayName.UsedNonApiType"
472+
groupBundle="uct.bundle.inspection" groupKey="inspection.api.group.name"
473+
enabledByDefault="false"
474+
level="WARNING"
475+
implementationClass="com.magento.idea.magento2uct.inspections.php.api.UsedNonApiType"/>
469476
<!-- \UCT inspection -->
470477

471478
<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>[1124] The used type is not marked as an API.</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
@@ -32,6 +32,7 @@ inspection.displayName.OverriddenNonApiConstant=Overridden non Adobe Commerce AP
3232
inspection.displayName.OverriddenNonApiProperty=Overridden non Adobe Commerce API property
3333
inspection.displayName.UsedNonApiConstant=Used non Adobe Commerce API constant
3434
inspection.displayName.UsedNonApiProperty=Used non Adobe Commerce API property
35+
inspection.displayName.UsedNonApiType=Used non Adobe Commerce API type
3536
customCode.warnings.deprecated.1131=[1131] Extended class ''{0}'' that is @deprecated in the ''{1}''
3637
customCode.warnings.deprecated.1132=[1132] Imported class ''{0}'' that is @deprecated in the ''{1}''
3738
customCode.warnings.deprecated.1134=[1134] Used class ''{0}'' that is @deprecated in the ''{1}''
@@ -62,3 +63,4 @@ customCode.errors.api.1225=[1225] Overridden constant ''{0}'' is not marked as a
6263
customCode.errors.api.1525=[1525] Overridden property ''{0}'' is not marked as an API
6364
customCode.errors.api.1224=[1224] Used constant ''{0}'' is not marked as an API
6465
customCode.errors.api.1524=[1524] Used property ''{0}'' is not marked as an API
66+
customCode.errors.api.1124=[1124] Used type ''{0}'' is not marked as an API

src/com/magento/idea/magento2uct/inspections/php/UsedTypeInspection.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,16 @@
1111
import com.intellij.psi.PsiElementVisitor;
1212
import com.jetbrains.php.lang.inspections.PhpInspection;
1313
import com.jetbrains.php.lang.psi.elements.ClassReference;
14+
import com.jetbrains.php.lang.psi.elements.ExtendsList;
15+
import com.jetbrains.php.lang.psi.elements.ImplementsList;
1416
import com.jetbrains.php.lang.psi.elements.Method;
1517
import com.jetbrains.php.lang.psi.elements.PhpClass;
18+
import com.jetbrains.php.lang.psi.elements.PhpUse;
1619
import com.jetbrains.php.lang.psi.resolve.types.PhpTypeAnalyserVisitor;
1720
import com.magento.idea.magento2plugin.magento.packages.MagentoPhpClass;
1821
import com.magento.idea.magento2uct.packages.IssueSeverityLevel;
1922
import com.magento.idea.magento2uct.settings.UctSettingsService;
23+
import com.magento.idea.magento2uct.util.php.ReferenceResolverUtil;
2024
import org.jetbrains.annotations.NotNull;
2125

2226
public abstract class UsedTypeInspection extends PhpInspection {
@@ -30,14 +34,19 @@ public abstract class UsedTypeInspection extends PhpInspection {
3034

3135
@Override
3236
public void visitPhpClassReference(final ClassReference reference) {
37+
if (reference.getContext() instanceof PhpUse
38+
|| reference.getContext() instanceof ExtendsList
39+
|| reference.getContext() instanceof ImplementsList) {
40+
return;
41+
}
3342
final Project project = reference.getProject();
3443
final UctSettingsService settings = UctSettingsService.getInstance(project);
3544

3645
if (!settings.isEnabled()
3746
|| !settings.isIssueLevelSatisfiable(getSeverityLevel())) {
3847
return;
3948
}
40-
PsiElement resolved = reference.resolve();
49+
PsiElement resolved = ReferenceResolverUtil.resolve(reference);
4150

4251
if (resolved instanceof Method
4352
&& MagentoPhpClass.CONSTRUCT_METHOD_NAME
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.php.api;
7+
8+
import com.intellij.codeInspection.ProblemHighlightType;
9+
import com.intellij.codeInspection.ProblemsHolder;
10+
import com.intellij.openapi.project.Project;
11+
import com.jetbrains.php.lang.psi.elements.ClassReference;
12+
import com.jetbrains.php.lang.psi.elements.PhpClass;
13+
import com.magento.idea.magento2uct.inspections.UctProblemsHolder;
14+
import com.magento.idea.magento2uct.inspections.php.UsedTypeInspection;
15+
import com.magento.idea.magento2uct.packages.IssueSeverityLevel;
16+
import com.magento.idea.magento2uct.packages.SupportedIssue;
17+
import com.magento.idea.magento2uct.versioning.VersionStateManager;
18+
import org.jetbrains.annotations.NotNull;
19+
20+
public class UsedNonApiType extends UsedTypeInspection {
21+
22+
@Override
23+
protected void execute(
24+
final Project project,
25+
final @NotNull ProblemsHolder problemsHolder,
26+
final PhpClass phpClass,
27+
final ClassReference reference
28+
) {
29+
if (VersionStateManager.getInstance(project).isApi(phpClass.getFQN())) {
30+
return;
31+
}
32+
final String message = SupportedIssue.USED_NON_API_TYPE.getMessage(phpClass.getFQN());
33+
34+
if (problemsHolder instanceof UctProblemsHolder) {
35+
((UctProblemsHolder) problemsHolder).setReservedErrorCode(
36+
SupportedIssue.USED_NON_API_TYPE.getCode()
37+
);
38+
}
39+
problemsHolder.registerProblem(reference, message, ProblemHighlightType.WARNING);
40+
}
41+
42+
@Override
43+
protected IssueSeverityLevel getSeverityLevel() {
44+
return SupportedIssue.USED_NON_API_TYPE.getLevel();
45+
}
46+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.magento.idea.magento2uct.inspections.php.api.OverriddenNonApiProperty;
1717
import com.magento.idea.magento2uct.inspections.php.api.UsedNonApiConstant;
1818
import com.magento.idea.magento2uct.inspections.php.api.UsedNonApiProperty;
19+
import com.magento.idea.magento2uct.inspections.php.api.UsedNonApiType;
1920
import com.magento.idea.magento2uct.inspections.php.deprecation.CallingDeprecatedMethod;
2021
import com.magento.idea.magento2uct.inspections.php.deprecation.ExtendingDeprecatedClass;
2122
import com.magento.idea.magento2uct.inspections.php.deprecation.ImplementedDeprecatedInterface;
@@ -226,6 +227,12 @@ public enum SupportedIssue {
226227
IssueSeverityLevel.ERROR,
227228
"customCode.errors.api.1524",
228229
UsedNonApiProperty.class
230+
),
231+
USED_NON_API_TYPE(
232+
1124,
233+
IssueSeverityLevel.ERROR,
234+
"customCode.errors.api.1124",
235+
UsedNonApiType.class
229236
);
230237

231238
private final int code;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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 java.util.regex.Matcher;
9+
import java.util.regex.Pattern;
10+
import org.jetbrains.annotations.NotNull;
11+
12+
public final class MagentoTypeEscapeUtil {
13+
14+
public static final String FACTORY_PROXY_TYPE_REGEX
15+
= "(Factory|\\\\Proxy|Factory\\\\Proxy)($|\\.)";
16+
public static final Pattern FACTORY_PROXY_TYPE_PATTERN
17+
= Pattern.compile(FACTORY_PROXY_TYPE_REGEX, Pattern.MULTILINE);
18+
19+
private MagentoTypeEscapeUtil() {
20+
}
21+
22+
/**
23+
* Escape Magento Type (convert Factory and Proxy types to the simple type).
24+
*
25+
* @param typeFqn String
26+
*
27+
* @return String
28+
*/
29+
public static @NotNull String escape(final @NotNull String typeFqn) {
30+
final Matcher matcher = FACTORY_PROXY_TYPE_PATTERN.matcher(typeFqn);
31+
32+
String result = typeFqn;
33+
34+
while (matcher.find()) {
35+
result = result.substring(0, matcher.start(0)) + result.substring(matcher.end(0));
36+
}
37+
38+
return typeFqn.equals(result) ? typeFqn : result;
39+
}
40+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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.psi.PsiElement;
9+
import com.jetbrains.php.PhpIndex;
10+
import com.jetbrains.php.lang.psi.elements.PhpClass;
11+
import com.jetbrains.php.lang.psi.elements.PhpReference;
12+
import java.util.Collection;
13+
import java.util.regex.Matcher;
14+
import org.jetbrains.annotations.NotNull;
15+
16+
public final class ReferenceResolverUtil {
17+
18+
private ReferenceResolverUtil() {
19+
}
20+
21+
/**
22+
* Resolve reference.
23+
*
24+
* @param reference PhpReference
25+
*
26+
* @return PsiElement
27+
*/
28+
public static PsiElement resolve(final @NotNull PhpReference reference) {
29+
final String fqn = reference.getFQN();
30+
31+
if (fqn == null) {
32+
return null;
33+
}
34+
PsiElement resolved = null;
35+
36+
if (isFactoryOrProxy(fqn)) {
37+
final Collection<PhpClass> classes = PhpIndex.getInstance(reference.getProject())
38+
.getAnyByFQN(
39+
MagentoTypeEscapeUtil.escape(reference.getFQN())
40+
);
41+
42+
if (!classes.isEmpty()) {
43+
resolved = classes.stream().iterator().next();
44+
}
45+
} else {
46+
resolved = reference.resolve();
47+
}
48+
49+
return resolved;
50+
}
51+
52+
/**
53+
* Check if provided FQN is a Factory or Proxy.
54+
*
55+
* @param fqn String
56+
*
57+
* @return boolean
58+
*/
59+
private static boolean isFactoryOrProxy(final @NotNull String fqn) {
60+
final Matcher matcher = MagentoTypeEscapeUtil.FACTORY_PROXY_TYPE_PATTERN.matcher(fqn);
61+
62+
return matcher.find();
63+
}
64+
}

src/com/magento/idea/magento2uct/versioning/VersionStateManager.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.intellij.openapi.project.Project;
99
import com.magento.idea.magento2uct.packages.SupportedVersion;
1010
import com.magento.idea.magento2uct.settings.UctSettingsService;
11+
import com.magento.idea.magento2uct.util.php.MagentoTypeEscapeUtil;
1112
import com.magento.idea.magento2uct.versioning.indexes.data.ApiCoverageStateIndex;
1213
import com.magento.idea.magento2uct.versioning.indexes.data.DeprecationStateIndex;
1314
import com.magento.idea.magento2uct.versioning.indexes.data.ExistenceStateIndex;
@@ -58,7 +59,7 @@ public static synchronized VersionStateManager getInstance(
5859
* @return boolean
5960
*/
6061
public boolean isDeprecated(final @NotNull String fqn) {
61-
return deprecationStateIndex.has(fqn);
62+
return deprecationStateIndex.has(escapeFqn(fqn));
6263
}
6364

6465
/**
@@ -69,7 +70,7 @@ public boolean isDeprecated(final @NotNull String fqn) {
6970
* @return String
7071
*/
7172
public String getDeprecatedInVersion(final @NotNull String fqn) {
72-
return deprecationStateIndex.getVersion(fqn);
73+
return deprecationStateIndex.getVersion(escapeFqn(fqn));
7374
}
7475

7576
/**
@@ -80,7 +81,7 @@ public String getDeprecatedInVersion(final @NotNull String fqn) {
8081
* @return boolean
8182
*/
8283
public boolean isExists(final @NotNull String fqn) {
83-
return existenceStateIndex.has(fqn);
84+
return existenceStateIndex.has(escapeFqn(fqn));
8485
}
8586

8687
/**
@@ -91,7 +92,7 @@ public boolean isExists(final @NotNull String fqn) {
9192
* @return String
9293
*/
9394
public String getRemovedInVersion(final @NotNull String fqn) {
94-
return existenceStateIndex.getVersion(fqn);
95+
return existenceStateIndex.getVersion(escapeFqn(fqn));
9596
}
9697

9798
/**
@@ -102,7 +103,7 @@ public String getRemovedInVersion(final @NotNull String fqn) {
102103
* @return boolean
103104
*/
104105
public boolean isApi(final @NotNull String fqn) {
105-
return apiCoverageStateIndex.has(fqn);
106+
return apiCoverageStateIndex.has(escapeFqn(fqn));
106107
}
107108

108109
/**
@@ -173,4 +174,15 @@ private void compute(final VersionStateIndex index) {
173174

174175
index.load(versionsToLoad);
175176
}
177+
178+
/**
179+
* Escape FQN for adding Factory and Proxy support.
180+
*
181+
* @param fqn String
182+
*
183+
* @return String
184+
*/
185+
private String escapeFqn(final @NotNull String fqn) {
186+
return MagentoTypeEscapeUtil.escape(fqn);
187+
}
176188
}

0 commit comments

Comments
 (0)