Skip to content

Commit 96e9f40

Browse files
authored
Merge pull request #113 from cedricziel/legacy-classes-for-ide
Add inspection and QuickFix for LegacyClassesForIde migrations
2 parents ace0d42 + e1e8970 commit 96e9f40

File tree

11 files changed

+381
-3
lines changed

11 files changed

+381
-3
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ cache:
1717
matrix:
1818
include:
1919
- env: IDEA_VERSION="IU-2017.2.5" PHP_PLUGIN_VERSION="172.4155.41"
20-
- env: IDEA_VERSION="IU-2017.3" PHP_PLUGIN_VERSION="173.3727.138"
20+
- env: IDEA_VERSION="IU-2017.3" PHP_PLUGIN_VERSION="173.3942.13"
2121
allow_failures:
22-
- env: IDEA_VERSION="IU-2017.3" PHP_PLUGIN_VERSION="173.3727.138"
22+
- env: IDEA_VERSION="IU-2017.3" PHP_PLUGIN_VERSION="173.3942.13"
2323

2424
before_install:
2525
- "export ORG_GRADLE_PROJECT_ideaVersion=${IDEA_VERSION}"

gradle.properties

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
ideaVersion = IU-2017.3
2-
phpPluginVersion = 173.3727.138
2+
phpPluginVersion = 173.3942.13
3+
#ideaVersion = IU-2017.2.5
4+
#phpPluginVersion = 172.4155.41
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.cedricziel.idea.typo3.codeInspection;
2+
3+
import com.cedricziel.idea.typo3.codeInspection.quickfix.LegacyClassesForIdeQuickFix;
4+
import com.cedricziel.idea.typo3.index.php.LegacyClassesForIDEIndex;
5+
import com.intellij.codeInsight.daemon.GroupNames;
6+
import com.intellij.codeInspection.ProblemHighlightType;
7+
import com.intellij.codeInspection.ProblemsHolder;
8+
import com.intellij.psi.PsiElementVisitor;
9+
import com.jetbrains.php.lang.inspections.PhpInspection;
10+
import com.jetbrains.php.lang.psi.elements.ClassConstantReference;
11+
import com.jetbrains.php.lang.psi.elements.ClassReference;
12+
import com.jetbrains.php.lang.psi.visitors.PhpElementVisitor;
13+
import org.jetbrains.annotations.Nls;
14+
import org.jetbrains.annotations.NotNull;
15+
16+
public class LegacyClassesForIDEInspection extends PhpInspection {
17+
@Nls
18+
@NotNull
19+
@Override
20+
public String getDisplayName() {
21+
return "Legacy class used";
22+
}
23+
24+
@Nls
25+
@NotNull
26+
@Override
27+
public String getGroupDisplayName() {
28+
return GroupNames.BUGS_GROUP_NAME;
29+
}
30+
31+
@NotNull
32+
@Override
33+
public String getShortName() {
34+
return "LegacyClassesForIDE";
35+
}
36+
37+
@NotNull
38+
@Override
39+
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder problemsHolder, boolean b) {
40+
return new PhpElementVisitor() {
41+
@Override
42+
public void visitPhpClassReference(ClassReference classReference) {
43+
if (classReference.getFQN() != null && LegacyClassesForIDEIndex.isLegacyClass(classReference.getProject(), classReference.getFQN())) {
44+
problemsHolder.registerProblem(classReference, "Legacy class usage", ProblemHighlightType.LIKE_DEPRECATED, new LegacyClassesForIdeQuickFix());
45+
}
46+
47+
super.visitPhpClassReference(classReference);
48+
}
49+
50+
@Override
51+
public void visitPhpClassConstantReference(ClassConstantReference constantReference) {
52+
super.visitPhpClassConstantReference(constantReference);
53+
}
54+
};
55+
}
56+
}

src/main/java/com/cedricziel/idea/typo3/codeInspection/TYPO3InspectionToolProvider.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public Class[] getInspectionClasses() {
1818
MissingRenderTypeInspection.class,
1919
MissingTableInspection.class,
2020
InvalidQuantityInspection.class,
21+
// Code Migration
22+
LegacyClassesForIDEInspection.class,
2123
// Extension Scanner
2224
ClassConstantMatcherInspection.class,
2325
ClassNameMatcherInspection.class,
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.cedricziel.idea.typo3.codeInspection.quickfix;
2+
3+
import com.cedricziel.idea.typo3.index.php.LegacyClassesForIDEIndex;
4+
import com.intellij.codeInspection.ProblemDescriptor;
5+
import com.intellij.openapi.project.DumbService;
6+
import com.intellij.openapi.project.Project;
7+
import com.intellij.psi.PsiElement;
8+
import com.intellij.util.IncorrectOperationException;
9+
import com.jetbrains.php.lang.inspections.quickfix.PhpQuickFixBase;
10+
import com.jetbrains.php.lang.psi.PhpPsiElementFactory;
11+
import com.jetbrains.php.lang.psi.elements.ClassReference;
12+
import org.jetbrains.annotations.Nls;
13+
import org.jetbrains.annotations.NotNull;
14+
15+
public class LegacyClassesForIdeQuickFix extends PhpQuickFixBase {
16+
@Nls
17+
@NotNull
18+
@Override
19+
public String getFamilyName() {
20+
return getName();
21+
}
22+
23+
@Nls
24+
@NotNull
25+
@Override
26+
public String getName() {
27+
return "Migrate class usage";
28+
}
29+
30+
@Override
31+
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
32+
33+
PsiElement psiElement = descriptor.getPsiElement();
34+
if (DumbService.isDumb(project)) {
35+
showIsInDumpModeMessage(project, psiElement);
36+
return;
37+
}
38+
39+
if (psiElement instanceof ClassReference) {
40+
ClassReference classReference = (ClassReference) psiElement;
41+
String fqn = classReference.getFQN();
42+
if (fqn != null) {
43+
String replacementFQN = LegacyClassesForIDEIndex.findReplacementClass(project, fqn);
44+
if (replacementFQN != null) {
45+
try {
46+
classReference.replace(PhpPsiElementFactory.createClassReference(project, replacementFQN));
47+
} catch (IncorrectOperationException e) {
48+
showErrorMessage(project, "Could not replace class reference", psiElement);
49+
}
50+
}
51+
}
52+
}
53+
}
54+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package com.cedricziel.idea.typo3.index.php;
2+
3+
import com.cedricziel.idea.typo3.index.externalizer.ObjectStreamDataExternalizer;
4+
import com.intellij.openapi.project.Project;
5+
import com.intellij.patterns.PlatformPatterns;
6+
import com.intellij.psi.PsiElement;
7+
import com.intellij.psi.PsiRecursiveElementVisitor;
8+
import com.intellij.psi.search.GlobalSearchScope;
9+
import com.intellij.util.indexing.*;
10+
import com.intellij.util.io.DataExternalizer;
11+
import com.intellij.util.io.EnumeratorStringDescriptor;
12+
import com.intellij.util.io.KeyDescriptor;
13+
import com.jetbrains.php.lang.psi.elements.ClassReference;
14+
import com.jetbrains.php.lang.psi.elements.PhpClass;
15+
import gnu.trove.THashMap;
16+
import org.jetbrains.annotations.NotNull;
17+
import org.jetbrains.annotations.Nullable;
18+
19+
import java.util.List;
20+
import java.util.Map;
21+
22+
public class LegacyClassesForIDEIndex extends FileBasedIndexExtension<String, String> {
23+
24+
public static ID<String, String> KEY = ID.create("com.cedricziel.idea.typo3.index.php.legacy_classes");
25+
26+
private final KeyDescriptor<String> myKeyDescriptor = new EnumeratorStringDescriptor();
27+
28+
public static boolean isLegacyClass(@NotNull Project project, @NotNull String fqn) {
29+
30+
return FileBasedIndex.getInstance().getAllKeys(LegacyClassesForIDEIndex.KEY, project).contains(fqn);
31+
}
32+
33+
@Nullable
34+
public static String findReplacementClass(@NotNull Project project, @NotNull String fqn) {
35+
List<String> values = FileBasedIndex.getInstance().getValues(KEY, fqn, GlobalSearchScope.allScope(project));
36+
if (values.size() > 0) {
37+
return values.iterator().next();
38+
}
39+
40+
return null;
41+
}
42+
43+
@NotNull
44+
@Override
45+
public ID<String, String> getName() {
46+
return KEY;
47+
}
48+
49+
@NotNull
50+
@Override
51+
public DataIndexer<String, String, FileContent> getIndexer() {
52+
return inputData -> {
53+
Map<String, String> map = new THashMap<>();
54+
55+
LegacyClassesRecursiveVisitor visitor = new LegacyClassesRecursiveVisitor();
56+
visitor.visitElement(inputData.getPsiFile());
57+
58+
visitor.getMap().forEach(map::put);
59+
60+
return map;
61+
};
62+
}
63+
64+
@NotNull
65+
@Override
66+
public KeyDescriptor<String> getKeyDescriptor() {
67+
return myKeyDescriptor;
68+
}
69+
70+
@NotNull
71+
@Override
72+
public DataExternalizer<String> getValueExternalizer() {
73+
return new ObjectStreamDataExternalizer<>();
74+
}
75+
76+
@Override
77+
public int getVersion() {
78+
return 0;
79+
}
80+
81+
@NotNull
82+
@Override
83+
public FileBasedIndex.InputFilter getInputFilter() {
84+
return file -> file.getName().equals("LegacyClassesForIde.php");
85+
}
86+
87+
@Override
88+
public boolean dependsOnFileContent() {
89+
return true;
90+
}
91+
92+
private static class LegacyClassesRecursiveVisitor extends PsiRecursiveElementVisitor {
93+
private final Map<String, String> map;
94+
95+
LegacyClassesRecursiveVisitor() {
96+
map = new THashMap<>();
97+
}
98+
99+
public Map<String, String> getMap() {
100+
return map;
101+
}
102+
103+
@Override
104+
public void visitElement(PsiElement element) {
105+
106+
if (!PlatformPatterns.psiElement(PhpClass.class).accepts(element)) {
107+
super.visitElement(element);
108+
return;
109+
}
110+
111+
PhpClass phpClass = (PhpClass) element;
112+
113+
String fqn = phpClass.getFQN();
114+
115+
String superFqn = null;
116+
if (!phpClass.isInterface()) {
117+
superFqn = phpClass.getSuperFQN();
118+
} else {
119+
List<ClassReference> referenceElements = phpClass.getExtendsList().getReferenceElements();
120+
for (ClassReference cr : referenceElements) {
121+
superFqn = cr.getFQN();
122+
}
123+
}
124+
125+
if (superFqn != null) {
126+
map.put(fqn, superFqn);
127+
}
128+
129+
super.visitElement(element);
130+
}
131+
}
132+
}

src/main/resources/META-INF/plugin.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,8 @@ It is a great inspiration for possible solutions and parts of the code.</p>
302302
<fileBasedIndex implementation="com.cedricziel.idea.typo3.index.ResourcePathIndex"/>
303303
<fileBasedIndex implementation="com.cedricziel.idea.typo3.index.RouteIndex"/>
304304
<fileBasedIndex implementation="com.cedricziel.idea.typo3.index.TablenameFileIndex"/>
305+
<fileBasedIndex implementation="com.cedricziel.idea.typo3.index.php.LegacyClassesForIDEIndex"/>
306+
305307

306308
<!-- completion -->
307309
<completion.contributor language="PHP"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<html>
2+
<body>
3+
<p>The class used is removed with TYPO3 v9. Migrate to use some other class.</p>
4+
<!-- tooltip end -->
5+
<p></p>
6+
</body>
7+
</html>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<html>
2+
<body>
3+
<p>The class used is aliased for backwards compatibility. Migrate the usage to be on top of the changes.</p>
4+
<!-- tooltip end -->
5+
<p></p>
6+
</body>
7+
</html>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.cedricziel.idea.typo3.index.php;
2+
3+
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
4+
5+
public class LegacyClassesForIDEIndexTest extends LightCodeInsightFixtureTestCase {
6+
7+
@Override
8+
protected String getTestDataPath() {
9+
return "testData/com/cedricziel/idea/typo3/index";
10+
}
11+
12+
13+
@Override
14+
public void setUp() throws Exception {
15+
super.setUp();
16+
17+
myFixture.configureByFile("LegacyClassesForIde.php");
18+
}
19+
20+
public void testLegacyClassesForIDEFilesAreIndexed() {
21+
assertTrue(LegacyClassesForIDEIndex.isLegacyClass(myFixture.getProject(), "\\TYPO3\\CMS\\Fluid\\Core\\Exception"));
22+
assertTrue(LegacyClassesForIDEIndex.isLegacyClass(myFixture.getProject(), "\\TYPO3\\CMS\\Fluid\\Core\\ViewHelper\\ViewHelperInterface"));
23+
}
24+
25+
public void testReplacementClassNamesCanBeRetrieved() {
26+
assertEquals(LegacyClassesForIDEIndex.findReplacementClass(myFixture.getProject(), "\\TYPO3\\CMS\\Fluid\\Core\\ViewHelper\\ViewHelperInterface"), "\\TYPO3Fluid\\Fluid\\Core\\ViewHelper\\ViewHelperInterface");
27+
assertNull(LegacyClassesForIDEIndex.findReplacementClass(myFixture.getProject(), "\\TYPO3\\CMS\\NoRealClass"));
28+
}
29+
}

0 commit comments

Comments
 (0)