Skip to content

Commit

Permalink
Add ability to get constantValue for final fields (#10693)
Browse files Browse the repository at this point in the history
* Add ability to get constantValue for final fields

Fixed #10692

* Update core-processor/src/main/java/io/micronaut/inject/ast/FieldElement.java

---------

Co-authored-by: Sergio del Amo <sergio.delamo@softamo.com>
  • Loading branch information
altro3 and sdelamo committed Apr 10, 2024
1 parent 5e6a9b0 commit 3182849
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,30 @@
*/
public interface FieldElement extends TypedElement, MemberElement {

/**
* Returns the value of this variable if this is a {@code final}
* field initialized to a compile-time constant. Returns {@code
* null} otherwise. The value will be of a primitive type or a
* {@code String}. If the value is of a primitive type, it is
* wrapped in the appropriate wrapper class (such as {@link
* Integer}).
*
* <p>Note that not all {@code final} fields will have
* constant values. In particular, {@code enum} constants are
* <em>not</em> considered to be compile-time constants. To have a
* constant value, a field's type must be either a primitive type
* or {@code String}.
*
* @return the value of this variable if this is a {@code final}
* field initialized to a compile-time constant, or {@code null}
* otherwise
*
* @since 4.5.0
*/
default Object getConstantValue() {
return null;
}

/**
* Obtain the generic type with the associated annotation metadata for the field.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.micronaut.inject.ast.annotation.ElementAnnotationMetadataFactory;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.expr.ConstantExpression;

import java.lang.reflect.Modifier;
import java.util.Map;
Expand Down Expand Up @@ -149,6 +150,15 @@ public boolean isPackagePrivate() {
return !Modifier.isPublic(fieldNode.getModifiers()) && !Modifier.isProtected(fieldNode.getModifiers()) && !Modifier.isPrivate(fieldNode.getModifiers());
}

@Override
public Object getConstantValue() {
if (fieldNode.hasInitialExpression()
&& fieldNode.getInitialValueExpression() instanceof ConstantExpression constExpression) {
return constExpression.getValue();
}
return null;
}

@NonNull
@Override
public ClassElement getType() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,36 @@ enum MyEnum {
enumConstantAnnotation.stringValue().get() == "C"
}
}

void "get enum constantValue for final fields"() {
given:
def element = (EnumElement) buildClassElement("""
package test
enum MyEnum {
ENUM_VAL1(10),
ENUM_VAL2(11),
UNRECOGNIZED(-1),
;
public static final int ENUM_VAL1_VALUE = 10
public static final int ENUM_VAL2_VALUE = 11
private final int value
MyEnum(int value) {
this.value = value
}
}
""")
expect:
for (def field : element.fields) {
if (field.name == 'ENUM_VAL1_VALUE') {
field.constantValue == 10
} else if (field.name == 'ENUM_VAL2_VALUE') {
field.constantValue == 11
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ public FieldElement withAnnotationMetadata(AnnotationMetadata annotationMetadata
return (FieldElement) super.withAnnotationMetadata(annotationMetadata);
}

@Override
public Object getConstantValue() {
return variableElement.getConstantValue();
}

@NonNull
@Override
public ClassElement getType() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,36 @@ enum MyEnum {
expect:
element.values().size() == 2;
}

void "get enum constantValue for final fields"() {
given:
def element = (EnumElement) buildClassElement("""
package test;
enum MyEnum {
ENUM_VAL1(10),
ENUM_VAL2(11),
UNRECOGNIZED(-1),
;
public static final int ENUM_VAL1_VALUE = 10;
public static final int ENUM_VAL2_VALUE = 11;
private final int value;
MyEnum(int value) {
this.value = value;
}
}
""")
expect:
for (def field : element.fields) {
if (field.name == 'ENUM_VAL1_VALUE') {
field.constantValue == 10
} else if (field.name == 'ENUM_VAL2_VALUE') {
field.constantValue == 11
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package io.micronaut.kotlin.processing.ast.visitor

import io.micronaut.annotation.processing.test.AbstractKotlinCompilerSpec
import io.micronaut.inject.ast.ClassElement
import io.micronaut.inject.ast.ElementQuery
import io.micronaut.inject.ast.EnumElement
import spock.lang.Ignore
import spock.lang.PendingFeature

class KotlinEnumElementSpec extends AbstractKotlinCompilerSpec {

void "test is enum"() {
given:
def element = buildClassElement("test.MyEnum", """
package test
enum class MyEnum {
A, B
}
""")
expect:
element.isEnum()
element.getPrimaryConstructor().get().getDeclaringType().isEnum()
}

void "test inner enum is enum"() {
given:
def element = buildClassElement("test.Foo","""
package test
class Foo {
enum class MyEnum {
A, B
}
}
""")
expect:
element.getEnclosedElement(ElementQuery.of(ClassElement.class)).get().getPrimaryConstructor().get().getDeclaringType().isEnum()
}

@Ignore
@PendingFeature(reason = "constantValue not available with KSP")
void "get enum constantValue for final fields"() {
given:
def element = (EnumElement) buildClassElement("test.MyEnum", """
package test
enum class MyEnum(
value: Int
) {
ENUM_VAL1(10),
ENUM_VAL2(11),
UNRECOGNIZED(-1),
;
companion object {
const val ENUM_VAL1_VALUE = 10
const val ENUM_VAL2_VALUE = 11
}
}
""")
expect:
for (def field : element.fields) {
if (field.name == 'ENUM_VAL1_VALUE') {
field.constantValue == 10
} else if (field.name == 'ENUM_VAL2_VALUE') {
field.constantValue == 11
}
}
}
}

0 comments on commit 3182849

Please sign in to comment.