diff --git a/src/main/java/spoon/support/reflect/reference/CtLocalVariableReferenceImpl.java b/src/main/java/spoon/support/reflect/reference/CtLocalVariableReferenceImpl.java index 29cd21ce0b3..19d55baa63a 100644 --- a/src/main/java/spoon/support/reflect/reference/CtLocalVariableReferenceImpl.java +++ b/src/main/java/spoon/support/reflect/reference/CtLocalVariableReferenceImpl.java @@ -17,16 +17,30 @@ package spoon.support.reflect.reference; import spoon.reflect.code.CtLocalVariable; -import spoon.reflect.code.CtStatement; -import spoon.reflect.code.CtStatementList; import spoon.reflect.declaration.CtElement; +import spoon.reflect.declaration.CtPackage; import spoon.reflect.declaration.ParentNotInitializedException; +import spoon.reflect.factory.Factory; import spoon.reflect.reference.CtLocalVariableReference; import spoon.reflect.visitor.CtVisitor; +import spoon.reflect.visitor.filter.AbstractFilter; -public class CtLocalVariableReferenceImpl extends CtVariableReferenceImpl implements CtLocalVariableReference { +import java.util.List; + +/** + * An implementation for {@link CtLocalVariableReference}. + */ +public class CtLocalVariableReferenceImpl + extends CtVariableReferenceImpl implements CtLocalVariableReference { + + /** + * Id for serialization. + */ private static final long serialVersionUID = 1L; + /** + * Default constructor. + */ public CtLocalVariableReferenceImpl() { super(); } @@ -38,30 +52,62 @@ public void accept(CtVisitor visitor) { @Override public CtLocalVariable getDeclaration() { - CtElement element = this; - CtLocalVariable optional = null; - String name = getSimpleName(); + // without a factory, we are not able to filter for local variables + final Factory factory = getFactory(); + if (factory == null) { + return null; + } + final SimpleNameFilter filter = new SimpleNameFilter(factory); + + // successively iterate through all parents of this reference and + // return first result (which must be the closest declaration + // respecting visible scope) try { - do { - CtStatementList block = element.getParent(CtStatementList.class); - if (block == null) { - return null; - } - for (CtStatement ctStatement : block.getStatements()) { - if (ctStatement instanceof CtLocalVariable && ((CtLocalVariable) ctStatement).getSimpleName().equals(name)) { - optional = (CtLocalVariable) ctStatement; + CtElement parent = getParent(); + // stop at `package` level to avoid lookups to foreign Java files + while (parent != null && !(parent instanceof CtPackage)) { + final List> localVariables = + parent.getElements(filter); + // since `parent` may be a class declaring multiple local + // variables with same name in different methods, we have to + // check if any of the findings is visible in current scope by + // validating that the parent of a finding is parent of this + // reference as well + for (final CtLocalVariable lv : localVariables) { + if (hasParent(lv.getParent())) { + return lv; } } - element = block; - } while (optional == null); - } catch (ParentNotInitializedException e) { - return null; + parent = parent.getParent(); + } + } catch (final ParentNotInitializedException e) { + // handle this case as 'not found' } - return optional; + return null; } @Override public CtLocalVariableReference clone() { return (CtLocalVariableReference) super.clone(); } + + /** + * A {@link spoon.reflect.visitor.Filter} that filters all + * {@link CtLocalVariable}s with simple name equals to + * {@link #getSimpleName()}. + */ + private final class SimpleNameFilter + extends AbstractFilter> { + + @SuppressWarnings("unchecked") + SimpleNameFilter(final Factory pFactory) { + super((Class>) + pFactory.Core().createLocalVariable().getClass()); + } + + @Override + public boolean matches(final CtLocalVariable element) { + return element.getSimpleName().equals(getSimpleName()); + } + } } diff --git a/src/test/java/spoon/test/reference/VariableAccessTest.java b/src/test/java/spoon/test/reference/VariableAccessTest.java index 6ffcd681609..55c2a94cd0c 100644 --- a/src/test/java/spoon/test/reference/VariableAccessTest.java +++ b/src/test/java/spoon/test/reference/VariableAccessTest.java @@ -3,6 +3,7 @@ import org.junit.Test; import spoon.Launcher; import spoon.reflect.code.CtArrayWrite; +import spoon.reflect.code.CtLiteral; import spoon.reflect.code.CtLocalVariable; import spoon.reflect.code.CtVariableAccess; import spoon.reflect.declaration.CtClass; @@ -13,6 +14,7 @@ import spoon.reflect.reference.CtLocalVariableReference; import spoon.reflect.reference.CtParameterReference; import spoon.reflect.reference.CtVariableReference; +import spoon.reflect.visitor.CtScanner; import spoon.reflect.visitor.filter.AbstractReferenceFilter; import spoon.reflect.visitor.filter.TypeFilter; import spoon.test.reference.testclasses.Pozole; @@ -146,6 +148,51 @@ public void testReferences() throws Exception { System.out.println(make); } + @Test + public void testReferenceToLocalVariableDeclaredInLoop() { + final class CtLocalVariableReferenceScanner extends CtScanner { + @Override + public void visitCtLocalVariableReference( + final CtLocalVariableReference reference) { + assertNotNull(reference.getDeclaration()); + assertEquals(reference.getDeclaration().getSimpleName(), + reference.getSimpleName()); + assertEquals(reference.getDeclaration().getType(), + reference.getType()); + super.visitCtLocalVariableReference(reference); + } + } + + final Launcher launcher = new Launcher(); + launcher.getEnvironment().setNoClasspath(true); + launcher.addInputResource("src/test/resources/reference-test/ChangeScanner.java"); + launcher.buildModel(); + new CtLocalVariableReferenceScanner().scan(launcher.getModel().getRootPackage()); + } + + @Test + public void testMultipleDeclarationsOfLocalVariable() { + final class CtLocalVariableReferenceScanner extends CtScanner { + @Override + public void visitCtLocalVariableReference( + final CtLocalVariableReference reference) { + assertNotNull(reference.getDeclaration()); + final CtLocalVariable decl = reference.getDeclaration(); + assertEquals(decl.getPosition().getLine(), 7); + assertTrue(decl.getDefaultExpression() instanceof CtLiteral); + final CtLiteral literal = (CtLiteral) decl.getDefaultExpression(); + assertEquals(literal.getValue(), 42); + super.visitCtLocalVariableReference(reference); + } + } + + final Launcher launcher = new Launcher(); + launcher.getEnvironment().setNoClasspath(true); + launcher.addInputResource("src/test/resources/reference-test/MultipleDeclarationsOfLocalVariable.java"); + launcher.buildModel(); + new CtLocalVariableReferenceScanner().scan(launcher.getModel().getRootPackage()); + } + private CtMethod getMethod(Launcher launcher, CtClass a2) { return a2.getMethod("b", launcher.getFactory().Type().integerPrimitiveType()); } diff --git a/src/test/resources/reference-test/ChangeScanner.java b/src/test/resources/reference-test/ChangeScanner.java new file mode 100644 index 00000000000..5486a5d76fd --- /dev/null +++ b/src/test/resources/reference-test/ChangeScanner.java @@ -0,0 +1,39 @@ +package net.sf.jabref.collab; + +import java.io.*; +import java.util.*; +import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; +import net.sf.jabref.*; +import net.sf.jabref.groups.*; +import net.sf.jabref.imports.*; + + +public class ChangeScanner extends Thread { + + + /** + * Finds the entry in neu best fitting the specified entry in old. If no entries get a score + * above zero, an entry is still returned. + * @param old EntrySorter + * @param neu EntrySorter + * @param index int + * @return BibtexEntry + */ + private BibtexEntry bestFit(EntrySorter old, EntrySorter neu, int index) { + double comp = -1; + int found = 0; + loop: for (int i=0; i comp) { + comp = res; + found = i; + } + if (comp > 1) + break loop; + } + return neu.getEntryAt(found); + } + +} diff --git a/src/test/resources/reference-test/MultipleDeclarationsOfLocalVariable.java b/src/test/resources/reference-test/MultipleDeclarationsOfLocalVariable.java new file mode 100644 index 00000000000..f7f4a69f6fe --- /dev/null +++ b/src/test/resources/reference-test/MultipleDeclarationsOfLocalVariable.java @@ -0,0 +1,10 @@ +public class MultipleDeclarationsOfLocalVariable { + + public static void main(final String[] args) { + { + int j = -1; + } + int j = 42; + int i=j; + } +}