1 module dsymbol.tests; 2 3 import std.experimental.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 non-importable symbols tests..."); 78 auto source = q{ 79 class A { this(int a){} } 80 class B : A {} 81 class C { A f; alias f this; } 82 }; 83 auto pair = generateAutocompleteTrees(source, cache); 84 auto A = pair.symbol.getFirstPartNamed(internString("A")); 85 auto B = pair.symbol.getFirstPartNamed(internString("B")); 86 auto C = pair.symbol.getFirstPartNamed(internString("C")); 87 assert(A.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME) !is null); 88 assert(B.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME) is null); 89 assert(C.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME) is null); 90 } 91 92 static StringCache stringCache = void; 93 static this() 94 { 95 stringCache = StringCache(StringCache.defaultBucketCount); 96 } 97 98 const(Token)[] lex(string source) 99 { 100 return lex(source, null); 101 } 102 103 const(Token)[] lex(string source, string filename) 104 { 105 import dparse.lexer : getTokensForParser; 106 import std..string : representation; 107 LexerConfig config; 108 config.fileName = filename; 109 return getTokensForParser(source.dup.representation, config, &stringCache); 110 } 111 112 unittest 113 { 114 auto tokens = lex(q{int a = 9;}); 115 foreach(i, t; 116 cast(IdType[]) [tok!"int", tok!"identifier", tok!"=", tok!"intLiteral", tok!";"]) 117 { 118 assert(tokens[i] == t); 119 } 120 assert(tokens[1].text == "a", tokens[1].text); 121 assert(tokens[3].text == "9", tokens[3].text); 122 } 123 124 string randomDFilename() 125 { 126 import std.uuid : randomUUID; 127 return "dsymbol_" ~ randomUUID().toString() ~ ".d"; 128 } 129 130 ScopeSymbolPair generateAutocompleteTrees(string source, ref ModuleCache cache) 131 { 132 return generateAutocompleteTrees(source, randomDFilename, cache); 133 } 134 135 ScopeSymbolPair generateAutocompleteTrees(string source, string filename, ref ModuleCache cache) 136 { 137 auto tokens = lex(source); 138 RollbackAllocator rba; 139 Module m = parseModule(tokens, filename, &rba); 140 141 auto first = scoped!FirstPass(m, internString(filename), 142 theAllocator, theAllocator, true, &cache); 143 first.run(); 144 145 secondPass(first.rootSymbol, first.moduleScope, cache); 146 auto r = first.rootSymbol.acSymbol; 147 typeid(SemanticSymbol).destroy(first.rootSymbol); 148 return ScopeSymbolPair(r, first.moduleScope); 149 } 150 151 ScopeSymbolPair generateAutocompleteTrees(string source, size_t cursorPosition, ref ModuleCache cache) 152 { 153 return generateAutocompleteTrees(source, null, cache); 154 } 155 156 ScopeSymbolPair generateAutocompleteTrees(string source, string filename, size_t cursorPosition, ref ModuleCache cache) 157 { 158 auto tokens = lex(source); 159 RollbackAllocator rba; 160 return dsymbol.conversion.generateAutocompleteTrees( 161 tokens, theAllocator, &rba, cursorPosition, cache); 162 }