Skip to content

Commit

Permalink
fix: keep track of declaring type for nested field accesses (#5408)
Browse files Browse the repository at this point in the history
  • Loading branch information
SirYwell authored Aug 30, 2023
1 parent 21f8b6b commit ec757f7
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 5 deletions.
3 changes: 2 additions & 1 deletion qodana.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ include:
- name: AssignmentToCatchBlockParameter
- name: AssignmentToLambdaParameter
- name: AssignmentToMethodParameter
- name: AssignmentToNull
# disabled as it does not support nullability annotations
#- name: AssignmentToNull
- name: Convert2Lambda
- name: DoubleBraceInitialization
- name: EqualsAndHashcode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
import org.jspecify.annotations.Nullable;
import spoon.SpoonException;
import spoon.reflect.code.CtCatchVariable;
import spoon.reflect.code.CtExecutableReferenceExpression;
Expand Down Expand Up @@ -283,11 +284,11 @@ <T> CtVariableAccess<T> createVariableAccess(QualifiedNameReference qualifiedNam
int i = 0; //positions index;
va.setPosition(jdtTreeBuilder.getPositionBuilder().buildPosition(sourceStart, sourceEnd));
sourceStart = (int) (positions[qualifiedNameReference.indexOfFirstFieldBinding - 1] >>> 32);
@Nullable TypeBinding declaringType = ((VariableBinding) qualifiedNameReference.binding).type;
for (FieldBinding b : qualifiedNameReference.otherBindings) {
isOtherBinding = qualifiedNameReference.otherBindings.length == i + 1;
TypeBinding type = ((VariableBinding) qualifiedNameReference.binding).type;
CtFieldAccess<T> other = createFieldAccess(
jdtTreeBuilder.getReferencesBuilder().getVariableReference(type, b, qualifiedNameReference.tokens[i + 1]), va, isOtherBinding && fromAssignment);
jdtTreeBuilder.getReferencesBuilder().getVariableReference(declaringType, b, qualifiedNameReference.tokens[i + 1]), va, isOtherBinding && fromAssignment);
//set source position of fa
if (i + qualifiedNameReference.indexOfFirstFieldBinding >= qualifiedNameReference.otherBindings.length) {
sourceEnd = qualifiedNameReference.sourceEnd();
Expand All @@ -296,6 +297,7 @@ <T> CtVariableAccess<T> createVariableAccess(QualifiedNameReference qualifiedNam
}
other.setPosition(jdtTreeBuilder.getPositionBuilder().buildPosition(sourceStart, sourceEnd));
va = other;
declaringType = b != null ? b.type : null; // no classpath mode might cause b to be null
i++;
}
} else if (!(qualifiedNameReference.binding instanceof FieldBinding) && qualifiedNameReference.tokens.length > 1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.VoidTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
import org.jspecify.annotations.Nullable;
import spoon.NoClasspathWorkaround;
import spoon.reflect.code.CtLambda;
import spoon.reflect.declaration.CtModule;
Expand Down Expand Up @@ -1195,7 +1196,7 @@ private CtTypeReference<?> getCtCircularTypeReference(TypeBinding b) {
return bindingCache.get(b).clone();
}

<T> CtFieldReference<T> getVariableReference(TypeBinding type, FieldBinding varbin) {
<T> CtFieldReference<T> getVariableReference(@Nullable TypeBinding type, FieldBinding varbin) {
CtFieldReference<T> ref = this.jdtTreeBuilder.getFactory().Core().createFieldReference();
if (varbin == null) {
return ref;
Expand All @@ -1212,7 +1213,7 @@ <T> CtFieldReference<T> getVariableReference(TypeBinding type, FieldBinding varb
return ref;
}

<T> CtFieldReference<T> getVariableReference(TypeBinding type, FieldBinding fieldBinding, char[] tokens) {
<T> CtFieldReference<T> getVariableReference(@Nullable TypeBinding type, FieldBinding fieldBinding, char[] tokens) {
final CtFieldReference<T> ref = getVariableReference(type, fieldBinding);
if (fieldBinding != null) {
return ref;
Expand Down
22 changes: 22 additions & 0 deletions src/test/java/spoon/test/field/FieldTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.factory.Factory;
import spoon.reflect.reference.CtArrayTypeReference;
import spoon.reflect.reference.CtFieldReference;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.filter.TypeFilter;
Expand Down Expand Up @@ -272,4 +273,25 @@ void testArrayLengthModifiers() {
assertEquals(Set.of(ModifierKind.PUBLIC, ModifierKind.FINAL), elements.get(0).getModifiers());
}

@Test
void testArrayLengthDeclaringTypeNested() {
// contract: the declaring type of a "length" access on arrays is set even when nested
Launcher launcher = new Launcher();
launcher.addInputResource(new VirtualFile(
"public class Example {\n" +
" public String[] array = new String[4];\n" +
" public static void main(String[] args) {\n" +
" Example other = new Example();\n" +
" int i = other.array.length;\n" +
" }\n" +
"}"
));

CtModel ctModel = launcher.buildModel();
List<CtFieldReference<?>> elements = ctModel.getElements(new TypeFilter<>(CtFieldReference.class));
assertEquals(2, elements.size());
CtArrayTypeReference<?> stringArrayRef = launcher.getFactory().createArrayReference("java.lang.String");
assertEquals(stringArrayRef, elements.get(1).getDeclaringType());
}

}

0 comments on commit ec757f7

Please sign in to comment.