diff --git a/java/java-impl-refactorings/src/com/intellij/refactoring/safeDelete/JavaSafeDeleteProcessor.java b/java/java-impl-refactorings/src/com/intellij/refactoring/safeDelete/JavaSafeDeleteProcessor.java index 5fa6f847ab39..e8e2552b6596 100644 --- a/java/java-impl-refactorings/src/com/intellij/refactoring/safeDelete/JavaSafeDeleteProcessor.java +++ b/java/java-impl-refactorings/src/com/intellij/refactoring/safeDelete/JavaSafeDeleteProcessor.java @@ -26,6 +26,7 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleSettings; import com.intellij.psi.codeStyle.VariableKind; import com.intellij.psi.impl.FindSuperElementsHelper; +import com.intellij.psi.impl.light.LightDefaultConstructor; import com.intellij.psi.impl.light.LightRecordMethod; import com.intellij.psi.impl.source.javadoc.PsiDocParamRef; import com.intellij.psi.javadoc.PsiDocTag; @@ -131,6 +132,7 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase { @Nullable Module module, @NotNull Collection allElementsToDelete) { Project project = element.getProject(); + if (element instanceof SyntheticElement && !(element instanceof LightDefaultConstructor)) return null; if (element instanceof PsiPackage aPackage && module != null) { PsiDirectory[] directories = aPackage.getDirectories(module.getModuleScope()); if (directories.length == 0) return null; @@ -145,6 +147,9 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase { if (ApplicationManager.getApplication().isUnitTestMode()) { return Collections.singletonList(element); } + if (method.isDefaultConstructor()) { + return Collections.singletonList(method.getContainingClass()); + } PsiMethod[] methods = SuperMethodWarningUtil.checkSuperMethods(method, allElementsToDelete); if (methods.length == 0) return null; ArrayList psiMethods = new ArrayList<>(Arrays.asList(methods)); diff --git a/java/java-impl/src/com/intellij/codeInsight/JavaTargetElementEvaluator.java b/java/java-impl/src/com/intellij/codeInsight/JavaTargetElementEvaluator.java index a013e5094fd1..e8c217dc9406 100644 --- a/java/java-impl/src/com/intellij/codeInsight/JavaTargetElementEvaluator.java +++ b/java/java-impl/src/com/intellij/codeInsight/JavaTargetElementEvaluator.java @@ -6,7 +6,6 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.util.Computable; import com.intellij.psi.*; import com.intellij.psi.augment.PsiExtensionMethod; -import com.intellij.psi.impl.light.LightDefaultConstructor; import com.intellij.psi.impl.light.LightRecordCanonicalConstructor; import com.intellij.psi.impl.light.LightRecordMember; import com.intellij.psi.javadoc.PsiDocTag; @@ -162,9 +161,6 @@ public class JavaTargetElementEvaluator extends TargetElementEvaluatorEx2 implem if (refElement instanceof LightRecordCanonicalConstructor) { return ((LightRecordCanonicalConstructor)refElement).getContainingClass(); } - if (refElement instanceof LightDefaultConstructor def) { - return def.getContainingClass(); - } } return super.adjustReferenceOrReferencedElement(file, editor, offset, flags, refElement); } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaSafeDeleteCompletionCommandProvider.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaSafeDeleteCompletionCommandProvider.kt index f0d23e9bb46a..af42fba751e4 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaSafeDeleteCompletionCommandProvider.kt +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaSafeDeleteCompletionCommandProvider.kt @@ -5,7 +5,6 @@ import com.intellij.codeInsight.completion.command.CompletionCommand import com.intellij.codeInsight.completion.command.commands.AbstractSafeDeleteCompletionCommandProvider import com.intellij.codeInsight.completion.command.commands.DirectInspectionFixCompletionCommand import com.intellij.codeInsight.completion.command.getCommandContext -import com.intellij.codeInsight.completion.command.getTargetContext import com.intellij.openapi.editor.Editor import com.intellij.psi.* import com.intellij.psi.util.PsiTreeUtil @@ -33,8 +32,6 @@ private class JavaSafeDeleteCompletionCommandProvider : AbstractSafeDeleteComple return targetElement } } - val targetContext = getTargetContext(offset, editor) - if (targetContext?.isWritable != true) return null return element } diff --git a/java/java-tests/testData/psi/search/findUsages/defaultConstructor/Animal.java b/java/java-tests/testData/psi/search/findUsages/defaultConstructor/Animal.java new file mode 100644 index 000000000000..ca2d2a6739a4 --- /dev/null +++ b/java/java-tests/testData/psi/search/findUsages/defaultConstructor/Animal.java @@ -0,0 +1,10 @@ +public class Animal { + + public static void main(String[] args) { + Animal animal = new Animal(); + } +} +class OneLeggedDog extends Animal { + OneLeggedDog() { + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/renameInplace/DefaultConstructor.java b/java/java-tests/testData/refactoring/renameInplace/DefaultConstructor.java new file mode 100644 index 000000000000..cc9e9712f46e --- /dev/null +++ b/java/java-tests/testData/refactoring/renameInplace/DefaultConstructor.java @@ -0,0 +1,6 @@ +public class DefaultConstructor { + + public static void main(String[] args) { + DefaultConstructor constructor = new DefaultConstructor(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/renameInplace/DefaultConstructor_after.java b/java/java-tests/testData/refactoring/renameInplace/DefaultConstructor_after.java new file mode 100644 index 000000000000..f5d773d1d0dc --- /dev/null +++ b/java/java-tests/testData/refactoring/renameInplace/DefaultConstructor_after.java @@ -0,0 +1,6 @@ +public class HeadlessHorseman { + + public static void main(String[] args) { + HeadlessHorseman constructor = new HeadlessHorseman(); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/psi/search/FindUsagesTargetTest.java b/java/java-tests/testSrc/com/intellij/java/psi/search/FindUsagesTargetTest.java index cb7e272cfcc0..cf076c65f0c0 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/search/FindUsagesTargetTest.java +++ b/java/java-tests/testSrc/com/intellij/java/psi/search/FindUsagesTargetTest.java @@ -1,4 +1,4 @@ -// 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.java.psi.search; import com.intellij.openapi.actionSystem.CommonDataKeys; @@ -47,6 +47,18 @@ public class FindUsagesTargetTest extends LightJavaCodeInsightFixtureTestCase { assertTrue(((PsiMethod)element).isConstructor()); assertEquals("Xyz", ((PsiMethod)element).getName()); } + + public void testDefaultConstructor() { + myFixture.configureByText("OneLeggedDog.java", """ + public class OneLeggedDog { + static void main(){ + OneLeggedDog dog = new OneLeggedDog(); + } + } + """); + PsiElement element = getTargetElement(); + assertTrue(element instanceof PsiMethod m && m.isConstructor() && "OneLeggedDog".equals(m.getName())); + } private PsiElement getTargetElement() { DataContext dataContext = ((EditorEx)myFixture.getEditor()).getDataContext(); @@ -55,7 +67,7 @@ public class FindUsagesTargetTest extends LightJavaCodeInsightFixtureTestCase { PsiElement psiElement = CommonDataKeys.PSI_ELEMENT.getData(dataContext); UsageTarget[] targets = UsageTargetUtil.findUsageTargets(editor, psiFile, psiElement); - assertTrue(targets.length > 0); + assertTrue(" is ", targets.length > 0); assertTrue(targets[0] instanceof PsiElementUsageTarget); return ((PsiElementUsageTarget)targets[0]).getElement(); } diff --git a/java/java-tests/testSrc/com/intellij/java/psi/search/FindUsagesTest.java b/java/java-tests/testSrc/com/intellij/java/psi/search/FindUsagesTest.java index 2d640c9a996b..c3fe67987ca6 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/search/FindUsagesTest.java +++ b/java/java-tests/testSrc/com/intellij/java/psi/search/FindUsagesTest.java @@ -1,4 +1,4 @@ -// Copyright 2000-2020 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. +// 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.JavaTestUtil; @@ -40,6 +40,7 @@ import com.intellij.util.ProcessingContext; import com.intellij.util.Processor; import com.intellij.util.TimeoutUtil; import com.intellij.util.ui.UIUtil; +import com.siyeh.ig.psiutils.PsiElementOrderComparator; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import org.jetbrains.annotations.NotNull; @@ -119,6 +120,20 @@ public class FindUsagesTest extends JavaPsiTestCase { ((PsiCompiledElement)myJavaFacade.findClass("javax.swing.JLabel", GlobalSearchScope.allScope(myProject))).getMirror(); assertEquals(2, ReferencesSearch.search(decompiled, GlobalSearchScope.projectScope(myProject)).findAll().size()); } + + public void testDefaultConstructor() { + PsiClass aClass = myJavaFacade.findClass("Animal", GlobalSearchScope.allScope(myProject)); + PsiMethod main = aClass.findMethodsByName("main", false)[0]; + PsiLocalVariable variable = (PsiLocalVariable)main.getBody().getStatements()[0].getFirstChild(); + PsiNewExpression newExpression = (PsiNewExpression)variable.getInitializer(); + PsiMethod defaultConstructor = newExpression.resolveMethod(); + Collection references = ReferencesSearch.search(defaultConstructor, GlobalSearchScope.projectScope(myProject)).findAll(); + List<@NotNull PsiElement> result = + references.stream().map(r -> r.getElement()).sorted(PsiElementOrderComparator.getInstance()).toList(); + assertEquals(2, references.size()); + assertTrue(result.get(0).getParent() instanceof PsiNewExpression); + assertTrue(result.get(1) instanceof PsiMethod m && m.isConstructor() && "OneLeggedDog".equals(m.getName())); + } public void testImplicitConstructorUsage() { PsiMethod[] ctrs = myJavaFacade.findClass("Foo", GlobalSearchScope.allScope(myProject)).getConstructors(); diff --git a/java/java-tests/testSrc/com/intellij/java/refactoring/RenameMembersInplaceTest.java b/java/java-tests/testSrc/com/intellij/java/refactoring/RenameMembersInplaceTest.java index 92d824d0b2d8..21199d9f6929 100644 --- a/java/java-tests/testSrc/com/intellij/java/refactoring/RenameMembersInplaceTest.java +++ b/java/java-tests/testSrc/com/intellij/java/refactoring/RenameMembersInplaceTest.java @@ -62,6 +62,10 @@ public class RenameMembersInplaceTest extends LightJavaCodeInsightTestCase { public void testSuperMethodAnonymousInheritor() { doTestInplaceRename("xxx"); } + + public void testDefaultConstructor() { + doTestInplaceRename("HeadlessHorseman"); + } public void testMultipleConstructors() { doTestInplaceRename("Bar"); diff --git a/platform/lang-impl/src/com/intellij/refactoring/actions/BaseRefactoringAction.java b/platform/lang-impl/src/com/intellij/refactoring/actions/BaseRefactoringAction.java index 6b54a96f194c..124b8ac4da20 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/actions/BaseRefactoringAction.java +++ b/platform/lang-impl/src/com/intellij/refactoring/actions/BaseRefactoringAction.java @@ -1,4 +1,4 @@ -// Copyright 2000-2024 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.refactoring.actions; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; @@ -227,18 +227,18 @@ public abstract class BaseRefactoringAction extends AnAction { @ApiStatus.Internal public static PsiElement findRefactoringTargetInEditor(@NotNull DataContext dataContext, @NotNull Predicate elementLanguagePredicate) { - Editor editor = dataContext.getData(CommonDataKeys.EDITOR); - PsiFile file = dataContext.getData(CommonDataKeys.PSI_FILE); PsiElement element = dataContext.getData(CommonDataKeys.PSI_ELEMENT); - Language[] languages = dataContext.getData(LangDataKeys.CONTEXT_LANGUAGES); - if (element == null || element instanceof SyntheticElement || !elementLanguagePredicate.test(element.getLanguage())) { + if (element == null || !elementLanguagePredicate.test(element.getLanguage())) { + Editor editor = dataContext.getData(CommonDataKeys.EDITOR); + PsiFile file = dataContext.getData(CommonDataKeys.PSI_FILE); if (file == null || editor == null) { return null; } element = getElementAtCaret(editor, file); } - if (element == null || element instanceof SyntheticElement || languages == null) { + Language[] languages = dataContext.getData(LangDataKeys.CONTEXT_LANGUAGES); + if (element == null || languages == null) { return null; } diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/MemberInplaceRenamer.java b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/MemberInplaceRenamer.java index 755501166f77..08d4a9c9ffcb 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/MemberInplaceRenamer.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/MemberInplaceRenamer.java @@ -1,4 +1,4 @@ -// Copyright 2000-2024 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.refactoring.rename.inplace; import com.intellij.codeInsight.TargetElementUtil; @@ -228,9 +228,8 @@ public class MemberInplaceRenamer extends VariableInplaceRenamer { private void appendAdditionalElement(Collection> stringUsages, PsiNamedElement variable, PsiElement element) { - if (element != variable && element instanceof PsiNameIdentifierOwner && - !notSameFile(null, element.getContainingFile())) { - final PsiElement identifier = ((PsiNameIdentifierOwner)element).getNameIdentifier(); + if (element != variable && element instanceof PsiNameIdentifierOwner owner && !notSameFile(null, element.getContainingFile())) { + final PsiElement identifier = owner.getNameIdentifier(); if (identifier != null) { stringUsages.add(Pair.create(identifier, new TextRange(0, identifier.getTextLength()))); } @@ -242,7 +241,14 @@ public class MemberInplaceRenamer extends VariableInplaceRenamer { try { startDumbIfPossible(); tryRollback(); - final PsiNamedElement variable = getVariable(); + PsiNamedElement variable = getVariable(); + if (variable == null) { + PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument()); + if (psiFile != null) { + variable = PsiTreeUtil.findElementOfClassAtRange(psiFile, mySubstitutedRange.getStartOffset(), mySubstitutedRange.getEndOffset(), + PsiNameIdentifierOwner.class); + } + } if (variable != null && !newName.equals(myOldName)) { if (isIdentifier(newName, variable.getLanguage())) { final PsiElement substituted = getSubstituted(); @@ -250,6 +256,8 @@ public class MemberInplaceRenamer extends VariableInplaceRenamer { return; } + String type = UsageViewUtil.getType(variable); + String name = DescriptiveNameUtil.getDescriptiveName(variable); Runnable performRunnable = () -> { try (var ignored = SlowOperations.startSection(SlowOperations.ACTION_PERFORM)) { if (DumbService.isDumb(myProject)) { @@ -259,9 +267,8 @@ public class MemberInplaceRenamer extends VariableInplaceRenamer { return; } - final String commandName = RefactoringBundle.message("renaming.0.1.to.2", - UsageViewUtil.getType(variable), - DescriptiveNameUtil.getDescriptiveName(variable), newName); + + final String commandName = RefactoringBundle.message("renaming.0.1.to.2", type, name, newName); CommandProcessor.getInstance().executeCommand(myProject, () -> { performRenameInner(substituted, newName); PsiDocumentManager.getInstance(myProject).commitAllDocuments(); diff --git a/platform/lang-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteProcessor.java b/platform/lang-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteProcessor.java index 19560548cd4b..5c76dc0db39a 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteProcessor.java +++ b/platform/lang-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteProcessor.java @@ -456,7 +456,6 @@ public final class SafeDeleteProcessor extends BaseRefactoringProcessor { public static boolean validElement(@NotNull PsiElement element) { if (element instanceof PsiFile) return true; - if (!element.isPhysical()) return false; RefactoringSupportProvider provider = LanguageRefactoringSupport.getInstance().forContext(element); return provider != null && provider.isSafeDeleteAvailable(element); }