From a16f436517bc50f615a240d17c427f40074bcd0f Mon Sep 17 00:00:00 2001 From: Pavel Vojtechovsky Date: Wed, 23 Jan 2019 14:27:06 +0100 Subject: [PATCH] feature: introduce the concept of lexical scope (interface LexicalScope) (#2813) --- .../spoon/reflect/visitor/LexicalScope.java | 59 ++++++ .../reflect/visitor/LexicalScopeScanner.java | 107 ++++++++++ .../spoon/reflect/visitor/NameScopeImpl.java | 104 ++++++++++ .../spoon/reflect/visitor/TypeNameScope.java | 189 ++++++++++++++++++ .../imports/name_scope/NameScopeTest.java | 139 +++++++++++++ .../name_scope/testclasses/Renata.java | 34 ++++ 6 files changed, 632 insertions(+) create mode 100644 src/main/java/spoon/reflect/visitor/LexicalScope.java create mode 100644 src/main/java/spoon/reflect/visitor/LexicalScopeScanner.java create mode 100644 src/main/java/spoon/reflect/visitor/NameScopeImpl.java create mode 100644 src/main/java/spoon/reflect/visitor/TypeNameScope.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/LexicalScope.java b/src/main/java/spoon/reflect/visitor/LexicalScope.java new file mode 100644 index 00000000000..6b74c1e6ce9 --- /dev/null +++ b/src/main/java/spoon/reflect/visitor/LexicalScope.java @@ -0,0 +1,59 @@ +/** + * 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 spoon.reflect.declaration.CtElement; +import spoon.reflect.declaration.CtNamedElement; +import spoon.support.Experimental; + +import java.util.function.Function; + +/** + * Represents that a lexical scope in the language + * + * Note that scopes are changing after variable declaration. For example: + * + * void draw() { + * //scope1 + * int a; + * //scope2 + * int b; + * //scope3 + * } + * + * See https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping + */ +@Experimental +public interface LexicalScope { + /** adds an element to the scope */ + LexicalScope addNamedElement(CtNamedElement element); + + /** + * @return the {@link CtElement} which represents the current scope + */ + CtElement getScopeElement(); + + /** + * @param name to be searched simple name + * @param fnc is called for each named element with same simple name, which is defined in this or parent {@link LexicalScope}. + * Function `fnc` is called as long as there are some matching elements and `fnc` returns null. + * If `fnc` returns not null value then searching is stopped and that value is a returned + * @return the value returned by `fnc` or null + */ + T forEachElementByName(String name, Function fnc); + +} diff --git a/src/main/java/spoon/reflect/visitor/LexicalScopeScanner.java b/src/main/java/spoon/reflect/visitor/LexicalScopeScanner.java new file mode 100644 index 00000000000..05f06a6dcda --- /dev/null +++ b/src/main/java/spoon/reflect/visitor/LexicalScopeScanner.java @@ -0,0 +1,107 @@ +package spoon.reflect.visitor; + +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 java.lang.annotation.Annotation; +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * Responsible for building lexical scopes. + * + */ +public class LexicalScopeScanner extends EarlyTerminatingScanner { + private final Deque scopes = new ArrayDeque<>(); + protected void enter(spoon.reflect.declaration.CtElement e) { + LexicalScope newFinder = onElement(scopes.peek(), e); + if (newFinder != null) { + scopes.push(newFinder); + } + } + protected void exit(spoon.reflect.declaration.CtElement e) { + LexicalScope topFinder = scopes.peek(); + if (topFinder != null && topFinder.getScopeElement() == e) { + //we are living scope of this ConflictFinder. Pop it + scopes.pop(); + } + } + private static NameScopeImpl EMPTY = new NameScopeImpl(null, null); + /** + * @return {@link LexicalScope} of actually scanned element. The {@link LexicalScope#forEachElementByName(String, java.util.function.Function)} can be used + * to get all {@link CtElement}s which are mapped to that simple name + */ + public LexicalScope getCurrentNameScope() { + LexicalScope ns = scopes.peek(); + return ns == null ? EMPTY : ns; + } + + /** + * Call it for each visited CtElement + * @param parent the parent ConflictFinder + * @param target an element + * @return new {@link NameScopeImpl} if `target` element declares new naming scope or null if there is no new scope + */ + private NameScopeImpl onElement(LexicalScope parent, CtElement target) { + class Visitor extends CtAbstractVisitor { + NameScopeImpl finder = null; + @Override + public void visitCtCompilationUnit(CtCompilationUnit compilationUnit) { + //compilation unit items are added in TypeNameScope, because they depend on the inhertance hierarchy of the type itself + } + @Override + public void visitCtClass(CtClass ctClass) { + finder = new TypeNameScope(parent, ctClass); + } + @Override + public void visitCtInterface(CtInterface intrface) { + finder = new TypeNameScope(parent, intrface); + } + @Override + public > void visitCtEnum(CtEnum ctEnum) { + finder = new TypeNameScope(parent, ctEnum); + } + @Override + public void visitCtAnnotationType(CtAnnotationType annotationType) { + finder = new TypeNameScope(parent, annotationType); + } + @Override + public void visitCtMethod(CtMethod m) { + finder = new NameScopeImpl(parent, m, m.getParameters()); + } + @Override + public void visitCtConstructor(CtConstructor c) { + finder = new NameScopeImpl(parent, c, c.getParameters()); + } + @Override + public void visitCtLambda(CtLambda lambda) { + finder = new NameScopeImpl(parent, lambda, lambda.getParameters()); + } + @Override + public void visitCtCatch(CtCatch catchBlock) { + finder = new NameScopeImpl(parent, catchBlock).addNamedElement(catchBlock.getParameter()); + } + @Override + public void visitCtBlock(CtBlock block) { + finder = new NameScopeImpl(parent, block); + } + @Override + public void visitCtLocalVariable(CtLocalVariable localVariable) { + parent.addNamedElement(localVariable); + } + }; + Visitor scanner = new Visitor(); + target.accept(scanner); + return scanner.finder; + } +} diff --git a/src/main/java/spoon/reflect/visitor/NameScopeImpl.java b/src/main/java/spoon/reflect/visitor/NameScopeImpl.java new file mode 100644 index 00000000000..c206ff15b10 --- /dev/null +++ b/src/main/java/spoon/reflect/visitor/NameScopeImpl.java @@ -0,0 +1,104 @@ +/** + * 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.Optional; +import java.util.function.Function; + +import spoon.reflect.declaration.CtElement; +import spoon.reflect.declaration.CtNamedElement; +import spoon.reflect.declaration.CtParameter; + +/** + * Maps names to CtElements, which are visible at current scanning place + */ +class NameScopeImpl implements LexicalScope { + + private final LexicalScope parent; + private final CtElement scopeElement; + + public Map getElementsByName() { + return elementsByName; + } + + private final Map elementsByName = new HashMap<>(); + + NameScopeImpl(LexicalScope parent, CtElement scopeElement, List> parameters) { + this(parent, scopeElement); + for (CtParameter parameter : parameters) { + addNamedElement(parameter); + } + } + + protected NameScopeImpl(LexicalScope parent, CtElement scopeElement) { + this.parent = parent; + this.scopeElement = scopeElement; + } + + @Override + public NameScopeImpl addNamedElement(CtNamedElement element) { + elementsByName.put(element.getSimpleName(), element); + return this; + } + + /** + * @return the {@link CtElement} which represents the current scope + */ + @Override + public final CtElement getScopeElement() { + return scopeElement; + } + + public final Optional getParent() { + return Optional.ofNullable(parent); + } + + /** + * @param name to be searched simple name + * @param consumer is called for each named element with same name which are accessible from this {@link NameScopeImpl} + * 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 + */ + @Override + public T forEachElementByName(String name, Function consumer) { + T r = forEachByName(elementsByName, name, consumer); + 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 static T forEachByName(Map map, String name, Function consumer) { + CtNamedElement named = map.get(name); + if (named != null) { + return consumer.apply(named); + } + return null; + } +} diff --git a/src/main/java/spoon/reflect/visitor/TypeNameScope.java b/src/main/java/spoon/reflect/visitor/TypeNameScope.java new file mode 100644 index 00000000000..c3570968dae --- /dev/null +++ b/src/main/java/spoon/reflect/visitor/TypeNameScope.java @@ -0,0 +1,189 @@ +/** + * 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 a lexical scope of a type, with all accessible fields, nested type names and method names + */ +class TypeNameScope extends NameScopeImpl { + private Map fieldsByName; + private Map typesByName; + private Map methodsByName; + + TypeNameScope(LexicalScope conflictFinder, CtType p_type) { + super(conflictFinder, p_type); + } + + @Override + public T forEachElementByName(String name, Function consumer) { + super.forEachElementByName(name, 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 putType(CtType t) { + putIfNotExists(typesByName, t); + } + 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) { + putType((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) { + putType(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 + putType(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 (packageType != getScopeElement() && !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/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..7830441f6bb --- /dev/null +++ b/src/test/java/spoon/test/imports/name_scope/NameScopeTest.java @@ -0,0 +1,139 @@ +/** + * 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 org.junit.Test; +import spoon.reflect.code.CtBlock; +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.visitor.LexicalScope; +import spoon.reflect.visitor.LexicalScopeScanner; +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; + +import java.io.File; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; + +public class NameScopeTest { + + @Test + public void testNameScopeScanner() throws Exception { + //contract: check that LexicalScope 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); + + LexicalScopeScanner scanner = new LexicalScopeScanner(); + scanner.setVisitCompilationUnitContent(true); + final LexicalScope[] scopes = new LexicalScope[2]; + scanner.setListener(new CtScannerListener() { + @Override + public ScanningMode enter(CtElement element) { + // saving the scope of method 'draw' + if (element instanceof CtBlock) { + scopes[0] = scanner.getCurrentNameScope(); + } + + // saving the scope at 'System.out.println("2");' + if (element instanceof CtLiteral && ((CtLiteral) element).getValue().equals("2")) { + scopes[1] = scanner.getCurrentNameScope(); + } + return ScanningMode.NORMAL; + } + }); + // we collect all scopes + scanner.scan(typeRenata.getPosition().getCompilationUnit()); + + // we have two different scopes + assertNotSame(scopes[0], scopes[1]); + + //contract: the local variables are visible after they are declared + checkThatScopeContains(scopes[0], Arrays.asList(), "count"); + checkThatScopeContains(scopes[0], Arrays.asList("String theme"), "theme"); + checkThatScopeContains(scopes[0], Arrays.asList(methodDraw), "draw"); + checkThatScopeContains(scopes[0], Arrays.asList(typeTereza), "Tereza"); + checkThatScopeContains(scopes[0], Arrays.asList(fieldMichal), "michal"); + checkThatScopeContains(scopes[0], Arrays.asList(typeRenata), "Renata"); + //contract: imported types are visible too + checkThatScopeContains(scopes[0], Arrays.asList(typeFile), "File"); + //contract: imported static methods are visible too + checkThatScopeContains(scopes[0], Arrays.asList(methodCurrentTimeMillis), "currentTimeMillis"); + //contract: type members imported by wildcard are visible too + checkThatScopeContains(scopes[0], Arrays.asList(methodsNewDirectoryStream), "newDirectoryStream"); + //contract: The names are case sensitive + checkThatScopeContains(scopes[0], Arrays.asList(), "Michal"); + //the names which are not visible, must not be returned + checkThatScopeContains(scopes[0], Arrays.asList(), "void"); + checkThatScopeContains(scopes[0], Arrays.asList(), "String"); + //type members of System are not visible + checkThatScopeContains(scopes[0], Arrays.asList(), "setIn"); + //type member itself whose field is imported is not visible + checkThatScopeContains(scopes[0], Arrays.asList(), "System"); + //type member itself whose type members are imported by wildcard are not visible + checkThatScopeContains(scopes[0], Arrays.asList(), "Fields"); + + //contract: the local variables is only visible in the block scope (not the method one) + checkThatScopeContains(scopes[0], Arrays.asList(), "count"); + checkThatScopeContains(scopes[1], Arrays.asList("int count"), "count"); + } + + + + private void checkThatScopeContains(LexicalScope lexicalScope, List expectedElements, String name) { + List realElements = new ArrayList<>(); + lexicalScope.forEachElementByName(name, e -> {realElements.add(e); + //System.out.println(e.getPosition().toString()); + return true;}); + 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"); + } + } +}