package spoon.test.reference; 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; import spoon.reflect.declaration.CtExecutable; import spoon.reflect.declaration.CtMethod; import spoon.reflect.declaration.CtParameter; import spoon.reflect.declaration.CtType; 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.LocalVariableReferenceFunction; import spoon.reflect.visitor.filter.TypeFilter; import spoon.test.reference.testclasses.Pozole; import spoon.test.reference.testclasses.Tortillas; import spoon.testing.utils.ModelUtils; import java.util.List; import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static spoon.testing.utils.ModelUtils.build; import static spoon.testing.utils.ModelUtils.buildClass; public class VariableAccessTest { @Test public void testVariableAccessDeclarationInAnonymousClass() throws Exception { CtClass<?> type = build("spoon.test.reference", "FooBar"); assertEquals("FooBar", type.getSimpleName()); final CtParameterReference<?> ref = type.getElements(new AbstractReferenceFilter<CtParameterReference<?>>(CtParameterReference.class) { @Override public boolean matches(CtParameterReference<?> reference) { return "myArg".equals(reference.getSimpleName()); } }).get(0); assertNotNull("Parameter can't be null", ref.getDeclaration()); assertNotNull("Declaring method reference can't be null", ref.getDeclaringExecutable()); assertNotNull("Declaring type of the method can't be null", ref.getDeclaringExecutable().getDeclaringType()); assertNotNull("Declaration of declaring type of the method can't be null", ref.getDeclaringExecutable().getDeclaringType().getDeclaration()); assertNotNull("Declaration of root class can't be null", ref.getDeclaringExecutable().getDeclaringType().getDeclaringType().getDeclaration()); } @Test public void testDeclarationArray() throws Exception { final CtType<Pozole> aPozole = ModelUtils.buildClass(Pozole.class); final CtMethod<Object> m2 = aPozole.getMethod("m2"); final CtArrayWrite<?> ctArrayWrite = m2.getElements(new TypeFilter<CtArrayWrite<?>>(CtArrayWrite.class)).get(0); final CtLocalVariable expected = m2.getElements(new TypeFilter<CtLocalVariable>(CtLocalVariable.class)).get(0); assertEquals(expected, ((CtVariableAccess) ctArrayWrite.getTarget()).getVariable().getDeclaration()); } @Test public void testParameterReferenceInConstructorNoClasspath () { final Launcher launcher = new Launcher(); // throws `NullPointerException` before PR #1098 launcher.addInputResource("./src/test/resources/noclasspath/org/elasticsearch/indices/analysis/HunspellService.java"); launcher.getEnvironment().setNoClasspath(true); launcher.buildModel(); } @Test public void testDeclarationOfVariableReference() throws Exception { final Launcher launcher = new Launcher(); launcher.addInputResource("./src/test/resources/noclasspath/Foo2.java"); launcher.getEnvironment().setNoClasspath(true); launcher.buildModel(); launcher.getModel().getElements(new TypeFilter<CtVariableReference>(CtVariableReference.class) { @Override public boolean matches(CtVariableReference element) { try { element.clone().getDeclaration(); } catch (NullPointerException e) { fail("Fail with " + element.getSimpleName() + " declared in " + element.getParent().getShortRepresentation()); } return super.matches(element); } }); } @Test public void testDeclaringTypeOfALambdaReferencedByParameterReference() { final spoon.Launcher launcher = new spoon.Launcher(); launcher.addInputResource("src/test/resources/noclasspath/Foo3.java"); launcher.getEnvironment().setNoClasspath(true); launcher.getEnvironment().setComplianceLevel(8); launcher.buildModel(); launcher.getModel().getElements(new TypeFilter<CtExecutable<?>>(CtExecutable.class) { @Override public boolean matches(CtExecutable<?> exec) { final List<CtParameterReference<?>> guiParams = exec.getParameters().stream().map(CtParameter::getReference).collect(Collectors.toList()); if (guiParams.size() != 1) { return false; } final CtParameterReference<?> param = guiParams.get(0); exec.getBody().getElements(new TypeFilter<CtParameterReference<?>>(CtParameterReference.class) { @Override public boolean matches(CtParameterReference<?> p) { assertEquals(p.getSimpleName(), param.getSimpleName()); return super.matches(p); } }); return super.matches(exec); } }); } @Test public void testGetDeclarationAfterClone() throws Exception { final Launcher launcher = new Launcher(); launcher.getEnvironment().setNoClasspath(true); launcher.addInputResource("./src/test/resources/noclasspath/A2.java"); launcher.buildModel(); final CtClass<Object> a2 = launcher.getFactory().Class().get("A2"); final CtClass<Object> a2Cloned = a2.clone(); assertEquals(a2, a2Cloned); final CtMethod<Object> methodA2 = getMethod(launcher, a2); final CtMethod<Object> methodA2Cloned = getMethod(launcher, a2Cloned); final CtLocalVariable declaration = methodA2.getBody().getStatement(0); final CtLocalVariable declarationCloned = methodA2Cloned.getBody().getStatement(0); final CtLocalVariableReference localVarRef = getLocalVariableRefF1(methodA2); final CtLocalVariableReference localVarRefCloned = getLocalVariableRefF1(methodA2Cloned); assertEquals(localVarRef.getDeclaration(), declaration); assertSame(localVarRef.getDeclaration(), declaration); assertEquals(localVarRefCloned.getDeclaration(), declarationCloned); assertSame(localVarRefCloned.getDeclaration(), declarationCloned); } @Test public void testReferences() throws Exception { /* test getReference on local variable * getReference().getDeclaration() must be circular */ final CtType<Tortillas> aTortillas = buildClass(Tortillas.class); final CtMethod<Object> make = aTortillas.getMethod("make", aTortillas.getFactory().Type().stringType()); final CtLocalVariable localVar = make.getBody().getStatement(0); final CtLocalVariable localVarCloned = localVar.clone(); final CtLocalVariableReference localVarRef = localVar.getReference(); final CtLocalVariableReference localVarRefCloned = localVarCloned.getReference(); assertEquals(localVarRef.getDeclaration(), localVar); assertSame(localVarRef.getDeclaration(), localVar); assertEquals(localVar.getReference().getDeclaration(), localVar); assertSame(localVar.getReference().getDeclaration(), localVar); assertEquals(localVarRefCloned.getDeclaration(), localVarCloned); assertSame(localVarRefCloned.getDeclaration(), localVarCloned); assertEquals(localVarCloned.getReference().getDeclaration(), localVarCloned); assertSame(localVarCloned.getReference().getDeclaration(), localVarCloned); } @Test public void testReferencesInInitExpression() throws Exception { /* test getReference on local variable * getReference().getDeclaration() must be circular */ final CtType<Tortillas> aTortillas = buildClass(Tortillas.class); final CtMethod<Object> make = aTortillas.getMethod("make", aTortillas.getFactory().Type().stringType()); final CtLocalVariable localVarNumber = make.getBody().getStatement(1); List<CtLocalVariableReference<?>> refs = localVarNumber.map(new LocalVariableReferenceFunction()).list(); assertEquals(1, refs.size()); assertSame(localVarNumber, refs.get(0).getParent(CtLocalVariable.class)); } @Test public void testReferenceToLocalVariableDeclaredInLoop() { final class CtLocalVariableReferenceScanner extends CtScanner { @Override public <T> void visitCtLocalVariableReference( final CtLocalVariableReference<T> 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 <T> void visitCtLocalVariableReference( final CtLocalVariableReference<T> 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<Object> getMethod(Launcher launcher, CtClass<Object> a2) { return a2.getMethod("b", launcher.getFactory().Type().integerPrimitiveType()); } private CtLocalVariableReference getLocalVariableRefF1(CtMethod<Object> method) { return method.getElements(new TypeFilter<CtLocalVariableReference>(CtLocalVariableReference.class) { @Override public boolean matches(CtLocalVariableReference element) { return "f1".equals(element.getSimpleName()) && super.matches(element); } }).get(0); } }