/*******************************************************************************
* 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 melnorme.utilbox.core.Assert.AssertNamespace.assertTrue;
import static melnorme.utilbox.core.CoreUtil.areEqual;
import java.nio.file.Path;
import java.util.regex.Pattern;
import org.junit.Test;
import dtool.ast.definitions.DefUnit;
import dtool.ast.definitions.ITemplatableElement;
import dtool.ast.references.RefTemplateInstance;
import dtool.ast.references.Reference;
import dtool.engine.ResolvedModule;
import dtool.engine.analysis.templates.InstantiatedDefUnit;
import dtool.engine.analysis.templates.RefTemplateInstanceSemantics;
import dtool.engine.analysis.templates.TemplateInstance;
import dtool.engine.analysis.templates.TemplateParameterAnalyser.NotInstantiatedErrorElement;
import dtool.engine.tests.DefUnitResultsChecker;
import dtool.engine.util.NamedElementUtil;
import dtool.parser.SourceEquivalenceChecker;
import melnorme.lang.tooling.BundlePath;
import melnorme.lang.tooling.ast.util.ASTSourceRangeChecker;
import melnorme.lang.tooling.ast_actual.ASTNode;
import melnorme.lang.tooling.engine.ErrorElement;
import melnorme.lang.tooling.engine.OverloadedNamedElement;
import melnorme.lang.tooling.engine.PickedElement;
import melnorme.lang.tooling.engine.completion.CompletionScopeLookup;
import melnorme.lang.tooling.symbols.INamedElement;
public class Template_SemanticsTest extends NamedElement_CommonTest {
@Test
public void test_UninstantiatedTemplate() throws Exception { test_UninstantiatedTemplate$(); }
public void test_UninstantiatedTemplate$() throws Exception {
test_NamedElement_Concrete(parseElement("template xxx/*M*/() { int foo; } ", INamedElement.class),
expectNotAValue("xxx"), array("foo") );
test_NamedElement_Concrete(parseElement("template Tpl(int xxx/*M*/) { int foo; } ", INamedElement.class),
"$/int", NE_LanguageIntrinsics_SemanticsTest.INT_PROPERTIES);
test_NamedElement_Alias(parseElement("template Tpl(xxx/*M*/) { int foo; } ", INamedElement.class),
ErrorElement.UNSUPPORTED__Name, expectNotAValue("xxx"), NO_MEMBERS);
test_NamedElement_Alias(parseElement("template Tpl(xxx/*M*/ : int) { int foo; } ", INamedElement.class),
"$/int", expectNotAValue("xxx"), NE_LanguageIntrinsics_SemanticsTest.INT_PROPERTIES);
test_NamedElement_Alias(parseElement("template Tpl(alias xxx/*M*/) { int foo; } ", INamedElement.class),
NotInstantiatedErrorElement.NAME, NotInstantiatedErrorElement.NAME, NO_MEMBERS);
test_NamedElement_Alias(parseElement("template Tpl(this xxx/*M*/) { int foo; } ", INamedElement.class),
NotInstantiatedErrorElement.NAME, NotInstantiatedErrorElement.NAME, NO_MEMBERS);
test_NamedElement_Concrete(parseElement("template Tpl(xxx/*M*/...) { int foo; } ", INamedElement.class),
expectNotAValue("xxx"), NO_MEMBERS);
}
/* ----------------- ----------------- */
public static void checkNamedElements(Iterable<INamedElement> originalElements, String... expectedResults) {
new DefUnitResultsChecker(originalElements).checkNamedElements(expectedResults);
}
protected static <T extends Reference> INamedElement resolveTarget(PickedElement<T> ref) {
return ref.element.resolveTargetElement(ref.context);
}
protected static INamedElement resolveTarget(Reference ref, PickedElement<?> other) {
return ref.resolveTargetElement(other.context);
}
protected static PickedElement<INamedElement> findTplParamInstance(TemplateInstance tplInstance,
String toStringAsCode) {
Reference ref = NodeFinderByString.find(tplInstance, Reference.class, toStringAsCode);
INamedElement typeAlias = resolveTarget(ref, picked(tplInstance, tplInstance.refContext));
return picked(typeAlias, tplInstance.refContext);
}
/* ----------------- ----------------- */
@Override
public void test_NamedElement________() throws Exception {
testTemplateInstantiation$();
}
public void testTemplateInstantiation$() throws Exception {
testTemplateInstantiation("class Tpl { }; ", "Tpl!(int)",
RefTemplateInstanceSemantics.ERROR__NotATemplate + ":Tpl"
);
testTemplateInstantiation("", "Tpl!(int)",
expectNotFound("Tpl")
);
testTemplateInstantiation("void Tpl(); int Tpl; ", "Tpl!(int)",
OverloadedNamedElement.ERROR_NAME + "[void Tpl() ;| int Tpl;]"
);
final String TPL_DEF_SAMPLE = "template Tpl(TYPE1) { TYPE1 foo; }";
TemplateInstance tplInstance = doTestTemplateInstantiation(
TPL_DEF_SAMPLE + "Tpl!(int)/*M*/ _dummy",
DEFAULT_ModuleName + "/Tpl!(int)",
"@{ @TYPE1 = /int; } template Tpl { TYPE1 foo; }",
expectNotAValue("Tpl!(int)"),
array("foo")
);
CompletionScopeLookup search = new CompletionScopeLookup(tplInstance.getStartPos(), tplInstance.refContext, "");
tplInstance.performNameLookup(search);
checkNamedElements(search.getMatchedElements(), array("@TYPE1 = /int;", "$_tests/", "$_tests/Tpl"));
// Some error cases
testTemplateInstantiation("template Tpl { }; ", "Tpl!()",
"@{ } template Tpl "
);
testTemplateInstantiation("template Tpl { }; ", "Tpl!(int)", // An extra parameters
RefTemplateInstanceSemantics.ERROR__TPL_REF_MATCHED_NONE
);
testTemplateInstantiation("template Tpl(A, B) { }; ", "Tpl!(int)", // An extra parameters
RefTemplateInstanceSemantics.ERROR__TPL_REF_MATCHED_NONE
);
test_TypeParam$();
test_VarParam$();
test_AliasParam$();
test_ThisParam$();
test_TupleParam$();
// Test template-like aggregates:
testTemplateInstantiation("class Tpl() { int bar; }; ", "Tpl!()",
"@{ } class Tpl { int bar; }"
);
testTemplateInstantiation("struct Tpl(T) { int foo; }; ", "Tpl!(int)",
"@{ @ T = /int;} struct Tpl { int foo;}"
);
doTestTemplateInstantiation(
sourcePlusRef("bool Tpl(T) (T myParam) { return myParam; }; ", "Tpl!(int)"),
DEFAULT_ModuleName + "/" + "Tpl!(int)(T myParam)",
"@{ @ T = /int; } bool Tpl(T myParam) { return myParam; }",
"$" + DEFAULT_ModuleName + "/Tpl!(int)(T myParam)" /* this should be changed at some point*/,
null
);
testParamKindOverloads$();
test_TemplateOverloads$();
test_templateContextAndCaching$();
}
protected static TemplateInstance testTemplateInstantiation(String baseSource,
String tplRef, String tplExpectedToStringAsCode) {
return doTestTemplateInstantiation(
sourcePlusRef(baseSource, tplRef),
DEFAULT_ModuleName + "/" + tplRef,
tplExpectedToStringAsCode,
expectNotAValue(tplRef),
null
);
}
protected static String sourcePlusRef(String baseSource, String tplRef) {
return baseSource + "; " + tplRef + "/*M*/ _dummy;";
}
protected static TemplateInstance doTestTemplateInstantiation(String source,
String expectedLabel, String expectedToStringAsCode, String expectedTypeLabel, String[] expectedMembers) {
PickedElement<RefTemplateInstance> tplRef = parseElement(source, "/*M*/", RefTemplateInstance.class);
return doTestTemplateInstantiation_____(tplRef, expectedLabel, expectedToStringAsCode,
expectedTypeLabel, expectedMembers);
}
protected static TemplateInstance doTestTemplateInstantiation_____(PickedElement<RefTemplateInstance> tplRef,
String expectedLabel, String expectedToStringAsCode, String expectedTypeLabel,
String[] expectedMembers) {
INamedElement tplRefTarget = resolveTarget(tplRef);
if(expectedToStringAsCode != null &&
expectedToStringAsCode.startsWith(ErrorElement.ERROR_PREFIX)) {
checkElementLabel(tplRefTarget, expectedToStringAsCode);
return null;
}
DefUnit instantiatedElement = assertCast(tplRefTarget, DefUnit.class);
TemplateInstance tplInstance = assertCast(instantiatedElement.getLexicalParent(), TemplateInstance.class);
ITemplatableElement templateDef = tplInstance.templateDef;
assertTrue(tplInstance.getLexicalParent() != null);
assertTrue(areEqual(instantiatedElement.getNameSourceRangeOrNull(),
((DefUnit) templateDef).getNameSourceRangeOrNull()));
// assertTrue(tplInstance.getOwnerElement() == tplInstance.templateDef.getParent());
assertTrue(tplInstance.getSemanticContainerKey() == templateDef.getSemanticContainerKey());
assertTrue(tplInstance.refContext == tplRef.context);
assertTrue(tplInstance.getElementSemanticContext(tplRef.context) ==
templateDef.getElementSemanticContext(tplRef.context));
if(expectedLabel != null) {
String elementLabel = NamedElementUtil.getElementTypedLabel(instantiatedElement, true);
assertAreEqual(expectedLabel, elementLabel);
}
checkSourceEquivalence(expectedToStringAsCode, tplInstance);
ASTSourceRangeChecker.checkConsistency(tplInstance);
test_NamedElement(picked2(tplInstance.instantiatedElement, tplRef.context),
null,
expectedTypeLabel,
expectedMembers
);
return tplInstance;
}
protected static void checkSourceEquivalence(String expectedToStringAsCode, ASTNode node) {
if(expectedToStringAsCode != null) {
String nodeToStringAsCode = node.toStringAsCode();
nodeToStringAsCode = nodeToStringAsCode.replaceAll(Pattern.quote("#"), "@");
expectedToStringAsCode = expectedToStringAsCode.replaceAll(Pattern.quote("#"), "@");
SourceEquivalenceChecker.assertCheck(nodeToStringAsCode, expectedToStringAsCode);
}
}
/* ----------------- ----------------- */
protected static class TemplateParamTester {
protected String templateSource;
protected String intArg_toStringAsCode;
protected String intArg_concreteTarget;
protected String intArg_type;
protected String intArgAlias_toStringAsCode;
protected String intArgAlias_concreteTarget;
protected String intArgAlias_type;
protected String varArg_toStringAsCode;
protected String varArg_concreteTarget;
protected String varArg_type;
protected String varArgAlias_toStringAsCode;
protected String varArgAlias_concreteTarget;
protected String varArgAlias_type;
protected String missingArg_ToStringAsCode;
protected String missingArg_concreteTarget;
protected String missingArg_type;
protected String numberArg_ToStringAsCode;
protected String numberArg_concreteTarget;
protected String numberArg_type;
protected void test_TplParameter$() {
// test type ref parameter (and alias)
testTemplateArgumentInstantiation(templateSource, "Tpl!(int)",
intArg_toStringAsCode,
intArg_concreteTarget,
intArg_type
);
testTemplateArgumentInstantiation(templateSource + "alias intAlias = int;", "Tpl!(intAlias)",
intArgAlias_toStringAsCode,
intArgAlias_concreteTarget,
intArgAlias_type
);
// Test name parameter, to var (and alias)
testTemplateArgumentInstantiation(templateSource + "int aVar;", "Tpl!(aVar)",
varArg_toStringAsCode,
varArg_concreteTarget,
varArg_type
);
testTemplateArgumentInstantiation(templateSource + "int aVar; alias aVarAlias = aVar", "Tpl!(aVarAlias)",
varArgAlias_toStringAsCode,
varArgAlias_concreteTarget,
varArgAlias_type
);
testTemplateArgumentInstantiation(templateSource, "Tpl!(missing)",
missingArg_ToStringAsCode,
missingArg_concreteTarget,
missingArg_type
);
// Test number parameter
testTemplateArgumentInstantiation(templateSource, "Tpl!(123)",
numberArg_ToStringAsCode,
numberArg_concreteTarget,
numberArg_type
);
}
}
protected static void testTemplateArgumentInstantiation(String baseSource,
String tplRef, String argExpectedToStringAsCode, String argConcreteTarget, String argType) {
if(argExpectedToStringAsCode == null) {
testTemplateInstantiation(baseSource,
tplRef, RefTemplateInstanceSemantics.ERROR__TPL_REF_MATCHED_NONE);
return;
}
TemplateInstance tplInstance = testTemplateInstantiation(baseSource, tplRef, null);
PickedElement<INamedElement> tplArgInstance = findTplParamInstance(tplInstance, "ARG");
assertTrue(tplArgInstance.element instanceof InstantiatedDefUnit);
checkSourceEquivalence(argExpectedToStringAsCode, (ASTNode) tplArgInstance.element);
test_NamedElement(tplArgInstance, argConcreteTarget, argType, null);
}
protected void test_TypeParam$() {
new TemplateParamTester() {
{
templateSource = "template Tpl(ARG) { ARG foo; }";
intArg_toStringAsCode = "@ARG = /int;";
intArg_concreteTarget = "$/int";
intArg_type = expectNotAValue("ARG");
intArgAlias_toStringAsCode = intArg_toStringAsCode;
intArgAlias_concreteTarget = intArg_concreteTarget;
intArgAlias_type = intArg_type;
varArg_toStringAsCode = null;
varArgAlias_toStringAsCode = null;
missingArg_ToStringAsCode = null;
numberArg_ToStringAsCode = null;
}
}.test_TplParameter$();
// Test default param
testTemplateInstantiation("template Tpl(ARG = int) { ARG foo; }", "Tpl!()",
"@{ @ARG = /int; } template Tpl { ARG foo; }"
);
}
protected void test_VarParam$() {
new TemplateParamTester() {
{
templateSource = "template Tpl(int ARG) { ARG foo; }";
intArg_toStringAsCode = null;
intArgAlias_toStringAsCode = null;
varArg_toStringAsCode = "@ int ARG = aVar;";
varArg_concreteTarget = varArg_toStringAsCode;
varArg_type = "$/int";
varArgAlias_toStringAsCode = "@ int ARG = aVarAlias;";
varArgAlias_concreteTarget = varArgAlias_toStringAsCode;
varArgAlias_type = "$/int";
missingArg_ToStringAsCode = null;
numberArg_ToStringAsCode = "@ int ARG = 123;";
numberArg_concreteTarget = numberArg_ToStringAsCode;
numberArg_type = "$/int";
}
}.test_TplParameter$();
new TemplateParamTester() {
{
templateSource = "template Tpl(bool ARG) { ARG foo; }";
intArg_toStringAsCode = null;
intArgAlias_toStringAsCode = null;
varArg_toStringAsCode = null;
varArgAlias_toStringAsCode = null;
missingArg_ToStringAsCode = null;
numberArg_ToStringAsCode = null;
}
}.test_TplParameter$();
}
protected void test_AliasParam$() {
new TemplateParamTester() {
{
templateSource = "template Tpl(alias ARG) { auto foo = ARG; }";
intArg_toStringAsCode = null;
intArgAlias_toStringAsCode = null;
varArg_toStringAsCode = "@value_alias ARG = aVar;";
varArg_concreteTarget = varArg_toStringAsCode;
varArg_type = "$/int";
varArgAlias_toStringAsCode = "@value_alias ARG = aVarAlias;";
varArgAlias_concreteTarget = varArgAlias_toStringAsCode;
varArgAlias_type = "$/int";
missingArg_ToStringAsCode = null;
numberArg_ToStringAsCode = "@value_alias ARG = 123;";
numberArg_concreteTarget = numberArg_ToStringAsCode;
numberArg_type = "$/int";
}
}.test_TplParameter$();
// Test default param
testTemplateInstantiation("template Tpl(alias ARG = 123) { }", "Tpl!()",
"@{ @value_alias ARG = 123; } template Tpl{ }"
);
}
protected void test_TupleParam$() {
new TemplateParamTester() {
{
templateSource = "template Tpl(ARG...) { auto foo = ARG; }";
intArg_toStringAsCode = "@ ARG... = (int);";
intArg_concreteTarget = intArg_toStringAsCode;
intArg_type = expectNotAValue("ARG");
intArgAlias_toStringAsCode = "@ ARG... = (intAlias);";
intArgAlias_concreteTarget = intArgAlias_toStringAsCode;
intArgAlias_type = expectNotAValue("ARG");
varArg_toStringAsCode = "@ ARG... = (aVar);";
varArg_concreteTarget = varArg_toStringAsCode;
varArg_type = expectNotAValue("ARG");
varArgAlias_toStringAsCode = "@ ARG... = (aVarAlias);";
varArgAlias_concreteTarget = varArgAlias_toStringAsCode;
varArgAlias_type = expectNotAValue("ARG");
missingArg_ToStringAsCode = "@ ARG... = (missing);";
missingArg_concreteTarget = missingArg_ToStringAsCode;
missingArg_type = expectNotAValue("ARG");
numberArg_ToStringAsCode = "@ ARG... = (123);";
numberArg_concreteTarget = numberArg_ToStringAsCode;
numberArg_type = expectNotAValue("ARG");
}
}.test_TplParameter$();
// Test tuple with multiple sizes
testTemplateInstantiation("template Tpl(ARG...) { }", "Tpl!()",
"@{ @ ARG... = (); } template Tpl{ }"
);
testTemplateInstantiation("template Tpl(ARG...) { }", "Tpl!(int,123)",
"@{ @ ARG... = (int,123); } template Tpl{ }"
);
}
protected void test_ThisParam$() {
// ThisParameter can only be correctly instantiated with templated functions, using IFTI
// (implicit Function Template Instantiation
new TemplateParamTester() {
{
templateSource = "template Tpl(this ARG){ auto foo = ARG; }";
intArg_toStringAsCode = null;
intArgAlias_toStringAsCode = null;
varArg_toStringAsCode = null;
varArgAlias_toStringAsCode = null;
missingArg_ToStringAsCode = null;
numberArg_ToStringAsCode = null;
}
}.test_TplParameter$();
}
/* ----------------- ----------------- */
protected void testParamKindOverloads$() {
String TPL_T = "template Tpl(ARG) { void T; }";
String TPL_Tint = "template Tpl(ARG : int) { void Tint; }";
String TPL_Tbool = "template Tpl(ARG : bool) { void Tbool; }";
String TPL_ALIAS = "template Tpl(alias ARG) { void tAlias; }";
String TPL_VALUEint = "template Tpl(int ARG) { void valueInt; }";
String TPL_TUPLE = "template Tpl(ARG...) { void tuple; }";
// --- Test specializations and overloads
testTemplateInstantiation(TPL_T + TPL_Tint, "Tpl!(int)",
"@{ @ARG = /int; } template Tpl{ void Tint; }"
);
// Multiple template matches should be an error, but match first tpl
testTemplateInstantiation(TPL_T + TPL_T, "Tpl!(int)",
"@{ @ARG = /int; } template Tpl{ void T; }"
);
testTemplateInstantiation(TPL_T + TPL_Tint, "Tpl!(bool)",
"@{ @ARG = /bool; } template Tpl{ void T; }"
);
testTemplateInstantiation(TPL_T + TPL_Tint + TPL_Tbool, "Tpl!(int)",
"@{ @ARG = /int; } template Tpl{ void Tint; }"
);
// Match none:
testTemplateInstantiation(TPL_Tint + TPL_Tbool, "Tpl!(float)",
RefTemplateInstanceSemantics.ERROR__TPL_REF_MATCHED_NONE
);
// ---- Value params
testTemplateInstantiation(TPL_T + TPL_Tint + TPL_Tbool, "Tpl!(123)",
RefTemplateInstanceSemantics.ERROR__TPL_REF_MATCHED_NONE
);
testTemplateInstantiation(TPL_T + TPL_Tint + TPL_VALUEint + TPL_ALIAS, "Tpl!(123)",
"@{ @int ARG = 123; } template Tpl { void valueInt; }"
);
testTemplateInstantiation(TPL_T + TPL_Tint + TPL_VALUEint + TPL_ALIAS, "Tpl!(true)",
"@{ @value_alias ARG = true; } template Tpl { void tAlias; }"
);
testTemplateInstantiation(
TPL_T + TPL_Tint + TPL_VALUEint + TPL_TUPLE, "Tpl!(int)",
"@{ @ARG = /int; } template Tpl { void Tint; }"
);
}
protected void test_TemplateOverloads$() {
/* ----------------- Test wrong number of arguments ----------------- */
final String TPL_DEF_0P = "template Tpl() { int A; }";
testTemplateInstantiation(TPL_DEF_0P, "Tpl!()",
"@{ } template Tpl { int A; }"
);
testTemplateInstantiation(TPL_DEF_0P, "Tpl!(int)",
RefTemplateInstanceSemantics.ERROR__TPL_REF_MATCHED_NONE
);
testTemplateInstantiation(TPL_DEF_0P, "Tpl!(int, 123)",
RefTemplateInstanceSemantics.ERROR__TPL_REF_MATCHED_NONE
);
final String TPL_DEF_1P = "template Tpl(ARG) { int B; }";
testTemplateInstantiation(TPL_DEF_1P, "Tpl!()",
RefTemplateInstanceSemantics.ERROR__TPL_REF_MATCHED_NONE
);
testTemplateInstantiation(TPL_DEF_1P, "Tpl!(int, 123)",
RefTemplateInstanceSemantics.ERROR__TPL_REF_MATCHED_NONE
);
final String TPL_DEF_1P_plus = "template Tpl(ARG, ARG = bool) { int C; }";
testTemplateInstantiation(TPL_DEF_1P_plus, "Tpl!()",
RefTemplateInstanceSemantics.ERROR__TPL_REF_MATCHED_NONE
);
testTemplateInstantiation(TPL_DEF_1P_plus, "Tpl!(int, int, int)",
RefTemplateInstanceSemantics.ERROR__TPL_REF_MATCHED_NONE
);
testTemplateInstantiation(TPL_DEF_1P_plus, "Tpl!(int,char)",
"@{ @ARG = /int; @ARG = /char; } template Tpl { int C; }"
);
testTemplateInstantiation(TPL_DEF_1P_plus, "Tpl!(char)",
"@ { @ARG = /char; @ARG = /bool; } template Tpl { int C; }"
);
/* ----------------- test param number overloads ----------------- */
testTemplateInstantiation(TPL_DEF_0P + TPL_DEF_1P, "Tpl!()",
"@{ } template Tpl { int A; }"
);
testTemplateInstantiation(TPL_DEF_0P + TPL_DEF_1P, "Tpl!(int)",
"@{ @ARG = /int; } template Tpl { int B; }"
);
testTemplateInstantiation(
"class Tpl(T, int NUM) { int A; } " +
"template Tpl(T, T2) { int B; } " ,
"Tpl!(int,123)",
"@{ @T = /int; @ int NUM = 123; } class Tpl { int A; }"
);
// Matches multiples
testTemplateInstantiation(TPL_DEF_1P_plus + TPL_DEF_1P, "Tpl!(char)",
"@{ @ARG = /char; @ARG = /bool; } template Tpl { int C; }"
);
/* ----------------- vs tuples ----------------- */
final String TPL_DEF_1P_Tuple = "template Tpl(ARG1, ARGT...) { int ARG1_Tuple; }";
testTemplateInstantiation(TPL_DEF_1P_Tuple, "Tpl!()",
RefTemplateInstanceSemantics.ERROR__TPL_REF_MATCHED_NONE // Not enough arguments
);
testTemplateInstantiation(TPL_DEF_1P_Tuple + TPL_DEF_0P, "Tpl!(int)",
"@{ @ ARG1 = /int; @ ARGT... = (); } template Tpl { int ARG1_Tuple; }"
);
testTemplateInstantiation(TPL_DEF_1P_Tuple + TPL_DEF_0P + TPL_DEF_1P, "Tpl!(int)",
null
);
testTemplateInstantiation(TPL_DEF_1P_Tuple + TPL_DEF_0P + TPL_DEF_1P_plus , "Tpl!(int)",
null
);
/* ----------------- ----------------- */
// Overload with different kinds of template entities
testTemplateInstantiation(
"class Tpl() { int bar; }; " + "template Tpl(T) { int foo; }; ",
"Tpl!()",
"@{ } class Tpl { int bar; }"
);
}
/* ----------------- ----------------- */
public static final BundlePath DEFAULT_TestsBundle = bundlePath(SEMANTICS_TEST_BUNDLES, "defaultBundle");
protected static final Path TESTER2 = loc(SEMANTICS_TEST_BUNDLES, "tester2/source/_tester.d").path;
protected void test_templateContextAndCaching$() {
ResolvedModule resModule = parseModule_(
"import lib_foo.mod; import tpl_sampleA; " + "Tpl_A!(Foo)/*M*/ _dummy;",
TESTER2);
doTestTemplateInstantiation_____(pickElement(resModule, "/*M*/", RefTemplateInstance.class),
"tpl_sampleA/" + "Tpl_A!(Foo)",
"@{ @ TYPE = lib_foo.mod/Foo; } template Tpl_A { TYPE foo; }",
expectNotAValue("Tpl_A!(Foo)"),
null);
// TODO: implement and test caching
}
}