Skip to content

Commit

Permalink
Fix for #1213: create type signatures for types in method's binding key
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Jan 4, 2021
1 parent 1ffa65a commit ef8ef76
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 55 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2020 the original author or authors.
* Copyright 2009-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,9 +15,6 @@
*/
package org.codehaus.jdt.groovy.internal.compiler.ast;

import java.util.HashMap;
import java.util.Map;

import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.GenericsType;
Expand Down Expand Up @@ -150,34 +147,31 @@ private ClassNode configureGenericArray(ArrayBinding genericArrayType) {
return result;
}

private Map<TypeVariableBinding, ClassNode> typeVariableConfigurationInProgress = new HashMap<>();

/**
* Based on Java5.configureTypeVariableReference()
*/
private ClassNode configureTypeVariableReference(TypeVariableBinding tv) {
ClassNode node = typeVariableConfigurationInProgress.get(tv);
if (node != null) {
return node;
}
String name = String.valueOf(tv.sourceName);
if (name.indexOf('@') >= 0) {
throw new IllegalStateException("Invalid type variable name: " + name);
}

ClassNode cn = ClassHelper.makeWithoutCaching(name);
cn.setGenericsPlaceHolder(true);
ClassNode cn2 = ClassHelper.makeWithoutCaching(name);
cn2.setGenericsPlaceHolder(true);
cn.setGenericsTypes(new GenericsType[] {new GenericsType(cn2)});

typeVariableConfigurationInProgress.put(tv, cn);
if (tv.firstBound != null && tv.firstBound.id != TypeIds.T_JavaLangObject) {
setRedirect(cn, configureType(tv.firstBound));
} else {
cn.setRedirect(ClassHelper.OBJECT_TYPE);
if (tv.enterRecursiveFunction()) {
ClassNode cn2 = ClassHelper.makeWithoutCaching(name);
cn2.setGenericsPlaceHolder(true);
cn.setGenericsTypes(new GenericsType[] {new GenericsType(cn2)});

if (tv.firstBound != null && tv.firstBound.id != TypeIds.T_JavaLangObject) {
setRedirect(cn, configureType(tv.firstBound));
} else {
cn.setRedirect(ClassHelper.OBJECT_TYPE);
}

tv.exitRecursiveFunction();
}
typeVariableConfigurationInProgress.remove(tv);

return cn;
}
Expand All @@ -193,8 +187,9 @@ private GenericsType configureTypeVariableDefinition(TypeVariableBinding tv) {
if (tBounds.length == 0) {
gt = new GenericsType(cn);
} else {
ClassNode[] cBounds = configureTypes(tBounds);
gt = new GenericsType(cn, cBounds, null);
tv.enterRecursiveFunction(); // "T extends Foo<? super T>"
gt = new GenericsType(cn, configureTypes(tBounds), null);
tv.exitRecursiveFunction();
gt.setName(cn.getName());
gt.setPlaceholder(true);
}
Expand All @@ -211,13 +206,13 @@ private ClassNode configureWildcardType(WildcardBinding wildcard) {

ClassNode[] uppers = configureTypes(getUpperBounds(wildcard));
ClassNode[] lowers = configureTypes(getLowerBounds(wildcard));
GenericsType t = new GenericsType(base, uppers,
GenericsType gt = new GenericsType(base, uppers,
lowers != null && lowers.length > 0 ? lowers[0] : null);
t.setWildcard(true);
gt.setWildcard(true);

ClassNode ref = ClassHelper.makeWithoutCaching(Object.class, false);
ref.setGenericsTypes(new GenericsType[] {t});
return ref;
ClassNode cn = ClassHelper.makeWithoutCaching(Object.class, false);
cn.setGenericsTypes(new GenericsType[] {gt});
return cn;
}

private ClassNode configureParameterizedType(ParameterizedTypeBinding tb) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2020 the original author or authors.
* Copyright 2009-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,6 +17,7 @@ package org.codehaus.groovy.eclipse.codebrowsing.tests

import org.codehaus.groovy.eclipse.test.GroovyEclipseTestSuite
import org.codehaus.jdt.groovy.model.GroovyCompilationUnit
import org.eclipse.jdt.core.BindingKey
import org.eclipse.jdt.core.ICompilationUnit
import org.eclipse.jdt.core.IJavaElement
import org.eclipse.jdt.core.SourceRange
Expand All @@ -41,6 +42,8 @@ abstract class BrowsingTestSuite extends GroovyEclipseTestSuite {

assert elements[0].elementName == elementName : "Should have found reference to: $elementName"
assert elements[0].exists() : 'Element should exist in the model'
if (elements[0] instanceof org.eclipse.jdt.core.IMember)
new BindingKey(elements[0].key).toSignature()
return elements[0]
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2020 the original author or authors.
* Copyright 2009-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -43,29 +43,46 @@ final class CodeSelectGenericsTests extends BrowsingTestSuite {

@Test
void testCodeSelectGenericField1() {
String structureContents = 'class Structure { java.util.List<String> field; }'
String javaContents = 'class Java { { new Structure().field = null; } }'
String groovyContents = 'new Structure().field'
String toFind = 'field'
assertCodeSelect([structureContents, javaContents, groovyContents], toFind)
String contents = '''\
|class Foo {
| List<String> bar
|}
|new Foo().bar
|'''.stripMargin()
assertCodeSelect([contents], 'bar')
}

@Test
void testCodeSelectGenericField2() {
String structureContents = 'class Structure { java.util.Map<String, Integer> field; }'
String javaContents = 'class Java { { new Structure().field = null; } }'
String groovyContents = 'new Structure().field'
String toFind = 'field'
assertCodeSelect([structureContents, javaContents, groovyContents], toFind)
String contents = '''\
|class Foo {
| Map<String, Integer> bar
|}
|new Foo().bar
|'''.stripMargin()
assertCodeSelect([contents], 'bar')
}

@Test
void testCodeSelectGenericField3() {
String structureContents = 'class Structure { java.util.Map<String[], java.util.List<Integer>> field; }'
String javaContents = 'class Java { { new Structure().field = null; } }'
String groovyContents = 'new Structure().field'
String toFind = 'field'
assertCodeSelect([structureContents, javaContents, groovyContents], toFind)
String contents = '''\
|class Foo {
| Map<String[], List<Integer>> bar
|}
|new Foo().bar
|'''.stripMargin()
assertCodeSelect([contents], 'bar')
}

@Test // https://github.com/groovy/groovy-eclipse/issues/920
void testCodeSelectGenericField4() {
String contents = '''\
|class Foo {
| List<? extends CharSequence> bar
|}
|new Foo().bar
|'''.stripMargin()
assertCodeSelect([contents], 'bar')
}

@Test
Expand Down Expand Up @@ -137,6 +154,15 @@ final class CodeSelectGenericsTests extends BrowsingTestSuite {
assert method.returnType.toString(false) == 'java.util.Set <java.util.Map$Entry>'
}

@Test // https://github.com/groovy/groovy-eclipse/issues/1213
void testCodeSelectGenericMethod9() {
String types = 'class Pogo { Date date }'
String usage = 'cmp = Comparator.<Pogo,Date>comparing{ it.date }'
IJavaElement elem = assertCodeSelect([types, usage], 'comparing')
MethodNode method = ((GroovyResolvedBinaryMethod) elem).inferredElement
assert method.returnType.toString(false) == 'java.util.Comparator <Pogo>'
}

@Test
void testCodeSelectGenericCategoryMethod() {
String contents = '[a: Number].collect { k,v -> "" }'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2020 the original author or authors.
* Copyright 2009-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -680,13 +680,13 @@ private static String createUniqueKey(final AnnotatedNode declaration, final IJa
} else {
appendUniqueKeyForResolvedType(sb, resolvedDeclaringType);
if (declaration instanceof FieldNode) {
sb.append(Signature.C_DOT).append(maybeRequested.getElementName()).append(')');
sb.append(Signature.C_DOT).append(maybeRequested.getElementName()).append(Signature.C_PARAM_END);
appendUniqueKeyForResolvedType(sb, result.type);
} else if (declaration instanceof MethodNode) {
MethodNode node = (MethodNode) declaration;
if (maybeRequested.getElementType() == IJavaElement.FIELD) {
// this is likely a generated getter or setter
sb.append(Signature.C_DOT).append(maybeRequested.getElementName()).append(')');
sb.append(Signature.C_DOT).append(maybeRequested.getElementName()).append(Signature.C_PARAM_END);
boolean setter = node.getName().startsWith("set") && node.getParameters() != null && node.getParameters().length > 0;
appendUniqueKeyForResolvedType(sb, setter ? node.getParameters()[0].getType() : result.type);
} else {
Expand All @@ -708,6 +708,9 @@ private static void appendUniqueKeyForMethod(final StringBuilder sb, final Metho
}
sb.append(Signature.C_DOT).append(methodName);

java.util.function.Function<ClassNode, String> signer = type ->
GroovyUtils.getTypeSignature(type, true, true).replace('.', '/');

// type parameters
GenericsType[] generics = GroovyUtils.getGenericsTypes(node);
if (generics.length > 0) {
Expand All @@ -721,11 +724,11 @@ private static void appendUniqueKeyForMethod(final StringBuilder sb, final Metho
sb.append(Signature.C_COLON);
sb.append(Signature.C_COLON);
if (lower != null) {
appendUniqueKeyForResolvedType(sb, lower);
sb.append(signer.apply(lower));
} else if (upper != null && upper.length > 0) {
for (int i = 0; i < upper.length; i += 1) {
if (i > 0) sb.append(Signature.C_COLON);
appendUniqueKeyForResolvedType(sb, upper[i]);
sb.append(signer.apply(upper[i]));
}
}
}
Expand All @@ -737,13 +740,13 @@ private static void appendUniqueKeyForMethod(final StringBuilder sb, final Metho
Parameter[] parameters = node.getParameters();
if (parameters != null) {
for (Parameter p : parameters) {
sb.append(GroovyUtils.getTypeSignature(p.getType(), true, true).replace('.', '/'));
sb.append(signer.apply(p.getType()));
}
}
sb.append(Signature.C_PARAM_END);

// return type
sb.append(GroovyUtils.getTypeSignature(returnType, true, true).replace('.', '/'));
sb.append(signer.apply(returnType));

// type parameter resolution
if (generics.length > 0) {
Expand All @@ -763,8 +766,7 @@ private static void appendUniqueKeyForMethod(final StringBuilder sb, final Metho
// exceptions
if (node.getExceptions() != null) {
for (ClassNode exception : node.getExceptions()) {
sb.append(Signature.C_INTERSECTION);
appendUniqueKeyForResolvedType(sb, exception);
sb.append(Signature.C_INTERSECTION).append(signer.apply(exception));
}
}
}
Expand All @@ -775,14 +777,14 @@ private static void appendUniqueKeyForResolvedType(final StringBuilder sb, final
sb.append(signature);

ClassNode baseType = GroovyUtils.getBaseType(resolvedType);
if (baseType.isUsingGenerics() && !baseType.isGenericsPlaceHolder()) {
if (!baseType.isGenericsPlaceHolder()) {
GenericsType[] generics = baseType.getGenericsTypes();
if (generics != null && generics.length > 0) {
if (generics != null) {
sb.setCharAt(sb.length() - 1, Signature.C_GENERIC_START);

for (int i = 0, n = generics.length; i < n; i += 1) {
GenericsType gt = generics[i];
if (gt.isPlaceholder() || !gt.isWildcard()) {
if (!gt.isWildcard()) {
appendUniqueKeyForResolvedType(sb, gt.getType());
} else {
// see org.eclipse.jdt.core.BindingKey#createWildcardTypeBindingKey(String,char,String,int)
Expand Down

0 comments on commit ef8ef76

Please sign in to comment.