1 module dsymbol.tests; 2 3 import stdx.allocator; 4 import dparse.ast, dparse.parser, dparse.lexer, dparse.rollback_allocator; 5 import dsymbol.cache_entry, dsymbol.modulecache, dsymbol.symbol; 6 import dsymbol.conversion, dsymbol.conversion.first, dsymbol.conversion.second; 7 import dsymbol.semantic, dsymbol.string_interning, dsymbol.builtin.names; 8 import std.file, std.path, std.format; 9 import std.stdio : writeln, stdout; 10 import std.typecons : scoped; 11 12 /** 13 * Parses `source`, caches its symbols and compares the the cache content 14 * with the `results`. 15 * 16 * Params: 17 * source = The source code to test. 18 * results = An array of string array. Each slot represents the variable name 19 * followed by the type strings. 20 */ 21 version (unittest): 22 void expectSymbolsAndTypes(const string source, const string[][] results, 23 string file = __FILE_FULL_PATH__, size_t line = __LINE__) 24 { 25 import core.exception : AssertError; 26 import std.exception : enforce; 27 28 ModuleCache mcache = ModuleCache(theAllocator); 29 auto pair = generateAutocompleteTrees(source, mcache); 30 scope(exit) pair.destroy(); 31 32 size_t i; 33 foreach(ss; (*pair.symbol)[]) 34 { 35 if (ss.type) 36 { 37 enforce!AssertError(i <= results.length, "not enough results", file, line); 38 enforce!AssertError(results[i].length > 1, 39 "at least one type must be present in a result row", file, line); 40 enforce!AssertError(ss.name == results[i][0], 41 "expected variableName: `%s` but got `%s`".format(results[i][0], ss.name), 42 file, line); 43 44 auto t = cast() ss.type; 45 foreach (immutable j; 1..results[i].length) 46 { 47 enforce!AssertError(t != null, "null symbol", file, line); 48 enforce!AssertError(t.name == results[i][j], 49 "expected typeName: `%s` but got `%s`".format(results[i][j], t.name), 50 file, line); 51 if (t.type is t && t.name.length && t.name[0] != '*') 52 break; 53 t = t.type; 54 } 55 i++; 56 } 57 } 58 } 59 60 @system unittest 61 { 62 writeln("Running type deduction tests..."); 63 q{bool b; int i;}.expectSymbolsAndTypes([["b", "bool"],["i", "int"]]); 64 q{auto b = false;}.expectSymbolsAndTypes([["b", "bool"]]); 65 q{auto b = true;}.expectSymbolsAndTypes([["b", "bool"]]); 66 q{auto b = [0];}.expectSymbolsAndTypes([["b", "*arr*", "int"]]); 67 q{auto b = [[0]];}.expectSymbolsAndTypes([["b", "*arr*", "*arr*", "int"]]); 68 q{auto b = [[[0]]];}.expectSymbolsAndTypes([["b", "*arr*", "*arr*", "*arr*", "int"]]); 69 //q{int* b;}.expectSymbolsAndTypes([["b", "*", "int"]]); 70 //q{int*[] b;}.expectSymbolsAndTypes([["b", "*arr*", "*", "int"]]); 71 } 72 73 unittest 74 { 75 ModuleCache cache = ModuleCache(theAllocator); 76 77 writeln("Running struct constructor tests..."); 78 auto source = q{ struct A {int a; struct B {bool b;} int c;} }; 79 auto pair = generateAutocompleteTrees(source, cache); 80 auto A = pair.symbol.getFirstPartNamed(internString("A")); 81 auto B = A.getFirstPartNamed(internString("B")); 82 auto ACtor = A.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME); 83 auto BCtor = B.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME); 84 assert(ACtor.callTip == "this(int a, int c)"); 85 assert(BCtor.callTip == "this(bool b)"); 86 } 87 88 unittest 89 { 90 ModuleCache cache = ModuleCache(theAllocator); 91 92 writeln("Running union constructor tests..."); 93 auto source = q{ union A {int a; bool b;} }; 94 auto pair = generateAutocompleteTrees(source, cache); 95 auto A = pair.symbol.getFirstPartNamed(internString("A")); 96 auto ACtor = A.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME); 97 assert(ACtor.callTip == "this(int a, bool b)"); 98 } 99 100 unittest 101 { 102 ModuleCache cache = ModuleCache(theAllocator); 103 writeln("Running non-importable symbols tests..."); 104 auto source = q{ 105 class A { this(int a){} } 106 class B : A {} 107 class C { A f; alias f this; } 108 }; 109 auto pair = generateAutocompleteTrees(source, cache); 110 auto A = pair.symbol.getFirstPartNamed(internString("A")); 111 auto B = pair.symbol.getFirstPartNamed(internString("B")); 112 auto C = pair.symbol.getFirstPartNamed(internString("C")); 113 assert(A.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME) !is null); 114 assert(B.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME) is null); 115 assert(C.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME) is null); 116 } 117 118 unittest 119 { 120 ModuleCache cache = ModuleCache(theAllocator); 121 122 writeln("Running alias this tests..."); 123 auto source = q{ struct A {int f;} struct B { A a; alias a this; void fun() { auto var = f; };} }; 124 auto pair = generateAutocompleteTrees(source, cache); 125 auto A = pair.symbol.getFirstPartNamed(internString("A")); 126 auto B = pair.symbol.getFirstPartNamed(internString("B")); 127 auto Af = A.getFirstPartNamed(internString("f")); 128 auto fun = B.getFirstPartNamed(internString("fun")); 129 auto var = fun.getFirstPartNamed(internString("var")); 130 assert(Af is pair.scope_.getFirstSymbolByNameAndCursor(internString("f"), var.location)); 131 } 132 133 static StringCache stringCache = void; 134 static this() 135 { 136 stringCache = StringCache(StringCache.defaultBucketCount); 137 } 138 139 const(Token)[] lex(string source) 140 { 141 return lex(source, null); 142 } 143 144 const(Token)[] lex(string source, string filename) 145 { 146 import dparse.lexer : getTokensForParser; 147 import std..string : representation; 148 LexerConfig config; 149 config.fileName = filename; 150 return getTokensForParser(source.dup.representation, config, &stringCache); 151 } 152 153 unittest 154 { 155 auto tokens = lex(q{int a = 9;}); 156 foreach(i, t; 157 cast(IdType[]) [tok!"int", tok!"identifier", tok!"=", tok!"intLiteral", tok!";"]) 158 { 159 assert(tokens[i] == t); 160 } 161 assert(tokens[1].text == "a", tokens[1].text); 162 assert(tokens[3].text == "9", tokens[3].text); 163 } 164 165 string randomDFilename() 166 { 167 import std.uuid : randomUUID; 168 return "dsymbol_" ~ randomUUID().toString() ~ ".d"; 169 } 170 171 ScopeSymbolPair generateAutocompleteTrees(string source, ref ModuleCache cache) 172 { 173 return generateAutocompleteTrees(source, randomDFilename, cache); 174 } 175 176 ScopeSymbolPair generateAutocompleteTrees(string source, string filename, ref ModuleCache cache) 177 { 178 auto tokens = lex(source); 179 RollbackAllocator rba; 180 Module m = parseModule(tokens, filename, &rba); 181 182 auto first = scoped!FirstPass(m, internString(filename), 183 theAllocator, theAllocator, true, &cache); 184 first.run(); 185 186 secondPass(first.rootSymbol, first.moduleScope, cache); 187 auto r = first.rootSymbol.acSymbol; 188 typeid(SemanticSymbol).destroy(first.rootSymbol); 189 return ScopeSymbolPair(r, first.moduleScope); 190 } 191 192 ScopeSymbolPair generateAutocompleteTrees(string source, size_t cursorPosition, ref ModuleCache cache) 193 { 194 return generateAutocompleteTrees(source, null, cache); 195 } 196 197 ScopeSymbolPair generateAutocompleteTrees(string source, string filename, size_t cursorPosition, ref ModuleCache cache) 198 { 199 auto tokens = lex(source); 200 RollbackAllocator rba; 201 return dsymbol.conversion.generateAutocompleteTrees( 202 tokens, theAllocator, &rba, cursorPosition, cache); 203 }