Community
Participate
Working Groups
Build id: I20100310-1800 Test case: package p; class A { /* * A very important comment. */ class X { } } 1) When you invoke "Move Type to New File" refactoring on class X, the comment gets deleted from class A along with class X, even though it does not belong to the AST node X. The extended source range of X does not include the comment since their is a new line after the comment. Expected : The comment should not be deleted from class A. The method org.eclipse.jdt.internal.core.dom.rewrite.ASTRewriteAnalyzer.ListRewriter.rewriteList(ASTNode parent, StructuralPropertyDescriptor property, int offset, String keyword) uses a wrong starting offset- at the beginning of the comment rather than at the beginning of declaration of class X, so even though the range is calculated correctly from the source range computer, the comment gets deleted along with the class X when the refactoring happens. Here's the stack trace where it sets the offset to beginning of the comment: Thread [ModalContext] (Suspended) ASTRewriteAnalyzer$ParagraphListRewriter(ASTRewriteAnalyzer$ListRewriter).rewriteList(ASTNode, StructuralPropertyDescriptor, int, String) line: 543 ASTRewriteAnalyzer.rewriteParagraphList(ASTNode, StructuralPropertyDescriptor, int, int, int, int) line: 998 ASTRewriteAnalyzer.visit(TypeDeclaration) line: 1579 TypeDeclaration.accept0(ASTVisitor) line: 467 TypeDeclaration(ASTNode).accept(ASTVisitor) line: 2480 ASTRewrite.internalRewriteAST(char[], LineInformation, String, List, Map, ASTNode, RecoveryScannerData) line: 271 ASTRewrite.rewriteAST() line: 260 CompilationUnitRewrite.attachChange(CompilationUnitChange, boolean, IProgressMonitor) line: 271 CompilationUnitRewrite.createChange(String, boolean, IProgressMonitor) line: 236 CompilationUnitRewrite.createChange(boolean, IProgressMonitor) line: 220 CompilationUnitRewrite.createChange(boolean) line: 187 MoveInnerToTopRefactoring.createChangeManager(IProgressMonitor, RefactoringStatus) line: 840 MoveInnerToTopRefactoring.checkFinalConditions(IProgressMonitor) line: 723 CheckConditionsOperation.run(IProgressMonitor) line: 85 CreateChangeOperation.run(IProgressMonitor) line: 121 UIPerformChangeOperation(PerformChangeOperation).run(IProgressMonitor) line: 209 Workspace.run(IWorkspaceRunnable, ISchedulingRule, int, IProgressMonitor) line: 1975 WorkbenchRunnableAdapter.run(IProgressMonitor) line: 87 ModalContext$ModalContextThread.run() line: 121 Here's the stack trace where it deletes the whole range : Thread [ModalContext] (Suspended) ASTRewriteAnalyzer.doTextRemove(int, int, TextEditGroup) line: 330 ASTRewriteAnalyzer.doTextRemoveAndVisit(int, int, ASTNode, TextEditGroup) line: 339 ASTRewriteAnalyzer$ParagraphListRewriter(ASTRewriteAnalyzer$ListRewriter).rewriteList(ASTNode, StructuralPropertyDescriptor, int, String) line: 601 ASTRewriteAnalyzer.rewriteParagraphList(ASTNode, StructuralPropertyDescriptor, int, int, int, int) line: 998 ASTRewriteAnalyzer.visit(TypeDeclaration) line: 1579 TypeDeclaration.accept0(ASTVisitor) line: 467 TypeDeclaration(ASTNode).accept(ASTVisitor) line: 2480 ASTRewrite.internalRewriteAST(char[], LineInformation, String, List, Map, ASTNode, RecoveryScannerData) line: 271 ASTRewrite.rewriteAST() line: 260 CompilationUnitRewrite.attachChange(CompilationUnitChange, boolean, IProgressMonitor) line: 271 CompilationUnitRewrite.createChange(String, boolean, IProgressMonitor) line: 236 CompilationUnitRewrite.createChange(boolean, IProgressMonitor) line: 220 CompilationUnitRewrite.createChange(boolean) line: 187 MoveInnerToTopRefactoring.createChangeManager(IProgressMonitor, RefactoringStatus) line: 840 MoveInnerToTopRefactoring.checkFinalConditions(IProgressMonitor) line: 723 CheckConditionsOperation.run(IProgressMonitor) line: 85 CreateChangeOperation.run(IProgressMonitor) line: 121 UIPerformChangeOperation(PerformChangeOperation).run(IProgressMonitor) line: 209 Workspace.run(IWorkspaceRunnable, ISchedulingRule, int, IProgressMonitor) line: 1975 WorkbenchRunnableAdapter.run(IProgressMonitor) line: 87 ModalContext$ModalContextThread.run() line: 121
It looks like the rewriter removes everything from the insertion position till the end of the removed node. In this case the insertion position is the position after the opening brace. It should only remove the contents of the node from its starting position up to its ending position (start + length). This might leave some whitespaces behind (indentation of the current node).
Any fix for this needs to be carefully reviewed, especially in situations where multiple consecutive nodes are removed. We have to make sure that no stray comments can stay around from between the removed nodes (e.g. when Extract Method moves multiple statements). If too risky, we may have to defer to 3.7.
Olivier, can this be targeted early 3.7?
Created attachment 172148 [details] proposed fix v1.0 + regression test There are 2 problems here : 1) JDT/Core problem: When we remove the type body, we also remove the leading and trailing separators, and with separators the comments are removed as well. The changes in ASTRewriterAnalyzer attempt to fix this. For leading separators, we move the current position to the start of the next node, and the prevEnd to the end of comments (if they're not part of the extended source range). This way the separator range is calculated omitting the comments. Similarly, for trailing comments, we set the end point to the extended end point of the node which we're removing, instead of setting it to the starting of next node as is done now. So in effect, for trailing comments we dont touch the separator at all. That means we dont even delete the whitespace from end of the current node until starting of the first subsequent comment. Consider what will happen if we also delete that whitespace instead of this approach: // start Type { } // end becomes // start // end because it tends to preserve the indentation of the removed line. Hence I preferred // start // end because even though it doesnt remove whitespace, it preserves indentation of comments. 2) JDT/UI problem: When the comments are in the extended source range of the node (i.e. no space between comments and the declaration body), we correctly remove the comments along with the type. Ideally, for "move type to new file", these comments should move along with the type to the new file. JDT/UI doesnt do this currently. Raksha, can you please run the UI tests on this patch and check if everything's ok? Thanks.
(In reply to comment #4) > 2) JDT/UI problem: When the comments are in the extended source range of the > node (i.e. no space between comments and the declaration body), we correctly > remove the comments along with the type. Ideally, for "move type to new file", > these comments should move along with the type to the new file. JDT/UI doesnt > do this currently. I agree, in MoveInnerToTopRefactoring#createNewSource(..), we should use: return ASTNodes.getNodeSource(declaration, true, false); We will do that in bug 305103.
(In reply to comment #4) > Created an attachment (id=172148) [details] [diff] > proposed fix v1.0 + regression test > So in effect, for trailing comments we dont touch the separator at > all. That means we dont even delete the whitespace from end of the current node > until starting of the first subsequent comment. Consider what will happen if we > also delete that whitespace instead of this approach: > > // start > > Type { > } > > > // end > > > becomes > > // start > > // end > > because it tends to preserve the indentation of the removed line. Hence I > preferred > > // start > > > > > // end > > because even though it doesnt remove whitespace, it preserves indentation of > comments. > > 2) JDT/UI problem: When the comments are in the extended source range of the > node (i.e. no space between comments and the declaration body), we correctly > remove the comments along with the type. Ideally, for "move type to new file", > these comments should move along with the type to the new file. JDT/UI doesnt > do this currently. > > Raksha, can you please run the UI tests on this patch and check if everything's > ok? Thanks. I'm running the UI tests and I can already see some failures due to the change in white space removal approach. I can adjust all of them if this patch is released along with the JDT/UI problem in bug 305103.
Ayush, I found another case where the deletion happens : package p; class A { int i; // First comment does not get deleted - OK // second comment does not belong to 'X' gets deleted - WRONG! //comment gets deleted - OK // second comment gets deleted- OK class X { } //comment gets deleted - OK //second comment gets deleted - OK // First comment does not get deleted - OK // second comment does not get deleted - OK } May be you need to use a 'while' rather than checking it once? Note: The second comment also gets deleted when there is space between first and second comment : package p; class A { int i; // First comment does not get deleted - OK // second comment does not belong to 'X' gets deleted - WRONG! //comment gets deleted - OK // second comment gets deleted- OK class X { } //comment gets deleted - OK //second comment gets deleted - OK // First comment does not get deleted - OK // second comment does not get deleted - OK }
Created attachment 172349 [details] improved fix + tests Raksha, good catch. Added a while loop to detect several comments. Note that this deletion of the leading comments takes place if the type being removed is the last one. Added another test ASTRewritingModifyingRemoveTest.test013() for this.
Looks good.
Released in HEAD for 3.7M1. Added tests org.eclipse.jdt.core.tests.rewrite.modifying.ASTRewritingModifyingTest.test0012() and test0013().
Verified for 3.7 M1 using build I20100802-1800