Java: call hierarchy for records (IJPL-3784)

GitOrigin-RevId: ec7930d33f3626d7f72b75e7709c9cf141a9f5b7
This commit is contained in:
Bas Leijdekkers
2024-11-29 16:15:15 +00:00
committed by intellij-monorepo-bot
parent 0e1e34768f
commit 77bb505f54
9 changed files with 77 additions and 17 deletions
@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.hierarchy.call;
import com.intellij.ide.hierarchy.CallHierarchyBrowserBase;
@@ -11,10 +11,7 @@ import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMember;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.*;
import com.intellij.ui.PopupHandler;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -64,7 +61,7 @@ public class CallHierarchyBrowser extends CallHierarchyBrowserBase {
@Override
protected boolean isApplicableElement(@NotNull PsiElement e) {
return e instanceof PsiMethod || e instanceof PsiField;
return e instanceof PsiMethod || e instanceof PsiField || e instanceof PsiClass aClass && aClass.isRecord();
}
@Override
@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.hierarchy.call;
import com.intellij.codeInsight.highlighting.HighlightManager;
@@ -46,6 +46,9 @@ public final class CallHierarchyNodeDescriptor extends HierarchyNodeDescriptor i
*/
public PsiMember getEnclosingElement() {
PsiElement element = getPsiElement();
if (element instanceof PsiClass aClass && aClass.isRecord()) {
return JavaPsiRecordUtil.findCanonicalConstructor(aClass);
}
return element == null ? null : getEnclosingElement(element);
}
@@ -90,7 +93,7 @@ public final class CallHierarchyNodeDescriptor extends HierarchyNodeDescriptor i
mainTextAttributes = new TextAttributes(myColor, null, null, null, Font.PLAIN);
}
if (enclosingElement instanceof PsiMethod || enclosingElement instanceof PsiField) {
if (enclosingElement instanceof SyntheticElement) {
if (FileTypeUtils.isInServerPageFile(enclosingElement)) {
PsiFile file = enclosingElement.getContainingFile();
myHighlightedText.getEnding().addText(file != null ? file.getName() : JavaBundle.message("node.call.hierarchy.unknown.jsp"), mainTextAttributes);
}
@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.hierarchy.call;
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
@@ -30,7 +30,7 @@ public final class CalleeMethodsTreeStructure extends HierarchyTreeStructure {
@Override
protected Object @NotNull [] buildChildren(@NotNull HierarchyNodeDescriptor descriptor) {
PsiMember enclosingElement = ((CallHierarchyNodeDescriptor)descriptor).getEnclosingElement();
if (!(enclosingElement instanceof PsiMethod method)) {
if (!(enclosingElement instanceof PsiMethod method) || enclosingElement instanceof SyntheticElement) {
return ArrayUtilRt.EMPTY_OBJECT_ARRAY;
}
@@ -7,21 +7,17 @@ import com.intellij.ide.hierarchy.HierarchyProvider;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMember;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
public class JavaCallHierarchyProvider implements HierarchyProvider {
@Override
public PsiElement getTarget(@NotNull DataContext dataContext) {
Project project = CommonDataKeys.PROJECT.getData(dataContext);
if (project == null) return null;
PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
if (element instanceof PsiField) return element;
if (element instanceof PsiField || element instanceof PsiClass aClass && aClass.isRecord()) return element;
return PsiTreeUtil.getParentOfType(element, PsiMethod.class, false);
}
@@ -0,0 +1,22 @@
class Action {
public static void main(String[] args) {
var a = new Person("sdf", "sdf");
var b = new PersonB("sdf", "sdf");
}
public static class PersonB {
private final String name;
private final String address;
public PersonB(String name, String address) {
this.name = name;
this.address = address;
}
}
}
record Person(String name, String address) {
public Person(String name) {
this(name, "");
}
}
@@ -0,0 +1,4 @@
<node text="Person.Person(String, String) ()" base="true">
<node text="Person.Person(String) ()"/>
<node text="Action.main(String[]) ()"/>
</node>
@@ -0,0 +1,22 @@
class Action {
public static void main(String[] args) {
var a = new Person("sdf", "sdf");
var b = new PersonB("sdf", "sdf");
}
public static class PersonB {
private final String name;
private final String address;
public PersonB(String name, String address) {
this.name = name;
this.address = address;
}
}
}
record Person(String name, String address) {
public Person(String name) {
this(name, "");
}
}
@@ -0,0 +1,2 @@
<node text="Person.Person(String, String) ()" base="true">
</node>
@@ -27,7 +27,7 @@ import java.util.Arrays;
public class JavaCallHierarchyTest extends HierarchyViewTestBase {
@Override
protected @NotNull LanguageLevel getProjectLanguageLevel() {
return LanguageLevel.JDK_1_8; // method refs are needed
return LanguageLevel.JDK_16; // records are needed
}
@NotNull
@@ -95,6 +95,20 @@ public class JavaCallHierarchyTest extends HierarchyViewTestBase {
doJavaCallerTypeHierarchyTest("A", "A", "A.java");
}
public void testRecordCanonicalConstructor() throws Exception {
doHierarchyTest(() -> {
PsiClass aClass = JavaPsiFacade.getInstance(getProject()).findClass("Person", ProjectScope.getProjectScope(getProject()));
return new CallerMethodsTreeStructure(getProject(), aClass, HierarchyBrowserBaseEx.SCOPE_PROJECT);
}, JavaHierarchyUtil.getComparator(myProject), "Action.java");
}
public void testRecordCanonicalConstructorReverse() throws Exception {
doHierarchyTest(() -> {
PsiClass aClass = JavaPsiFacade.getInstance(getProject()).findClass("Person", ProjectScope.getProjectScope(getProject()));
return new CalleeMethodsTreeStructure(getProject(), aClass, HierarchyBrowserBaseEx.SCOPE_PROJECT);
}, JavaHierarchyUtil.getComparator(myProject), "Action.java");
}
public void testMethodRef() throws Exception {
doJavaCalleeTypeHierarchyTest("A", "testMethod", "A.java");
}