Skip to content

Commit

Permalink
[lang] Generating type parameters in default valued parameter functions.
Browse files Browse the repository at this point in the history
close #723

Signed-off-by: Stéphane Galland <galland@arakhne.org>
  • Loading branch information
gallandarakhneorg committed Aug 23, 2017
1 parent 8b4cffb commit 3172093
Show file tree
Hide file tree
Showing 2 changed files with 255 additions and 35 deletions.
Expand Up @@ -53,6 +53,7 @@
import com.google.inject.Inject;
import com.google.inject.MembersInjector;
import com.google.inject.Singleton;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
Expand Down Expand Up @@ -1821,19 +1822,22 @@ protected void transform(final XtendFunction source, final JvmGenericType contai
operation2.setVarArgs(operation.isVarArgs());
operation2.setAbstract(operation.isAbstract());
operation2.setDeprecated(operation.isDeprecated());
operation2.setReturnType(
SARLJvmModelInferrer.this.typeBuilder.cloneWithProxies(selectedReturnType));
operation2.setStatic(operation.isStatic());
operation2.setFinal(!operation.isStatic() && !container.isInterface());
operation2.setNative(false);
operation2.setStrictFloatingPoint(false);
operation2.setSynchronized(false);

copyTypeParametersFromJvmOperation(operation, operation2);

for (final JvmTypeReference exception : operation.getExceptions()) {
operation2.getExceptions().add(SARLJvmModelInferrer.this.typeBuilder
.cloneWithProxies(exception));
}

operation2.setReturnType(
cloneWithTypeParametersAndProxies(selectedReturnType, operation2));

translateAnnotationsTo(source.getAnnotations(), operation2);
if (source.isOverride()
&& SARLJvmModelInferrer.this.annotationFinder.findAnnotation(operation,
Expand Down Expand Up @@ -2864,12 +2868,25 @@ protected void appendSARLElementType(XtendTypeDeclaration source, JvmDeclaredTyp
}

/** Remove the type parameters from the given type.
*
* <p><table>
* <thead><tr><th>Referenced type</th><th>Input</th><th>Replied referenced type</th><th>Output</th></tr></thead>
* <tbody>
* <tr><td>Type with generic type parameter</td><td>{@code T<G>}</td><td>the type itself</td><td>{@code T}</td></tr>
* <tr><td>Type without generic type parameter</td><td>{@code T}</td><td>the type itself</td><td>{@code T}</td></tr>
* <tr><td>Type parameter without bound</td><td>{@code <S>}</td><td>{@code Object}</td><td>{@code Object}</td></tr>
* <tr><td>Type parameter with lower bound</td><td>{@code <S super B>}</td><td>{@code Object}</td><td>{@code Object}</td></tr>
* <tr><td>Type parameter with upper bound</td><td>{@code <S extends B>}</td><td>the bound type</td><td>{@code B}</td></tr>
* </tbody>
* </table>
*
* @param type the type.
* @param context the context in which the reference is located.
* @return the same type without the type parameters.
*/
protected JvmTypeReference skipTypeParameters(JvmTypeReference type) {
return this._typeReferenceBuilder.typeRef(type.getType());
protected JvmTypeReference skipTypeParameters(JvmTypeReference type, Notifier context) {
final LightweightTypeReference ltr = Utils.toLightweightTypeReference(type, this.services);
return ltr.getRawTypeReference().toJavaCompliantTypeReference();
}

/** Generate a list of formal parameters with annotations for the default values.
Expand Down Expand Up @@ -2912,8 +2929,9 @@ protected void translateSarlFormalParameters(
assert inferredParam != null;
final String namePostPart = inferredParam.getDefaultValueAnnotationValue();
final String name = this.sarlSignatureProvider.createFieldNameForDefaultValueID(namePostPart);
final JvmTypeReference fieldType = skipTypeParameters(paramType, actionContainer);
// FIXME: Hide these attributes into an inner interface.
final JvmField field = this.typeBuilder.toField(defaultValue, name, skipTypeParameters(paramType), (it) -> {
final JvmField field = this.typeBuilder.toField(defaultValue, name, fieldType, (it) -> {
SARLJvmModelInferrer.this.typeBuilder.setDocumentation(it,
MessageFormat.format(Messages.SARLJvmModelInferrer_11, paramName));
it.setStatic(true);
Expand Down Expand Up @@ -2956,20 +2974,30 @@ protected List<String> translateSarlFormalParametersForSyntheticOperation(JvmExe
boolean varargs, List<InferredStandardParameter> signature) {
final List<String> arguments = CollectionLiterals.newArrayList();
for (final InferredStandardParameter parameterSpec : signature) {
final JvmTypeReference paramType = parameterSpec.getType();
if (parameterSpec instanceof InferredValuedParameter) {
arguments.add(
this.sarlSignatureProvider.toJavaArgument(
actionContainer.getIdentifier(),
((InferredValuedParameter) parameterSpec).getCallingArgument()));
final StringBuilder argumentValue = new StringBuilder();
if (paramType.getType() instanceof JvmTypeParameter) {
argumentValue.append("("); //$NON-NLS-1$
argumentValue.append(paramType.getSimpleName());
argumentValue.append(") "); //$NON-NLS-1$
}
argumentValue.append(this.sarlSignatureProvider.toJavaArgument(
actionContainer.getIdentifier(),
((InferredValuedParameter) parameterSpec).getCallingArgument()));
arguments.add(argumentValue.toString());
} else {
final EObject param = parameterSpec.getParameter();
final String paramName = parameterSpec.getName();
final JvmTypeReference paramType = parameterSpec.getType();
if (!Strings.isNullOrEmpty(paramName) && paramType != null) {
final JvmFormalParameter lastParam = this.typesFactory.createJvmFormalParameter();
owner.getParameters().add(lastParam);
lastParam.setName(paramName);
lastParam.setParameterType(this.typeBuilder.cloneWithProxies(paramType));
if (owner instanceof JvmOperation) {
lastParam.setParameterType(cloneWithTypeParametersAndProxies(paramType, (JvmOperation) owner));
} else {
lastParam.setParameterType(this.typeBuilder.cloneWithProxies(paramType));
}
this.associator.associate(param, lastParam);
arguments.add(paramName);
}
Expand Down Expand Up @@ -3250,7 +3278,7 @@ protected JvmTypeReference cloneWithTypeParametersAndProxies(JvmTypeReference ty
// Do the clone according to the type of the entity.
assert typeCandidate != null;
final JvmTypeReference returnType;
if (InferredTypeIndicator.isInferred(typeCandidate) || !cloneType) {
if (!cloneType) {
returnType = typeCandidate;
} else {
returnType = this.typeBuilder.cloneWithProxies(typeCandidate);
Expand Down Expand Up @@ -3422,6 +3450,42 @@ private String reentrantSerialize(EObject object) {
return code;
}

/** Copy the type parameters from a JvmOperation.
*
* <p>This function differs from {@link #copyAndFixTypeParameters(List, org.eclipse.xtext.common.types.JvmTypeParameterDeclarator)}
* and {@link #copyTypeParameters(List, org.eclipse.xtext.common.types.JvmTypeParameterDeclarator)}
* in the fact that the type parameters were already generated and fixed. The current function supper generic types by
* clone the types references with {@link #cloneWithTypeParametersAndProxies(JvmTypeReference, JvmOperation)}.
*
* @param fromOperation the operation from which the type parameters are copied.
* @param toOperation the operation that will receives the new type parameters.
*/
protected void copyTypeParametersFromJvmOperation(JvmOperation fromOperation, JvmOperation toOperation) {
// Copy the generic types in two steps: first step is the name's copy.
for (final JvmTypeParameter typeParameter : fromOperation.getTypeParameters()) {
final JvmTypeParameter typeParameterCopy = this.typesFactory.createJvmTypeParameter();
typeParameterCopy.setName(typeParameter.getName());
toOperation.getTypeParameters().add(typeParameterCopy);
}
// Second step is the constraints' copy
for (int i = 0; i < fromOperation.getTypeParameters().size(); ++i) {
final JvmTypeParameter typeParameter = fromOperation.getTypeParameters().get(i);
final JvmTypeParameter typeParameterCopy = toOperation.getTypeParameters().get(i);
for (final JvmTypeConstraint constraint : typeParameter.getConstraints()) {
JvmTypeConstraint cst = null;
if (constraint instanceof JvmLowerBound) {
cst = this.typesFactory.createJvmLowerBound();
} else if (constraint instanceof JvmUpperBound) {
cst = this.typesFactory.createJvmUpperBound();
}
if (cst != null) {
typeParameterCopy.getConstraints().add(cst);
cst.setTypeReference(cloneWithTypeParametersAndProxies(constraint.getTypeReference(), toOperation));
}
}
}
}

/** Copy the JVM operations from the source to the destination.
*
* @param source the source.
Expand Down Expand Up @@ -3461,29 +3525,7 @@ protected void copyNonStaticPublicJvmOperations(JvmGenericType source, JvmGeneri
newOp.setSimpleName(operation.getSimpleName());
newOp.setStrictFloatingPoint(operation.isStrictFloatingPoint());

// Copy the generic types in two steps: first step is the name's copy.
for (final JvmTypeParameter typeParameter : operation.getTypeParameters()) {
final JvmTypeParameter typeParameterCopy = this.typesFactory.createJvmTypeParameter();
typeParameterCopy.setName(typeParameter.getName());
newOp.getTypeParameters().add(typeParameterCopy);
}
// Second step is the constraints' copy
for (int i = 0; i < operation.getTypeParameters().size(); ++i) {
final JvmTypeParameter typeParameter = operation.getTypeParameters().get(i);
final JvmTypeParameter typeParameterCopy = newOp.getTypeParameters().get(i);
for (final JvmTypeConstraint constraint : typeParameter.getConstraints()) {
JvmTypeConstraint cst = null;
if (constraint instanceof JvmLowerBound) {
cst = this.typesFactory.createJvmLowerBound();
} else if (constraint instanceof JvmUpperBound) {
cst = this.typesFactory.createJvmUpperBound();
}
if (cst != null) {
typeParameterCopy.getConstraints().add(cst);
cst.setTypeReference(cloneWithTypeParametersAndProxies(constraint.getTypeReference(), newOp));
}
}
}
copyTypeParametersFromJvmOperation(operation, newOp);

for (final JvmTypeReference exception : operation.getExceptions()) {
newOp.getExceptions().add(cloneWithTypeParametersAndProxies(exception, newOp));
Expand Down
@@ -0,0 +1,178 @@
/*
* Copyright (C) 2014-2017 the original authors or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.sarl.lang.tests.bugs.to00999;

import com.google.inject.Inject;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.testing.CompilationTestHelper;
import org.junit.Test;

import io.sarl.lang.SARLVersion;
import io.sarl.lang.sarl.SarlPackage;
import io.sarl.lang.sarl.SarlScript;
import io.sarl.tests.api.AbstractSarlTest;

/** Testing class for issue: Invalid generic default value generation.
*
* <p>https://github.com/sarl/sarl/issues/723
*
* @author $Author: sgalland$
* @version $Name$ $Revision$ $Date$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@SuppressWarnings("all")
public class Bug723 extends AbstractSarlTest {

private static final String SNIPSET1 = multilineString(
"package io.sarl.lang.tests.bug723",
"class MyClass",
"{",
" static def getSystemPropertyAsClass(type : Class<S>, name : String,",
" defaultValue : Class<? extends S> = null) : Class<? extends S> with S {",
" }",
"}");

private final String EXPECTED1 = multilineString(
"package io.sarl.lang.tests.bug723;",
"",
"import io.sarl.lang.annotation.DefaultValue;",
"import io.sarl.lang.annotation.DefaultValueSource;",
"import io.sarl.lang.annotation.DefaultValueUse;",
"import io.sarl.lang.annotation.SarlElementType;",
"import io.sarl.lang.annotation.SarlSourceCode;",
"import io.sarl.lang.annotation.SarlSpecification;",
"import io.sarl.lang.annotation.SyntheticMember;",
"import org.eclipse.xtext.xbase.lib.Pure;",
"",
"@SarlSpecification(\"" + SARLVersion.SPECIFICATION_RELEASE_VERSION_STRING + "\")",
"@SarlElementType(" + SarlPackage.SARL_CLASS + ")",
"@SuppressWarnings(\"all\")",
"public class MyClass {",
" @DefaultValueSource",
" @Pure",
" public static <S extends Object> Class<? extends S> getSystemPropertyAsClass(final Class<S> type, final String name, @DefaultValue(\"io.sarl.lang.tests.bug723.MyClass#GETSYSTEMPROPERTYASCLASS_0\") final Class<? extends S> defaultValue) {",
" return null;",
" }",
" ",
" /**",
" * Default value for the parameter defaultValue",
" */",
" @SyntheticMember",
" @SarlSourceCode(\"null\")",
" private final static Class $DEFAULT_VALUE$GETSYSTEMPROPERTYASCLASS_0 = null;",
" ",
" @DefaultValueUse(\"java.lang.Class<S>,java.lang.String,java.lang.Class<? extends S>\")",
" @SyntheticMember",
" @Pure",
" public static <S extends Object> Class<? extends S> getSystemPropertyAsClass(final Class<S> type, final String name) {",
" return getSystemPropertyAsClass(type, name, $DEFAULT_VALUE$GETSYSTEMPROPERTYASCLASS_0);",
" }",
" ",
" @SyntheticMember",
" public MyClass() {",
" super();",
" }",
"}",
"");

private static final String SNIPSET2 = multilineString(
"package io.sarl.lang.tests.bug723",
"class MyClass",
"{",
" static def getSystemPropertyAsEnum(type : Class<S>, name : String,",
" defaultValue : S = null) : S with S extends Enum<S> {",
" }",
"}");

private final String EXPECTED2 = multilineString(
"package io.sarl.lang.tests.bug723;",
"",
"import io.sarl.lang.annotation.DefaultValue;",
"import io.sarl.lang.annotation.DefaultValueSource;",
"import io.sarl.lang.annotation.DefaultValueUse;",
"import io.sarl.lang.annotation.SarlElementType;",
"import io.sarl.lang.annotation.SarlSourceCode;",
"import io.sarl.lang.annotation.SarlSpecification;",
"import io.sarl.lang.annotation.SyntheticMember;",
"import org.eclipse.xtext.xbase.lib.Pure;",
"",
"@SarlSpecification(\"" + SARLVersion.SPECIFICATION_RELEASE_VERSION_STRING + "\")",
"@SarlElementType(" + SarlPackage.SARL_CLASS + ")",
"@SuppressWarnings(\"all\")",
"public class MyClass {",
" @DefaultValueSource",
" @Pure",
" public static <S extends Enum<S>> S getSystemPropertyAsEnum(final Class<S> type, final String name, @DefaultValue(\"io.sarl.lang.tests.bug723.MyClass#GETSYSTEMPROPERTYASENUM_0\") final S defaultValue) {",
" return null;",
" }",
" ",
" /**",
" * Default value for the parameter defaultValue",
" */",
" @SyntheticMember",
" @SarlSourceCode(\"null\")",
" private final static Enum $DEFAULT_VALUE$GETSYSTEMPROPERTYASENUM_0 = null;",
" ",
" @DefaultValueUse(\"java.lang.Class<S>,java.lang.String,S\")",
" @SyntheticMember",
" @Pure",
" public static <S extends Enum<S>> S getSystemPropertyAsEnum(final Class<S> type, final String name) {",
" return getSystemPropertyAsEnum(type, name, (S) $DEFAULT_VALUE$GETSYSTEMPROPERTYASENUM_0);",
" }",
" ",
" @SyntheticMember",
" public MyClass() {",
" super();",
" }",
"}",
"");

@Inject
private CompilationTestHelper compiler;

@Test
public void parsing_01() throws Exception {
SarlScript mas = file(SNIPSET1);
final Validator validator = validate(mas);
validator.assertNoErrors();
}

@Test
public void compiling_01() throws Exception {
this.compiler.compile(SNIPSET1, (it) -> {
final String actual = it.getGeneratedCode("io.sarl.lang.tests.bug723.MyClass");
assertEquals(EXPECTED1, actual);
});
}

@Test
public void parsing_02() throws Exception {
SarlScript mas = file(SNIPSET2);
final Validator validator = validate(mas);
validator.assertNoErrors();
}

@Test
public void compiling_02() throws Exception {
this.compiler.compile(SNIPSET2, (it) -> {
final String actual = it.getGeneratedCode("io.sarl.lang.tests.bug723.MyClass");
assertEquals(EXPECTED2, actual);
});
}

}

0 comments on commit 3172093

Please sign in to comment.