IDEA-250544 Inspection "Unused property" incorrectly reports plugin.pluginID.description properties

GitOrigin-RevId: e54531ce96593b3eb578cd703b2463461fb8c7d7
This commit is contained in:
Yann Cébron
2020-09-14 15:48:26 +00:00
committed by intellij-monorepo-bot
parent e614b3021c
commit b54be4d3cd
5 changed files with 83 additions and 6 deletions
@@ -28,18 +28,18 @@ import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
import com.intellij.util.xml.DomTarget;
import com.intellij.util.xml.ElementPresentationManager;
import com.intellij.util.xml.GenericAttributeValue;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.devkit.DevKitBundle;
import org.jetbrains.idea.devkit.dom.Action;
import org.jetbrains.idea.devkit.dom.ActionOrGroup;
import org.jetbrains.idea.devkit.dom.Extension;
import org.jetbrains.idea.devkit.dom.OverrideText;
import org.jetbrains.idea.devkit.dom.*;
import org.jetbrains.idea.devkit.dom.index.IdeaPluginRegistrationIndex;
import org.jetbrains.idea.devkit.util.DescriptorUtil;
import org.jetbrains.idea.devkit.util.PsiUtil;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -58,6 +58,8 @@ public class MessageBundleReferenceContributor extends PsiReferenceContributor {
@NonNls private static final String EXPORTABLE_PREFIX = "exportable.";
@NonNls private static final String EXPORTABLE_SUFFIX = ".presentable.name";
@NonNls private static final String PLUGIN = "plugin.";
@Override
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
registrar.registerReferenceProvider(
@@ -74,7 +76,8 @@ public class MessageBundleReferenceContributor extends PsiReferenceContributor {
return JBIterable.of(
createActionOrGroupIdReference(element, text),
createToolwindowIdReference(element, text),
createExportableIdReference(element, text)
createExportableIdReference(element, text),
createPluginIdReference(element, text)
).filter(Objects::nonNull).toArray(PsiReference.EMPTY_ARRAY);
}
@@ -104,6 +107,14 @@ public class MessageBundleReferenceContributor extends PsiReferenceContributor {
String id = text.replace(EXPORTABLE_PREFIX, "").replace(EXPORTABLE_SUFFIX, "");
return new ExportableIdReference(element, id);
}
@Nullable
private PsiReference createPluginIdReference(@NotNull PsiElement element, String text) {
if (!isPluginDescriptionKey(text)) return null;
String id = StringUtil.substringAfter(StringUtil.substringBefore(text, DESCRIPTION), PLUGIN);
return new PluginIdReference(element, id);
}
});
}
@@ -128,6 +139,48 @@ public class MessageBundleReferenceContributor extends PsiReferenceContributor {
return name.startsWith(TOOLWINDOW_STRIPE_PREFIX);
}
private static boolean isPluginDescriptionKey(String name) {
return name.startsWith(PLUGIN) && name.endsWith(DESCRIPTION);
}
private static class PluginIdReference extends PsiPolyVariantReferenceBase<PsiElement> {
private PluginIdReference(@NotNull PsiElement element, String id) {
super(element, TextRange.allOf(id).shiftRight(PLUGIN.length()));
}
@Override
public ResolveResult @NotNull [] multiResolve(boolean incompleteCode) {
List<PsiElement> psiElements = new SmartList<>();
final String id = getValue();
final Project project = getElement().getProject();
for (IdeaPlugin plugin : getRelevantPlugins()) {
if (id.equals(plugin.getPluginId())) {
final DomTarget target = DomTarget.getTarget(plugin);
assert target != null;
psiElements.add(PomService.convertToPsi(project, target));
}
}
return PsiElementResolveResult.createResults(psiElements);
}
@Override
public Object @NotNull [] getVariants() {
return ContainerUtil.map2Array(getRelevantPlugins(), LookupElement.class,
plugin -> LookupElementBuilder.create(Objects.requireNonNull(plugin.getPluginId()))
.withPsiElement(plugin.getXmlElement())
.withTailText(" " + StringUtil.notNullize(plugin.getName().getValue()))
.withIcon(ElementPresentationManager.getIcon(plugin)));
}
private Collection<IdeaPlugin> getRelevantPlugins() {
return ContainerUtil.filter(DescriptorUtil.getPlugins(getElement().getProject(), getElement().getResolveScope()),
plugin -> plugin.hasRealPluginId() && Boolean.TRUE != plugin.getImplementationDetail().getValue());
}
}
private static final class ActionOrGroupIdReference extends PsiPolyVariantReferenceBase<PsiElement> {
@@ -310,7 +363,8 @@ public class MessageBundleReferenceContributor extends PsiReferenceContributor {
if (isActionOrGroupKey(name) ||
isExportableKey(name) ||
isToolwindowKey(name)) {
isToolwindowKey(name) ||
isPluginDescriptionKey(name)) {
PsiElement key = property.getFirstChild();
PsiReference[] references = key == null ? PsiReference.EMPTY_ARRAY : key.getReferences();
@@ -0,0 +1,3 @@
<idea-plugin>
<id>my.plugin.id</id>
</idea-plugin>
@@ -0,0 +1,5 @@
plugin.my.plugin.id.description=Plugin Description Text
<warning descr="Unused property">plugin.INVALID_PLUGIN_ID.description</warning>=No such plugin ID
<warning descr="Unused property">plugin.implementation.id.description</warning>=Valid ID but implementation-detail is true
@@ -0,0 +1,3 @@
<idea-plugin implementation-detail="true">
<id>implementation.id</id>
</idea-plugin>
@@ -31,6 +31,10 @@ public class MessageBundleReferenceTest extends JavaCodeInsightFixtureTestCase {
doHighlightImplicitUsagesTest();
}
public void testPluginDescriptionKeyImplicitUsage() {
doHighlightImplicitUsagesTest("PluginDescriptionKeyImplicitUsage.xml");
}
public void testActionOrGroupImplicitUsage() {
doHighlightImplicitUsagesTest("ActionOrGroupImplicitUsage.xml");
}
@@ -70,4 +74,12 @@ public class MessageBundleReferenceTest extends JavaCodeInsightFixtureTestCase {
myFixture.completeBasic();
assertContainsElements(myFixture.getLookupElementStrings(), "MyStateName");
}
public void testPluginIdCompletionVariants() {
myFixture.copyFileToProject("PluginDescriptionKeyImplicitUsage.xml");
myFixture.configureByText("MyBundle.properties", "plugin.<caret>.description");
myFixture.completeBasic();
assertContainsElements(myFixture.getLookupElementStrings(), "my.plugin.id");
}
}