Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [jdt-ui-dev] [PATCH] fix for inline refactoring bugs #24941 and #35905




Hello Dimitry,

thanks for providing the patch for the two bugs. I will look at the patch
the next couple of days and will let you know
if there is something missing besides test cases for the new functionality
;--).

Are you aware of the org.eclipse.jdt.ui.tests.refactoring projects. It
contains all our refactoring tests. So for the new
functionality we should have test cases as well. The interesting test case
class is InlineMethodTests and the
test resources reside in resources/InlineMethodWorkspace/TestCases.

Dirk



                                                                           
             "Stalnov, Dmitry"                                             
             <dstalnov@fusionO                                             
             ne.com>                                                    To 
             Sent by:                  "'jdt-ui-dev@xxxxxxxxxxx'"          
             jdt-ui-dev-admin@         <jdt-ui-dev@xxxxxxxxxxx>            
             eclipse.org                                                cc 
                                                                           
                                                                   Subject 
             06/14/2003 12:13          [jdt-ui-dev] [PATCH] fix for inline 
             AM                        refactoring bugs #24941 and #35905  
                                                                           
                                                                           
             Please respond to                                             
             jdt-ui-dev@eclips                                             
                   e.org                                                   
                                                                           
                                                                           




    Hello all,

In our company we use Eclipse as a main tool for Java programming and are
very happy with it. I'd like to participate in its development. For a
start, to get familiar with the codebase, I decided to fix several bugs.
Attached is a patch fixing two refactoring bugs (24941, 35905). Could
somebody review it and either apply to CVS or make some comments and
suggestions how I can improve the fix?

Thank you in advance,
    Dmitry(See attached file: patch.txt)
Index: CallInliner.java
===================================================================
RCS file: /home/eclipse/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/CallInliner.java,v
retrieving revision 1.27
diff -u -r1.27 CallInliner.java
--- CallInliner.java	12 Jun 2003 10:41:58 -0000	1.27
+++ CallInliner.java	13 Jun 2003 22:00:13 -0000
@@ -14,40 +14,46 @@
 import java.util.Iterator;
 import java.util.List;
 
-import org.eclipse.core.runtime.CoreException;
-
 import org.eclipse.core.resources.IFile;
-
+import org.eclipse.core.runtime.CoreException;
 import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.AST;
 import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
 import org.eclipse.jdt.core.dom.Block;
 import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.CastExpression;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
 import org.eclipse.jdt.core.dom.DoStatement;
 import org.eclipse.jdt.core.dom.Expression;
 import org.eclipse.jdt.core.dom.FieldAccess;
 import org.eclipse.jdt.core.dom.ForStatement;
 import org.eclipse.jdt.core.dom.IBinding;
+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.IfStatement;
 import org.eclipse.jdt.core.dom.Initializer;
 import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
 import org.eclipse.jdt.core.dom.Name;
 import org.eclipse.jdt.core.dom.ParenthesizedExpression;
 import org.eclipse.jdt.core.dom.SimpleName;
 import org.eclipse.jdt.core.dom.Statement;
 import org.eclipse.jdt.core.dom.SuperFieldAccess;
 import org.eclipse.jdt.core.dom.ThisExpression;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
 import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
 import org.eclipse.jdt.core.dom.WhileStatement;
-
 import org.eclipse.jdt.internal.corext.Assert;
 import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
 import org.eclipse.jdt.internal.corext.codemanipulation.ImportEdit;
 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
 import org.eclipse.jdt.internal.corext.dom.ASTRewrite;
+import org.eclipse.jdt.internal.corext.dom.Bindings;
 import org.eclipse.jdt.internal.corext.dom.CodeScopeBuilder;
 import org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor;
 import org.eclipse.jdt.internal.corext.dom.LocalVariableIndex;
@@ -60,6 +66,7 @@
 import org.eclipse.jdt.internal.corext.textmanipulation.TextBuffer;
 import org.eclipse.jdt.internal.corext.textmanipulation.TextEdit;
 import org.eclipse.jdt.internal.corext.util.WorkingCopyUtil;
+import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
 
 public class CallInliner {
 
@@ -230,7 +237,11 @@
 			Expression expression= (Expression)arguments.get(i);
 			ParameterData parameter= fSourceProvider.getParameterData(i);
 			if (canInline(expression, parameter)) {
-				realArguments[i]= getContent(expression);
+				realArguments[i] = getContent(expression);
+				// fixes bug #35905
+				if(expression instanceof CastExpression) {
+					realArguments[i] = "(" + realArguments[i] + ")";  //$NON-NLS-1$//$NON-NLS-2$
+				}
 			} else {
 				String name= fInvocationScope.createName(parameter.getName(), true);
 				realArguments[i]= name;
@@ -284,7 +295,19 @@
 		}
 	}
 
-	private void replaceCall(int callType, String[] blocks) {
+	private static class ImportAnalyzer extends ASTVisitor {
+		private ImportEdit fImportEdit;
+		public ImportAnalyzer(ImportEdit importEdit) {
+			fImportEdit = importEdit;
+		}
+		public boolean visit(ClassInstanceCreation node) {
+			fImportEdit.addImport(node.resolveTypeBinding());
+			return false;
+		}
+	}
+
+	private void replaceCall(int callType, String[] blocks)
+	throws JavaModelException {
 		// Inline empty body
 		if (blocks.length == 0) {
 			if (fNeedsStatement) {
@@ -293,6 +316,9 @@
 				fRewriter.markAsRemoved(fTargetNode);
 			}
 		} else {
+			ImportAnalyzer analyzer = new ImportAnalyzer(fImportEdit);
+			fSourceProvider.getDeclaration().getBody().accept(analyzer);
+			
 			ASTNode node= null;
 			for (int i= 0; i < blocks.length - 1; i++) {
 				node= fRewriter.createPlaceholder(blocks[i], ASTRewrite.STATEMENT);
@@ -318,7 +344,20 @@
 					node= null;
 				}
 			} else if (fTargetNode instanceof Expression) {
+				
 				node= fRewriter.createPlaceholder(block, ASTRewrite.EXPRESSION);
+				
+				// fixes bug #24941
+				if(needsExplicitCast()) {
+					AST ast = node.getAST();
+					CastExpression castExpression = ast.newCastExpression();
+					ITypeBinding returnType = fSourceProvider.getReturnType();
+					fImportEdit.addImport(returnType);
+					castExpression.setType(ASTNodeFactory.newType(ast, returnType, false));
+					castExpression.setExpression((Expression)node);
+					node = castExpression;
+				}
+				
 				if (needsParenthesis()) {
 					ParenthesizedExpression pExp= fTargetNode.getAST().newParenthesizedExpression();
 					pExp.setExpression((Expression)node);
@@ -342,6 +381,107 @@
 				}
 			}
 		}
+	}
+
+	/**
+	 * @return <code>true</code> if explicit cast is needed otherwise <code>false</code>
+	 * @throws JavaModelException
+	 */
+	private boolean needsExplicitCast()
+	throws JavaModelException {
+		
+		ASTNode parent = fTargetNode.getParent();
+		int nodeType = parent.getNodeType();
+		if(nodeType == ASTNode.METHOD_INVOCATION) {
+			MethodInvocation methodInvocation = (MethodInvocation)parent;
+			IMethodBinding method = methodInvocation.resolveMethodBinding();
+			ITypeBinding[] parameters = method.getParameterTypes();
+			ITypeBinding type = resolveTypeBinding(methodInvocation);
+			
+			// finds all overloaded methods with the same name in hierarchy
+			IMethodBinding[] methods = findMethodsInHierarchy(type, method.getName());
+			for(int i = 0; i < methods.length; i++) {
+				// skip the method itself
+				if(!Bindings.equals(methods[i], method)) {
+					// access modifier of the method can restrict the call but 
+					// still it is better to have explicit cast in the place
+					if(canImplicitlyCall(methods[i], parameters)) {
+						return true;
+					}
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Utility method that returns type used in the method invocation.
+	 * 
+	 * @param methodInvocation invocation to resolve type of
+	 * @return type used in the method invocation. NB! not the type declaring
+	 * the method, it can be declared in a super class.
+	 * @throws JavaModelException
+	 */
+	private ITypeBinding resolveTypeBinding(MethodInvocation methodInvocation)
+	throws JavaModelException {
+		
+		ITypeBinding typeBinding = null;
+		Expression exp = methodInvocation.getExpression();
+		if(exp != null) {
+			typeBinding = exp.resolveTypeBinding();
+		}
+		else {
+			TypeDeclaration type = (TypeDeclaration)ASTResolving.findParentType(methodInvocation);
+			typeBinding = type.resolveBinding();
+		}
+		return typeBinding;
+	}
+
+	/**
+	 * The utility method returns all method bindings with the name specified 
+	 * in type hierarchy.
+	 * 
+	 * @param type type to start from
+	 * @param methodName name of the method to search for in the hierarchy
+	 * @return array of methods with the name specified
+	 */
+	private IMethodBinding[] findMethodsInHierarchy(ITypeBinding type, String methodName) {
+		
+		ArrayList result = new ArrayList();
+		
+		while(type != null) {
+			IMethodBinding[] methods = type.getDeclaredMethods();
+			for(int i = 0; i < methods.length; i++) {
+				if(methodName.equals(methods[i].getName())) {
+					result.add(methods[i]);
+				}
+			}
+			type = type.getSuperclass();
+		}
+		
+		return (IMethodBinding[])result.toArray(new IMethodBinding[result.size()]);
+	}
+
+	/**
+	 * @param method method binding to query for
+	 * @param types method parameters
+	 * @return <code>true</code> if the method can be called with specified parameters
+	 * without explicit casts, otherwise <code>false</code>.
+	 * @throws JavaModelException
+	 */
+	private boolean canImplicitlyCall(IMethodBinding method, ITypeBinding[] types)
+	throws JavaModelException {
+		
+		ITypeBinding[] parameters = method.getParameterTypes();
+		if(types.length != parameters.length)  {
+			return false;
+		}
+		for(int i = 0; i < parameters.length; i++)  {
+			if(!ASTResolving.canAssign(parameters[i], types[i]))  {
+				return false;
+			}
+		}
+		return true;
 	}
 
 	private boolean needsParenthesis() {

Back to the top