Skip to content

Commit

Permalink
fix(VariableAccessFilter): ensure all accesses are returned for gener…
Browse files Browse the repository at this point in the history
…ic fields #5391
  • Loading branch information
Luro02 authored and I-Al-Istannen committed Aug 27, 2023
1 parent 2c64874 commit 6c64910
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@
package spoon.reflect.visitor.filter;

import spoon.reflect.code.CtVariableAccess;
import spoon.reflect.declaration.CtVariable;
import spoon.reflect.reference.CtVariableReference;
import spoon.reflect.visitor.Filter;

/**
* This simple filter matches all the accesses to a given field.
* This simple filter matches all the accesses to a given variable.
*/
public class VariableAccessFilter<T extends CtVariableAccess<?>> implements Filter<T> {
CtVariableReference<?> variable;
private final CtVariableReference<?> variable;
private CtVariable<?> variableDeclaration;

/**
* Creates a new field access filter.
* Creates a new variable access filter.
*
* @param variable
* the accessed variable
* @param variable the variable to find accesses for, must not be {@code null}
*/
public VariableAccessFilter(CtVariableReference<?> variable) {
if (variable == null) {
Expand All @@ -32,7 +33,22 @@ public VariableAccessFilter(CtVariableReference<?> variable) {

@Override
public boolean matches(T variableAccess) {
return variable.equals(variableAccess.getVariable());
if (this.variable.equals(variableAccess.getVariable())) {
return true;
}
// If this.variable is a reference to a generic field, then the references might not be equal:
//
// Given `class A<T> { T t; }`, the reference would be to `T t`, but an access to `t` could look
// like this:
// `A<String> a = new A<>(); a.t = "foo";`
// ^^^ reference to `String t`
// As (String t) != (T t), the references are not equal, even though the same variable is accessed.
// Therefore, we fall back to comparing the declaration if the references were different.
if (this.variableDeclaration == null) {
this.variableDeclaration = this.variable.getDeclaration();
}

return this.variableDeclaration.equals(variableAccess.getVariable().getDeclaration());
}

}
25 changes: 25 additions & 0 deletions src/test/java/spoon/test/filters/FilterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import spoon.reflect.code.CtNewClass;
import spoon.reflect.code.CtStatement;
import spoon.reflect.code.CtSwitch;
import spoon.reflect.code.CtVariableAccess;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtExecutable;
Expand Down Expand Up @@ -75,6 +76,7 @@
import spoon.reflect.visitor.filter.ReturnOrThrowFilter;
import spoon.reflect.visitor.filter.SubtypeFilter;
import spoon.reflect.visitor.filter.TypeFilter;
import spoon.reflect.visitor.filter.VariableAccessFilter;
import spoon.support.comparator.DeepRepresentationComparator;
import spoon.support.reflect.declaration.CtMethodImpl;
import spoon.support.visitor.SubInheritanceHierarchyResolver;
Expand Down Expand Up @@ -1430,4 +1432,27 @@ public boolean matches(CtElement element) {

}

@Test
public void testVariableAccessFilterWithGenericField() {
// contract: VariableAccessFilter returns all accesses to a given field, even if it is generic
CtClass<?> ctClass = Launcher.parseClass(
"public class Example<T> {\n" +
" T field;\n" +
"\n" +
" public static void main(String[] args) {\n" +
" Example<String> example = new Example<>();\n" +
"\n" +
" example.field = \"Hello\"; // write access to Example#field\n" +
" System.out.println(example.field); // read access to Example#field\n" +
" }\n" +
"}\n"
);

List<CtVariableAccess<?>> accesses = ctClass.getElements(
new VariableAccessFilter<>(ctClass.getField("field").getReference())
);

assertEquals(2, accesses.size());
}

}

0 comments on commit 6c64910

Please sign in to comment.