diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/RecordAugmentProvider.java b/java/java-psi-impl/src/com/intellij/psi/impl/RecordAugmentProvider.java index 41d52c8c6e36..aba3630137f1 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/RecordAugmentProvider.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/RecordAugmentProvider.java @@ -1,6 +1,7 @@ // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.psi.impl; +import com.intellij.codeInsight.AnnotationTargetUtil; import com.intellij.psi.*; import com.intellij.psi.augment.PsiAugmentProvider; import com.intellij.psi.impl.light.LightMethod; @@ -9,9 +10,11 @@ import com.intellij.psi.impl.light.LightRecordMethod; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static com.intellij.util.ObjectUtils.tryCast; public class RecordAugmentProvider extends PsiAugmentProvider { @NotNull @@ -36,9 +39,9 @@ public class RecordAugmentProvider extends PsiAugmentProvider { PsiElementFactory factory = JavaPsiFacade.getInstance(element.getProject()).getElementFactory(); ArrayList methods = new ArrayList<>(components.length); for (PsiRecordComponent component : components) { - String name = component.getName(); - if (name == null) continue; - LightMethod method = new LightRecordMethod(element.getManager(), factory.createMethod(name, component.getType()), aClass, component); + PsiMethod recordMethod = createRecordMethod(component, factory); + if (recordMethod == null) continue; + LightMethod method = new LightRecordMethod(element.getManager(), recordMethod, aClass, component); //noinspection unchecked methods.add((Psi)method); } @@ -64,9 +67,50 @@ public class RecordAugmentProvider extends PsiAugmentProvider { private static PsiField createRecordField(@NotNull PsiRecordComponent component, @NotNull PsiElementFactory factory) { String name = component.getName(); if (name == null) return null; - PsiTypeElement typeElement = component.getTypeElement(); - if (typeElement == null) return null; - PsiClass aClass = factory.createClassFromText("private final " + typeElement.getText() + " " + name + ";", null); + String typeText = getTypeText(component, RecordAugmentProvider::hasTargetApplicableForField); + if (typeText == null) return null; + PsiClass aClass = factory.createClassFromText("private final " + typeText + " " + name + ";", null); return aClass.getFields()[0]; } + + @Nullable + private static PsiMethod createRecordMethod(@NotNull PsiRecordComponent component, @NotNull PsiElementFactory factory) { + String name = component.getName(); + if (name == null) return null; + String typeText = getTypeText(component, RecordAugmentProvider::hasTargetApplicableForMethod); + if (typeText == null) return null; + PsiClass aClass = factory.createClassFromText("public " + typeText + " " + name + "(){}", null); + return aClass.getMethods()[0]; + } + + @Nullable + private static String getTypeText(@NotNull PsiRecordComponent component, Predicate annotationPredicate) { + PsiTypeElement typeElement = component.getTypeElement(); + if (typeElement == null) return null; + String annotations = Arrays.stream(component.getAnnotations()) + .filter(annotationPredicate) + .map(annotation -> annotation.getText()) + .collect(Collectors.joining(" ")); + return annotations + " " + typeElement.getText(); + } + + private static boolean hasTargetApplicableForField(PsiAnnotation annotation) { + Set targets = getTargets(annotation); + if (targets == null) return false; + return targets.contains(PsiAnnotation.TargetType.TYPE) || targets.contains(PsiAnnotation.TargetType.FIELD); + } + + private static boolean hasTargetApplicableForMethod(PsiAnnotation annotation) { + Set targets = getTargets(annotation); + if (targets == null) return false; + return targets.contains(PsiAnnotation.TargetType.TYPE) || targets.contains(PsiAnnotation.TargetType.METHOD); + } + + private static Set getTargets(PsiAnnotation annotation) { + PsiJavaCodeReferenceElement element = annotation.getNameReferenceElement(); + if (element == null) return null; + PsiClass annotationClass = tryCast(element.resolve(), PsiClass.class); + if (annotationClass == null) return null; + return AnnotationTargetUtil.getAnnotationTargets(annotationClass); + } } diff --git a/java/java-tests/testData/psi/resolve/method/records/RecordComponent.java b/java/java-tests/testData/psi/resolve/method/records/RecordComponent.java index 463c88230ab3..ca51d6124caf 100644 --- a/java/java-tests/testData/psi/resolve/method/records/RecordComponent.java +++ b/java/java-tests/testData/psi/resolve/method/records/RecordComponent.java @@ -1,7 +1,17 @@ +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; + public class Test1 { - record A(int value) {} + record A(@F @M int value) {} void test(A a){ a.value(); } } + +@Target(value={FIELD}) +@interface F{} + +@Target(value={METHOD}) +@interface M{} \ No newline at end of file diff --git a/java/java-tests/testData/psi/resolve/method/records/RecordField.java b/java/java-tests/testData/psi/resolve/method/records/RecordField.java index 74222ccf3fd9..2c03433f8fc8 100644 --- a/java/java-tests/testData/psi/resolve/method/records/RecordField.java +++ b/java/java-tests/testData/psi/resolve/method/records/RecordField.java @@ -1,5 +1,15 @@ -record A(int value) { +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; + +record A(@F @M int value) { void test(){ value } } + +@Target(value={FIELD}) +@interface F{} + +@Target(value={METHOD}) +@interface M{} diff --git a/java/java-tests/testSrc/com/intellij/java/psi/resolve/ResolveRecordMethodsTest.java b/java/java-tests/testSrc/com/intellij/java/psi/resolve/ResolveRecordMethodsTest.java index 54c45c1e05d1..3bd9d71630a6 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/resolve/ResolveRecordMethodsTest.java +++ b/java/java-tests/testSrc/com/intellij/java/psi/resolve/ResolveRecordMethodsTest.java @@ -22,7 +22,8 @@ public class ResolveRecordMethodsTest extends LightResolveTestCase { public void testRecordComponent() { PsiElement target = resolve(); assertTrue(target instanceof PsiMethod); - assertEquals(PsiType.INT, ((PsiMethod)target).getReturnType()); + PsiMethod targetMethod = (PsiMethod)target; + assertEquals(PsiType.INT, targetMethod.getReturnType()); PsiJavaFile file = (PsiJavaFile)getFile(); @@ -31,12 +32,15 @@ public class ResolveRecordMethodsTest extends LightResolveTestCase { PsiRecordComponent[] components = record.getRecordComponents(); assertSize(1, components); assertEquals(target.getTextOffset(), components[0].getTextOffset()); + assertFalse(targetMethod.hasAnnotation("F")); + assertTrue(targetMethod.hasAnnotation("M")); } public void testRecordField() { PsiElement target = resolve(); assertTrue(target instanceof PsiField); - assertEquals(PsiType.INT, ((PsiField)target).getType()); + PsiField targetField = (PsiField)target; + assertEquals(PsiType.INT, targetField.getType()); PsiJavaFile file = (PsiJavaFile)getFile(); @@ -44,5 +48,7 @@ public class ResolveRecordMethodsTest extends LightResolveTestCase { PsiRecordComponent[] components = record.getRecordComponents(); assertSize(1, components); assertEquals(target.getTextOffset(), components[0].getTextOffset()); + assertTrue(targetField.hasAnnotation("F")); + assertFalse(targetField.hasAnnotation("M")); } }