Skip to content

Commit

Permalink
Fix for #1340: support array type class literal for annotation attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Jan 24, 2022
1 parent 2732da2 commit c36f6e0
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2021 the original author or authors.
* Copyright 2009-2022 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 @@ -840,6 +840,75 @@ public void testClassLiteralAttributeValue3() {
runNegativeTest(sources, "");
}

@Test
public void testClassLiteralAttributeValue4() {
//@formatter:off
String[] sources = {
"C.groovy",
"@Category(String[])\n" +
"class C {\n" +
" def m() {}\n" +
"}\n",
};
//@formatter:on

runNegativeTest(sources, "");

checkGCUDeclaration("C.groovy",
"public @Category(String[].class) class C {\n" +
" public @groovy.transform.Generated C() {\n" +
" }\n" +
" public static java.lang.Object m(String... $this) {\n" +
" }\n" +
"}\n");
}

@Test
public void testClassLiteralAttributeValue5() {
//@formatter:off
String[] sources = {
"C.groovy",
"@Category(String[].class)\n" +
"class C {\n" +
" def m() {}\n" +
"}\n",
};
//@formatter:on

runNegativeTest(sources, "");

checkGCUDeclaration("C.groovy",
"public @Category(String[].class) class C {\n" +
" public @groovy.transform.Generated C() {\n" +
" }\n" +
" public static java.lang.Object m(String... $this) {\n" +
" }\n" +
"}\n");
}

@Test
public void testClassLiteralAttributeValue6() {
//@formatter:off
String[] sources = {
"C.groovy",
"@Category(java.lang.String[][].class)\n" +
"class C {\n" +
" def m() {}\n" +
"}\n",
};
//@formatter:on

runNegativeTest(sources, "");

checkGCUDeclaration("C.groovy",
"public @Category(java.lang.String[][].class) class C {\n" +
" public @groovy.transform.Generated C() {\n" +
" }\n" +
" public static java.lang.Object m(java.lang.String[]... $this) {\n" +
" }\n" +
"}\n");
}

@Test
public void testClosureExpressionAttributeValue() {
//@formatter:off
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1570,8 +1570,10 @@ private AbstractMethodDeclaration createMethodDeclaration(ClassNode classNode, M

for (AnnotationNode annotation : classNode.getAnnotations()) {
if (isType("groovy.lang.Category", annotation.getClassNode().getName())) {
ClassNode selfType = ClassHelper.make(java.util.Optional.ofNullable(annotation.getMember("value")).map(Expression::getText).orElse(ClassHelper.OBJECT));
params = (Parameter[]) ArrayUtils.add(params, 0, new Parameter(selfType, "$this"));
Object value = createAnnotationMemberExpression(annotation.getMember("value"), ClassHelper.CLASS_Type);
value = (value instanceof ClassLiteralAccess ? ((ClassLiteralAccess) value).type : ClassHelper.OBJECT);
Parameter firstParam = new Parameter(GroovyUtils.makeType(value.toString()), "$this");
params = (Parameter[]) ArrayUtils.add(params, 0, firstParam);
modifiers |= Flags.AccStatic;
break;
}
Expand Down Expand Up @@ -1753,6 +1755,8 @@ private org.eclipse.jdt.internal.compiler.ast.Expression createAnnotationMemberE
expression.sourceStart = expr.getStart();
expression.sourceEnd = expr.getEnd() - 1;
return expression;
} else if (be.getOperation().getType() == Types.LEFT_SQUARE_BRACKET && be.getRightExpression() instanceof ListExpression) {
return new ClassLiteralAccess(expr.getEnd() - 1, createTypeReferenceForClassLiteral(new PropertyExpression(expr, "class")));
}
// or annotation may be something like "@Tag(value = List<String)" (incomplete generics specification)
} else {
Expand Down Expand Up @@ -2056,21 +2060,21 @@ private TypeReference createTypeReferenceForArrayNameLeadingBrackets(ClassNode n
}

private TypeReference createTypeReferenceForArrayName(char[] typeName, ClassNode typeNode, int dim, int sourceStart, int sourceEnd) {
int nameEnd = (sourceEnd == -2 ? -1 : typeNode.getEnd());
if (!typeNode.isUsingGenerics()) {
if (CharOperation.indexOf('.', typeName) < 0) {
// For a single array reference, for example 'String[]' start will be 'S' and end will be the char after ']'. When the
// ArrayTypeReference is built we need these positions for the result: sourceStart - the 'S'; sourceEnd - the ']';
// ArrayTypeReference is built we need these positions for the result: sourceStart - the 'S'; sourceEnd - the last ']';
// originalSourceEnd - the 'g'
ArrayTypeReference tr = new ArrayTypeReference(typeName, dim, toPos(sourceStart, sourceEnd - 1));
tr.originalSourceEnd = typeNode.getEnd() - 1;
ArrayTypeReference tr = new ArrayTypeReference(typeName, dim, toPos(sourceStart, nameEnd - 1));
tr.sourceEnd = sourceEnd == -2 ? -2 : sourceEnd - 1;
return tr;
} else {
// For a qualified array reference, for example 'java.lang.Number[][]' start will be 'j' and end will be the char after ']'.
// When the ArrayQualifiedTypeReference is built we need these positions for the result: sourceStart - the 'j'; sourceEnd - the
// final ']'; the positions computed for the reference components would be j..a l..g and N..r
// last ']'; the positions computed for the reference components would be j..a l..g and N..r
char[][] compoundName = CharOperation.splitOn('.', typeName);
ArrayQualifiedTypeReference tr = new ArrayQualifiedTypeReference(compoundName, dim,
positionsFor(compoundName, sourceStart, (sourceEnd == -2 ? -2 : sourceEnd - dim * 2)));
ArrayQualifiedTypeReference tr = new ArrayQualifiedTypeReference(compoundName, dim, positionsFor(compoundName, sourceStart, nameEnd - 1));
tr.sourceEnd = sourceEnd == -2 ? -2 : sourceEnd - 1;
return tr;
}
Expand All @@ -2082,25 +2086,32 @@ private TypeReference createTypeReferenceForArrayName(char[] typeName, ClassNode
}

if (CharOperation.indexOf('.', typeName) < 0) {
ParameterizedSingleTypeReference tr = new ParameterizedSingleTypeReference(typeName, typeArgs, dim, toPos(sourceStart, sourceEnd - 1));
tr.originalSourceEnd = typeNode.getEnd() - 1;
ParameterizedSingleTypeReference tr = new ParameterizedSingleTypeReference(typeName, typeArgs, dim, toPos(sourceStart, nameEnd - 1));
tr.sourceEnd = sourceEnd == -2 ? -2 : sourceEnd - 1;
return tr;
} else {
char[][] compoundName = CharOperation.splitOn('.', typeName);
TypeReference[][] compoundArgs = new TypeReference[compoundName.length][];
compoundArgs[compoundName.length - 1] = typeArgs;
ParameterizedQualifiedTypeReference tr = new ParameterizedQualifiedTypeReference(compoundName, compoundArgs, dim,
positionsFor(compoundName, sourceStart, (sourceEnd == -2 ? -2 : sourceEnd - dim * 2)));
ParameterizedQualifiedTypeReference tr = new ParameterizedQualifiedTypeReference(compoundName, compoundArgs, dim, positionsFor(compoundName, sourceStart, nameEnd - 1));
tr.sourceEnd = sourceEnd == -2 ? -2 : sourceEnd - 1;
return tr;
}
}
}

private TypeReference createTypeReferenceForClassLiteral(PropertyExpression expression) {
int arrayDims = 0;
// FIXASC ignore type parameters for now
Expression candidate = expression.getObjectExpression();
List<char[]> nameParts = new LinkedList<>();
while (candidate instanceof BinaryExpression) {
assert ((BinaryExpression) candidate).getOperation().getType() == Types.LEFT_SQUARE_BRACKET;
assert ((BinaryExpression) candidate).getRightExpression() instanceof ListExpression;
candidate = ((BinaryExpression) candidate).getLeftExpression();
arrayDims += 1;
}

List<char[]> nameParts = new ArrayList<>(8);
while (candidate instanceof PropertyExpression) {
nameParts.add(0, ((PropertyExpression) candidate).getPropertyAsString().toCharArray());
candidate = ((PropertyExpression) candidate).getObjectExpression();
Expand All @@ -2109,13 +2120,23 @@ private TypeReference createTypeReferenceForClassLiteral(PropertyExpression expr
nameParts.add(0, ((VariableExpression) candidate).getName().toCharArray());
}
char[][] namePartsArr = nameParts.toArray(new char[nameParts.size()][]);
long[] poss = positionsFor(namePartsArr, expression.getObjectExpression().getStart(), expression.getObjectExpression().getEnd());
long[] poss = positionsFor(namePartsArr, expression.getObjectExpression().getStart(), expression.getObjectExpression().getEnd() - (arrayDims * 2));

TypeReference ref;
if (namePartsArr.length > 1) {
ref = new QualifiedTypeReference(namePartsArr, poss);
if (arrayDims > 0) {
ref = new ArrayQualifiedTypeReference(namePartsArr, arrayDims, poss);
ref.sourceEnd = expression.getObjectExpression().getEnd() - 1;
} else {
ref = new QualifiedTypeReference(namePartsArr, poss);
}
} else if (namePartsArr.length == 1) {
ref = new SingleTypeReference(namePartsArr[0], poss[0]);
if (arrayDims > 0) {
ref = new ArrayTypeReference(namePartsArr[0], arrayDims, poss[0]);
ref.sourceEnd = expression.getObjectExpression().getEnd() - 1;
} else {
ref = new SingleTypeReference(namePartsArr[0], poss[0]);
}
} else { // should not happen
ref = TypeReference.baseTypeReference(TypeIds.T_void, 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-2022 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 @@ -525,6 +525,14 @@ public static boolean implementsTrait(ClassNode concreteType) {
}).booleanValue();
}

public static ClassNode makeType(String typeNameWithoutGenerics) {
int i = typeNameWithoutGenerics.lastIndexOf('[');
if (i < 0) {
return ClassHelper.make(typeNameWithoutGenerics);
}
return makeType(typeNameWithoutGenerics.substring(0, i)).makeArray();
}

public static void updateClosureWithInferredTypes(ClassNode closure, ClassNode returnType, Parameter[] parameters) {
if (!"groovy.lang.Closure".equals(closure.getName()) || closure == closure.redirect()) {
return;
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-2022 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,12 +15,13 @@
*/
package org.eclipse.jdt.groovy.search;

import static org.eclipse.jdt.groovy.core.util.GroovyUtils.makeType;

import java.util.Optional;
import java.util.function.Function;

import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.AnnotatedNode;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ConstructorNode;
import org.codehaus.groovy.ast.Parameter;
Expand Down Expand Up @@ -142,12 +143,4 @@ protected boolean hasMatchingParameters(Parameter[] declarationParameters) {
}
return false;
}

protected static ClassNode makeType(String typeName) {
int i = typeName.indexOf('[');
if (i < 0) {
return ClassHelper.make(typeName);
}
return makeType(typeName.substring(0, i)).makeArray();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2020 the original author or authors.
* Copyright 2009-2022 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 @@ -361,7 +361,7 @@ private boolean argumentTypesMatch(List<ClassNode> argumentTypes, ClassNode decl
if (argumentTypes.size() == parameterTypeNames.length) {
for (int i = 0; i < parameterTypeNames.length; i += 1) {
if (parameterTypeNames[i] == null) continue; // skip check
ClassNode source = argumentTypes.get(i), target = ConstructorReferenceSearchRequestor.makeType(parameterTypeNames[i]);
ClassNode source = argumentTypes.get(i), target = GroovyUtils.makeType(parameterTypeNames[i]);
if (Boolean.FALSE.equals(SimpleTypeLookup.isTypeCompatible(source, target))) {
return false;
}
Expand Down

0 comments on commit c36f6e0

Please sign in to comment.