Skip to content
Permalink
Browse files
GROOVY-10502: NamedVariant: improve consistency of default value trea…
…tment
  • Loading branch information
paulk-asert committed Feb 21, 2022
1 parent df346d5 commit a842616cbd0c2b189f223abe5036d95b67f0a8d3
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 10 deletions.
@@ -57,6 +57,7 @@
import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching;
import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
import static org.codehaus.groovy.ast.tools.GeneralUtils.asX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
import static org.codehaus.groovy.ast.tools.GeneralUtils.boolX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
@@ -68,14 +69,16 @@
import static org.codehaus.groovy.ast.tools.GeneralUtils.elvisX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.entryX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.getAllProperties;
import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS;
import static org.codehaus.groovy.ast.tools.GeneralUtils.isNullX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.list2args;
import static org.codehaus.groovy.ast.tools.GeneralUtils.mapX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.notNullX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
import static org.codehaus.groovy.ast.tools.GeneralUtils.plusX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
import static org.codehaus.groovy.ast.tools.GeneralUtils.ternaryX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.throwS;
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;

@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
@@ -85,6 +88,7 @@ public class NamedVariantASTTransformation extends AbstractASTTransformation {
private static final String NAMED_VARIANT = "@" + NAMED_VARIANT_TYPE.getNameWithoutPackage();
private static final ClassNode NAMED_PARAM_TYPE = makeWithoutCaching(NamedParam.class, false);
private static final ClassNode NAMED_DELEGATE_TYPE = makeWithoutCaching(NamedDelegate.class, false);
private static final ClassNode ILLEGAL_ARGUMENT = makeWithoutCaching(IllegalArgumentException.class);

@Override
public void visit(final ASTNode[] nodes, final SourceUnit source) {
@@ -101,7 +105,7 @@ public void visit(final ASTNode[] nodes, final SourceUnit source) {

boolean autoDelegate = memberHasValue(anno, "autoDelegate", true);
boolean coerce = memberHasValue(anno, "coerce", true);
Parameter mapParam = param(GenericsUtils.nonGeneric(MAP_TYPE), "__namedArgs");
Parameter mapParam = param(GenericsUtils.nonGeneric(MAP_TYPE), "namedArgs");
List<Parameter> genParams = new ArrayList<>();
genParams.add(mapParam);
ClassNode cNode = mNode.getDeclaringClass();
@@ -141,7 +145,7 @@ public void visit(final ASTNode[] nodes, final SourceUnit source) {
createMapVariant(this, mNode, anno, mapParam, genParams, cNode, inner, args, propNames);
}

static boolean processImplicitNamedParam(final ErrorCollecting xform, final MethodNode mNode, final Parameter mapParam, final BlockStatement inner, final ArgumentListExpression args, final List<String> propNames, final Parameter fromParam, final boolean coerce) {
static boolean processImplicitNamedParam(final ErrorCollecting xform, final MethodNode mNode, final Parameter mapParam, final BlockStatement inner, final ArgumentListExpression args, final List<String> propNames, final Parameter fromParam, final boolean coerce) {
String name = fromParam.getName();
ClassNode type = fromParam.getType();
boolean required = !fromParam.hasInitialExpression();
@@ -233,6 +237,10 @@ private static boolean hasDuplicates(final ErrorCollecting xform, final MethodNo

static void createMapVariant(final ErrorCollecting xform, final MethodNode mNode, final AnnotationNode anno, final Parameter mapParam, final List<Parameter> genParams, final ClassNode cNode, final BlockStatement inner, final ArgumentListExpression args, final List<String> propNames) {
Parameter namedArgKey = param(STRING_TYPE, "namedArgKey");
if (!(mNode instanceof ConstructorNode)) {
inner.getStatements().add(0, ifS(isNullX(varX(mapParam)),
throwS(ctorX(ILLEGAL_ARGUMENT, args(constX("Named parameter map cannot be null"))))));
}
inner.addStatement(
new ForStatement(
namedArgKey,
@@ -280,9 +288,6 @@ private static Expression namedParamValue(final Parameter mapParam, final String
defaultValue = defaultValueX(type);
}
if (defaultValue != null) {
if (isPrimitiveType(type)) { // handle null for primitive
value = ternaryX(notNullX(value), value, defaultValueX(type));
}
value = ternaryX(containsKey(mapParam, name), value, defaultValue);
}
return asType(value, type, coerce);
@@ -76,8 +76,11 @@ assert new ColoredPoint(5).toString() == 'ColoredPoint[x=5, y=0, color=white]'
assert new ColoredPoint(x: 5).toString() == 'ColoredPoint[x=5, y=0, color=white]'
assert new ColoredPoint(x: 0, y: 5).toString() == 'ColoredPoint[x=0, y=5, color=white]'
// end::record_point_named_args[]
assert new ColoredPoint(x: 0, y: null).toString() == 'ColoredPoint[x=0, y=0, color=white]'
def ex = shouldFail { new ColoredPoint(x: 0, z: 5) }
def ex = shouldFail(ClassCastException) { new ColoredPoint(x: 0, y: null) }
assert ex.message.contains("Cannot cast object 'null' with class 'null' to class 'int'")
ex = shouldFail(ClassCastException) { new ColoredPoint(x: null) }
assert ex.message.contains("Cannot cast object 'null' with class 'null' to class 'int'")
ex = shouldFail { new ColoredPoint(x: 0, z: 5) }
assert ex.message.contains('Unrecognized namedArgKey: z')
// tag::record_point_named_args_off[]
@TupleConstructor(defaultsMode=DefaultsMode.OFF)
@@ -229,6 +229,7 @@ final class NamedVariantTransformTest {

assertScript '''
import groovy.transform.*
import static groovy.test.GroovyAssert.shouldFail
@NamedVariant
def m(int one, int two = 42) {
@@ -238,8 +239,8 @@ final class NamedVariantTransformTest {
String result = m(one:0, two:0)
assert result == '0 0'
result = m(one:0, two:null)
assert result == '0 0'
shouldFail(MissingMethodException) { m(one:null) }
shouldFail(MissingMethodException) { m(one:0, two:null) }
'''
}

0 comments on commit a842616

Please sign in to comment.