diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearchHelper.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearchHelper.java index 379c9eade601..2e6aff735a47 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearchHelper.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearchHelper.java @@ -17,6 +17,7 @@ import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiUtil; +import com.intellij.util.JavaPsiConstructorUtil; import com.intellij.util.PairProcessor; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; @@ -97,17 +98,15 @@ class ConstructorReferencesSearchHelper { // search usages like "this(..)" if (!DumbService.getInstance(project).runReadActionInSmartMode( () -> processSuperOrThis(containingClass, constructor, constructorCanBeCalledImplicitly[0], searchScope, project, - isStrictSignatureSearch, - JavaKeywords.THIS, JavaKeywords.SUPER, processor))) { + isStrictSignatureSearch, JavaKeywords.THIS, processor))) { return false; } // search usages like "super(..)" Processor processor2 = inheritor -> { - final PsiElement navigationElement = inheritor.getNavigationElement(); - if (navigationElement instanceof PsiClass) { - return processSuperOrThis((PsiClass)navigationElement, constructor, constructorCanBeCalledImplicitly[0], searchScope, project, - isStrictSignatureSearch, JavaKeywords.SUPER, JavaKeywords.THIS, processor); + if (inheritor.getNavigationElement() instanceof PsiClass aClass) { + return processSuperOrThis(aClass, constructor, constructorCanBeCalledImplicitly[0], searchScope, project, + isStrictSignatureSearch, JavaKeywords.SUPER, processor); } return true; }; @@ -160,37 +159,28 @@ class ConstructorReferencesSearchHelper { @NotNull Project project, final boolean isStrictSignatureSearch, @NotNull String superOrThisKeyword, - @NotNull String thisOrSuperKeyword, @NotNull Processor processor) { PsiMethod[] constructors = inheritor.getConstructors(); if (constructors.length == 0 && constructorCanBeCalledImplicitly) { if (!processImplicitConstructorCall(inheritor, processor, constructor, project, inheritor)) return false; } for (PsiMethod method : constructors) { - PsiCodeBlock body = method.getBody(); - if (body == null || method == constructor && isStrictSignatureSearch || method instanceof SyntheticElement) { + if (method == constructor && isStrictSignatureSearch || method instanceof SyntheticElement) { continue; } - PsiStatement[] statements = body.getStatements(); - if (statements.length != 0 && statements[0] instanceof PsiExpressionStatement exprStatement && - exprStatement.getExpression() instanceof PsiMethodCallExpression call) { - PsiReferenceExpression refExpr = call.getMethodExpression(); - if (PsiSearchScopeUtil.isInScope(searchScope, refExpr)) { - if (refExpr.textMatches(superOrThisKeyword)) { - PsiElement referencedElement = refExpr.resolve(); - if (referencedElement instanceof PsiMethod constructor1) { - boolean match = isStrictSignatureSearch - ? myManager.areElementsEquivalent(constructor1, constructor) - : myManager.areElementsEquivalent(constructor.getContainingClass(), constructor1.getContainingClass()); - if (match && !processor.process(refExpr)) return false; - } - //as long as we've encountered super/this keyword, no implicit ctr calls are possible here - continue; - } - else if (refExpr.textMatches(thisOrSuperKeyword)) { - continue; + PsiMethodCallExpression thisOrSuperCall = JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(method); + if (thisOrSuperCall != null) { + if (PsiSearchScopeUtil.isInScope(searchScope, thisOrSuperCall)) { + PsiReferenceExpression ref = thisOrSuperCall.getMethodExpression(); + if (ref.textMatches(superOrThisKeyword) && ref.resolve() instanceof PsiMethod referencedConstructor) { + boolean match = isStrictSignatureSearch + ? myManager.areElementsEquivalent(referencedConstructor, constructor) + : myManager.areElementsEquivalent(constructor.getContainingClass(), referencedConstructor.getContainingClass()); + if (match && !processor.process(ref)) return false; } } + //when we've encountered a super/this call, no implicit ctr calls are possible here + continue; } if (constructorCanBeCalledImplicitly && PsiSearchScopeUtil.isInScope(searchScope, method)) { if (!processImplicitConstructorCall(method, processor, constructor, project, inheritor)) return false; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsageModern/afterFlexibleConstructorBody.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsageModern/afterFlexibleConstructorBody.java new file mode 100644 index 000000000000..293d50ff1a04 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsageModern/afterFlexibleConstructorBody.java @@ -0,0 +1,14 @@ +// "Add 'int' as 1st parameter to constructor 'Friend()'" "true" +class Friend { + Friend(int i) { + System.out.println(0); + } +} +class Flexible extends Friend { + + Flexible() { + System.out.println("before"); + super(1); + System.out.println("after"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsageModern/beforeFlexibleConstructorBody.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsageModern/beforeFlexibleConstructorBody.java new file mode 100644 index 000000000000..eca2f88e43d3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsageModern/beforeFlexibleConstructorBody.java @@ -0,0 +1,14 @@ +// "Add 'int' as 1st parameter to constructor 'Friend()'" "true" +class Friend { + Friend() { + System.out.println(0); + } +} +class Flexible extends Friend { + + Flexible() { + System.out.println("before"); + super(1); + System.out.println("after"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/changeSignature/FlexibleConstructorBody.java b/java/java-tests/testData/refactoring/changeSignature/FlexibleConstructorBody.java new file mode 100644 index 000000000000..dd4eb7d4a9cf --- /dev/null +++ b/java/java-tests/testData/refactoring/changeSignature/FlexibleConstructorBody.java @@ -0,0 +1,13 @@ +class C { + C() { + } +} + +class C1 extends C { + int i; + C1(int i, int k) { + System.out.println(i); + super(); + this.i = i; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/changeSignature/FlexibleConstructorBody_after.java b/java/java-tests/testData/refactoring/changeSignature/FlexibleConstructorBody_after.java new file mode 100644 index 000000000000..0c787b004f16 --- /dev/null +++ b/java/java-tests/testData/refactoring/changeSignature/FlexibleConstructorBody_after.java @@ -0,0 +1,13 @@ +class C { + C(int i) { + } +} + +class C1 extends C { + int i; + C1(int i, int k) { + System.out.println(i); + super(0); + this.i = i; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/ChangeMethodSignatureFromUsageModernTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/ChangeMethodSignatureFromUsageModernTest.java index c0b70d27c0e5..df20225627f7 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/ChangeMethodSignatureFromUsageModernTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/ChangeMethodSignatureFromUsageModernTest.java @@ -1,12 +1,11 @@ -// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.codeInsight.daemon.impl.quickfix; import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase; import com.intellij.testFramework.LightProjectDescriptor; +import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; import org.jetbrains.annotations.NotNull; -import static com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase.JAVA_21_ANNOTATED; - public class ChangeMethodSignatureFromUsageModernTest extends LightQuickFixParameterizedTestCase { @Override @@ -16,6 +15,6 @@ public class ChangeMethodSignatureFromUsageModernTest extends LightQuickFixParam @Override protected @NotNull LightProjectDescriptor getProjectDescriptor() { - return JAVA_21_ANNOTATED; + return LightJavaCodeInsightFixtureTestCase.JAVA_25; } } diff --git a/java/java-tests/testSrc/com/intellij/java/psi/search/FindFixtureBasedTest.kt b/java/java-tests/testSrc/com/intellij/java/psi/search/FindFixtureBasedTest.kt index b3dfaf50a865..8d5238afb618 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/search/FindFixtureBasedTest.kt +++ b/java/java-tests/testSrc/com/intellij/java/psi/search/FindFixtureBasedTest.kt @@ -1,20 +1,7 @@ -/* - * Copyright 2000-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.java.psi.search +import com.intellij.psi.PsiReferenceExpression import com.intellij.psi.search.searches.MethodReferencesSearch import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase @@ -41,4 +28,20 @@ class FindFixtureBasedTest : LightJavaCodeInsightFixtureTestCase() { assertSize(3, constructors) assertNull(MethodReferencesSearch.search(constructors[0]).findFirst()) } + + fun testFlexibleConstructorBody() { + val aClass = myFixture.addClass("class Friend { Friend() {}}"); + myFixture.addClass("class Flexible extends Friend {\n" + + " Flexible() {\n" + + " System.out.println(\"before\");\n" + + " super();\n" + + " System.out.println(\"after\");\n" + + " }\n" + + "}") + + val constructors = aClass.constructors + assertSize(1, constructors) + val reference = MethodReferencesSearch.search(constructors[0]).findFirst() + assertTrue(reference?.element is PsiReferenceExpression) + } } diff --git a/java/java-tests/testSrc/com/intellij/java/refactoring/ChangeSignatureTest.java b/java/java-tests/testSrc/com/intellij/java/refactoring/ChangeSignatureTest.java index ec452299cde5..59b24ffd0ccc 100644 --- a/java/java-tests/testSrc/com/intellij/java/refactoring/ChangeSignatureTest.java +++ b/java/java-tests/testSrc/com/intellij/java/refactoring/ChangeSignatureTest.java @@ -99,6 +99,14 @@ public class ChangeSignatureTest extends ChangeSignatureBaseTest { ); } + public void testFlexibleConstructorBody() { + doTest(null, + new ParameterInfoImpl[]{ + ParameterInfoImpl.createNew().withName("i").withType(PsiTypes.intType()).withDefaultValue("0") + }, false + ); + } + public void testGenerateDelegate() { doTest(null, new ParameterInfoImpl[]{