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!
Please sign in to leave a comment.
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.