From e60923ac0b6eb529bd2a17ae9fe72035edb3c40f Mon Sep 17 00:00:00 2001 From: Mikhail Pyltsin Date: Wed, 28 Dec 2022 10:10:37 +0100 Subject: [PATCH] [java-inspections] IDEA-266365. Added support record deconstruction GitOrigin-RevId: 13f50bf376221046e2903de076ee4f8d143d5ff5 --- .../intellij/psi/util/JavaPsiPatternUtil.java | 7 ++- ...eParameterExtendsFinalClassInspection.java | 45 ++++++++++++++++--- .../TypeParameterExtendsFinalClassJava20.java | 45 +++++++++++++++++++ ...ExtendsFinalClassJava20InspectionTest.java | 25 +++++++++++ 4 files changed, 115 insertions(+), 7 deletions(-) create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/inheritance/type_parameter_extends_final_class/TypeParameterExtendsFinalClassJava20.java create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/inheritance/TypeParameterExtendsFinalClassJava20InspectionTest.java diff --git a/java/java-psi-impl/src/com/intellij/psi/util/JavaPsiPatternUtil.java b/java/java-psi-impl/src/com/intellij/psi/util/JavaPsiPatternUtil.java index 5664752ec7d4..5674590b9ad9 100644 --- a/java/java-psi-impl/src/com/intellij/psi/util/JavaPsiPatternUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/util/JavaPsiPatternUtil.java @@ -337,9 +337,12 @@ public final class JavaPsiPatternUtil { } public static @Nullable PsiType getDeconstructedImplicitPatternVariableType(@NotNull PsiPatternVariable parameter) { - PsiRecordComponent recordComponent = getRecordComponentForPattern(parameter.getPattern()); + return getDeconstructedImplicitPatternType(parameter.getPattern()); + } + + public static @Nullable PsiType getDeconstructedImplicitPatternType(@NotNull PsiPattern pattern) { + PsiRecordComponent recordComponent = getRecordComponentForPattern(pattern); if (recordComponent != null) { - PsiTypeTestPattern pattern = (PsiTypeTestPattern)parameter.getParent(); PsiDeconstructionList deconstructionList = ObjectUtils.tryCast(pattern.getParent(), PsiDeconstructionList.class); if (deconstructionList == null) return null; PsiDeconstructionPattern deconstructionPattern = (PsiDeconstructionPattern)deconstructionList.getParent(); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/TypeParameterExtendsFinalClassInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/TypeParameterExtendsFinalClassInspection.java index af2ba11055a7..ca6e85405e49 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/TypeParameterExtendsFinalClassInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/TypeParameterExtendsFinalClassInspection.java @@ -21,6 +21,7 @@ import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.util.JavaPsiPatternUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; @@ -180,7 +181,31 @@ public class TypeParameterExtendsFinalClassInspection extends BaseInspection imp private static boolean isWildcardRequired(PsiTypeElement typeElement) { final PsiElement ancestor = PsiTreeUtil.skipParentsOfType( typeElement, PsiTypeElement.class, PsiJavaCodeReferenceElement.class, PsiReferenceParameterList.class); - if (ancestor instanceof PsiParameter) { + if (ancestor instanceof PsiDeconstructionPattern deconstructionPattern) { + PsiForeachStatement parentForEach = PsiTreeUtil.getParentOfType(deconstructionPattern, PsiForeachStatement.class); + if (parentForEach == null) { + return false; + } + if (!PsiTreeUtil.isAncestor(parentForEach.getIterationDeclaration(), deconstructionPattern, false)) { + return false; + } + if (ancestor.getParent() instanceof PsiForeachStatement foreachStatement) { + PsiExpression iteratedValue = foreachStatement.getIteratedValue(); + if (iteratedValue == null) { + return false; + } + return isWildcardRequired(typeElement, deconstructionPattern.getTypeElement(), + JavaGenericsUtil.getCollectionItemType(iteratedValue)); + } + else if (deconstructionPattern.getParent() instanceof PsiDeconstructionList) { + PsiType type = JavaPsiPatternUtil.getDeconstructedImplicitPatternType(deconstructionPattern); + if (type == null) { + return false; + } + return isWildcardRequired(typeElement, deconstructionPattern.getTypeElement(), type); + } + } + else if (ancestor instanceof PsiParameter) { final PsiParameter parameter = (PsiParameter)ancestor; final PsiElement scope = parameter.getDeclarationScope(); if (scope instanceof PsiMethod method) { @@ -196,10 +221,20 @@ public class TypeParameterExtendsFinalClassInspection extends BaseInspection imp if (iteratedValue == null) { return true; // incomplete code } - final PsiParameter iterationParameter = foreachStatement.getIterationParameter(); - final PsiTypeElement foreachTypeElement = iterationParameter.getTypeElement(); - assert foreachTypeElement != null; - return isWildcardRequired(typeElement, foreachTypeElement, JavaGenericsUtil.getCollectionItemType(iteratedValue)); + PsiForeachDeclarationElement declaration = foreachStatement.getIterationDeclaration(); + //patterns check before + if (declaration instanceof PsiParameter iterationParameter) { + final PsiTypeElement foreachTypeElement = iterationParameter.getTypeElement(); + assert foreachTypeElement != null; + return isWildcardRequired(typeElement, foreachTypeElement, JavaGenericsUtil.getCollectionItemType(iteratedValue)); + } + else if (ancestor instanceof PsiPatternVariable patternVariable) { + PsiType type = JavaPsiPatternUtil.getDeconstructedImplicitPatternVariableType(patternVariable); + if (type == null) { + return true; + } + return isWildcardRequired(typeElement, patternVariable.getTypeElement(), type); + } } } else if (ancestor instanceof PsiLocalVariable localVariable) { diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/inheritance/type_parameter_extends_final_class/TypeParameterExtendsFinalClassJava20.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/inheritance/type_parameter_extends_final_class/TypeParameterExtendsFinalClassJava20.java new file mode 100644 index 000000000000..4092ab044037 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/inheritance/type_parameter_extends_final_class/TypeParameterExtendsFinalClassJava20.java @@ -0,0 +1,45 @@ +package com.siyeh.igtest.inheritance.type_parameter_extends_final_class; + +import java.util.*; + + + +public class TypeParameterExtendsFinalClassJava20{ + public static void testGenericWithWildcard(List? extends String>> list){ + for (RecordWithGeneric(var x): list) { + System.out.println(x); + } + } + + public static void testGenericWithoutWildcard(List> list){ + for (RecordWithGeneric<? extends String>(var x): list) { + System.out.println(x); + } + } + + public static void testRecordWithWildcard(){ + List lists2 = List.of(new RecordWithWildcard(List.of("1"), "2")); + for (RecordWithWildcard(List x, var y ) : lists2) { + System.out.println(x); + } + } + + public static void testRecordWithoutWildcard(){ + List lists3 = List.of(new RecordWithoutWildcard(List.of("1"), "2")); + for (RecordWithoutWildcard(List<? extends String> x, var y ) : lists3) { + System.out.println(x); + } + } +} + + +record RecordWithWildcard(List<? extends String> x, String y){ + +} +record RecordWithoutWildcard(List x, String y){ + +} + +record RecordWithGeneric(T x){ + +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/inheritance/TypeParameterExtendsFinalClassJava20InspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/inheritance/TypeParameterExtendsFinalClassJava20InspectionTest.java new file mode 100644 index 000000000000..d3fadeac7bc9 --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/inheritance/TypeParameterExtendsFinalClassJava20InspectionTest.java @@ -0,0 +1,25 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.siyeh.ig.inheritance; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.intellij.testFramework.LightProjectDescriptor; +import com.siyeh.ig.LightJavaInspectionTestCase; +import org.jetbrains.annotations.NotNull; + +public class TypeParameterExtendsFinalClassJava20InspectionTest extends LightJavaInspectionTestCase { + + public void testTypeParameterExtendsFinalClassJava20() { + doTest(); + } + + @Override + protected InspectionProfileEntry getInspection() { + return new TypeParameterExtendsFinalClassInspection(); + } + + @Override + @NotNull + protected LightProjectDescriptor getProjectDescriptor() { + return JAVA_20; + } +}