SSR - Check whether an attribute value (immediate class type) is an interface
HI Everyone,
The context
I'm working on a permanent SSR inspection that would check Mockito's @Mock annotation whether the classes in its extraInterfaces attribute are actually interfaces.
So there may be two cases: a single interface or, an array of interfaces specified, that I'm interested in:
@Mock(extraInterfaces = SearchContext.class)
@Mock(extraInterfaces = {SearchContext.class, List.class})
The template
My template so far is:
@org.mockito.Mock($extraInterfaces$ = $attributes$)
@Modifier("Instance") $FieldType$ $field$;
with the following Script filter for Complete Match (I know it may be optimized a bit):
boolean hasOnlyInterfaces = true
//A single annotation attribute value like @Mock(extraInterfaces = SearchContext.class)
if (attributes.getType() instanceof com.intellij.psi.impl.source.PsiImmediateClassType) {
if (!attributes.getType().resolve().isInterface()) {
hasOnlyInterfaces = false
}
}
//An array of annotation attribute values like @Mock(extraInterfaces = {SearchContext.class, List.class})
else if (attributes instanceof com.intellij.psi.PsiArrayInitializerMemberValue) {
attributes.getInitializers().each { attribute ->
//This is the problematic part because getType() doesn't exist on this type
if (!attribute.getType().resolve().isInterface()) {
hasOnlyInterfaces = false
}
}
}
!hasOnlyInterfaces
With the template above if I have a single attribute value, the $attributes$ node is recognized as a PsiImmediateClassType from which I can easily resolve its PsiClass and call isInterface() on it. But with array values it is different, the node in that case is a PsiArrayInitializerMemberValue from which (or from its PsiAnnotationMemberValue initializers) I haven't been able to find out how I could get any type info, especially a PsiClass to check whether they are interfaces or not.
My questions are:
- Since PsiClass and PsiArrayInitializerMemberValue seem to be on a different "branch" of classes (one implementing JvmElement, the other implementing PsiElement), is there any way get a PsiClass instance from this PsiElement?
- Is there any other way of retrieving the type of the annotation attribute values?
Thank you in advance!
请先登录再写评论。
Your script retrieves the type of the class object access expression, which is always `java.lang.Class`. Here is a script that should work:
This script could be simplified a bit by splitting the template into two separate ones, one for each case:
Then you would only need the
case.
Hope this helps,
Bas
Thank you for the suggestions, I managed to learn some new things from that.
I gave them a try and they seem to be working like a charm.
I realized that for the second template (the one with the curly brackets) the elements will automatically be a List (or a single object obviously) so I don't need bother with checking PsiArrayInitializerMemberValue, just simply iterate through the items. So the seconds script filter could be simplified to this one:
import com.intellij.psi.*;
boolean hasOnlyInterfaces = true
attributes.each { attribute ->
if (attribute instanceof PsiClassObjectAccessExpression) {
PsiJavaCodeReferenceElement ref = (PsiJavaCodeReferenceElement)attribute.firstChild?.firstChild;
if (!ref?.resolve()?.isInterface()) {
hasOnlyInterfaces = false
}
}
}
!hasOnlyInterfaces
I'm not quite sure that this is exactly what you meant because I cannot see how attributes could be instance of PsiClassObjectAccessExpression when the annotation attribute has multiple values. Though it definitely would work for a single element.