How to check whether element ever referenced by custom reference
Answered
I have implemented a PsiReferenceContributor for specific PsiExpressionLiteral elements. They refer to methods.
But method is highlighted as "never used".
How can I suppress this warning?
I've started with HighlightInfoFilter:
@Override
public boolean accept(@NotNull HighlightInfo highlighted, @Nullable PsiFile psiFile) {
if (psiFile == null | !highlighted.getSeverity().equals(HighlightSeverity.WARNING)) {
return true;
}
PsiElement element = psiFile.findElementAt(highlighted.getStartOffset());
if (element == null | highlighted.getDescription() == null) {
return true;
}
if (!(element instanceof PsiIdentifier)) {
return true;
}
PsiMethod method = getParentOfType(element, PsiMethod.class);
if (method == null) {
return true;
}
...other method specific filtering...
// Now here how can I make sure this method is really referenced at least once?
}
method.getReference()
method.getReferences()
method.getOwnReferences()
all of these return null of empty array.
Please sign in to leave a comment.
Hi Serhii,
Please take a look at com.intellij.codeInsight.daemon.ImplicitUsageProvider.
Karol Lewandowski
Tried to implement this, but I don't get how to complete my validation...
In my case it should return true if any reference leads to my element (please see comment in the code):
public class MyUsageProvider implements ImplicitUsageProvider {
@Override
public boolean isImplicitUsage(@NotNull PsiElement psiElement) {
if (psiElement instanceof PsiMethod && ((PsiMethod) psiElement).hasAnnotation("some.annotation.of.mine.Example")) {
// should only return true if any reference leads to this method
return true;
}
return false;
}
@Override
public boolean isImplicitRead(@NotNull PsiElement psiElement) {
return false;
}
@Override
public boolean isImplicitWrite(@NotNull PsiElement psiElement) {
return false;
}
}
So basically I am looking for a solution of problem:
You should check if element is references and it can be done by searching for references:
Additionally, it can be cached:
CachedValuesManager.getManager(project).getCachedValue(element) {CachedValueProvider.Result.create(
ReferencesSearch.search(element, scope).findFirst() != null,
PsiModificationTracker.SERVICE.getInstance(project).forLanguage(JavaLanguage.INSTANCE) // if references are only in Java
)
}
Yes, I tried that as well, however with .findAll();
But it always returns me empty collection
Is the isReferenceTo() in your references called? What is the scope you pass to the references search?
Just to skip all misunderstandings here is the example:
I have a literal in code (lets imagine it works only for literals with value "I am literal"):
When I Hold CTRL it resolves as it has a reference:
After CTRL + click idea navigates me to class:
But as you can see class indicates that it is not used anywhere.
Now, my code looks like this:
public class MyReference extends PsiReferenceBase<PsiLiteralExpression> {
private final JavaPsiFacade psiFacade = new JavaPsiFacadeImpl(myElement.getProject());
public MyReference(@NotNull PsiLiteralExpression element) {
super(element);
}
@Nullable
@Override
public PsiElement resolve() {
return psiFacade.findClass(
"my.example.reference.MyClass",
myElement.getResolveScope()
);
}
@Override
public boolean isReferenceTo(@NotNull PsiElement element) {
if (element instanceof PsiClass) {
PsiClass myPsiClass = psiFacade.findClass(
"my.example.reference.MyClass",
myElement.getResolveScope()
);
return element.isEquivalentTo(myPsiClass);
}
return false;
}
}
public class MyReferenceContributor extends PsiReferenceContributor {
@Override
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
registrar.registerReferenceProvider(
literalExpression(),
new PsiReferenceProvider() {
@NotNull
@Override
public PsiReference[] getReferencesByElement(@NotNull PsiElement psiElement, @NotNull ProcessingContext processingContext) {
if (!(psiElement instanceof PsiLiteralExpression)) {
return PsiReference.EMPTY_ARRAY;
}
PsiLiteralExpression literal = (PsiLiteralExpression) psiElement;
String literalValue = PsiLiteralUtil.getStringLiteralContent(literal);
if (!literalValue.equals("I am literal")) {
return PsiReference.EMPTY_ARRAY;
}
PsiReference[] references = PsiReference.ARRAY_FACTORY.create(1);
references[0] = new MyReference(literal);
return references;
}
});
}
}
How can I suppress "class never used" warning if in the project I have ANYWHERE at least one literal used with value - "I am literal" ?
Thanks for sharing, but the previous question remains: is the isReferenceTo() called when usages are searched?
If the class doesn't see the usages in your custom references, then I suspect that they are in different scopes. If the scope is the case, then in implicit usage provider you will probably have to use a wide scope that includes your references (I mean scope in ReferencesSearch.search(aClass, scope).
Yes, it is called when usages are searched.
Should I use this - is this the widest scope available?
EDIT:
Also tried with this - neither worked for me ;(
Got this working for methods, exactly what I was looking for:
Thank you Karol Lewandowski