755 lines
22 KiB
D
755 lines
22 KiB
D
module dsymbol.tests;
|
|
|
|
import std.experimental.allocator;
|
|
import dparse.ast, dparse.parser, dparse.lexer, dparse.rollback_allocator;
|
|
import dsymbol.cache_entry, dsymbol.modulecache, dsymbol.symbol;
|
|
import dsymbol.conversion, dsymbol.conversion.first, dsymbol.conversion.second, dsymbol.conversion.third;
|
|
import dsymbol.semantic, dsymbol.string_interning, dsymbol.builtin.names;
|
|
import std.file, std.path, std.format;
|
|
import std.stdio : writeln, stdout;
|
|
import dsymbol.ufcs;
|
|
|
|
/**
|
|
* Parses `source`, caches its symbols and compares the the cache content
|
|
* with the `results`.
|
|
*
|
|
* Params:
|
|
* source = The source code to test.
|
|
* results = An array of string array. Each slot represents the variable name
|
|
* followed by the type strings.
|
|
*/
|
|
version (unittest):
|
|
void expectSymbolsAndTypes(const string source, const string[][] results,
|
|
string file = __FILE_FULL_PATH__, size_t line = __LINE__)
|
|
{
|
|
import core.exception : AssertError;
|
|
import std.exception : enforce;
|
|
|
|
ModuleCache mcache;
|
|
auto pair = generateAutocompleteTrees(source, mcache);
|
|
scope(exit) pair.destroy();
|
|
|
|
size_t i;
|
|
foreach(ss; (*pair.symbol)[])
|
|
{
|
|
if (ss.type)
|
|
{
|
|
enforce!AssertError(i <= results.length, "not enough results", file, line);
|
|
enforce!AssertError(results[i].length > 1,
|
|
"at least one type must be present in a result row", file, line);
|
|
enforce!AssertError(ss.name == results[i][0],
|
|
"expected variableName: `%s` but got `%s`".format(results[i][0], ss.name),
|
|
file, line);
|
|
|
|
auto t = cast() ss.type;
|
|
foreach (immutable j; 1..results[i].length)
|
|
{
|
|
enforce!AssertError(t != null, "null symbol", file, line);
|
|
enforce!AssertError(t.name == results[i][j],
|
|
"expected typeName: `%s` but got `%s`".format(results[i][j], t.name),
|
|
file, line);
|
|
if (t.type is t && t.name.length && t.name[0] != '*')
|
|
break;
|
|
t = t.type;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
enforce!AssertError(i == results.length, "too many expected results, %s is left".format(results[i .. $]), file, line);
|
|
}
|
|
|
|
@system unittest
|
|
{
|
|
writeln("Running type deduction tests...");
|
|
q{bool b; int i;}.expectSymbolsAndTypes([["b", "bool"],["i", "int"]]);
|
|
q{auto b = false;}.expectSymbolsAndTypes([["b", "bool"]]);
|
|
q{auto b = true;}.expectSymbolsAndTypes([["b", "bool"]]);
|
|
q{auto b = [0];}.expectSymbolsAndTypes([["b", "*arr-literal*", "int"]]);
|
|
q{auto b = [[0]];}.expectSymbolsAndTypes([["b", "*arr-literal*", "*arr-literal*", "int"]]);
|
|
q{auto b = [[[0]]];}.expectSymbolsAndTypes([["b", "*arr-literal*", "*arr-literal*", "*arr-literal*", "int"]]);
|
|
q{auto b = [];}.expectSymbolsAndTypes([["b", "*arr-literal*", "void"]]);
|
|
q{auto b = [[]];}.expectSymbolsAndTypes([["b", "*arr-literal*", "*arr-literal*", "void"]]);
|
|
//q{int* b;}.expectSymbolsAndTypes([["b", "*", "int"]]);
|
|
//q{int*[] b;}.expectSymbolsAndTypes([["b", "*arr*", "*", "int"]]);
|
|
|
|
q{auto b = new class {int i;};}.expectSymbolsAndTypes([["b", "__anonclass1"]]);
|
|
|
|
// got a crash before but solving is not yet working ("foo" instead of "__anonclass1");
|
|
q{class Bar{} auto foo(){return new class Bar{};} auto b = foo();}.expectSymbolsAndTypes([["b", "foo"]]);
|
|
}
|
|
|
|
// this one used to crash, see #125
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
auto source = q{ auto a = true ? [42] : []; };
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
}
|
|
|
|
// https://github.com/dlang-community/D-Scanner/issues/749
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
auto source = q{ void test() { foo(new class A {});} };
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
}
|
|
|
|
// https://github.com/dlang-community/D-Scanner/issues/738
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
auto source = q{ void b() { c = } alias b this; };
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
|
|
writeln("Running function literal tests...");
|
|
const sources = [
|
|
q{ int a; auto dg = { }; },
|
|
q{ void f() { int a; auto dg = { }; } },
|
|
q{ auto f = (int a) { }; },
|
|
q{ auto f() { return (int a) { }; } },
|
|
q{ auto f() { return g((int a) { }); } },
|
|
q{ void f() { g((int a) { }); } },
|
|
q{ void f() { auto x = (int a) { }; } },
|
|
q{ void f() { auto x = g((int a) { }); } },
|
|
];
|
|
foreach (src; sources)
|
|
{
|
|
auto pair = generateAutocompleteTrees(src, cache);
|
|
auto a = pair.scope_.getFirstSymbolByNameAndCursor(istring("a"), 35);
|
|
assert(a, src);
|
|
assert(a.type, src);
|
|
assert(a.type.name == "int", src);
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
writeln("Get return type name");
|
|
auto source = q{ int meaningOfLife() { return 42; } };
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
auto meaningOfLife = pair.symbol.getFirstPartNamed(istring("meaningOfLife"));
|
|
assert(meaningOfLife.type.name == "int");
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
writeln("Get return type name from class method");
|
|
auto source = q{ class Life { uint meaningOfLife() { return 42; } }};
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
auto lifeClass = pair.symbol.getFirstPartNamed(istring("Life"));
|
|
auto meaningOfLife = lifeClass.getFirstPartNamed(istring("meaningOfLife"));
|
|
assert(meaningOfLife.type.name == "uint");
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
writeln("Return type of auto should be null");
|
|
auto source = q{ class Life { auto meaningOfLife() { return 42; } }};
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
auto lifeClass = pair.symbol.getFirstPartNamed(istring("Life"));
|
|
auto meaningOfLife = lifeClass.getFirstPartNamed(istring("meaningOfLife"));
|
|
assert(meaningOfLife.type is null);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
writeln("Return type of scope should be null");
|
|
auto source = q{ class Life { scope meaningOfLife() { return 42; } }};
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
auto lifeClass = pair.symbol.getFirstPartNamed(istring("Life"));
|
|
auto meaningOfLife = lifeClass.getFirstPartNamed(istring("meaningOfLife"));
|
|
assert(meaningOfLife.type is null);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
writeln("Templated return type should be deduced correctly");
|
|
auto source = q{
|
|
struct AnswerToLife(T) {
|
|
T life;
|
|
}
|
|
AnswerToLife!int meaningOfLife() { return AnswerToLife!int(42); }
|
|
|
|
};
|
|
ScopeSymbolPair pair = generateAutocompleteTrees(source, cache);
|
|
auto answerToLife = pair.symbol.getFirstPartNamed(istring("AnswerToLife"));
|
|
DSymbol* meaningOfLife = pair.symbol.getFirstPartNamed(istring("meaningOfLife"));
|
|
assert(meaningOfLife.type.name == "AnswerToLife");
|
|
assert(meaningOfLife.type is answerToLife);
|
|
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
writeln("Array return type should be deduced correctly");
|
|
auto source = q{
|
|
int[] meaningOfLife() { return [42]; }
|
|
|
|
};
|
|
ScopeSymbolPair pair = generateAutocompleteTrees(source, cache);
|
|
DSymbol* meaningOfLife = pair.symbol.getFirstPartNamed(istring("meaningOfLife"));
|
|
assert(meaningOfLife.type.name == "*arr*");
|
|
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
writeln("Int* return type should be deduced correctly");
|
|
auto source = q{
|
|
int* meaningOfLife()
|
|
{
|
|
auto life = 42;
|
|
return &life;
|
|
}
|
|
|
|
};
|
|
ScopeSymbolPair pair = generateAutocompleteTrees(source, cache);
|
|
DSymbol* meaningOfLife = pair.symbol.getFirstPartNamed(istring("meaningOfLife"));
|
|
writeln(meaningOfLife.type.formatType);
|
|
assert(meaningOfLife.type.formatType == "int*");
|
|
}
|
|
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
|
|
writeln("Running struct constructor tests...");
|
|
auto source = q{ struct A {int a; struct B {bool b;} int c;} };
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
auto A = pair.symbol.getFirstPartNamed(internString("A"));
|
|
auto B = A.getFirstPartNamed(internString("B"));
|
|
auto ACtor = A.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME);
|
|
auto BCtor = B.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME);
|
|
assert(ACtor.callTip == "this(int a, int c)");
|
|
assert(BCtor.callTip == "this(bool b)");
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
|
|
writeln("Running union constructor tests...");
|
|
auto source = q{ union A {int a; bool b;} };
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
auto A = pair.symbol.getFirstPartNamed(internString("A"));
|
|
auto ACtor = A.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME);
|
|
assert(ACtor.callTip == "this(int a, bool b)");
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
writeln("Running non-importable symbols tests...");
|
|
auto source = q{
|
|
class A { this(int a){} }
|
|
class B : A {}
|
|
class C { A f; alias f this; }
|
|
};
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
auto A = pair.symbol.getFirstPartNamed(internString("A"));
|
|
auto B = pair.symbol.getFirstPartNamed(internString("B"));
|
|
auto C = pair.symbol.getFirstPartNamed(internString("C"));
|
|
assert(A.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME) !is null);
|
|
assert(B.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME) is null);
|
|
assert(C.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME) is null);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
|
|
writeln("Running alias this tests...");
|
|
auto source = q{ struct A {int f;} struct B { A a; alias a this; void fun() { auto var = f; };} };
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
auto A = pair.symbol.getFirstPartNamed(internString("A"));
|
|
auto B = pair.symbol.getFirstPartNamed(internString("B"));
|
|
auto Af = A.getFirstPartNamed(internString("f"));
|
|
auto fun = B.getFirstPartNamed(internString("fun"));
|
|
auto var = fun.getFirstPartNamed(internString("var"));
|
|
assert(Af is pair.scope_.getFirstSymbolByNameAndCursor(internString("f"), var.location));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
|
|
writeln("Running anon struct tests...");
|
|
auto source = q{ struct A { struct {int a;}} };
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
auto A = pair.symbol.getFirstPartNamed(internString("A"));
|
|
assert(A);
|
|
auto Aa = A.getFirstPartNamed(internString("a"));
|
|
assert(Aa);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
|
|
writeln("Running anon class tests...");
|
|
const sources = [
|
|
q{ auto a = new class Object { int i; }; },
|
|
q{ auto a = new class Object { int i; void m() { } }; },
|
|
q{ auto a = g(new class Object { int i; }); },
|
|
q{ auto a = g(new class Object { int i; void m() { } }); },
|
|
q{ void f() { new class Object { int i; }; } },
|
|
q{ void f() { new class Object { int i; void m() { } }; } },
|
|
q{ void f() { g(new class Object { int i; }); } },
|
|
q{ void f() { g(new class Object { int i; void m() { } }); } },
|
|
q{ void f() { auto a = new class Object { int i; }; } },
|
|
q{ void f() { auto a = new class Object { int i; void m() { } }; } },
|
|
q{ void f() { auto a = g(new class Object { int i; }); } },
|
|
q{ void f() { auto a = g(new class Object { int i; void m() { } }); } },
|
|
];
|
|
foreach (src; sources)
|
|
{
|
|
auto pair = generateAutocompleteTrees(src, cache);
|
|
auto a = pair.scope_.getFirstSymbolByNameAndCursor(istring("i"), 60);
|
|
assert(a, src);
|
|
assert(a.type, src);
|
|
assert(a.type.name == "int", src);
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
|
|
writeln("Running the deduction from index expr tests...");
|
|
{
|
|
auto source = q{struct S{} S[] s; auto b = s[i];};
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
DSymbol* S = pair.symbol.getFirstPartNamed(internString("S"));
|
|
DSymbol* b = pair.symbol.getFirstPartNamed(internString("b"));
|
|
assert(S);
|
|
assert(b);
|
|
assert(b.type);
|
|
assert(b.type is S);
|
|
}
|
|
{
|
|
auto source = q{struct S{} S[1] s; auto b = s[i];};
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
DSymbol* S = pair.symbol.getFirstPartNamed(internString("S"));
|
|
DSymbol* b = pair.symbol.getFirstPartNamed(internString("b"));
|
|
assert(S);
|
|
assert(b);
|
|
assert(b.type);
|
|
assert(b.type is S);
|
|
}
|
|
{
|
|
auto source = q{struct S{} S[][] s; auto b = s[0];};
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
DSymbol* S = pair.symbol.getFirstPartNamed(internString("S"));
|
|
DSymbol* b = pair.symbol.getFirstPartNamed(internString("b"));
|
|
assert(S);
|
|
assert(b);
|
|
assert(b.type);
|
|
assert(b.type.type is S);
|
|
}
|
|
{
|
|
auto source = q{struct S{} S[][][] s; auto b = s[0][0];};
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
DSymbol* S = pair.symbol.getFirstPartNamed(internString("S"));
|
|
DSymbol* b = pair.symbol.getFirstPartNamed(internString("b"));
|
|
assert(S);
|
|
assert(b);
|
|
assert(b.type);
|
|
assert(b.type.name == ARRAY_SYMBOL_NAME);
|
|
assert(b.type.type is S);
|
|
}
|
|
{
|
|
auto source = q{struct S{} S s; auto b = [s][0];};
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
DSymbol* S = pair.symbol.getFirstPartNamed(internString("S"));
|
|
DSymbol* b = pair.symbol.getFirstPartNamed(internString("b"));
|
|
assert(S);
|
|
assert(b);
|
|
assert(b.type is S);
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
|
|
writeln("Running `super` tests...");
|
|
auto source = q{ class A {} class B : A {} };
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
assert(pair.symbol);
|
|
auto A = pair.symbol.getFirstPartNamed(internString("A"));
|
|
auto B = pair.symbol.getFirstPartNamed(internString("B"));
|
|
auto scopeA = (pair.scope_.getScopeByCursor(A.location + A.name.length));
|
|
auto scopeB = (pair.scope_.getScopeByCursor(B.location + B.name.length));
|
|
assert(scopeA !is scopeB);
|
|
|
|
assert(!scopeA.getSymbolsByName(SUPER_SYMBOL_NAME).length);
|
|
assert(scopeB.getSymbolsByName(SUPER_SYMBOL_NAME)[0].type is A);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
|
|
writeln("Running the \"access chain with inherited type\" tests...");
|
|
auto source = q{ class A {} class B : A {} };
|
|
auto pair = generateAutocompleteTrees(source, cache);
|
|
assert(pair.symbol);
|
|
auto A = pair.symbol.getFirstPartNamed(internString("A"));
|
|
assert(A);
|
|
auto B = pair.symbol.getFirstPartNamed(internString("B"));
|
|
assert(B);
|
|
auto AfromB = B.getFirstPartNamed(internString("A"));
|
|
assert(AfromB.kind == CompletionKind.aliasName);
|
|
assert(AfromB.type is A);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
|
|
writeln("Running template type parameters tests...");
|
|
{
|
|
auto source = q{ struct Foo(T : int){} struct Bar(T : Foo){} };
|
|
auto pair = generateAutocompleteTreesProd(source, "", 0, cache);
|
|
DSymbol* T1 = pair.symbol.getFirstPartNamed(internString("Foo"));
|
|
DSymbol* T2 = T1.getFirstPartNamed(internString("T"));
|
|
assert(T2.type.name == "int");
|
|
DSymbol* T3 = pair.symbol.getFirstPartNamed(internString("Bar"));
|
|
DSymbol* T4 = T3.getFirstPartNamed(internString("T"));
|
|
assert(T4.type);
|
|
assert(T4.type == T1);
|
|
}
|
|
{
|
|
auto source = q{ struct Foo(T){ }};
|
|
auto pair = generateAutocompleteTreesProd(source, "", 0, cache);
|
|
DSymbol* T1 = pair.symbol.getFirstPartNamed(internString("Foo"));
|
|
assert(T1);
|
|
DSymbol* T2 = T1.getFirstPartNamed(internString("T"));
|
|
assert(T2);
|
|
assert(T2.kind == CompletionKind.typeTmpParam);
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
|
|
writeln("Running template variadic parameters tests...");
|
|
auto source = q{ struct Foo(T...){ }};
|
|
auto pair = generateAutocompleteTreesProd(source, "", 0, cache);
|
|
DSymbol* T1 = pair.symbol.getFirstPartNamed(internString("Foo"));
|
|
assert(T1);
|
|
DSymbol* T2 = T1.getFirstPartNamed(internString("T"));
|
|
assert(T2);
|
|
assert(T2.kind == CompletionKind.variadicTmpParam);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
writeln("Running public import tests...");
|
|
|
|
const dir = buildPath(tempDir(), "dsymbol");
|
|
const fnameA = buildPath(dir, "a.d");
|
|
const fnameB = buildPath(dir, "b.d");
|
|
const fnameC = buildPath(dir, "c.d");
|
|
const fnameD = buildPath(dir, "d.d");
|
|
const srcA = q{ int x; int w; };
|
|
const srcB = q{ float y; private float z; };
|
|
const srcC = q{ public { import a : x; import b; } import a : w; long t; };
|
|
const srcD = q{ public import c; };
|
|
// A simpler diagram:
|
|
// a = x w
|
|
// b = y [z]
|
|
// c = t + (x y) [w]
|
|
// d = (t x y)
|
|
|
|
mkdir(dir);
|
|
write(fnameA, srcA);
|
|
write(fnameB, srcB);
|
|
write(fnameC, srcC);
|
|
write(fnameD, srcD);
|
|
scope (exit)
|
|
{
|
|
remove(fnameA);
|
|
remove(fnameB);
|
|
remove(fnameC);
|
|
remove(fnameD);
|
|
rmdir(dir);
|
|
}
|
|
|
|
ModuleCache cache;
|
|
cache.addImportPaths([dir]);
|
|
|
|
const a = cache.getModuleSymbol(istring(fnameA));
|
|
const b = cache.getModuleSymbol(istring(fnameB));
|
|
const c = cache.getModuleSymbol(istring(fnameC));
|
|
const d = cache.getModuleSymbol(istring(fnameD));
|
|
const ax = a.getFirstPartNamed(istring("x"));
|
|
const aw = a.getFirstPartNamed(istring("w"));
|
|
assert(ax);
|
|
assert(aw);
|
|
assert(ax.type && ax.type.name == "int");
|
|
assert(aw.type && aw.type.name == "int");
|
|
const by = b.getFirstPartNamed(istring("y"));
|
|
const bz = b.getFirstPartNamed(istring("z"));
|
|
assert(by);
|
|
assert(bz);
|
|
assert(by.type && by.type.name == "float");
|
|
assert(bz.type && bz.type.name == "float");
|
|
const ct = c.getFirstPartNamed(istring("t"));
|
|
const cw = c.getFirstPartNamed(istring("w"));
|
|
const cx = c.getFirstPartNamed(istring("x"));
|
|
const cy = c.getFirstPartNamed(istring("y"));
|
|
const cz = c.getFirstPartNamed(istring("z"));
|
|
assert(ct);
|
|
assert(ct.type && ct.type.name == "long");
|
|
assert(cw is null); // skipOver is true
|
|
assert(cx is ax);
|
|
assert(cy is by);
|
|
assert(cz is bz); // should not be there, but it is handled by DCD
|
|
const dt = d.getFirstPartNamed(istring("t"));
|
|
const dw = d.getFirstPartNamed(istring("w"));
|
|
const dx = d.getFirstPartNamed(istring("x"));
|
|
const dy = d.getFirstPartNamed(istring("y"));
|
|
const dz = d.getFirstPartNamed(istring("z"));
|
|
assert(dt is ct);
|
|
assert(dw is null);
|
|
assert(dx is cx);
|
|
assert(dy is cy);
|
|
assert(dz is cz);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
ModuleCache cache;
|
|
|
|
writeln("Testing protection scopes");
|
|
auto source = q{version(all) { private: } struct Foo{ }};
|
|
auto pair = generateAutocompleteTreesProd(source, "", 0, cache);
|
|
DSymbol* T1 = pair.symbol.getFirstPartNamed(internString("Foo"));
|
|
assert(T1);
|
|
assert(T1.protection != tok!"private");
|
|
}
|
|
|
|
// check for memory leaks on thread termination (in static constructors)
|
|
version (linux) unittest
|
|
{
|
|
import core.memory : GC;
|
|
import core.thread : Thread;
|
|
import fs = std.file;
|
|
import std.array : split;
|
|
import std.conv : to;
|
|
|
|
// get the resident set size
|
|
static long getRSS()
|
|
{
|
|
GC.collect();
|
|
GC.minimize();
|
|
// read Linux process statistics
|
|
const txt = fs.readText("/proc/self/stat");
|
|
const parts = split(txt);
|
|
return to!long(parts[23]);
|
|
}
|
|
|
|
const rssBefore = getRSS();
|
|
// create and destroy a lot of dummy threads
|
|
foreach (j; 0 .. 50)
|
|
{
|
|
Thread[100] arr;
|
|
foreach (i; 0 .. 100)
|
|
arr[i] = new Thread({}).start();
|
|
foreach (i; 0 .. 100)
|
|
arr[i].join();
|
|
}
|
|
const rssAfter = getRSS();
|
|
// check the process memory increase with some eyeballed threshold
|
|
assert(rssAfter - rssBefore < 6_000);
|
|
}
|
|
|
|
// this is for testing that internString data is always on the same address
|
|
// since we use this special property for modulecache recursion guard
|
|
unittest
|
|
{
|
|
istring a = internString("foo_bar_baz".idup);
|
|
istring b = internString("foo_bar_baz".idup);
|
|
assert(a.data.ptr == b.data.ptr);
|
|
}
|
|
|
|
private StringCache stringCache = void;
|
|
static this()
|
|
{
|
|
stringCache = StringCache(StringCache.defaultBucketCount);
|
|
}
|
|
|
|
static ~this()
|
|
{
|
|
destroy(stringCache);
|
|
}
|
|
|
|
const(Token)[] lex(string source)
|
|
{
|
|
return lex(source, null);
|
|
}
|
|
|
|
const(Token)[] lex(string source, string filename)
|
|
{
|
|
import dparse.lexer : getTokensForParser;
|
|
import std.string : representation;
|
|
|
|
LexerConfig config;
|
|
config.fileName = filename;
|
|
return getTokensForParser(source.dup.representation, config, &stringCache);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto tokens = lex(q{int a = 9;});
|
|
foreach(i, t;
|
|
cast(IdType[]) [tok!"int", tok!"identifier", tok!"=", tok!"intLiteral", tok!";"])
|
|
{
|
|
assert(tokens[i] == t);
|
|
}
|
|
assert(tokens[1].text == "a", tokens[1].text);
|
|
assert(tokens[3].text == "9", tokens[3].text);
|
|
}
|
|
|
|
string randomDFilename()
|
|
{
|
|
import std.uuid : randomUUID;
|
|
return "dsymbol_" ~ randomUUID().toString() ~ ".d";
|
|
}
|
|
|
|
ScopeSymbolPair generateAutocompleteTrees(string source, ref ModuleCache cache)
|
|
{
|
|
return generateAutocompleteTrees(source, randomDFilename, cache);
|
|
}
|
|
|
|
ScopeSymbolPair generateAutocompleteTrees(string source, string filename, ref ModuleCache cache, size_t cursorPosition = -1)
|
|
{
|
|
auto tokens = lex(source);
|
|
RollbackAllocator rba;
|
|
Module m = parseModule(tokens, filename, &rba);
|
|
|
|
scope first = new FirstPass(m, internString(filename), &cache);
|
|
first.run();
|
|
|
|
|
|
secondPass(first.rootSymbol, first.moduleScope, cache);
|
|
thirdPass(first.rootSymbol, first.moduleScope, cache, cursorPosition);
|
|
auto ufcsSymbols = getUFCSSymbolsForCursor(first.moduleScope, tokens, cursorPosition);
|
|
auto r = first.rootSymbol.acSymbol;
|
|
typeid(SemanticSymbol).destroy(first.rootSymbol);
|
|
return ScopeSymbolPair(r, first.moduleScope, ufcsSymbols);
|
|
}
|
|
|
|
ScopeSymbolPair generateAutocompleteTreesProd(string source, string filename, size_t cursorPosition, ref ModuleCache cache)
|
|
{
|
|
auto tokens = lex(source);
|
|
RollbackAllocator rba;
|
|
return dsymbol.conversion.generateAutocompleteTrees(
|
|
tokens, &rba, cursorPosition, cache);
|
|
}
|
|
|
|
|
|
|
|
version (linux)
|
|
{
|
|
|
|
unittest
|
|
{
|
|
enum string ufcsExampleCode =
|
|
q{class Incrementer
|
|
{
|
|
int run(int x)
|
|
{
|
|
return x++;
|
|
}
|
|
}
|
|
int increment(int x)
|
|
{
|
|
return x++;
|
|
}
|
|
void doIncrement()
|
|
{
|
|
int life = 42;
|
|
life.
|
|
}};
|
|
|
|
|
|
writeln("Getting UFCS Symbols For life");
|
|
ModuleCache cache;
|
|
// position of variable life
|
|
size_t cursorPos = 139;
|
|
auto pair = generateAutocompleteTreesProd(ufcsExampleCode, randomDFilename, cursorPos, cache);
|
|
assert(pair.ufcsSymbols.length > 0);
|
|
assert(pair.ufcsSymbols[0].name == "increment");
|
|
|
|
}
|
|
|
|
enum string ufcsTemplateExampleCode =
|
|
q{int increment(T)(T x)
|
|
{
|
|
return x++;
|
|
}
|
|
void doIncrement()
|
|
{
|
|
int life = 42;
|
|
life.
|
|
}};
|
|
|
|
unittest
|
|
{
|
|
enum string ufcsTemplateExampleCode =
|
|
q{int increment(T)(T x)
|
|
{
|
|
return x++;
|
|
}
|
|
void doIncrement()
|
|
{
|
|
int life = 42;
|
|
life.
|
|
}};
|
|
|
|
writeln("Getting Templated UFCS Symbols For life");
|
|
ModuleCache cache;
|
|
// position of variable life
|
|
size_t cursorPos = 82;
|
|
auto pair = generateAutocompleteTreesProd(ufcsTemplateExampleCode, randomDFilename, cursorPos, cache);
|
|
assert(pair.ufcsSymbols.length > 0);
|
|
assert(pair.ufcsSymbols[0].name == "increment");
|
|
|
|
}
|
|
|
|
unittest
|
|
{
|
|
enum string ufcsPointerExampleCode =
|
|
q{void increment(int* x) { }
|
|
void doIncrement(int* x, int* y)
|
|
{
|
|
y.
|
|
}};
|
|
|
|
writeln("Getting Ptr UFCS completion");
|
|
ModuleCache cache;
|
|
// position of variable life
|
|
size_t cursorPos = 65;
|
|
auto pair = generateAutocompleteTreesProd(ufcsPointerExampleCode, randomDFilename, cursorPos, cache);
|
|
assert(pair.ufcsSymbols[0].name == "increment");
|
|
assert(pair.ufcsSymbols[1].name == "doIncrement");
|
|
}
|
|
|
|
}
|