Skip to content

Commit

Permalink
Fix for issue #389: populate elements for anonymous inner's members
Browse files Browse the repository at this point in the history
Fixes ClassCastException: org.eclipse.jdt.internal.core.SourceType
cannot be cast to org.eclipse.jdt.core.IMethod -- due to passing
enclosing type instead of enclosing method during source visitation
  • Loading branch information
eric-milles committed Nov 17, 2017
1 parent 5ad82f6 commit 5b71e39
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,20 @@
import java.util.List;

import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.InnerClassNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.expr.DeclarationExpression;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
Expand All @@ -41,22 +46,30 @@
import org.eclipse.jdt.groovy.core.util.GroovyUtils;
import org.eclipse.jdt.internal.core.JavaElement;
import org.eclipse.jdt.internal.core.LocalVariable;
import org.eclipse.jdt.internal.core.ResolvedSourceField;
import org.eclipse.jdt.internal.core.ResolvedSourceMethod;
import org.eclipse.jdt.internal.core.ResolvedSourceType;
import org.eclipse.jdt.internal.core.SourceType;
import org.eclipse.jdt.internal.core.util.Util;

public class GroovyProjectFacade {

private IType parent;
private IJavaProject project;

public GroovyProjectFacade(IJavaElement element) {
this(element.getJavaProject());
if (element instanceof IType) {
parent = (IType) element;
} else {
parent = (IType) element.getAncestor(IJavaElement.TYPE);
}
}

public GroovyProjectFacade(IJavaProject project) {
this.project = project;
}

private IJavaProject project;

public IJavaProject getProject() {
return project;
}
Expand Down Expand Up @@ -96,27 +109,26 @@ public IJavaElement groovyNodeToJavaElement(ASTNode node, IFile file) {
}

public IType groovyClassToJavaType(ClassNode node) {
if (parent != null && parent.getFullyQualifiedName().equals(node.getName())) {
return parent;
}
try {
// GRECLIPSE-1628: handle anonymous inner classes; only go one level deep
ClassNode toLookFor = node;
if (GroovyUtils.isAnonymous(node)) {
toLookFor = node.getOuterClass();
IType enclosing = groovyClassToJavaType(toLookFor);
if (enclosing != null) {
if (!enclosing.isBinary()) {
return fakeAnonymousInnerClass(enclosing, (InnerClassNode) node);
} else {
// if the 'enclosing' is binary we may assume this one is also binary,
// so we should just be able to look for it with binary type name (including the $ etc.)
return project.findType(node.getName(), new NullProgressMonitor());
}
if (enclosing != null && !enclosing.isBinary()) {
return fakeAnonymousInnerClass(enclosing, (InnerClassNode) node);
} else {
return null;
// if the 'enclosing' is binary we may assume this one is also binary,
// so we should just be able to look for it with the type name (including the $ etc.)
return project.findType(node.getName(), (IProgressMonitor) null);
}
}

// GRECLIPSE-800: ensure that inner class nodes are handled properly
String name = toLookFor.getName().replace('$', '.');
IType type = project.findType(name, new NullProgressMonitor());
IType type = project.findType(name, (IProgressMonitor) null);
if (type != null && toLookFor != node) {
type = type.getType("", 1);
if (!type.exists()) {
Expand All @@ -132,6 +144,7 @@ public IType groovyClassToJavaType(ClassNode node) {

private IType fakeAnonymousInnerClass(IType outer, final InnerClassNode inner) {
return new ResolvedSourceType((JavaElement) outer, inner.getName(), GroovyUtils.getTypeSignature(inner, true, true)) {
@Override
public Object getElementInfo() throws JavaModelException {
try {
return super.getElementInfo();
Expand Down Expand Up @@ -159,6 +172,69 @@ public Object getElementInfo() throws JavaModelException {
}};
}
}

@Override
public IField getField(String fieldName) {
final FieldNode fieldNode = inner.getDeclaredField(fieldName);
if (fieldNode == null) {
return super.getField(fieldName);
}
String uniqueKey = GroovyUtils.getTypeSignature(fieldNode.getDeclaringClass(), true, true) +
Signature.C_DOT + fieldName + ")" + GroovyUtils.getTypeSignature(fieldNode.getType(), true, true);
return new ResolvedSourceField(this, fieldName, uniqueKey) {
@Override // NOTE: Copied from GroovyResolvedSourceField:
public Object getElementInfo() throws JavaModelException {
try {
return super.getElementInfo();
} catch (JavaModelException jme) {
if (!jme.getJavaModelStatus().isDoesNotExist()) {
throw jme;
}
return new org.eclipse.jdt.internal.core.SourceFieldElementInfo() {{
setTypeName(fieldNode.getType().toString(false).toCharArray());
setNameSourceStart(fieldNode.getNameStart());
setNameSourceEnd(fieldNode.getNameEnd());
setSourceRangeStart(fieldNode.getStart());
setSourceRangeEnd(fieldNode.getEnd());
setFlags(fieldNode.getModifiers());
}};
}
}
};
}

@Override
public IMethod getMethod(String methodName, String[] parameterTypeSignatures) {
final MethodNode methodNode = inner.getDeclaredMethod(methodName, getParametersForTypes(parameterTypeSignatures));
if (methodNode == null) {
return super.getMethod(methodName, parameterTypeSignatures);
}
String uniqueKey = GroovyUtils.getTypeSignature(methodNode.getDeclaringClass(), true, true) +
Signature.C_DOT + methodName + Signature.C_PARAM_START /*+ type of each param*/ + Signature.C_PARAM_END +
GroovyUtils.getTypeSignature(methodNode.getReturnType(), true, true); // if exceptions, Signature.C_INTERSECTION + exception type
return new ResolvedSourceMethod(this, methodName, parameterTypeSignatures, uniqueKey) {
@Override // NOTE: Copied from GroovyResolvedSourceMethod:
public Object getElementInfo() throws JavaModelException {
try {
return super.getElementInfo();
} catch (JavaModelException jme) {
if (!jme.getJavaModelStatus().isDoesNotExist()) {
throw jme;
}
return new org.eclipse.jdt.internal.core.SourceMethodInfo() {{
setReturnType(methodNode.getReturnType().getNameWithoutPackage().toCharArray());
//setExceptionTypeNames(buildExceptionTypeNames(methodNode.getExceptions()));
//setArgumentNames(buildArgumentNames(methodNode.getParameters()));
setNameSourceStart(methodNode.getNameStart());
setNameSourceEnd(methodNode.getNameEnd());
setSourceRangeStart(methodNode.getStart());
setSourceRangeEnd(methodNode.getEnd());
setFlags(methodNode.getModifiers());
}};
}
}
};
}
};
}

Expand All @@ -177,13 +253,13 @@ GroovyCompilationUnit groovyModuleToCompilationUnit(ModuleNode node) {

/**
* If this fully qualified name is in a groovy file, then return the
* classnode
* ClassNode.
*
* If this is not a groovy file, then return null
*/
public ClassNode getClassNodeForName(String name) {
try {
IType type = project.findType(name, new NullProgressMonitor());
IType type = project.findType(name, (IProgressMonitor) null);
if (type instanceof SourceType) {
return javaTypeToGroovyClass(type);
}
Expand All @@ -193,6 +269,15 @@ public ClassNode getClassNodeForName(String name) {
return null;
}

private Parameter[] getParametersForTypes(String[] signatures) {
int n = signatures.length;
Parameter[] parameters = new Parameter[n];
for (int i = 0; i < n; i += 1) {
parameters[i] = new Parameter(ClassHelper.makeWithoutCaching(Signature.toString(signatures[i])), null);
}
return parameters;
}

private ClassNode javaTypeToGroovyClass(IType type) {
ICompilationUnit unit = type.getCompilationUnit();
if (unit instanceof GroovyCompilationUnit) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,15 @@ public static List<ClassNode> getParameterTypes(Parameter... params) {
return types;
}

public static String[] getParameterTypeSignatures(MethodNode methodNode, boolean resolved) {
List<ClassNode> types = getParameterTypes(methodNode.getParameters());
String[] signatures = new String[types.size()];
for (int i = 0; i < types.size(); i += 1) {
signatures[i] = getTypeSignatureWithoutGenerics(types.get(i), true, resolved);
}
return signatures;
}

public static Set<ASTNode> getTransformNodes(ClassNode classNode, Class<? extends ASTTransformation> xformType) {
CompilePhase phase = xformType.getAnnotation(GroovyASTTransformation.class).phase();
Map<?, Set<ASTNode>> map = classNode.getTransforms(phase);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,7 @@ private void visitMethodOverrides(ClassNode node) {
try {
for (MethodNode method : node.getMethods()) {
if (method.getEnd() > 0) {
//enclosingElement = findAnonType(node).getMethod(method.getName(), getParameterTypeSignatures(method.getParameters()));
enclosingElement = findAnonType(node).getMethod(method.getName(), GroovyUtils.getParameterTypeSignatures(method, true));
visitMethodInternal(method, false);
}
}
Expand Down Expand Up @@ -1147,13 +1147,13 @@ public void visitConstructorCallExpression(ConstructorCallExpression node) {
}
for (FieldNode field : type.getFields()) {
if (field.getEnd() > 0) {
//enclosingElement = findAnonType(type).getField(field.getName());
enclosingElement = findAnonType(type).getField(field.getName());
visitField(field);
}
}
for (MethodNode method : type.getMethods()) {
if (method.getEnd() > 0) {
//enclosingElement = findAnonType(type).getMethod(method.getName(), getParameterTypeSignatures(method.getParameters()));
enclosingElement = findAnonType(type).getMethod(method.getName(), GroovyUtils.getParameterTypeSignatures(method, true));
visitMethodInternal(method, false);
}
}
Expand Down Expand Up @@ -2673,15 +2673,6 @@ private static String findUnaryOperatorName(String text) {
return null;
}

private static String[] getParameterTypeSignatures(Parameter[] params) {
List<ClassNode> types = GroovyUtils.getParameterTypes(params);
String[] signatures = new String[types.size()];
for (int i = 0; i < types.size(); i += 1) {
signatures[i] = GroovyUtils.getTypeSignature(types.get(i), true, true);
}
return signatures;
}

/**
* Makes assumption that no one has overloaded the basic arithmetic operations on numbers.
* These operations will bypass the mop in most situations anyway.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ else if (existsOnlyInGroovyModel(node.getField(), name, declaringType, jdtDeclar
// short-circuit if declaration exists in the Groovy model but not in the Java model
if (existsOnlyInGroovyModel(node, name, declaringType, jdtDeclaringType)) {
assert jdtDeclaringType instanceof SourceType;
return jdtDeclaringType.getMethod(name, null);
return jdtDeclaringType.getMethod(name, GroovyUtils.getParameterTypeSignatures(node, true));
}
}

Expand Down

0 comments on commit 5b71e39

Please sign in to comment.