diff --git a/src/main/java/spoon/reflect/visitor/filter/LocalVariableReferenceFunction.java b/src/main/java/spoon/reflect/visitor/filter/LocalVariableReferenceFunction.java new file mode 100644 index 00000000000..d11477e52da --- /dev/null +++ b/src/main/java/spoon/reflect/visitor/filter/LocalVariableReferenceFunction.java @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2006-2017 INRIA and contributors + * Spoon - http://spoon.gforge.inria.fr/ + * + * This software is governed by the CeCILL-C License under French law and + * abiding by the rules of distribution of free software. You can use, modify + * and/or redistribute the software under the terms of the CeCILL-C license as + * circulated by CEA, CNRS and INRIA at http://www.cecill.info. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +package spoon.reflect.visitor.filter; + +import spoon.reflect.code.CtLocalVariable; +import spoon.reflect.reference.CtLocalVariableReference; +import spoon.reflect.visitor.chain.CtConsumableFunction; +import spoon.reflect.visitor.chain.CtConsumer; + +/** + * This Query expects a {@link CtLocalVariable} as input + * and returns all {@link CtLocalVariableReference}s, which refers this input. + *
+ * Usage:
+ *
 {@code
+ * CtLocalVariable var = ...;
+ * var
+ *   .map(new LocalVariableReferenceFunction())
+ *   .forEach((CtLocalVariableReference ref)->...process references...);
+ * }
+ * 
+ */ +public class LocalVariableReferenceFunction implements CtConsumableFunction> { + + public LocalVariableReferenceFunction() { + } + + @Override + public void apply(CtLocalVariable localVariable, CtConsumer outputConsumer) { + localVariable + .map(new LocalVariableScopeFunction()) + .select(new DirectReferenceFilter>(localVariable.getReference())) + .forEach(outputConsumer); + } +} diff --git a/src/main/java/spoon/reflect/visitor/filter/LocalVariableScopeFunction.java b/src/main/java/spoon/reflect/visitor/filter/LocalVariableScopeFunction.java new file mode 100644 index 00000000000..f72fab4b829 --- /dev/null +++ b/src/main/java/spoon/reflect/visitor/filter/LocalVariableScopeFunction.java @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2006-2017 INRIA and contributors + * Spoon - http://spoon.gforge.inria.fr/ + * + * This software is governed by the CeCILL-C License under French law and + * abiding by the rules of distribution of free software. You can use, modify + * and/or redistribute the software under the terms of the CeCILL-C license as + * circulated by CEA, CNRS and INRIA at http://www.cecill.info. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +package spoon.reflect.visitor.filter; + +import java.util.List; + +import spoon.SpoonException; +import spoon.reflect.code.CtLocalVariable; +import spoon.reflect.code.CtStatement; +import spoon.reflect.code.CtStatementList; +import spoon.reflect.visitor.chain.CtConsumableFunction; +import spoon.reflect.visitor.chain.CtConsumer; +import spoon.reflect.visitor.chain.CtQuery; + +/** + * This Query expects a {@link CtLocalVariable} as input + * and returns all CtElements, + * which are in visibility scope of that local variable. + * In other words, it returns all elements, + * which might be reference to that local variable. + *
+ * It can be used to search for variable declarations or + * variable references which might be in name conflict with input local variable. + *
+ * Usage:
+ *
 {@code
+ * CtLocalVariable var = ...;
+ * var.map(new LocalVariableScopeFunction()).forEach(...process result...);
+ * }
+ * 
+ */ +public class LocalVariableScopeFunction implements CtConsumableFunction> { + + public LocalVariableScopeFunction() { + } + + @Override + public void apply(CtLocalVariable localVariable, CtConsumer outputConsumer) { + CtStatementList statements = localVariable.getParent(CtStatementList.class); + if (statements == null) { + //cannot search for variable references of variable which has no parent statement list/block + return; + } + //create query which will be evaluated on each statement after local variable declaration + CtQuery query = localVariable.getFactory().createQuery().filterChildren(null); + List stats = statements.getStatements(); + //search for variable declaration in statements of current block + int idxOfVar = stats.indexOf(localVariable); + if (idxOfVar < 0) { + throw new SpoonException("Cannot found index of local variable declaration " + localVariable + " in statement list " + statements); + } + //scan only all elements AFTER this variable declaration + for (int i = idxOfVar + 1; i < stats.size(); i++) { + query.setInput(stats.get(i)).forEach(outputConsumer); + } + } +} diff --git a/src/test/java/spoon/test/query_function/QueryTest.java b/src/test/java/spoon/test/query_function/QueryTest.java index 7c13b8cb86d..d5163deb9d0 100644 --- a/src/test/java/spoon/test/query_function/QueryTest.java +++ b/src/test/java/spoon/test/query_function/QueryTest.java @@ -9,6 +9,7 @@ import spoon.reflect.code.CtCatchVariable; import spoon.reflect.code.CtExpression; import spoon.reflect.code.CtLiteral; +import spoon.reflect.code.CtLocalVariable; import spoon.reflect.cu.SourcePosition; import spoon.reflect.cu.position.NoSourcePosition; import spoon.reflect.declaration.CtClass; @@ -23,6 +24,7 @@ import spoon.reflect.reference.CtVariableReference; import spoon.reflect.visitor.chain.CtConsumableFunction; import spoon.reflect.visitor.filter.CatchVariableReferenceFunction; +import spoon.reflect.visitor.filter.LocalVariableReferenceFunction; import spoon.reflect.visitor.filter.ParameterReferenceFunction; import spoon.test.query_function.testclasses.packageA.ClassA; @@ -96,7 +98,7 @@ class Context { assertEquals(countOfModelClasses, context.classCount); } - @Test + @Test public void testParameterReferenceFunction() throws Exception { //visits all the CtParameter elements whose name is "field" and search for all their references //The test detects whether found references are correct by these two checks: @@ -127,7 +129,23 @@ public void testCatchVariableReferenceFunction() throws Exception { return false; }).list(); } - + + @Test + public void testLocalVariableReferenceFunction() throws Exception { + //visits all the CtLocalVariable elements whose name is "field" and search for all their references + //The test detects whether found references are correct by these two checks: + //1) the each found reference is on the left side of binary operator and on the right side there is unique reference identification number. Like: (field == 7) + //2) the model is searched for all variable references which has same identification number and counts them + //Then it checks that counted number of references and found number of references is same + factory.Package().getRootPackage().filterChildren((CtLocalVariable var)->{ + if(var.getSimpleName().equals("field")) { + int value = getLiteralValue(var); + checkVariableAccess(var, value, new LocalVariableReferenceFunction()); + } + return false; + }).list(); + } + private void checkVariableAccess(CtVariable var, int value, CtConsumableFunction query) { class Context { int classCount = 0;