From 44d377a1cd9a770953d56cd894d8f66c7e154823 Mon Sep 17 00:00:00 2001 From: Ed Merks Date: Wed, 8 Jul 2026 07:53:23 +0200 Subject: [PATCH 01/16] Force qualifier changes for org.eclipse.jdt.core.manipulation - It shares split packages with org.eclipse.jdt.uu. https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/3927 --- org.eclipse.jdt.core.manipulation/forceQualifierUpdate.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jdt.core.manipulation/forceQualifierUpdate.txt b/org.eclipse.jdt.core.manipulation/forceQualifierUpdate.txt index 26b4cd9fe7d..8f589835e6e 100644 --- a/org.eclipse.jdt.core.manipulation/forceQualifierUpdate.txt +++ b/org.eclipse.jdt.core.manipulation/forceQualifierUpdate.txt @@ -1,4 +1,5 @@ https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1979 https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/2044 https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/2595 -https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/2995 \ No newline at end of file +https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/2995 +https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/3927 From 11c5a251b40428f84964dc0c3f002e2b3607be2e Mon Sep 17 00:00:00 2001 From: Suby S Surendran Date: Thu, 9 Jul 2026 11:47:38 +0530 Subject: [PATCH 02/16] NPE in SmartTypingConfigurationBlock when no Java editor is active The Typing preference page threw an NPE when opened with no active Java editor, since a null compilation unit was dereferenced without a check; this adds the missing null guard, consistent with the existing null-safe handling right below it. Fix: https://github.com/eclipse-jdt/eclipse.jdt.ui/issues/3068 --- .../internal/ui/preferences/SmartTypingConfigurationBlock.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/SmartTypingConfigurationBlock.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/SmartTypingConfigurationBlock.java index 845ee52e254..0a3cc2d5ebd 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/SmartTypingConfigurationBlock.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/SmartTypingConfigurationBlock.java @@ -232,7 +232,7 @@ private void addAutoclosingSection(Composite composite) { createDependency(master, slave); ICompilationUnit cu= getCompilationUnit(); - IJavaProject project= cu.getJavaProject(); + IJavaProject project= cu != null ? cu.getJavaProject() : null; String compliance= project != null ? project.getOption(JavaCore.COMPILER_COMPLIANCE, true) : JavaCore.getOption(JavaCore.COMPILER_COMPLIANCE); From c6fffdd4cc0ad4aa4cd2a61b2187d369f810c072 Mon Sep 17 00:00:00 2001 From: Simeon Andreev Date: Fri, 10 Jul 2026 10:12:19 +0300 Subject: [PATCH 03/16] Wait on quick diff initialize before editing CU in test Tracings of recent SaveParticipantTest fail show the quick diff initialize job, accessing GapTextStore in parallel to the tests. This change joins this initialize job, prior to editing CUs from the tests - to avoid potential race conditions. See: https://github.com/eclipse-jdt/eclipse.jdt.ui/issues/79 --- .../org/eclipse/jdt/ui/tests/quickfix/SaveParticipantTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/SaveParticipantTest.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/SaveParticipantTest.java index 6f4f6579642..89a26b811da 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/SaveParticipantTest.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/SaveParticipantTest.java @@ -35,6 +35,8 @@ import org.eclipse.jface.text.CopyOnWriteTextStore; +import org.eclipse.ui.internal.texteditor.quickdiff.DocumentLineDiffer; + import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; @@ -105,6 +107,7 @@ public void tearDown() throws Exception { private static void editCUInEditor(ICompilationUnit cu, String newContent) throws Exception { JavaEditor editor= (JavaEditor) EditorUtility.openInEditor(cu); Job.getJobManager().join(ReconcilerJobFamilies.FAMILY_RECONCILER, null); + Job.getJobManager().join(DocumentLineDiffer.QUICKDIFF_INITIALIZE_FAMILY, null); TestUtils.cancelDecorationJob(); cu.getBuffer().setContents(newContent); From fcb4f27e9b47f1677feaf2c7bae242dda8374374 Mon Sep 17 00:00:00 2001 From: Federico Jeanne Date: Tue, 30 Jun 2026 12:43:54 +0200 Subject: [PATCH 04/16] Only fold inner types when preference is set #3031 Fixes https://github.com/eclipse-jdt/eclipse.jdt.ui/issues/3031 --- .../jdt/text/tests/folding/FoldingTest.java | 43 ++++++++++++ .../text/tests/folding/FoldingTestUtils.java | 70 +++++++++++++++++-- .../DefaultJavaFoldingStructureProvider.java | 3 +- 3 files changed, 111 insertions(+), 5 deletions(-) diff --git a/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/folding/FoldingTest.java b/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/folding/FoldingTest.java index 243533cda9c..b8e42b4fe92 100644 --- a/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/folding/FoldingTest.java +++ b/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/folding/FoldingTest.java @@ -784,4 +784,47 @@ public static void main(String[] args) { FoldingTestUtils.assertContainsRegionUsingStartAndEndLine(regions, str, 9, 10); // if FoldingTestUtils.assertContainsRegionUsingStartAndEndLine(regions, str, 11, 13); // else } + + /** + * See GitHub Issue #3031 + */ + @Test + public void testFoldInnerTypesByDefault() throws Exception { + IPreferenceStore store= JavaPlugin.getDefault().getPreferenceStore(); + store.setValue(PreferenceConstants.EDITOR_FOLDING_INNERTYPES, true); + + String str= """ + class B { // Should not be folded + static class B1 { // Should be folded + + } + } + + public class A { // Should not be folded + static class A1 { // Should be folded + } + + static class A2 { // Should be folded + public static void main(String[] args) { + Runnable r = new Runnable() { // Should be folded + @Override + public void run() { + + } + }; + } + + } + } + """; + List regions= FoldingTestUtils.getProjectionRegionsOfPackage(packageFragment, str); + FoldingTestUtils.assertContainsExpandedRegionUsingStartAndEndLine(regions, str, 0, 4); // class B + FoldingTestUtils.assertContainsCollapsedRegionUsingStartAndEndLine(regions, str, 1, 3); // class B1 + FoldingTestUtils.assertContainsExpandedRegionUsingStartAndEndLine(regions, str, 6, 21); // class A + FoldingTestUtils.assertContainsCollapsedRegionUsingStartAndEndLine(regions, str, 7, 8); // class A1 + FoldingTestUtils.assertContainsCollapsedRegionUsingStartAndEndLine(regions, str, 10, 20); // class A2 + FoldingTestUtils.assertContainsCollapsedRegionUsingStartAndEndLine(regions, str, 12, 17); // Runnable + JavaPlugin.getDefault().getPreferenceStore().setToDefault(PreferenceConstants.EDITOR_FOLDING_INNERTYPES); + + } } diff --git a/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/folding/FoldingTestUtils.java b/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/folding/FoldingTestUtils.java index ebcb68b75f5..bd8159d4038 100644 --- a/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/folding/FoldingTestUtils.java +++ b/org.eclipse.jdt.text.tests/src/org/eclipse/jdt/text/tests/folding/FoldingTestUtils.java @@ -24,6 +24,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Position; @@ -41,17 +42,33 @@ public final class FoldingTestUtils { private record StartEnd(int start, int end) {} + public record ProjectionRegion(IRegion region, boolean collapsed) {} + private FoldingTestUtils() { } public static List getProjectionRangesOfPackage(IPackageFragment packageFragment, String code) throws Exception { ICompilationUnit cu= packageFragment.createCompilationUnit("A.java", code, true, null); JavaEditor editor= (JavaEditor) EditorUtility.openInEditor(cu); - ProjectionAnnotationModel model= editor.getAdapter(ProjectionAnnotationModel.class); + try { + ProjectionAnnotationModel model= editor.getAdapter(ProjectionAnnotationModel.class); - List regions= extractRegions(model); - editor.close(false); - return regions; + return extractRegions(model); + } finally { + editor.close(false); + } + } + + public static List getProjectionRegionsOfPackage(IPackageFragment packageFragment, String code) throws Exception { + ICompilationUnit cu= packageFragment.createCompilationUnit("A.java", code, true, null); + JavaEditor editor= (JavaEditor) EditorUtility.openInEditor(cu); + try { + ProjectionAnnotationModel model= editor.getAdapter(ProjectionAnnotationModel.class); + + return extractProjectionRegions(model); + } finally { + editor.close(false); + } } public static List extractRegions(ProjectionAnnotationModel model) { @@ -69,6 +86,22 @@ public static List extractRegions(ProjectionAnnotationModel model) { return regions; } + public static List extractProjectionRegions(ProjectionAnnotationModel model) { + List regions= new ArrayList<>(); + Iterator it= model.getAnnotationIterator(); + while (it.hasNext()) { + Annotation a= it.next(); + if (a instanceof ProjectionAnnotation projectionAnnotation) { + Position p= model.getPosition(a); + regions.add(new ProjectionRegion(new Region(p.getOffset(), p.getLength()), projectionAnnotation.isCollapsed())); + } + } + List projectionRanges= regions.stream().map(ProjectionRegion::region).collect(Collectors.toList()); + assertNoDuplicatedRegions(projectionRanges); + assertNoRegionsStartInTheSameOffset(projectionRanges); + return regions; + } + private static void assertNoDuplicatedRegions(List regions) { long distinctRegions= regions.stream() .map(r -> Map.entry(r.getOffset(), r.getLength())) // map to offset-length pairs @@ -113,6 +146,35 @@ public static void assertContainsRegionUsingStartAndEndLine(List projec assertContainsRegionWithOffsetAndLength(projectionRanges, startLine, endLine, startEnd.start(), startEnd.end()); } + public static void assertContainsCollapsedRegionUsingStartAndEndLine(List projectionRanges, String input, int startLine, int endLine) { + assertContainsRegionWithCollapsedState(projectionRanges, input, startLine, endLine, true); + } + + public static void assertContainsExpandedRegionUsingStartAndEndLine(List projectionRanges, String input, int startLine, int endLine) { + assertContainsRegionWithCollapsedState(projectionRanges, input, startLine, endLine, false); + } + + private static void assertContainsRegionWithCollapsedState(List projectionRanges, String input, int startLine, int endLine, boolean collapsed) { + StartEnd startEnd= getStartEnd(input, startLine, endLine); + int expectedRegionBegin= startEnd.start(); + int expectedRegionLength= startEnd.end() - startEnd.start() + 1; + + for (ProjectionRegion projectionRegion : projectionRanges) { + IRegion region= projectionRegion.region(); + if (region.getOffset() == expectedRegionBegin && region.getLength() == expectedRegionLength) { + assertEquals(collapsed, projectionRegion.collapsed(), + "incorrect collapsed state for region from line " + startLine + " to line " + endLine + " (offset: " + expectedRegionBegin + + ", length: " + expectedRegionLength + ")"); + return; + } + } + + fail( + "missing region from line " + startLine + " to line " + endLine + " (offset: " + expectedRegionBegin + + ", length: " + expectedRegionLength + ")" + + ", actual regions: " + sorted(projectionRanges.stream().map(ProjectionRegion::region).collect(Collectors.toList()))); + } + private static StartEnd getStartEnd(String input, int startLine, int endLine) { assertTrue(startLine <= endLine, "start line must be smaller or equal to end line"); int startLineBegin= findLineStartIndex(input, startLine); diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/text/folding/DefaultJavaFoldingStructureProvider.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/text/folding/DefaultJavaFoldingStructureProvider.java index cfba6621a60..c5990bee4ae 100755 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/text/folding/DefaultJavaFoldingStructureProvider.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/text/folding/DefaultJavaFoldingStructureProvider.java @@ -1256,7 +1256,8 @@ protected void computeFoldingStructure(IJavaElement element, FoldingStructureCom collapse= ctx.collapseImportContainer(); break; case IJavaElement.TYPE: - collapse= ctx.collapseInnerTypes(); + // only inner types may be collapsed + collapse= ctx.collapseInnerTypes() && isInnerType((IType) element); break; case IJavaElement.METHOD: case IJavaElement.FIELD: From 9e1646b562aa1c85239d989ca31b5c4319442d84 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Fri, 10 Jul 2026 17:57:52 -0400 Subject: [PATCH 05/16] Fix add missing method declaration when target is another CU (#3076) * Fix add missing method declaration when target is another CU - modify AddMissingMethodDeclarationFixCore to properly find the type of the ExpressionMethodReference instead of looking for an ancestor of the node - verify that the method binding doesn't already exist - use the target compilation unit rather than the compilation unit where the assist is started - modify QuickAssistProcessor.getAddMethodDeclaration() to create a special FixCorrectionProposal which specifies the target CU rather than the one in the assist context - add new test to AssistQuickFixTest1d8 - fixes #3061 * Fix modifier logic --- .../AddMissingMethodDeclarationFixCore.java | 118 ++++++++++++++++-- .../tests/quickfix/AssistQuickFixTest1d8.java | 60 +++++++-- .../text/correction/QuickAssistProcessor.java | 14 ++- 3 files changed, 173 insertions(+), 19 deletions(-) diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/AddMissingMethodDeclarationFixCore.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/AddMissingMethodDeclarationFixCore.java index e825dbd24fb..c700383c82e 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/AddMissingMethodDeclarationFixCore.java +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/AddMissingMethodDeclarationFixCore.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 IBM Corporation and others. + * Copyright (c) 2024, 2026 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -16,9 +16,14 @@ import java.util.List; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTMatcher; import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.CompilationUnit; @@ -33,7 +38,6 @@ import org.eclipse.jdt.core.dom.ParameterizedType; import org.eclipse.jdt.core.dom.PrimitiveType; import org.eclipse.jdt.core.dom.ReturnStatement; -import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; @@ -42,9 +46,12 @@ import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; import org.eclipse.jdt.core.dom.rewrite.ListRewrite; +import org.eclipse.jdt.core.refactoring.CompilationUnitChange; import org.eclipse.jdt.internal.corext.dom.ASTNodes; +import org.eclipse.jdt.internal.corext.dom.AbortSearchException; import org.eclipse.jdt.internal.corext.dom.Bindings; +import org.eclipse.jdt.internal.corext.refactoring.Checks; import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; import org.eclipse.jdt.internal.ui.text.correction.CorrectionMessages; @@ -52,8 +59,32 @@ public class AddMissingMethodDeclarationFixCore extends CompilationUnitRewriteOperationsFixCore { + private final CompilationUnit fCompilationUnit; + public AddMissingMethodDeclarationFixCore(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation operation) { super(name, compilationUnit, operation); + fCompilationUnit= compilationUnit; + } + + /** + * Returns the compilation unit being modified by this fix. + * This may be different from the compilation unit containing the quick assist invocation. + * + * @return the target compilation unit + */ + public ICompilationUnit getCompilationUnit() { + return (ICompilationUnit) fCompilationUnit.getJavaElement(); + } + + @Override + public CompilationUnitChange createChange(IProgressMonitor progressMonitor) throws CoreException { + CompilationUnitChange change= super.createChange(progressMonitor); + if (change != null) { + // When modifying a file that may not be open in an editor, we need to ensure + // the change is saved. The default LEAVE_DIRTY mode doesn't work for unopened files. + change.setSaveMode(org.eclipse.ltk.core.refactoring.TextFileChange.FORCE_SAVE); + } + return change; } public static AddMissingMethodDeclarationFixCore createAddMissingMethodDeclaration(CompilationUnit compilationUnit, ASTNode node) { @@ -64,7 +95,37 @@ public static AddMissingMethodDeclarationFixCore createAddMissingMethodDeclarati return null; } - TypeDeclaration typeDeclaration= ASTNodes.getParent(methodReferenceNode, TypeDeclaration.class); + IMethodBinding refBinding= methodReferenceNode.resolveMethodBinding(); + if (refBinding != null) { + return null; + } + + Expression exp= methodReferenceNode.getExpression(); + ITypeBinding expBinding= exp.resolveTypeBinding(); + if (expBinding == null) { + return null; + } + + IJavaElement element= expBinding.getJavaElement(); + CompilationUnit cu= null; + if (element == null) { + return null; + } + element= element.getAncestor(IJavaElement.COMPILATION_UNIT); + if (element instanceof ICompilationUnit icu) { + cu= Checks.convertICUtoCU(icu); + } + if (cu == null) { + return null; + } + + TypeDeclaration typeDeclaration= null; + TypeDeclarationFinder finder= new TypeDeclarationFinder(expBinding); + try { + cu.accept(finder); + } catch (AbortSearchException e) { + typeDeclaration= finder.getTypeDeclaration(); + } if (typeDeclaration == null) { return null; } @@ -85,7 +146,7 @@ public static AddMissingMethodDeclarationFixCore createAddMissingMethodDeclarati Assignment variableAssignment= ASTNodes.getParent(methodReferenceNode, Assignment.class); String label= Messages.format(CorrectionMessages.AddUnimplementedMethodReferenceOperation_AddMissingMethod_group, - new String[] { methodReferenceNode.getName().getIdentifier(), typeDeclaration.getName().getIdentifier() }); + new String[] { methodReferenceNode.getName().getIdentifier(), expBinding.getName() }); if ((variableAssignment != null || variableDeclarationStatement != null) && methodInvocationNode == null) { /* @@ -109,7 +170,9 @@ public static AddMissingMethodDeclarationFixCore createAddMissingMethodDeclarati if (returnType.binding == null) { return null; } - return new AddMissingMethodDeclarationFixCore(label, compilationUnit, new AddMissingMethodDeclarationProposalOperation(methodReferenceNode, returnType, null)); + // Note: passing cu (the target compilation unit) instead of compilationUnit (the current one) + // This fix modifies the target type's compilation unit, which may be different from the current file + return new AddMissingMethodDeclarationFixCore(label, cu, new AddMissingMethodDeclarationProposalOperation(methodReferenceNode, typeDeclaration, returnType, null)); } else { if (methodInvocationNode == null) { return null; @@ -132,7 +195,9 @@ public static AddMissingMethodDeclarationFixCore createAddMissingMethodDeclarati // node not found return null; } - return new AddMissingMethodDeclarationFixCore(label, compilationUnit, new AddMissingMethodDeclarationProposalOperation(methodReferenceNode, null, methodBinding)); + // Note: passing cu (the target compilation unit) instead of compilationUnit (the current one) + // This fix modifies the target type's compilation unit, which may be different from the current file + return new AddMissingMethodDeclarationFixCore(label, cu, new AddMissingMethodDeclarationProposalOperation(methodReferenceNode, typeDeclaration, null, methodBinding)); } } @@ -160,6 +225,28 @@ private static class ReturnType { public ITypeBinding binding; } + private static class TypeDeclarationFinder extends ASTVisitor { + private TypeDeclaration foundDeclaration; + private final ITypeBinding fTypeBinding; + + public TypeDeclarationFinder(ITypeBinding typeBinding) { + this.fTypeBinding= typeBinding; + } + + public TypeDeclaration getTypeDeclaration() { + return foundDeclaration; + } + + @Override + public boolean visit(TypeDeclaration node) { + if (this.fTypeBinding.isEqualTo(node.resolveBinding())) { + foundDeclaration= node; + throw new AbortSearchException(); + } + return true; + } + } + private static class AddMissingMethodDeclarationProposalOperation extends CompilationUnitRewriteOperation { @@ -167,9 +254,12 @@ private static class AddMissingMethodDeclarationProposalOperation extends Compil private ReturnType returnType; + private TypeDeclaration typeDeclaration; + private IMethodBinding methodBinding; - public AddMissingMethodDeclarationProposalOperation(ExpressionMethodReference methodReferenceNode, ReturnType returnType, IMethodBinding methodBinding) { + + public AddMissingMethodDeclarationProposalOperation(ExpressionMethodReference methodReferenceNode, TypeDeclaration typeDeclaration, ReturnType returnType, IMethodBinding methodBinding) { if (returnType == null && methodBinding == null) { throw new IllegalArgumentException("both returnType and methodBinding cannot be null."); //$NON-NLS-1$ } @@ -177,12 +267,13 @@ public AddMissingMethodDeclarationProposalOperation(ExpressionMethodReference me this.methodReferenceNode= methodReferenceNode; this.returnType= returnType; this.methodBinding= methodBinding; + this.typeDeclaration= typeDeclaration; } + @Override public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore linkedModel) throws CoreException { boolean addStaticModifier= false; - TypeDeclaration typeDeclaration= ASTNodes.getParent(this.methodReferenceNode, TypeDeclaration.class); if (QuickAssistProcessorUtil.isTypeReferenceToInstanceMethod(methodReferenceNode)) { addStaticModifier= true; @@ -201,7 +292,7 @@ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore returnType.type= importRewrite.addImport(returnType.binding, ast); MethodDeclaration newMethodDeclaration= ast.newMethodDeclaration(); - newMethodDeclaration.setName((SimpleName) rewrite.createCopyTarget(methodReferenceNode.getName())); + newMethodDeclaration.setName(ast.newSimpleName(methodReferenceNode.getName().getFullyQualifiedName())); newMethodDeclaration.modifiers().add(ast.newModifier(ModifierKeyword.PRIVATE_KEYWORD)); if (addStaticModifier) { newMethodDeclaration.modifiers().add(ast.newModifier(ModifierKeyword.STATIC_KEYWORD)); @@ -241,7 +332,12 @@ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore ITypeBinding[] parameterTypesFunctionalInterface= parameterTypes[index].getFunctionalInterfaceMethod().getParameterTypes(); ITypeBinding returnTypeBindingFunctionalInterface= parameterTypes[index].getFunctionalInterfaceMethod().getReturnType(); MethodDeclaration newMethodDeclaration= ast.newMethodDeclaration(); - newMethodDeclaration.modifiers().add(ast.newModifier(ModifierKeyword.PRIVATE_KEYWORD)); + CompilationUnit root= (CompilationUnit) methodReferenceNode.getRoot(); + if (root.subtreeMatch(new ASTMatcher(), cuRewrite.getRoot())) { + newMethodDeclaration.modifiers().add(ast.newModifier(ModifierKeyword.PRIVATE_KEYWORD)); + } else { + newMethodDeclaration.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD)); + } if (addStaticModifier) { newMethodDeclaration.modifiers().add(ast.newModifier(ModifierKeyword.STATIC_KEYWORD)); } @@ -263,7 +359,7 @@ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore addIfMissing(newMethodDeclaration, newTypeParameter); } } - newMethodDeclaration.setName((SimpleName) rewrite.createCopyTarget(methodReferenceNode.getName())); + newMethodDeclaration.setName(ast.newSimpleName(methodReferenceNode.getName().getFullyQualifiedName())); newMethodDeclaration.setReturnType2(newReturnType); pLoop: for (int i= 0; i < parameterTypesFunctionalInterface.length; i++) { ITypeBinding parameterType2= parameterTypesFunctionalInterface[i]; diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java index f9da4508bca..228fe201564 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013, 2025 IBM Corporation and others. + * Copyright (c) 2013, 2026 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -3945,7 +3945,7 @@ static String method4() { AssistContext context= getCorrectionContext(cu, offset, 0); assertNoErrors(context); List proposals= collectAssists(context, false); - assertNumberOfProposals(proposals, 4); + assertNumberOfProposals(proposals, 3); assertCorrectLabels(proposals); String expected= """ @@ -3998,7 +3998,7 @@ static String method4() { context= getCorrectionContext(cu, offset, 0); assertNoErrors(context); proposals= collectAssists(context, false); - assertNumberOfProposals(proposals, 4); + assertNumberOfProposals(proposals, 3); assertCorrectLabels(proposals); expected= """ @@ -4051,7 +4051,7 @@ static String method4() { context= getCorrectionContext(cu, offset, 0); assertNoErrors(context); proposals= collectAssists(context, false); - assertNumberOfProposals(proposals, 5); + assertNumberOfProposals(proposals, 4); assertCorrectLabels(proposals); expected= """ @@ -4104,7 +4104,7 @@ static String method4() { context= getCorrectionContext(cu, offset, 0); assertNoErrors(context); proposals= collectAssists(context, false); - assertNumberOfProposals(proposals, 5); + assertNumberOfProposals(proposals, 4); assertCorrectLabels(proposals); expected= """ @@ -4157,7 +4157,7 @@ static String method4() { context= getCorrectionContext(cu, offset, 0); assertNoErrors(context); proposals= collectAssists(context, false); - assertNumberOfProposals(proposals, 5); + assertNumberOfProposals(proposals, 4); assertCorrectLabels(proposals); expected= """ @@ -4210,7 +4210,7 @@ static String method4() { context= getCorrectionContext(cu, offset, 0); assertNoErrors(context); proposals= collectAssists(context, false); - assertNumberOfProposals(proposals, 5); + assertNumberOfProposals(proposals, 4); assertCorrectLabels(proposals); expected= """ @@ -7884,5 +7884,51 @@ public testExample() { assertNumberOfProposals(proposals, 0); } + @Test + public void testIssue3061() throws Exception { + // We have a superclass that contains another TestClass as package private. No proposal expected. + IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); + String src1= """ + package test.test1; + public class Second { + + public void consume1(String s) { + } + } + """; + pack1.createCompilationUnit("Second.java", src1, false, null); + + String src= """ + package test.test1; + import java.util.function.Consumer; + + public class First { + void f(Consumer s) { + } + + void g() { + Second second = new Second(); + f(second::consume1); + f(second::consume2); + } + } + """; + ICompilationUnit cu1= pack1.createCompilationUnit("First.java", src, false, null); + int offset= src.indexOf("second::consume2"); + AssistContext context= getCorrectionContext(cu1, offset, 0); + List proposals= collectAssists(context, false); + String expected= """ + package test.test1; + public class Second { + + public void consume1(String s) { + } + + public void consume2(String string1) { + } + } + """; + assertExpectedExistInProposals(proposals, new String[] { expected }); + } } diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java index 77e4d9a2f32..5139304bc93 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java @@ -227,6 +227,7 @@ import org.eclipse.jdt.internal.ui.text.correction.proposals.AssignToVariableAssistProposal; import org.eclipse.jdt.internal.ui.text.correction.proposals.ConvertFieldNamingConventionProposal; import org.eclipse.jdt.internal.ui.text.correction.proposals.FixCorrectionProposal; +import org.eclipse.jdt.internal.ui.text.correction.proposals.FixCorrectionProposalCore; import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedCorrectionProposal; import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedNamesAssistProposal; import org.eclipse.jdt.internal.ui.text.correction.proposals.NewDefiningMethodProposal; @@ -1247,7 +1248,18 @@ private static boolean getAddMethodDeclaration(IInvocationContext context, ASTNo } Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE); - FixCorrectionProposal proposal= new FixCorrectionProposal(fix, null, IProposalRelevance.ADD_INFERRED_LAMBDA_PARAMETER_TYPES, image, context); + // Create a proposal that properly handles the change to the target compilation unit + // The fix may modify a different file than the current one, so we need special handling + ICompilationUnit targetCU= fix.getCompilationUnit(); + FixCorrectionProposal proposal= new FixCorrectionProposal(fix, null, IProposalRelevance.ADD_INFERRED_LAMBDA_PARAMETER_TYPES, image, context, + new FixCorrectionProposalCore(fix, null, IProposalRelevance.ADD_INFERRED_LAMBDA_PARAMETER_TYPES, context) { + @Override + public ICompilationUnit getCompilationUnit() { + // Return the target compilation unit, not the current one from the context + return targetCU; + } + }) { + }; resultingCollections.add(proposal); return true; } From 1d2e5a43021faa85535524961381e82aa586cd49 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Fri, 10 Jul 2026 21:55:31 -0400 Subject: [PATCH 06/16] Fix inline of method with instantiation of local class (#3077) - modify SourceAnalyzer.visit(ClassInstanceCreation) to only mark a member class as a receiver - add new tests to InlineMethodTests - fixes #3070 --- .../refactoring/code/SourceAnalyzer.java | 4 ++-- .../TestCases/bugs_in/Test_issue_3070_1.java | 19 +++++++++++++++++++ .../TestCases/bugs_in/Test_issue_3070_2.java | 18 ++++++++++++++++++ .../TestCases/bugs_out/Test_issue_3070_1.java | 15 +++++++++++++++ .../TestCases/bugs_out/Test_issue_3070_2.java | 14 ++++++++++++++ .../tests/refactoring/InlineMethodTests.java | 10 ++++++++++ 6 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 org.eclipse.jdt.ui.tests.refactoring/resources/InlineMethodWorkspace/TestCases/bugs_in/Test_issue_3070_1.java create mode 100644 org.eclipse.jdt.ui.tests.refactoring/resources/InlineMethodWorkspace/TestCases/bugs_in/Test_issue_3070_2.java create mode 100644 org.eclipse.jdt.ui.tests.refactoring/resources/InlineMethodWorkspace/TestCases/bugs_out/Test_issue_3070_1.java create mode 100644 org.eclipse.jdt.ui.tests.refactoring/resources/InlineMethodWorkspace/TestCases/bugs_out/Test_issue_3070_2.java diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/SourceAnalyzer.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/SourceAnalyzer.java index 9bb9352645d..9c6270bec40 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/SourceAnalyzer.java +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/SourceAnalyzer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2024 IBM Corporation and others. + * Copyright (c) 2000, 2026 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -268,7 +268,7 @@ public boolean visit(ClassInstanceCreation node) { if (fTypeCounter == 0) { Expression receiver= node.getExpression(); if (receiver == null) { - if (node.resolveTypeBinding().isLocal() && !node.resolveTypeBinding().isAnonymous()) + if (node.resolveTypeBinding().isMember()) fImplicitReceivers.add(node); } } diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/InlineMethodWorkspace/TestCases/bugs_in/Test_issue_3070_1.java b/org.eclipse.jdt.ui.tests.refactoring/resources/InlineMethodWorkspace/TestCases/bugs_in/Test_issue_3070_1.java new file mode 100644 index 00000000000..c3d691260eb --- /dev/null +++ b/org.eclipse.jdt.ui.tests.refactoring/resources/InlineMethodWorkspace/TestCases/bugs_in/Test_issue_3070_1.java @@ -0,0 +1,19 @@ +package bugs_in; + +public class Test_issue_3070_1 { + public static void main(String[] args) { + Test_issue_3070_1 obj = new Test_issue_3070_1(); + int result = obj.f(); + System.out.println(result); + } + + class H { + int g() { + return 7; + } + } + + int /*]*/f()/*[*/ { + return new H().g(); + } +} diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/InlineMethodWorkspace/TestCases/bugs_in/Test_issue_3070_2.java b/org.eclipse.jdt.ui.tests.refactoring/resources/InlineMethodWorkspace/TestCases/bugs_in/Test_issue_3070_2.java new file mode 100644 index 00000000000..bde01ab80c9 --- /dev/null +++ b/org.eclipse.jdt.ui.tests.refactoring/resources/InlineMethodWorkspace/TestCases/bugs_in/Test_issue_3070_2.java @@ -0,0 +1,18 @@ +package bugs_in; + +public class Test_issue_3070_2 { + public static void main(String[] args) { + Test_issue_3070_2 obj = new Test_issue_3070_2(); + int result = obj.f(); + System.out.println(result); + } + + int /*]*/f()/*[*/ { + class H { + int g() { + return 7; + } + } + return new H().g(); + } +} diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/InlineMethodWorkspace/TestCases/bugs_out/Test_issue_3070_1.java b/org.eclipse.jdt.ui.tests.refactoring/resources/InlineMethodWorkspace/TestCases/bugs_out/Test_issue_3070_1.java new file mode 100644 index 00000000000..6c4f2655852 --- /dev/null +++ b/org.eclipse.jdt.ui.tests.refactoring/resources/InlineMethodWorkspace/TestCases/bugs_out/Test_issue_3070_1.java @@ -0,0 +1,15 @@ +package bugs_in; + +public class Test_issue_3070_1 { + public static void main(String[] args) { + Test_issue_3070_1 obj = new Test_issue_3070_1(); + int result = obj.new H().g(); + System.out.println(result); + } + + class H { + int g() { + return 7; + } + } +} diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/InlineMethodWorkspace/TestCases/bugs_out/Test_issue_3070_2.java b/org.eclipse.jdt.ui.tests.refactoring/resources/InlineMethodWorkspace/TestCases/bugs_out/Test_issue_3070_2.java new file mode 100644 index 00000000000..87d4972d3e1 --- /dev/null +++ b/org.eclipse.jdt.ui.tests.refactoring/resources/InlineMethodWorkspace/TestCases/bugs_out/Test_issue_3070_2.java @@ -0,0 +1,14 @@ +package bugs_in; + +public class Test_issue_3070_2 { + public static void main(String[] args) { + Test_issue_3070_2 obj = new Test_issue_3070_2(); + class H { + int g() { + return 7; + } + } + int result = new H().g(); + System.out.println(result); + } +} diff --git a/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/InlineMethodTests.java b/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/InlineMethodTests.java index 29f0674218c..b49ee4655cb 100644 --- a/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/InlineMethodTests.java +++ b/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/InlineMethodTests.java @@ -572,6 +572,16 @@ public void test_issue_2376() throws Exception { performBugTest(); } + @Test + public void test_issue_3070_1() throws Exception { + performBugTest(); + } + + @Test + public void test_issue_3070_2() throws Exception { + performBugTest(); + } + /* *********************** Argument Tests ******************************* */ private void performArgumentTest() throws Exception { From b74fb23a45b62025974a6fa154132e81b8335c2d Mon Sep 17 00:00:00 2001 From: wenyt <75360946+wenytang-ms@users.noreply.github.com> Date: Tue, 14 Jul 2026 02:34:30 +0800 Subject: [PATCH 07/16] Expose JUnit VM runner configuration to subclasses (#3079) * Expose JUnit VM runner configuration to subclasses Allow launch delegate subclasses to reuse the fully resolved VMRunnerConfiguration without reflection or duplicating the launch orchestration. Copilot-Session: c3cd113b-dad4-4d4d-90c2-60abfd204405 Signed-off-by: wenytang-ms * Bump JUnit unit test feature version Update the containing feature after the org.eclipse.jdt.ui.unittest.junit bundle minor version increase. Copilot-Session: c3cd113b-dad4-4d4d-90c2-60abfd204405 Signed-off-by: wenytang-ms --------- Signed-off-by: wenytang-ms --- org.eclipse.jdt.junit.core/META-INF/MANIFEST.MF | 2 +- .../JUnitLaunchConfigurationDelegate.java | 15 ++++++++++++++- .../feature.xml | 2 +- .../META-INF/MANIFEST.MF | 2 +- org.eclipse.jdt.ui.unittest.junit/pom.xml | 2 +- .../JUnitLaunchConfigurationDelegate.java | 15 ++++++++++++++- 6 files changed, 32 insertions(+), 6 deletions(-) diff --git a/org.eclipse.jdt.junit.core/META-INF/MANIFEST.MF b/org.eclipse.jdt.junit.core/META-INF/MANIFEST.MF index 4ed4206e43e..57b2707c0fc 100644 --- a/org.eclipse.jdt.junit.core/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.junit.core/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Automatic-Module-Name: org.eclipse.jdt.junit.core Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.jdt.junit.core;singleton:=true -Bundle-Version: 3.14.300.qualifier +Bundle-Version: 3.15.0.qualifier Bundle-Activator: org.eclipse.jdt.internal.junit.JUnitCorePlugin Bundle-ActivationPolicy: lazy Bundle-Vendor: %providerName diff --git a/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/junit/launcher/JUnitLaunchConfigurationDelegate.java b/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/junit/launcher/JUnitLaunchConfigurationDelegate.java index db6b67141e5..30ae5538b9c 100644 --- a/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/junit/launcher/JUnitLaunchConfigurationDelegate.java +++ b/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/junit/launcher/JUnitLaunchConfigurationDelegate.java @@ -124,7 +124,20 @@ public String showCommandLine(ILaunchConfiguration configuration, String mode, I } } - private VMRunnerConfiguration getVMRunnerConfiguration(ILaunchConfiguration configuration, ILaunch launch, String mode, IProgressMonitor monitor) throws CoreException { + /** + * Creates the VM runner configuration for the given JUnit launch configuration. + * Subclasses can use this method to obtain the fully resolved launch configuration + * while customizing the existing protected launch hooks. + * + * @param configuration the launch configuration + * @param launch the launch + * @param mode the launch mode + * @param monitor the progress monitor + * @return the VM runner configuration, or {@code null} if the operation was canceled + * @throws CoreException if the launch configuration cannot be resolved + * @since 3.15 + */ + protected final VMRunnerConfiguration getVMRunnerConfiguration(ILaunchConfiguration configuration, ILaunch launch, String mode, IProgressMonitor monitor) throws CoreException { SubMonitor subMon= SubMonitor.convert(monitor, JUnitMessages.JUnitLaunchConfigurationDelegate_verifying_attriburtes_description, 4); // check for cancellation if (subMon.isCanceled()) { diff --git a/org.eclipse.jdt.ui.unittest.junit.feature/feature.xml b/org.eclipse.jdt.ui.unittest.junit.feature/feature.xml index 5802fe03bf8..23af0625e5f 100644 --- a/org.eclipse.jdt.ui.unittest.junit.feature/feature.xml +++ b/org.eclipse.jdt.ui.unittest.junit.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/org.eclipse.jdt.ui.unittest.junit/META-INF/MANIFEST.MF b/org.eclipse.jdt.ui.unittest.junit/META-INF/MANIFEST.MF index 6d109083bae..0be42ed242f 100644 --- a/org.eclipse.jdt.ui.unittest.junit/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.ui.unittest.junit/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Automatic-Module-Name: org.eclipse.jdt.ui.unittest.junit Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.jdt.ui.unittest.junit;singleton:=true -Bundle-Version: 1.3.100.qualifier +Bundle-Version: 1.4.0.qualifier Bundle-Activator: org.eclipse.jdt.ui.unittest.junit.JUnitTestPlugin Bundle-ActivationPolicy: lazy Bundle-Vendor: %providerName diff --git a/org.eclipse.jdt.ui.unittest.junit/pom.xml b/org.eclipse.jdt.ui.unittest.junit/pom.xml index abe760784eb..c113e8c5ebf 100644 --- a/org.eclipse.jdt.ui.unittest.junit/pom.xml +++ b/org.eclipse.jdt.ui.unittest.junit/pom.xml @@ -18,6 +18,6 @@ org.eclipse.jdt org.eclipse.jdt.ui.unittest.junit - 1.3.100-SNAPSHOT + 1.4.0-SNAPSHOT eclipse-plugin diff --git a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchConfigurationDelegate.java b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchConfigurationDelegate.java index 31d88ef019c..3a4769d82a8 100644 --- a/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchConfigurationDelegate.java +++ b/org.eclipse.jdt.ui.unittest.junit/src/org/eclipse/jdt/ui/unittest/junit/launcher/JUnitLaunchConfigurationDelegate.java @@ -134,7 +134,20 @@ public String showCommandLine(ILaunchConfiguration configuration, String mode, I } } - private VMRunnerConfiguration getVMRunnerConfiguration(ILaunchConfiguration configuration, ILaunch launch, + /** + * Creates the VM runner configuration for the given JUnit launch configuration. + * Subclasses can use this method to obtain the fully resolved launch configuration + * while customizing the existing protected launch hooks. + * + * @param configuration the launch configuration + * @param launch the launch + * @param mode the launch mode + * @param monitor the progress monitor + * @return the VM runner configuration, or {@code null} if the operation was canceled + * @throws CoreException if the launch configuration cannot be resolved + * @since 1.4 + */ + protected final VMRunnerConfiguration getVMRunnerConfiguration(ILaunchConfiguration configuration, ILaunch launch, String mode, IProgressMonitor monitor) throws CoreException { VMRunnerConfiguration runConfig = null; monitor.beginTask(MessageFormat.format("{0}...", configuration.getName()), 5); //$NON-NLS-1$ From cbb6d57a7b9f78891ed41403904c9ec2f2b5b0d3 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Tue, 14 Jul 2026 13:44:46 -0400 Subject: [PATCH 08/16] Modify MoveInstanceMethodProcessor to check for possible null comparison (#3082) - add new MoveInstanceMethodProcessor.checkTargetNullCheck() to check if the method being moved compares the field to use against null which will be replaced by a comparison of "this" to null - add new test to MoveInstanceMethodTests - fixes #3035 --- .../refactoring/RefactoringCoreMessages.java | 5 +- .../corext/refactoring/refactoring.properties | 1 + .../MoveInstanceMethodProcessor.java | 45 +++++++++- .../cannotMove/testFail21/in/A.java | 10 +++ .../cannotMove/testFail21/in/B.java | 5 ++ .../refactoring/MoveInstanceMethodTests.java | 84 ++++++++++--------- 6 files changed, 108 insertions(+), 42 deletions(-) create mode 100644 org.eclipse.jdt.ui.tests.refactoring/resources/MoveInstanceMethod/cannotMove/testFail21/in/A.java create mode 100644 org.eclipse.jdt.ui.tests.refactoring/resources/MoveInstanceMethod/cannotMove/testFail21/in/B.java diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/RefactoringCoreMessages.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/RefactoringCoreMessages.java index 6edf3626b6a..18745ee67f6 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/RefactoringCoreMessages.java +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/RefactoringCoreMessages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2025 IBM Corporation and others. + * Copyright (c) 2000, 2026 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -1405,6 +1405,8 @@ public final class RefactoringCoreMessages extends NLS { public static String MoveInstanceMethodProcessor_target_name_already_used; + public static String MoveInstanceMethodProcessor_target_null_comparison; + public static String MoveInstanceMethodProcessor_this_reference; public static String MoveInstanceMethodProcessor_uses_super; @@ -2550,6 +2552,7 @@ public final class RefactoringCoreMessages extends NLS { public static String ConvertToRecordRefactoring_member_types_not_supported; + static { NLS.initializeMessages(BUNDLE_NAME, RefactoringCoreMessages.class); } diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/refactoring.properties b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/refactoring.properties index 899ee0f3006..9b143b3c671 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/refactoring.properties +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/refactoring.properties @@ -934,6 +934,7 @@ MoveInstanceMethodProcessor_this_reference=A reference to 'this' has been found MoveInstanceMethodProcessor_no_resolved_target=The target of the method could not be resolved. MoveInstanceMethodProcessor_no_null_argument=The method invocation ''{0}'' cannot be updated, since it uses null as argument. MoveInstanceMethodProcessor_target_name_already_used=The name of the target conflicts with the method parameter ''{0}''. +MoveInstanceMethodProcessor_target_null_comparison=The move will cause ''{0}'' being replaced by ''this'' and compared to null which is invalid logic and may imply an NPE can occur. MoveInstanceMethodProcessor_target_element_pattern=Target element: ''{0}'' MoveInstanceMethodProcessor_present_type_parameter_warning=The type parameter ''{0}'' is already present in the target type ''{1}'' and will be removed from the method. MoveInstanceMethodProcessor_remove_original_method=Remove method declaration diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/structure/MoveInstanceMethodProcessor.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/structure/MoveInstanceMethodProcessor.java index 7a0c602fcad..2d064115457 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/structure/MoveInstanceMethodProcessor.java +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/structure/MoveInstanceMethodProcessor.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2025 IBM Corporation and others. + * Copyright (c) 2000, 2026 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -97,6 +97,8 @@ import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IVariableBinding; +import org.eclipse.jdt.core.dom.InfixExpression; +import org.eclipse.jdt.core.dom.InfixExpression.Operator; import org.eclipse.jdt.core.dom.Javadoc; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; @@ -1713,7 +1715,7 @@ public RefactoringStatus checkFinalConditions(final IProgressMonitor monitor, fi final RefactoringStatus status= new RefactoringStatus(); fChangeManager= new TextChangeManager(); try { - monitor.beginTask("", 6); //$NON-NLS-1$ + monitor.beginTask("", 7); //$NON-NLS-1$ monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_checking); status.merge(Checks.checkIfCuBroken(fMethod)); if (!status.hasError()) { @@ -1732,6 +1734,7 @@ public RefactoringStatus checkFinalConditions(final IProgressMonitor monitor, fi checkConflictingTarget(Progress.subMonitor(monitor, 1), status); checkConflictingMethod(Progress.subMonitor(monitor, 1), status); checkOverrideOuterMethod(Progress.subMonitor(monitor, 1), status); + checkTargetNullCheck(Progress.subMonitor(monitor, 1), status); checkFinalMethod(status); Checks.addModifiedFilesToChecker(computeModifiedFiles(fMethod.getCompilationUnit(), type.getCompilationUnit()), context); @@ -1751,6 +1754,44 @@ public RefactoringStatus checkFinalConditions(final IProgressMonitor monitor, fi return status; } + private void checkTargetNullCheck(IProgressMonitor monitor, RefactoringStatus status) throws JavaModelException { + Assert.isNotNull(monitor); + Assert.isNotNull(status); + try { + monitor.beginTask("", 1); //$NON-NLS-1$ + monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_checking); + ASTVisitor visitor= new ASTVisitor() { + @Override + public boolean visit(InfixExpression node) { + if (node.getOperator() == Operator.EQUALS || node.getOperator() == Operator.NOT_EQUALS) { + Expression rightOperand= node.getRightOperand(); + if (rightOperand instanceof NullLiteral) { + Expression leftOperand= node.getLeftOperand(); + if (leftOperand instanceof Name name) { + IBinding nameBinding= name.resolveBinding(); + if (nameBinding.isEqualTo(fTarget)) { + throw new AbortSearchException(); + } + } + } + } + return true; + } + }; + try { + final MethodDeclaration declaration= ASTNodeSearchUtil.getMethodDeclarationNode(fMethod, fSourceRewrite.getRoot()); + if (declaration != null) { + declaration.accept(visitor); + } + } catch (AbortSearchException e) { + status.merge(RefactoringStatus.createErrorStatus( + Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_target_null_comparison, new String[] {fTarget.getName()}), JavaStatusContext.create(fMethod))); + } + } finally { + monitor.done(); + } + } + /** * Checks whether the target is a type variable or a generic type. * diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/MoveInstanceMethod/cannotMove/testFail21/in/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/MoveInstanceMethod/cannotMove/testFail21/in/A.java new file mode 100644 index 00000000000..f185d91c015 --- /dev/null +++ b/org.eclipse.jdt.ui.tests.refactoring/resources/MoveInstanceMethod/cannotMove/testFail21/in/A.java @@ -0,0 +1,10 @@ +package p1; + +class A { + B b; + + int m() { + if (b != null) return 2; + return 0; + } +} diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/MoveInstanceMethod/cannotMove/testFail21/in/B.java b/org.eclipse.jdt.ui.tests.refactoring/resources/MoveInstanceMethod/cannotMove/testFail21/in/B.java new file mode 100644 index 00000000000..df33307636d --- /dev/null +++ b/org.eclipse.jdt.ui.tests.refactoring/resources/MoveInstanceMethod/cannotMove/testFail21/in/B.java @@ -0,0 +1,5 @@ +package p1; + +public class B { + +} \ No newline at end of file diff --git a/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/MoveInstanceMethodTests.java b/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/MoveInstanceMethodTests.java index 62b577c4068..063f3ab0f34 100644 --- a/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/MoveInstanceMethodTests.java +++ b/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/MoveInstanceMethodTests.java @@ -789,6 +789,48 @@ public void testFail1() throws Exception { failHelper1("p1.A", 5, 26, 5, 29, PARAMETER, "b", true, true); } + // Cannot move static method + @Test + public void testFail2() throws Exception { + failHelper1(new String[] { "p1.A", "p2.B"}, "p1.A", 6, 23, 6, 24, PARAMETER, "b", true, true); + } + + // Cannot move native method + @Test + public void testFail3() throws Exception { + failHelper1(new String[] { "p1.A", "p2.B"}, "p1.A", 6, 23, 6, 24, PARAMETER, "b", true, true); + } + + // Cannot move method that references "super" + @Test + public void testFail4() throws Exception { + failHelper1(new String[] { "p1.A", "p2.B"}, "p1.A", 11, 20, 11, 21, PARAMETER, "b", true, true); + } + + // Cannot move method that references an enclosing instance + @Test + public void testFail5() throws Exception { + failHelper1(new String[] { "p1.A", "p2.B"}, "p1.A", 8, 21, 8, 21, PARAMETER, "b", true, true); + } + + // Cannot move potentially directly recursive method + @Test + public void testFail6() throws Exception { + failHelper1(new String[] { "p1.A", "p2.B"}, "p1.A", 6, 16, 6, 17, PARAMETER, "b", true, true); + } + + // Cannot move synchronized method + @Test + public void testFail8() throws Exception { + failHelper1(new String[] { "p1.A", "p2.B"}, "p1.A", 6, 29, 6, 29, PARAMETER, "b", true, true); + } + + // Cannot move method if there's no new potential receiver + @Test + public void testFail9() throws Exception { + failHelper1(new String[] { "p1.A", "p2.B", "p3.C"}, "p1.A", 7, 17, 7, 20, PARAMETER, "b", true, true); + } + // Cannot move method if there's no new potential receiver @Test public void testFail10() throws Exception { @@ -857,45 +899,9 @@ public void testFail20() throws Exception { failHelper1(new String[] { "p1.A" }, "p1.A", 6, 17, 6, 23, FIELD, "b", true, true); } - // Cannot move static method - @Test - public void testFail2() throws Exception { - failHelper1(new String[] { "p1.A", "p2.B"}, "p1.A", 6, 23, 6, 24, PARAMETER, "b", true, true); - } - - // Cannot move native method - @Test - public void testFail3() throws Exception { - failHelper1(new String[] { "p1.A", "p2.B"}, "p1.A", 6, 23, 6, 24, PARAMETER, "b", true, true); - } - - // Cannot move method that references "super" - @Test - public void testFail4() throws Exception { - failHelper1(new String[] { "p1.A", "p2.B"}, "p1.A", 11, 20, 11, 21, PARAMETER, "b", true, true); - } - - // Cannot move method that references an enclosing instance - @Test - public void testFail5() throws Exception { - failHelper1(new String[] { "p1.A", "p2.B"}, "p1.A", 8, 21, 8, 21, PARAMETER, "b", true, true); - } - - // Cannot move potentially directly recursive method - @Test - public void testFail6() throws Exception { - failHelper1(new String[] { "p1.A", "p2.B"}, "p1.A", 6, 16, 6, 17, PARAMETER, "b", true, true); - } - - // Cannot move synchronized method - @Test - public void testFail8() throws Exception { - failHelper1(new String[] { "p1.A", "p2.B"}, "p1.A", 6, 29, 6, 29, PARAMETER, "b", true, true); - } - - // Cannot move method if there's no new potential receiver + // Issue 3035 @Test - public void testFail9() throws Exception { - failHelper1(new String[] { "p1.A", "p2.B", "p3.C"}, "p1.A", 7, 17, 7, 20, PARAMETER, "b", true, true); + public void testFail21() throws Exception { + failHelper1(new String[] { "p1.A", "p1.B" }, "p1.A", 6, 9, 6, 10, FIELD, "b", true, true); } } From dd445d9fb5aa30733f1cb3fb81f503fbc7c43783 Mon Sep 17 00:00:00 2001 From: Ed Merks Date: Wed, 15 Jul 2026 08:12:22 +0200 Subject: [PATCH 09/16] Force qualifier changes for org.org.eclipse.jdt.junit - It shares split packages with org.eclipse.jdt.junit.core. https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/3927 --- org.eclipse.jdt.junit/forceQualifierUpdate.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jdt.junit/forceQualifierUpdate.txt b/org.eclipse.jdt.junit/forceQualifierUpdate.txt index 543e637bd78..4bd1a00751d 100644 --- a/org.eclipse.jdt.junit/forceQualifierUpdate.txt +++ b/org.eclipse.jdt.junit/forceQualifierUpdate.txt @@ -16,4 +16,5 @@ https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/16 https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1781 https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1923 https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/2044 -https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/2595 \ No newline at end of file +https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/2595 +https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/3927 From c4e7056a9ddc0a2b789cbb548a2c3b2b7c6c0e6b Mon Sep 17 00:00:00 2001 From: Eclipse JDT Bot Date: Wed, 15 Jul 2026 06:18:11 +0000 Subject: [PATCH 10/16] Version bump(s) for 4.41 stream --- org.eclipse.jdt.junit/META-INF/MANIFEST.MF | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.eclipse.jdt.junit/META-INF/MANIFEST.MF b/org.eclipse.jdt.junit/META-INF/MANIFEST.MF index db838e50d29..79c2fe29870 100644 --- a/org.eclipse.jdt.junit/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.junit/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Automatic-Module-Name: org.eclipse.jdt.junit Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.jdt.junit;singleton:=true -Bundle-Version: 3.17.500.qualifier +Bundle-Version: 3.17.600.qualifier Bundle-Activator: org.eclipse.jdt.internal.junit.ui.JUnitPlugin Bundle-ActivationPolicy: lazy Bundle-Vendor: %providerName From d9b7811bf822d2694ea6f5d16aafaade41f0dff1 Mon Sep 17 00:00:00 2001 From: Sougandh S Date: Thu, 16 Jul 2026 08:38:02 +0530 Subject: [PATCH 11/16] Fix Missing Markdown Template contexts and MD Type comment generation (#3063) This commit includes adding the missing Markdown code template for overrides and fixes unwanted "///" generating on types with empty params --- .../manipulation/CodeTemplateContextType.java | 4 + .../core/manipulation/StubUtility.java | 9 +- .../jdt/ui/tests/core/TemplateStoreTest.java | 3 +- .../source/GetTypeCommentMarkdownTest.java | 108 ++++++++++++++++++ .../tests/core/source/SourceActionTests.java | 3 +- .../templates/default-codetemplates.xml | 2 + .../ui/preferences/CodeTemplateBlock.java | 3 + 7 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/source/GetTypeCommentMarkdownTest.java diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/CodeTemplateContextType.java b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/CodeTemplateContextType.java index d2f1198367d..eaefd786341 100644 --- a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/CodeTemplateContextType.java +++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/CodeTemplateContextType.java @@ -66,6 +66,7 @@ public class CodeTemplateContextType extends TemplateContextType { public static final String MARKDOWNGETTERCOMMENT_CONTEXTTYPE= "markdowngettercomment_context"; //$NON-NLS-1$ public static final String MARKDOWNSETTERCOMMENT_CONTEXTTYPE= "markdownsettercomment_context"; //$NON-NLS-1$ public static final String MARKDOWNMODULECOMMENT_CONTEXTTYPE= "markdownmodulecomment_context"; //$NON-NLS-1$ + public static final String MARKDOWNOVERRIDECOMMENT_CONTEXTTYPE= "markdownoverridecomment_context"; //$NON-NLS-1$ /* templates */ @@ -104,6 +105,7 @@ public class CodeTemplateContextType extends TemplateContextType { public static final String MARKDOWNGETTERCOMMENT_ID= CODETEMPLATES_PREFIX + "markdowngetter" + COMMENT_SUFFIX; //$NON-NLS-1$ public static final String MARKDOWNSETTERCOMMENT_ID= CODETEMPLATES_PREFIX + "markdownsetter" + COMMENT_SUFFIX; //$NON-NLS-1$ public static final String MARKDOWNMODULECOMMENT_ID= CODETEMPLATES_PREFIX + "markdownmodule" + COMMENT_SUFFIX; //$NON-NLS-1$ + public static final String MARKDOWNOVERRIDECOMMENT_ID= CODETEMPLATES_PREFIX + "markdownoverride" + COMMENT_SUFFIX; //$NON-NLS-1$ /* resolver types */ public static final String EXCEPTION_TYPE= "exception_type"; //$NON-NLS-1$ @@ -285,6 +287,7 @@ public CodeTemplateContextType(String contextName) { addCompilationUnitVariables(); fIsComment= true; break; + case MARKDOWNOVERRIDECOMMENT_CONTEXTTYPE: case OVERRIDECOMMENT_CONTEXTTYPE: addResolver(new CodeTemplateVariableResolver(ENCLOSING_TYPE, JavaManipulationMessages.CodeTemplateContextType_variable_description_enclosingtype)); addResolver(new CodeTemplateVariableResolver(ENCLOSING_METHOD, JavaManipulationMessages.CodeTemplateContextType_variable_description_enclosingmethod)); @@ -403,6 +406,7 @@ public static void registerContextTypes(ContextTypeRegistry registry) { registry.addContextType(new CodeTemplateContextType(CodeTemplateContextType.MARKDOWNDELEGATECOMMENT_CONTEXTTYPE)); registry.addContextType(new CodeTemplateContextType(CodeTemplateContextType.MARKDOWNGETTERCOMMENT_CONTEXTTYPE)); registry.addContextType(new CodeTemplateContextType(CodeTemplateContextType.MARKDOWNSETTERCOMMENT_CONTEXTTYPE)); + registry.addContextType(new CodeTemplateContextType(CodeTemplateContextType.MARKDOWNOVERRIDECOMMENT_CONTEXTTYPE)); } /* diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/StubUtility.java b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/StubUtility.java index 2c261493ee4..19bb56dd895 100644 --- a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/StubUtility.java +++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/StubUtility.java @@ -326,6 +326,11 @@ public static String getTypeComment(ICompilationUnit cu, String typeQualifiedNam throw new CoreException(new Status(IStatus.ERROR, JavaManipulation.ID_PLUGIN, IStatus.ERROR, e.getMessage(), e)); } } + if (useMarkdown && typeParameterNames.length == 0 && params.length == 0 ) { + str = document.get(); + str = str.replace("\n/// ", ""); //$NON-NLS-1$ //$NON-NLS-2$ + return str; + } return document.get(); } @@ -444,7 +449,7 @@ public static String getMethodComment(ICompilationUnit cu, String typeName, Stri if (delegate) templateName= useMarkdown ? CodeTemplateContextType.MARKDOWNDELEGATECOMMENT_ID : CodeTemplateContextType.DELEGATECOMMENT_ID; else - templateName= CodeTemplateContextType.OVERRIDECOMMENT_ID; + templateName= useMarkdown ? CodeTemplateContextType.MARKDOWNOVERRIDECOMMENT_ID : CodeTemplateContextType.OVERRIDECOMMENT_ID; } Template template= getCodeTemplate(templateName, cu.getJavaProject()); if (template == null) { @@ -720,7 +725,7 @@ public static String getMethodComment(ICompilationUnit cu, String typeName, Meth if (delegate) templateName= useMarkdown ? CodeTemplateContextType.MARKDOWNDELEGATECOMMENT_ID : CodeTemplateContextType.DELEGATECOMMENT_ID; else - templateName= CodeTemplateContextType.OVERRIDECOMMENT_ID; + templateName= useMarkdown ? CodeTemplateContextType.MARKDOWNOVERRIDECOMMENT_ID : CodeTemplateContextType.OVERRIDECOMMENT_ID; } Template template= getCodeTemplate(templateName, cu.getJavaProject()); if (template == null) { diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/TemplateStoreTest.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/TemplateStoreTest.java index 3a1531371c3..938e4cd908d 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/TemplateStoreTest.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/TemplateStoreTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2020 IBM Corporation and others. + * Copyright (c) 2000, 2026 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -73,6 +73,7 @@ public class TemplateStoreTest extends CoreTests { CodeTemplateContextType.MARKDOWNGETTERCOMMENT_ID, CodeTemplateContextType.MARKDOWNSETTERCOMMENT_ID, CodeTemplateContextType.MARKDOWNMODULECOMMENT_ID, + CodeTemplateContextType.MARKDOWNOVERRIDECOMMENT_ID, }; private TemplatePersistenceData find(String id, TemplatePersistenceData[] templateData) { diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/source/GetTypeCommentMarkdownTest.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/source/GetTypeCommentMarkdownTest.java new file mode 100644 index 00000000000..ade3fb184e7 --- /dev/null +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/source/GetTypeCommentMarkdownTest.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2026 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.ui.tests.core.source; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import org.eclipse.jdt.testplugin.JavaProjectHelper; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.jface.preference.IPreferenceStore; + +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IPackageFragmentRoot; + +import org.eclipse.jdt.internal.core.manipulation.CodeTemplateContextType; +import org.eclipse.jdt.internal.core.manipulation.StubUtility; + +import org.eclipse.jdt.ui.PreferenceConstants; +import org.eclipse.jdt.ui.tests.core.rules.Java23ProjectTestSetup; +import org.eclipse.jdt.ui.tests.core.rules.ProjectTestSetup; + +import org.eclipse.jdt.internal.ui.JavaPlugin; + +public class GetTypeCommentMarkdownTest { + + private static final String MARKDOWN_TYPECOMMENT_TEMPLATE= "/// ${type_name}" + "\n/// ${tags}"; + @Rule + public ProjectTestSetup pts= new Java23ProjectTestSetup(false); + + private IJavaProject fJavaProject; + private IPackageFragmentRoot fSourceFolder; + private IPackageFragment fPackageP; + + @Before + public void setUp() throws CoreException { + fJavaProject= pts.getProject(); + fSourceFolder= JavaProjectHelper.addSourceContainer(fJavaProject, "src"); + fPackageP= fSourceFolder.createPackageFragment("p", true, null); + + IPreferenceStore store= JavaPlugin.getDefault().getPreferenceStore(); + store.setValue(PreferenceConstants.CODEGEN_USE_MARKDOWN, true); + store.setValue(PreferenceConstants.CODEGEN_ADD_COMMENTS, true); + + StubUtility.setCodeTemplate(CodeTemplateContextType.MARKDOWNTYPECOMMENT_ID, MARKDOWN_TYPECOMMENT_TEMPLATE, null); + } + + @After + public void tearDown() { + IPreferenceStore store= JavaPlugin.getDefault().getPreferenceStore(); + store.setValue(PreferenceConstants.CODEGEN_USE_MARKDOWN, false); + store.setValue(PreferenceConstants.CODEGEN_ADD_COMMENTS, false); + fJavaProject= null; + fPackageP= null; + } + + @Test + public void testNoTagsStripsTrailingMarkdownLine() throws Exception { + ICompilationUnit cu= fPackageP.createCompilationUnit("A.java", + "package p;\n\npublic class A {\n}\n", true, null); + + String comment= StubUtility.getTypeComment(cu, "p.A", new String[0], new String[0], "\n"); + + assertEquals("/// A", comment); + } + + @Test + public void testTypeParameterTagsNotStripped() throws Exception { + ICompilationUnit cu= fPackageP.createCompilationUnit("B.java", + "package p;\n\npublic class B {\n}\n", true, null); + + String comment= StubUtility.getTypeComment(cu, "p.B", new String[] { "T" }, new String[0], "\n"); + + assertTrue("expected type-name line", comment.startsWith("/// B\n")); + assertTrue("expected @param tag to be preserved", comment.contains("@param ")); + } + + @Test + public void testRecordComponentParamsNotStripped() throws Exception { + ICompilationUnit cu= fPackageP.createCompilationUnit("R.java", + "package p;\n\npublic record R(String x, int y) {\n}\n", true, null); + + String comment= StubUtility.getTypeComment(cu, "p.R", new String[0], new String[] { "x", "y" }, "\n"); + + assertTrue("expected type-name line", comment.startsWith("/// R\n")); + assertTrue("missing @param tag for record component x", comment.contains("@param x")); + assertTrue("missing @param tag for record component y", comment.contains("@param y")); + } +} \ No newline at end of file diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/source/SourceActionTests.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/source/SourceActionTests.java index 164e7fa1e1e..14339167d28 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/source/SourceActionTests.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/source/SourceActionTests.java @@ -33,7 +33,8 @@ GenerateHashCodeEqualsTest.class, GenerateToStringTest.class, GenerateRecordConstructorTest.class, -AddJavaDocStubOperationTest.class +AddJavaDocStubOperationTest.class, +GetTypeCommentMarkdownTest.class }) public class SourceActionTests { } diff --git a/org.eclipse.jdt.ui/templates/default-codetemplates.xml b/org.eclipse.jdt.ui/templates/default-codetemplates.xml index 770bfe00344..7b067b0753d 100644 --- a/org.eclipse.jdt.ui/templates/default-codetemplates.xml +++ b/org.eclipse.jdt.ui/templates/default-codetemplates.xml @@ -76,6 +76,8 @@ + +