From aed68f3c5f1cef6e8ca96b841bc51bd3627b6cc2 Mon Sep 17 00:00:00 2001
From: Pavel Vojtechovsky
Date: Sat, 1 Dec 2018 08:03:19 +0100
Subject: [PATCH] feature: NameScope detects conflicts on names
---
.../java/spoon/reflect/visitor/NameScope.java | 219 ++++++++++++++++++
.../reflect/visitor/NameScopeOfType.java | 185 +++++++++++++++
.../reflect/visitor/SimpleNameScope.java | 64 +++++
.../imports/name_scope/NameScopeTest.java | 124 ++++++++++
.../name_scope/testclasses/Renata.java | 34 +++
5 files changed, 626 insertions(+)
create mode 100644 src/main/java/spoon/reflect/visitor/NameScope.java
create mode 100644 src/main/java/spoon/reflect/visitor/NameScopeOfType.java
create mode 100644 src/main/java/spoon/reflect/visitor/SimpleNameScope.java
create mode 100644 src/test/java/spoon/test/imports/name_scope/NameScopeTest.java
create mode 100644 src/test/java/spoon/test/imports/name_scope/testclasses/Renata.java
diff --git a/src/main/java/spoon/reflect/visitor/NameScope.java b/src/main/java/spoon/reflect/visitor/NameScope.java
new file mode 100644
index 00000000000..ffb9b24a122
--- /dev/null
+++ b/src/main/java/spoon/reflect/visitor/NameScope.java
@@ -0,0 +1,219 @@
+/**
+ * Copyright (C) 2006-2018 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;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Map;
+import java.util.function.Function;
+
+import spoon.SpoonException;
+import spoon.reflect.code.CtBlock;
+import spoon.reflect.code.CtCatch;
+import spoon.reflect.code.CtLambda;
+import spoon.reflect.code.CtLocalVariable;
+import spoon.reflect.declaration.CtAnnotationType;
+import spoon.reflect.declaration.CtClass;
+import spoon.reflect.declaration.CtCompilationUnit;
+import spoon.reflect.declaration.CtConstructor;
+import spoon.reflect.declaration.CtElement;
+import spoon.reflect.declaration.CtEnum;
+import spoon.reflect.declaration.CtInterface;
+import spoon.reflect.declaration.CtMethod;
+import spoon.reflect.declaration.CtNamedElement;
+import spoon.reflect.declaration.CtType;
+import spoon.reflect.declaration.CtTypeMember;
+import spoon.support.Experimental;
+
+/**
+ * Maps names to CtElements, which are visible at current scanning place
+ */
+@Experimental
+public abstract class NameScope {
+
+ private static NameScope EMPTY = new NameScope(null, null) {
+ @Override
+ protected T forEachLocalElementByName(String name, Function super CtNamedElement, T> consumer) {
+ return null;
+ }
+ };
+
+ /**
+ * The {@link EarlyTerminatingScanner} implementation, which knows all visible elements in the scope
+ * @param
+ */
+ public static class Scanner extends EarlyTerminatingScanner {
+ private final Deque scopes = new ArrayDeque<>();
+ protected void enter(spoon.reflect.declaration.CtElement e) {
+ NameScope newFinder = NameScope.onElement(scopes.peek(), e);
+ if (newFinder != null) {
+ scopes.push(newFinder);
+ }
+ }
+ protected void exit(spoon.reflect.declaration.CtElement e) {
+ NameScope topFinder = scopes.peek();
+ if (topFinder != null && topFinder.getScopeElement() == e) {
+ //we are living scope of this ConflictFinder. Pop it
+ scopes.pop();
+ }
+ }
+ public NameScope getNameScope() {
+ NameScope ns = scopes.peek();
+ return ns == null ? EMPTY : ns;
+ }
+ public Deque getScopes() {
+ return scopes;
+ }
+ }
+
+ /**
+ * Call it for each visited CtElement
+ * @param parent the parent ConflictFinder
+ * @param target an element
+ * @return new {@link NameScope} if `target` element declares new naming scope or null if there is no new scope
+ */
+ protected static NameScope onElement(NameScope parent, CtElement target) {
+ class Visitor extends CtAbstractVisitor {
+ NameScope finder = null;
+ @Override
+ public void visitCtCompilationUnit(CtCompilationUnit compilationUnit) {
+ //compilation unit items are added in NameScopeOfType, because they depend on the inhertance hierarchy of the type itself
+ }
+ @Override
+ public void visitCtClass(CtClass ctClass) {
+ finder = new NameScopeOfType(parent, ctClass);
+ }
+ @Override
+ public void visitCtInterface(CtInterface intrface) {
+ finder = new NameScopeOfType(parent, intrface);
+ }
+ @Override
+ public > void visitCtEnum(CtEnum ctEnum) {
+ finder = new NameScopeOfType(parent, ctEnum);
+ }
+ @Override
+ public void visitCtAnnotationType(CtAnnotationType annotationType) {
+ finder = new NameScopeOfType(parent, annotationType);
+ }
+ @Override
+ public void visitCtMethod(CtMethod m) {
+ finder = new SimpleNameScope(parent, m, m.getParameters());
+ }
+ @Override
+ public void visitCtConstructor(CtConstructor c) {
+ finder = new SimpleNameScope(parent, c, c.getParameters());
+ }
+ @Override
+ public void visitCtLambda(CtLambda lambda) {
+ finder = new SimpleNameScope(parent, lambda, lambda.getParameters());
+ }
+ @Override
+ public void visitCtCatch(CtCatch catchBlock) {
+ finder = new SimpleNameScope(parent, catchBlock).addVariable(catchBlock.getParameter());
+ }
+ @Override
+ public void visitCtBlock(CtBlock block) {
+ finder = new SimpleNameScope(parent, block);
+ }
+ @Override
+ public void visitCtLocalVariable(CtLocalVariable localVariable) {
+ if (parent instanceof SimpleNameScope) {
+ ((SimpleNameScope) parent).addVariable(localVariable);
+ } else {
+ throw new SpoonException("Cannot add local variable when parent is missing");
+ }
+ }
+ };
+ Visitor scanner = new Visitor();
+ target.accept(scanner);
+ return scanner.finder;
+ }
+
+ private final NameScope parent;
+ private final CtElement scopeElement;
+
+ protected NameScope(NameScope parent, CtElement scopeElement) {
+ this.parent = parent;
+ this.scopeElement = scopeElement;
+ }
+
+ /**
+ * @return the {@link CtElement} which represents the current scope
+ */
+ public final CtElement getScopeElement() {
+ return scopeElement;
+ }
+
+ /**
+ * @param name to be searched simple name
+ * @param consumer is called for each named element with same name which are accessible from this {@link NameScope}
+ * as long as there are some elements and consumer returns null. If `consumer` return not null value then it is returned
+ * @return the value returned by `consumer` or null
+ */
+ public final T forEachElementByName(String name, Function super CtNamedElement, T> consumer) {
+ T r = forEachLocalElementByName(name, consumer);
+ if (r != null) {
+ return r;
+ }
+ if (scopeElement instanceof CtNamedElement) {
+ CtNamedElement named = (CtNamedElement) scopeElement;
+ if (name.equals(named.getSimpleName())) {
+ r = consumer.apply(named);
+ if (r != null) {
+ return r;
+ }
+ }
+ }
+ if (parent != null) {
+ return parent.forEachElementByName(name, consumer);
+ }
+ return null;
+ }
+
+ protected abstract T forEachLocalElementByName(String name, Function super CtNamedElement, T> consumer);
+
+ protected static T forEachByName(Map map, String name, Function super CtNamedElement, T> consumer) {
+ CtNamedElement named = map.get(name);
+ if (named != null) {
+ return consumer.apply(named);
+ }
+ return null;
+ }
+
+ /**
+ * @return true if two methods can come from same static import
+ *
+ * For example `import static org.junit.Assert.assertEquals;`
+ * imports methods with signatures:
+ * assertEquals(Object, Object)
+ * assertEquals(Object[], Object[])
+ * assertEquals(long, long)
+ * ...
+ */
+ static boolean isSameStaticImport(CtNamedElement m1, CtNamedElement m2) {
+ if (m1 instanceof CtTypeMember && m2 instanceof CtTypeMember) {
+ if (m1.getSimpleName().equals(m2.getSimpleName())) {
+ CtType> declType1 = ((CtTypeMember) m1).getDeclaringType();
+ CtType> declType2 = ((CtTypeMember) m2).getDeclaringType();
+ //may be we should check isSubTypeOf instead
+ return declType1 == declType2;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/spoon/reflect/visitor/NameScopeOfType.java b/src/main/java/spoon/reflect/visitor/NameScopeOfType.java
new file mode 100644
index 00000000000..b968fba044b
--- /dev/null
+++ b/src/main/java/spoon/reflect/visitor/NameScopeOfType.java
@@ -0,0 +1,185 @@
+/**
+ * Copyright (C) 2006-2018 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;
+
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+
+import spoon.reflect.declaration.CtCompilationUnit;
+import spoon.reflect.declaration.CtField;
+import spoon.reflect.declaration.CtImport;
+import spoon.reflect.declaration.CtImportKind;
+import spoon.reflect.declaration.CtMethod;
+import spoon.reflect.declaration.CtNamedElement;
+import spoon.reflect.declaration.CtPackage;
+import spoon.reflect.declaration.CtType;
+import spoon.reflect.declaration.CtTypeMember;
+import spoon.reflect.reference.CtExecutableReference;
+import spoon.reflect.reference.CtFieldReference;
+import spoon.reflect.reference.CtPackageReference;
+import spoon.reflect.reference.CtTypeMemberWildcardImportReference;
+import spoon.reflect.reference.CtTypeReference;
+import spoon.reflect.visitor.filter.AllTypeMembersFunction;
+
+/**
+ * Represents NameScope of Type. Knows all accessible fields, nested type names and method names
+ */
+class NameScopeOfType extends NameScope {
+ private Map fieldsByName;
+ private Map typesByName;
+ private Map methodsByName;
+
+ NameScopeOfType(NameScope conflictFinder, CtType> p_type) {
+ super(conflictFinder, p_type);
+ }
+
+ @Override
+ protected T forEachLocalElementByName(String name, Function super CtNamedElement, T> consumer) {
+ assureCacheInitialized();
+ T r = forEachByName(fieldsByName, name, consumer);
+ if (r != null) {
+ return r;
+ }
+ r = forEachByName(typesByName, name, consumer);
+ if (r != null) {
+ return r;
+ }
+ r = forEachByName(methodsByName, name, consumer);
+ if (r != null) {
+ return r;
+ }
+ return null;
+ }
+
+ private void assureCacheInitialized() {
+ if (fieldsByName == null) {
+ //collect names of type members which are visible in this type
+ fieldsByName = new HashMap<>();
+ typesByName = new HashMap<>();
+ methodsByName = new HashMap<>();
+ CtType> type = (CtType>) getScopeElement();
+ type.map(new AllTypeMembersFunction().setMode(AllTypeMembersFunction.Mode.SKIP_PRIVATE)).forEach((CtTypeMember typeMember) -> {
+ //the local members are visited first. Then members of super types/interfaces
+ if (typeMember instanceof CtField) {
+ putIfNotExists(fieldsByName, (CtField>) typeMember);
+ } else if (typeMember instanceof CtType) {
+ putIfNotExists(typesByName, (CtType>) typeMember);
+ } else if (typeMember instanceof CtMethod) {
+ putIfNotExists(methodsByName, (CtMethod>) typeMember);
+ }
+ });
+ if (type.isTopLevel()) {
+ CtCompilationUnit cu = type.getPosition().getCompilationUnit();
+ if (cu != null) {
+ //add types and static fields and methods from compilation unit
+ addCompilationUnitNames(cu);
+ }
+ }
+ }
+ }
+
+ /*
+ * sort wildcard imports as last. The wildcard import of type has lower priority then explicit import of type
+ */
+ private static final Comparator importComparator = new Comparator() {
+ @Override
+ public int compare(CtImport o1, CtImport o2) {
+ CtImportKind k1 = o1.getImportKind();
+ CtImportKind k2 = o2.getImportKind();
+ return getOrderOfImportKind(k1) - getOrderOfImportKind(k2);
+ }
+
+ private int getOrderOfImportKind(CtImportKind ik) {
+ switch (ik) {
+ case ALL_STATIC_MEMBERS:
+ return 2;
+ case ALL_TYPES:
+ return 1;
+ default:
+ return 0;
+ }
+ }
+ };
+
+ private void addCompilationUnitNames(CtCompilationUnit compilationUnit) {
+ CtType> type = (CtType>) getScopeElement();
+ CtTypeReference> typeRef = type.getReference();
+ //all imported types and static members are visible too
+ compilationUnit.getImports().stream().sorted(importComparator).forEach(aImport -> {
+ aImport.accept(new CtImportVisitor() {
+ @Override
+ public void visitTypeImport(CtTypeReference typeReference) {
+ putIfNotExists(typesByName, typeReference.getTypeDeclaration());
+ }
+ @Override
+ public void visitMethodImport(CtExecutableReference executableReference) {
+ putIfNotExists(methodsByName, executableReference.getExecutableDeclaration());
+ }
+ @Override
+ public void visitFieldImport(CtFieldReference fieldReference) {
+ putIfNotExists(fieldsByName, fieldReference.getFieldDeclaration());
+ }
+ @Override
+ public void visitAllTypesImport(CtPackageReference packageReference) {
+ CtPackage pack = packageReference.getDeclaration();
+ if (pack != null) {
+ for (CtType> type : pack.getTypes()) {
+ //add only types which are not yet imported. Explicit import wins over wildcard import
+ putIfNotExists(typesByName, type);
+ }
+ }
+ }
+ @Override
+ public void visitAllStaticMembersImport(CtTypeMemberWildcardImportReference typeReference) {
+ CtType> type = typeReference.getDeclaration();
+ type.map(new AllTypeMembersFunction().setMode(AllTypeMembersFunction.Mode.SKIP_PRIVATE)).forEach((CtTypeMember typeMember) -> {
+ if (typeMember.isStatic() && typeRef.canAccess(typeMember)) {
+ if (typeMember instanceof CtField) {
+ putIfNotExists(fieldsByName, typeMember);
+ } else if (typeMember instanceof CtMethod) {
+ putIfNotExists(methodsByName, typeMember);
+ }
+ }
+ });
+ }
+ });
+ });
+ //names of all types of same package are visible too, but with lower priority then explicitly imported elements
+ CtPackage pack = compilationUnit.getDeclaredPackage();
+ if (pack != null) {
+ for (CtType> packageType : pack.getTypes()) {
+ if (!typesByName.containsKey(packageType.getSimpleName())) {
+ typesByName.put(packageType.getSimpleName(), packageType);
+ }
+ }
+ }
+ }
+
+ //assures that type members nearer to local type are used
+ private void putIfNotExists(Map map, T element) {
+ if (element == null) {
+ //noclasspath mode. Ignore that.
+ return;
+ }
+ String name = element.getSimpleName();
+ if (!map.containsKey(name)) {
+ map.put(name, element);
+ }
+ }
+}
diff --git a/src/main/java/spoon/reflect/visitor/SimpleNameScope.java b/src/main/java/spoon/reflect/visitor/SimpleNameScope.java
new file mode 100644
index 00000000000..b66db2b60bb
--- /dev/null
+++ b/src/main/java/spoon/reflect/visitor/SimpleNameScope.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (C) 2006-2018 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;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import spoon.reflect.declaration.CtElement;
+import spoon.reflect.declaration.CtNamedElement;
+import spoon.reflect.declaration.CtParameter;
+import spoon.reflect.declaration.CtVariable;
+
+/**
+ * {@link NameScope} for exact set of named elements. There can be:
+ *
+ * - parameter names of constructor, method or lambda
+ *
- parameter name of catch variable
+ *
- local variables of block
+ *
+ */
+class SimpleNameScope extends NameScope {
+
+ private final Map elementsByName;
+
+ SimpleNameScope(NameScope parent, CtElement scopeElement, List> parameters) {
+ super(parent, scopeElement);
+ elementsByName = new HashMap<>(parameters.size());
+ for (CtParameter> parameter : parameters) {
+ elementsByName.put(parameter.getSimpleName(), parameter);
+ }
+ }
+
+ SimpleNameScope(NameScope parent, CtElement scopeElement) {
+ super(parent, scopeElement);
+ elementsByName = new HashMap<>();
+ }
+
+ @Override
+ protected T forEachLocalElementByName(String name, Function super CtNamedElement, T> consumer) {
+ return forEachByName(elementsByName, name, consumer);
+ }
+
+ SimpleNameScope addVariable(CtVariable> var) {
+ //do not check conflict here. It is OK that local variable hides parameter
+ elementsByName.put(var.getSimpleName(), var);
+ return this;
+ }
+}
diff --git a/src/test/java/spoon/test/imports/name_scope/NameScopeTest.java b/src/test/java/spoon/test/imports/name_scope/NameScopeTest.java
new file mode 100644
index 00000000000..70bf3bf541c
--- /dev/null
+++ b/src/test/java/spoon/test/imports/name_scope/NameScopeTest.java
@@ -0,0 +1,124 @@
+/**
+ * Copyright (C) 2006-2018 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.test.imports.name_scope;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+import spoon.reflect.code.CtLiteral;
+import spoon.reflect.declaration.CtElement;
+import spoon.reflect.declaration.CtField;
+import spoon.reflect.declaration.CtMethod;
+import spoon.reflect.declaration.CtType;
+import spoon.reflect.path.CtRole;
+import spoon.reflect.visitor.NameScope;
+import spoon.reflect.visitor.chain.CtScannerListener;
+import spoon.reflect.visitor.chain.ScanningMode;
+import spoon.test.imports.name_scope.testclasses.Renata;
+import spoon.testing.utils.ModelUtils;
+
+public class NameScopeTest {
+
+ @Test
+ public void testNameScopeScanner() throws Exception {
+ //contract: check that NameScope knows expected name.
+ CtType> typeRenata = ModelUtils.buildClass(launcher -> {
+ //needed to compute imports
+ launcher.getEnvironment().setAutoImports(true);
+ }, Renata.class);
+
+ CtField> fieldMichal = typeRenata.getField("michal");
+ CtType> typeTereza = typeRenata.getNestedType("Tereza");
+
+ CtMethod> methodDraw = typeTereza.getMethodsByName("draw").get(0);
+
+ CtType> typeFile = typeRenata.getFactory().Type().createReference(File.class).getTypeDeclaration();
+ CtType> typeSystem = typeRenata.getFactory().Type().createReference(System.class).getTypeDeclaration();
+ CtMethod> methodCurrentTimeMillis = typeSystem.getMethodsByName("currentTimeMillis").get(0);
+
+ CtType> typeFiles = typeRenata.getFactory().Type().createReference(Files.class).getTypeDeclaration();
+ CtMethod> methodsNewDirectoryStream = typeFiles.getMethodsByName("newDirectoryStream").get(0);
+
+ NameScope.Scanner scanner = new NameScope.Scanner<>();
+ scanner.setVisitCompilationUnitContent(true);
+ scanner.setListener(new CtScannerListener() {
+ @Override
+ public ScanningMode enter(CtRole role, CtElement element) {
+ if (element instanceof CtLiteral) {
+ CtLiteral literal = (CtLiteral) element;
+ //check that NameScope is aware of all names, which are visible at position of the literals
+ if ("1".equals(literal.getValue())) {
+ //contract: the local variables are visible after they are declared
+ assertNameScope(Arrays.asList(), scanner.getNameScope(), "count");
+ assertNameScope(Arrays.asList("String theme"), scanner.getNameScope(), "theme");
+ assertNameScope(Arrays.asList(methodDraw), scanner.getNameScope(), "draw");
+ assertNameScope(Arrays.asList(typeTereza), scanner.getNameScope(), "Tereza");
+ assertNameScope(Arrays.asList(fieldMichal), scanner.getNameScope(), "michal");
+ assertNameScope(Arrays.asList(typeRenata), scanner.getNameScope(), "Renata");
+ //contract: imported types are visible too
+ assertNameScope(Arrays.asList(typeFile), scanner.getNameScope(), "File");
+ //contract: imported static methods are visible too
+ assertNameScope(Arrays.asList(methodCurrentTimeMillis), scanner.getNameScope(), "currentTimeMillis");
+ //contract: type members imported by wildcard are visible too
+ assertNameScope(Arrays.asList(methodsNewDirectoryStream), scanner.getNameScope(), "newDirectoryStream");
+ //contract: The names are case sensitive
+ assertNameScope(Arrays.asList(), scanner.getNameScope(), "Michal");
+ //the names which are not visible, must not be returned
+ assertNameScope(Arrays.asList(), scanner.getNameScope(), "void");
+ assertNameScope(Arrays.asList(), scanner.getNameScope(), "String");
+ //type members of System are not visible
+ assertNameScope(Arrays.asList(), scanner.getNameScope(), "setIn");
+ //type member itself whose field is imported is not visible
+ assertNameScope(Arrays.asList(), scanner.getNameScope(), "System");
+ //type member itself whose type members are imported by wildcard are not visible
+ assertNameScope(Arrays.asList(), scanner.getNameScope(), "Fields");
+ } else if ("2".equals(literal.getValue())) {
+ //contract: the local variables are visible after they are declared
+ assertNameScope(Arrays.asList("int count"), scanner.getNameScope(), "count");
+ }
+ }
+ return ScanningMode.NORMAL;
+ }
+ });
+ scanner.scan(typeRenata.getPosition().getCompilationUnit());
+ }
+
+ private void assertNameScope(List> expectedElements, NameScope nameScope, String name) {
+ List realElements = new ArrayList<>();
+ nameScope.forEachElementByName(name, e -> realElements.add(e));
+ assertEquals(expectedElements.size(), realElements.size());
+ for (int i = 0; i < expectedElements.size(); i++) {
+ Object expected = expectedElements.get(i);
+ assertNotNull(expected);
+ if (expected instanceof String) {
+ String expectedString = (String) expected;
+ assertEquals(expectedString, realElements.get(i).toString());
+ } else {
+ assertSame(expected, realElements.get(i));
+ }
+ }
+ }
+}
diff --git a/src/test/java/spoon/test/imports/name_scope/testclasses/Renata.java b/src/test/java/spoon/test/imports/name_scope/testclasses/Renata.java
new file mode 100644
index 00000000000..6043f3acd75
--- /dev/null
+++ b/src/test/java/spoon/test/imports/name_scope/testclasses/Renata.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (C) 2006-2018 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.test.imports.name_scope.testclasses;
+
+import java.io.File;
+import static java.lang.System.currentTimeMillis;
+import static java.nio.file.Files.*;
+
+public class Renata {
+
+ String michal;
+
+ class Tereza {
+ void draw(String theme) {
+ System.out.println("1");
+ int count;
+ System.out.println("2");
+ }
+ }
+}