/*******************************************************************************
* Copyright (c) 2014 Bruno Medeiros and other Contributors.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Bruno Medeiros - initial API and implementation
*******************************************************************************/
package dtool.engine.analysis;
import static dtool.engine.analysis.Resolvables_SemanticsTest.testResolveElement;
import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue;
import java.util.concurrent.ExecutionException;
import melnorme.lang.tooling.engine.ErrorElement;
import melnorme.lang.tooling.engine.PickedElement;
import melnorme.lang.tooling.engine.ErrorElement.NotFoundErrorElement;
import melnorme.lang.tooling.engine.resolver.ReferenceResult;
import melnorme.lang.tooling.symbols.IConcreteNamedElement;
import melnorme.lang.tooling.symbols.INamedElement;
import melnorme.lang.tooling.symbols.PackageNamespace;
import java.util.function.Predicate;
import org.junit.Test;
import dtool.ast.definitions.Module;
import dtool.ast.references.NamedReference;
import dtool.ast.references.RefModule;
import dtool.engine.ResolvedModule;
import dtool.engine.util.NamedElementUtil;
public class Import_LookupTest extends CommonLookupTest {
protected PickedElement<NamedReference> parseRef(String source, String marker) throws ExecutionException {
return parseElement(source, marker, NamedReference.class);
}
protected void testRefModule(PickedElement<RefModule> refModuleElement, String fqn, boolean isNotFound) {
ReferenceResult resolution = testResolveElement(refModuleElement);
INamedElement result = resolution.result;
assertTrue(result.getFullyQualifiedName().equals(fqn));
// Test that it resolves to an alias of the actual module unit.
// This is an optimization to not parse the module until really necessary.
assertTrue(result instanceof ModuleProxy);
IConcreteNamedElement moduleTarget = result.resolveConcreteElement(refModuleElement.context);
if(isNotFound) {
assertTrue(moduleTarget instanceof ErrorElement);
assertTrue(moduleTarget.getFullyQualifiedName().equals(NotFoundErrorElement.NOT_FOUND__Name));
} else {
assertTrue(moduleTarget instanceof Module);
assertTrue(moduleTarget.getFullyQualifiedName().equals(fqn));
}
}
protected void testPackageRef(PickedElement<? extends NamedReference> refToPackage, String fqn) {
ReferenceResult resolution = testResolveElement(refToPackage);
INamedElement result = resolution.result;
assertTrue(result.getFullyQualifiedName().equals(fqn));
if(fqn == NotFoundErrorElement.NOT_FOUND__Name) {
return;
}
assertTrue(result instanceof PackageNamespace);
IConcreteNamedElement concreteTarget = result.resolveConcreteElement(refToPackage.context);
assertTrue(concreteTarget instanceof PackageNamespace);
assertTrue(concreteTarget.getFullyQualifiedName().equals(fqn));
}
@Test
public void testResolveOfDirectRef() throws Exception { testResolveOfDirectRef$(); }
public void testResolveOfDirectRef$() throws Exception {
testRefModule(parseElement("import target;", "target", RefModule.class), "target", false);
testRefModule(parseElement("import pack.target;", "pack.target", RefModule.class), "pack.target", false);
testRefModule(parseElement("import not_found;", "not_found", RefModule.class), "not_found", true);
// Test package refs.
testPackageRef(parseRef("import pack.target; auto x = pack;", "pack;"), "pack");
testPackageRef(parseRef("import pack.subpack.target; auto x = pack;", "pack;"), "pack");
testPackageRef(parseRef("import pack.subpack.target; auto x = pack.subpack/**/;", "subpack/**/"),
"pack.subpack");
}
/* ----------------- ----------------- */
protected static final String SRC_IMPORT_SELF = "import " + DEFAULT_ModuleName + "; ";
@Test
public void test_ImportContent() throws Exception { test_ImportContent$(); }
public void test_ImportContent$() throws Exception {
// ---------- Basic test - module statement
testLookup(parseModule_WithRef("module xxx;", "xxx"),
namedElementChecker("$xxx/")
);
testLookup(parseModule_WithRef("", DEFAULT_ModuleName),
namedElementChecker("$"+DEFAULT_ModuleName+"/")
);
testLookup(parseModule_WithRef("module xxx; int foo;", "xxx.foo"),
namedElementChecker("int foo;")
);
testLookup(parseModule_WithRef("module xxx; int foo;", "xxx.foo"),
namedElementChecker("int foo;")
);
testLookup(parseModule_WithRef("module pack.xxx; int foo;", "pack.xxx.foo"),
namedElementChecker("int foo;")
);
// ---------- Basic test
testLookup(parseModule_WithRef("import pack.foobar; ", "PackFoobar_member"),
namedElementChecker("int PackFoobar_member;")
);
testLookup(parseModule_WithRef("import pack.foobar; void PackFoobar_member;", "PackFoobar_member"),
namedElementChecker("void PackFoobar_member;")
);
// vs. package name
testLookup(parseModule_WithRef("import pack.foobar;", "pack"),
namedElementChecker("PNamespace[pack]")
);
testLookup(parseModule_WithRef("import pack.foobar; int pack;", "pack"),
checkNameConflict("PNamespace[pack]", "int pack;")
);
// Special case: import self:
testLookup(parseModule_WithRef(SRC_IMPORT_SELF, DEFAULT_ModuleName),
namedElementChecker("$"+DEFAULT_ModuleName+"/")
);
testLookup(parseModule_WithRef(SRC_IMPORT_SELF + "int xxx;", "xxx"),
namedElementChecker("int xxx;")
);
testLookup(parseModule_WithRef("module "+DEFAULT_ModuleName+";"+ SRC_IMPORT_SELF, DEFAULT_ModuleName),
namedElementChecker("$"+DEFAULT_ModuleName+"/")
);
testLookup(parseModule_WithRef("module "+DEFAULT_ModuleName+";"+ SRC_IMPORT_SELF + "int xxx;", "xxx"),
namedElementChecker("int xxx;")
);
testLookup(getUpdatedModule(DEFAULT_TestsBundle_Source.resolve_fromValid("pack/import_self.d")),
namedElementChecker("$pack.import_self/")
);
testLookup(getUpdatedModule(DEFAULT_TestsBundle_Source.resolve_fromValid("pack/import_self.d")),
"/*M2*/", namedElementChecker("int xpto;")
);
testLookup(getUpdatedModule(DEFAULT_TestsBundle_Source.resolve_fromValid("pack/import_self_indirect.d")),
namedElementChecker("$pack.import_self_indirect/")
);
testLookup(getUpdatedModule(DEFAULT_TestsBundle_Source.resolve_fromValid("pack/import_self_indirect.d")),
"/*M2*/", namedElementChecker("int xpto;")
);
testLookup(getUpdatedModule(DEFAULT_TestsBundle_Source.resolve_fromValid("pack/import_self.d")),
"/*M3*/", namedElementChecker("int xpto;")
);
// ------- Test namespace aggregation
testLookup(parseModule_WithRef("import pack.foo; import pack.foobar; import pack.non_existant;", "pack"),
checkIsPackageNamespace(array(
"module[pack.foo]", "module[pack.foobar]", "module[pack.non_existant]"
))
);
// Test namespace aggregation - across scopes
testLookup(parseModule_("import pack.foo; class Xpto { import pack.foobar; " + mref("pack") + "}"),
checkIsPackageNamespace(array(
"module[pack.foo]", "module[pack.foobar]"
))
);
testLookup(parseModule_WithRef(
"import a.xxx.foo; import a.xxx.bar; import a.xxx.xpto.foo; import a.xxx.; "
+ "import a.yyy.bar; import a.zzz;",
"a"),
checkIsPackageNamespace(array(
"PNamespace[a.xxx]", "PNamespace[a.yyy]","module[a.zzz]"
))
);
// Aggregation on the second level
testLookup(parseModule_WithRef(
"import a.xxx.foo; import a.xxx.bar; import a.xxx.xpto.foo; import a.xxx.; "
+ "import a.yyy.bar; import a.zzz;",
"a.xxx"),
checkIsPackageNamespace(array(
"module[a.xxx.foo]", "module[a.xxx.bar]", "PNamespace[a.xxx.xpto]"
))
);
// Test overload: packagevs. a module import/ns
testLookup(parseModule_WithRef("import xxx.foo; import xxx; ", "xxx"),
checkModuleProxy("module[xxx]")
);
testLookup(parseModule_WithRef("import xxx; import xxx.foo; ", "xxx"),
checkModuleProxy("module[xxx]")
);
// ------- Test duplicate import
testLookup(parseModule_WithRef("import xxx; import xxx;", "xxx"),
checkModuleProxy("module[xxx]")
);
testLookup(parseModule_WithRef("module xxx; import xxx;", "xxx"),
checkModuleProxy("module[xxx]")
);
testLookup(
parseModule_WithRef("import xxx.foo; import xxx.bar.z; import xxx.bar.z; import xxx.foo;", "xxx"),
checkIsPackageNamespace(array("module[xxx.foo]", "PNamespace[xxx.bar]"))
);
testLookup(
parseModule_WithRef("import xxx.foo; import xxx.bar.z; import xxx.bar.z; import xxx.foo;", "xxx.bar"),
checkIsPackageNamespace(array("module[xxx.bar.z]"))
);
// ------- Test name conflicts
testLookup(parseModule_WithRef("module xxx; int xxx; ", "xxx"),
namedElementChecker("int xxx;")
);
testLookup(parseModule_WithRef("int xxx; import xxx; ", "xxx"),
checkNameConflict("int xxx;", "module[xxx]")
);
testLookup(parseModule_WithRef("int xxx; char xxx; import xxx; ", "xxx"),
checkNameConflict("int xxx;", "char xxx;", "module[xxx]")
);
// ----------------------
// Test contents of fully-qualified namespace
testLookup(parseModule_WithRef("import pack.foobar; ", "pack.foobar.PackFoobar_member"),
namedElementChecker("int PackFoobar_member;")
);
testLookup(parseModule_WithRef("import pack.foobar; ", "pack.foobar.pack"),
namedElementChecker(expectNotFound("pack"))
);
// ------- import vs. parent lexical scopes
String scopeOverload_vsImport =
"void xxx;" +
"void func() {" +
" import xxx; auto _ = xxx/*M*/" +
"}";
testLookup(parseModule_(scopeOverload_vsImport),
namedElementChecker(
"module[xxx]"
));
test_public_imports$();
}
protected void doNamespaceLookupTest(ResolvedModule resolvedModule, String offsetMarker,
final String[] expectedResults) {
testLookup(resolvedModule, offsetMarker, checkIsPackageNamespace(expectedResults));
}
protected Predicate<INamedElement> checkModuleProxy(final String expectedToString) {
return new Predicate<INamedElement>() {
@Override
public boolean test(INamedElement matchedElement) {
ModuleProxy moduleProxy = assertInstance(matchedElement, ModuleProxy.class);
assertTrue(NamedElementUtil.namedElementToString(moduleProxy).equals(expectedToString));
return true;
}
};
}
/* ----------------- ----------------- */
public void test_public_imports$() throws Exception {
// Check test sample file is correct for subsequent tests
String FOO_PRIVATE_XXX = "foo_private__xxx";
testLookup(parseModule_WithRef("import pack.foo_private; ", FOO_PRIVATE_XXX),
namedElementChecker("PackFooPrivate " + FOO_PRIVATE_XXX + ";"));
/* ----------------- ----------------- */
String PUBLIC_IMPORT = "import pack.public_import; import pack.zzz.non_existant";
testLookup(parseModule_WithRef(PUBLIC_IMPORT, "xxx"), namedElementChecker("PackFoo xxx;"));
testLookup(parseModule_WithRef(PUBLIC_IMPORT, FOO_PRIVATE_XXX), notfoundChecker(FOO_PRIVATE_XXX));
testLookup(parseModule_WithRef(PUBLIC_IMPORT, "pack"),
checkIsPackageNamespace(array(
"module[pack.public_import]", "module[pack.foo]",
"PNamespace[pack.zzz]"
))
);
// Test as members scope
// -> note this behavior is not according to DMD, but is it according to spec?
// Should be, if not, ugly spec behavior
testLookup(parseModule_WithRef(PUBLIC_IMPORT, "pack.public_import.xxx"),
notfoundChecker("xxx"));
testLookup(parseModule_WithRef(PUBLIC_IMPORT, "pack.public_import." + FOO_PRIVATE_XXX),
notfoundChecker(FOO_PRIVATE_XXX));
testLookup(parseModule_WithRef(PUBLIC_IMPORT, "pack.public_import.pack"),
notfoundChecker("pack"));
testLookup(parseModule_WithRef("class Xpto { import pack.foo; }", "Xpto.pack"),
notfoundChecker("pack"));
String PUBLIC_IMPORT2 = "import pack.public_import2; import pack.zzz.non_existant";
testLookup(parseModule_WithRef(PUBLIC_IMPORT2, "xxx"), namedElementChecker("PackFoo xxx;"));
testLookup(parseModule_WithRef(PUBLIC_IMPORT2, FOO_PRIVATE_XXX), notfoundChecker(FOO_PRIVATE_XXX));
testLookup(parseModule_WithRef(PUBLIC_IMPORT2, "pack"),
checkIsPackageNamespace(array(
"module[pack.public_import2]", "module[pack.foo]",
"PNamespace[pack.zzz]"
))
);
String PUBLIC_IMPORT_INDIRECT = "import pack.public_import_x; import pack.zzz.non_existant";
testLookup(parseModule_WithRef(PUBLIC_IMPORT_INDIRECT, "xxx"), namedElementChecker("PackFoo xxx;"));
testLookup(parseModule_WithRef(PUBLIC_IMPORT_INDIRECT, FOO_PRIVATE_XXX), notfoundChecker(FOO_PRIVATE_XXX));
testLookup(parseModule_WithRef(PUBLIC_IMPORT_INDIRECT, "pack"),
checkIsPackageNamespace(array(
"module[pack.public_import_x]",
"module[pack.public_import]", "module[pack.foo]",
"PNamespace[pack.zzz]"
))
);
// test visiting lexical module scope, after visiting imported scope.
testLookup(parseModule_(
" int xxx;"
+ "class Xpto { "
+ SRC_IMPORT_SELF
+ " auto _ = xxx/*M*/; }"),
namedElementChecker("int xxx;")
);
testLookup(parseModule_(
" import foo;"
+ "class Xpto { "
+ SRC_IMPORT_SELF
+ " auto _ = foo_member/*M*/; }"),
namedElementChecker("int foo_member;")
);
}
public static Predicate<INamedElement> checkIsPackageNamespace(final String[] expectedResults) {
return new Predicate<INamedElement>() {
@Override
public boolean test(INamedElement matchedElement) {
PackageNamespace packageNameSpace = assertInstance(matchedElement, PackageNamespace.class);
assertEqualSet(
hashSet(elementToStringArray(packageNameSpace.getNamespaceElements())),
hashSet(expectedResults)
);
return true;
}
};
}
}