-
-
Notifications
You must be signed in to change notification settings - Fork 142
Expand file tree
/
Copy pathTwigEnumFunctionInspection.java
More file actions
107 lines (91 loc) · 3.94 KB
/
TwigEnumFunctionInspection.java
File metadata and controls
107 lines (91 loc) · 3.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package fr.adrienbrault.idea.symfony2plugin.templating.inspection;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.patterns.ElementPattern;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.jetbrains.php.lang.psi.elements.PhpClass;
import com.jetbrains.twig.TwigTokenTypes;
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
import fr.adrienbrault.idea.symfony2plugin.templating.TwigPattern;
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
/**
* Inspection for Twig enum() and enum_cases() functions to validate that:
* - The provided class name exists
* - The class is actually an enum type
*
* Examples:
* {{ enum('App\\SomeEnum') }} - valid if SomeEnum is an enum
* {{ enum('App\\NotAnEnum') }} - warning if NotAnEnum exists but is not an enum
* {{ enum('App\\MissingClass') }} - error if class doesn't exist
*
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class TwigEnumFunctionInspection extends LocalInspectionTool {
@NotNull
public PsiElementVisitor buildVisitor(final @NotNull ProblemsHolder holder, boolean isOnTheFly) {
if (!Symfony2ProjectComponent.isEnabled(holder.getProject())) {
return super.buildVisitor(holder, isOnTheFly);
}
return new MyPsiElementVisitor(holder);
}
private static class MyPsiElementVisitor extends PsiElementVisitor {
@NotNull
private final ProblemsHolder holder;
private ElementPattern<?> enumFunctionPattern;
MyPsiElementVisitor(@NotNull ProblemsHolder holder) {
this.holder = holder;
}
@Override
public void visitElement(@NotNull PsiElement element) {
// Fast pre-filter: only STRING_TEXT elements can be enum/enum_cases arguments
if (!(element instanceof LeafPsiElement)) {
super.visitElement(element);
return;
}
ASTNode node = element.getNode();
if (node == null || node.getElementType() != TwigTokenTypes.STRING_TEXT) {
super.visitElement(element);
return;
}
// enum('App\Config\SomeOption')
// enum_cases('App\Config\SomeOption')
if (getEnumFunctionPattern().accepts(element)) {
visitEnumFunction(element);
}
super.visitElement(element);
}
private void visitEnumFunction(PsiElement element) {
String contents = element.getText();
if (StringUtils.isBlank(contents)) {
return;
}
// Unescape backslashes: 'App\\Bike\\FooEnum' => 'App\Bike\FooEnum'
String className = contents.replace("\\\\", "\\");
PhpClass phpClass = PhpElementsUtil.getClassInterface(element.getProject(), className);
if (phpClass == null) {
// Class doesn't exist
holder.registerProblem(
element,
"Missing class: " + className,
ProblemHighlightType.WARNING
);
} else if (!phpClass.isEnum()) {
// Class exists but is not an enum
holder.registerProblem(
element,
"Class '" + phpClass.getName() + "' is not an enum",
ProblemHighlightType.WARNING
);
}
}
private ElementPattern<?> getEnumFunctionPattern() {
return enumFunctionPattern != null ? enumFunctionPattern : (enumFunctionPattern = TwigPattern.getPrintBlockOrTagFunctionPattern("enum", "enum_cases"));
}
}
}