1 /**
2  * This file is part of DCD, a development tool for the D programming language.
3  * Copyright (C) 2014 Brian Schott
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 module dsymbol.conversion.second;
20 
21 import dsymbol.semantic;
22 import dsymbol.string_interning;
23 import dsymbol.symbol;
24 import dsymbol.scope_;
25 import dsymbol.builtin.names;
26 import dsymbol.builtin.symbols;
27 import dsymbol.type_lookup;
28 import dsymbol.deferred;
29 import dsymbol.import_;
30 import dsymbol.modulecache;
31 import containers.unrolledlist;
32 import stdx.allocator;
33 import stdx.allocator.mallocator;
34 import std.experimental.logger;
35 import dparse.ast;
36 import dparse.lexer;
37 
38 void secondPass(SemanticSymbol* currentSymbol, Scope* moduleScope, ref ModuleCache cache)
39 {
40 	with (CompletionKind) final switch (currentSymbol.acSymbol.kind)
41 	{
42 	case className:
43 	case interfaceName:
44 		resolveInheritance(currentSymbol.acSymbol, currentSymbol.typeLookups,
45 			moduleScope, cache);
46 		break;
47 	case withSymbol:
48 	case variableName:
49 	case memberVariableName:
50 	case functionName:
51 	case aliasName:
52 		// type may not be null in the case of a renamed import
53 		if (currentSymbol.acSymbol.type is null)
54 		{
55 			resolveType(currentSymbol.acSymbol, currentSymbol.typeLookups,
56 				moduleScope, cache);
57 		}
58 		break;
59 	case importSymbol:
60 		if (currentSymbol.acSymbol.type is null)
61 			resolveImport(currentSymbol.acSymbol, currentSymbol.typeLookups, cache);
62 		break;
63 	case variadicTmpParam:
64 		currentSymbol.acSymbol.type = variadicTmpParamSymbol;
65 		break;
66 	case typeTmpParam:
67 		currentSymbol.acSymbol.type = typeTmpParamSymbol;
68 		break;
69 	case structName:
70 	case unionName:
71 	case enumName:
72 	case keyword:
73 	case enumMember:
74 	case packageName:
75 	case moduleName:
76 	case dummy:
77 	case templateName:
78 	case mixinTemplateName:
79 		break;
80 	}
81 
82 	foreach (child; currentSymbol.children)
83 		secondPass(child, moduleScope, cache);
84 
85 	// Alias this and mixin templates are resolved after child nodes are
86 	// resolved so that the correct symbol information will be available.
87 	with (CompletionKind) switch (currentSymbol.acSymbol.kind)
88 	{
89 	case className:
90 	case interfaceName:
91 	case structName:
92 	case unionName:
93 		resolveAliasThis(currentSymbol.acSymbol, currentSymbol.typeLookups, moduleScope, cache);
94 		resolveMixinTemplates(currentSymbol.acSymbol, currentSymbol.typeLookups,
95 			moduleScope, cache);
96 		break;
97 	default:
98 		break;
99 	}
100 }
101 
102 void resolveImport(DSymbol* acSymbol, ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups,
103 	ref ModuleCache cache)
104 in
105 {
106 	assert(acSymbol.kind == CompletionKind.importSymbol);
107 	assert(acSymbol.symbolFile !is null);
108 }
109 body
110 {
111 	DSymbol* moduleSymbol = cache.cacheModule(acSymbol.symbolFile);
112 	if (acSymbol.qualifier == SymbolQualifier.selectiveImport)
113 	{
114 		if (moduleSymbol is null)
115 		{
116 		tryAgain:
117 			DeferredSymbol* deferred = Mallocator.instance.make!DeferredSymbol(acSymbol);
118 			deferred.typeLookups.insert(typeLookups[]);
119 			// Get rid of the old references to the lookups, this new deferred
120 			// symbol owns them now
121 			typeLookups.clear();
122 			cache.deferredSymbols.insert(deferred);
123 		}
124 		else
125 		{
126 			immutable size_t breadcrumbCount = typeLookups.front.breadcrumbs.length;
127 			assert(breadcrumbCount <= 2 && breadcrumbCount > 0, "Malformed selective import");
128 
129 			istring symbolName = typeLookups.front.breadcrumbs.front;
130 			DSymbol* selected = moduleSymbol.getFirstPartNamed(symbolName);
131 			if (selected is null)
132 				goto tryAgain;
133 			acSymbol.type = selected;
134 			acSymbol.ownType = false;
135 
136 			// count of 1 means selective import
137 			// count of 2 means a renamed selective import
138 			if (breadcrumbCount == 2)
139 			{
140 				acSymbol.kind = CompletionKind.aliasName;
141 				acSymbol.symbolFile = acSymbol.altFile;
142 			}
143 		}
144 	}
145 	else
146 	{
147 		if (moduleSymbol is null)
148 		{
149 			DeferredSymbol* deferred = Mallocator.instance.make!DeferredSymbol(acSymbol);
150 			cache.deferredSymbols.insert(deferred);
151 		}
152 		else
153 		{
154 			acSymbol.type = moduleSymbol;
155 			acSymbol.ownType = false;
156 		}
157 	}
158 }
159 
160 void resolveTypeFromType(DSymbol* symbol, TypeLookup* lookup, Scope* moduleScope,
161 	ref ModuleCache cache, UnrolledList!(DSymbol*, Mallocator, false)* imports)
162 in
163 {
164 	if (imports !is null)
165 		foreach (i; imports.opSlice())
166 			assert(i.kind == CompletionKind.importSymbol);
167 }
168 body
169 {
170 	// The left-most suffix
171 	DSymbol* suffix;
172 	// The right-most suffix
173 	DSymbol* lastSuffix;
174 
175 	// Create symbols for the type suffixes such as array and
176 	// associative array
177 	while (!lookup.breadcrumbs.empty)
178 	{
179 		auto back = lookup.breadcrumbs.back;
180 		immutable bool isArr = back == ARRAY_SYMBOL_NAME;
181 		immutable bool isAssoc = back == ASSOC_ARRAY_SYMBOL_NAME;
182 		immutable bool isFunction = back == FUNCTION_SYMBOL_NAME;
183 		if (back == POINTER_SYMBOL_NAME)
184 		{
185 			lastSuffix.isPointer = true;
186 			lookup.breadcrumbs.popBack();
187 			continue;
188 		}
189 		if (!isArr && !isAssoc && !isFunction)
190 			break;
191 		immutable qualifier = isAssoc ? SymbolQualifier.assocArray :
192 			(isFunction ? SymbolQualifier.func : SymbolQualifier.array);
193 		lastSuffix = cache.symbolAllocator.make!DSymbol(back, CompletionKind.dummy, lastSuffix);
194 		lastSuffix.qualifier = qualifier;
195 		lastSuffix.ownType = true;
196 		if (isFunction)
197 		{
198 			lookup.breadcrumbs.popBack();
199 			lastSuffix.callTip = lookup.breadcrumbs.back();
200 		}
201 		else
202 			lastSuffix.addChildren(isArr ? arraySymbols[] : assocArraySymbols[], false);
203 
204 		if (suffix is null)
205 			suffix = lastSuffix;
206 		lookup.breadcrumbs.popBack();
207 	}
208 
209 	UnrolledList!(DSymbol*, Mallocator, false) remainingImports;
210 
211 	DSymbol* currentSymbol;
212 
213 	void getSymbolFromImports(UnrolledList!(DSymbol*, Mallocator, false)* importList, istring name)
214 	{
215 		foreach (im; importList.opSlice())
216 		{
217 			assert(im.symbolFile !is null);
218 			// Try to find a cached version of the module
219 			DSymbol* moduleSymbol = cache.getModuleSymbol(im.symbolFile);
220 			// If the module has not been cached yet, store it in the
221 			// remaining imports list
222 			if (moduleSymbol is null)
223 			{
224 				remainingImports.insert(im);
225 				continue;
226 			}
227 			// Try to get the symbol from the imported module
228 			currentSymbol = moduleSymbol.getFirstPartNamed(name);
229 			if (currentSymbol is null)
230 				continue;
231 		}
232 	}
233 
234 	// Follow all the names and try to resolve them
235 	size_t i = 0;
236 	foreach (part; lookup.breadcrumbs[])
237 	{
238 		if (i == 0)
239 		{
240 			if (moduleScope is null)
241 				getSymbolFromImports(imports, part);
242 			else
243 			{
244 				auto symbols = moduleScope.getSymbolsByNameAndCursor(part, symbol.location);
245 				if (symbols.length > 0)
246 					currentSymbol = symbols[0];
247 				else
248 					return;
249 			}
250 		}
251 		else
252 		{
253 			if (currentSymbol.kind == CompletionKind.aliasName)
254 				currentSymbol = currentSymbol.type;
255 			if (currentSymbol is null)
256 				return;
257 			if (currentSymbol.kind == CompletionKind.moduleName && currentSymbol.type !is null)
258 				currentSymbol = currentSymbol.type;
259 			if (currentSymbol is null)
260 				return;
261 			if (currentSymbol.kind == CompletionKind.importSymbol)
262 				currentSymbol = currentSymbol.type;
263 			if (currentSymbol is null)
264 				return;
265 			currentSymbol = currentSymbol.getFirstPartNamed(part);
266 		}
267 		++i;
268 		if (currentSymbol is null)
269 			return;
270 	}
271 
272 	if (lastSuffix !is null)
273 	{
274 		assert(suffix !is null);
275 		suffix.type = currentSymbol;
276 		suffix.ownType = false;
277 		symbol.type = lastSuffix;
278 		symbol.ownType = true;
279 		if (currentSymbol is null && !remainingImports.empty)
280 		{
281 //			info("Deferring type resolution for ", symbol.name);
282 			auto deferred = Mallocator.instance.make!DeferredSymbol(suffix);
283 			// TODO: The scope has ownership of the import information
284 			deferred.imports.insert(remainingImports[]);
285 			deferred.typeLookups.insert(lookup);
286 			cache.deferredSymbols.insert(deferred);
287 		}
288 	}
289 	else if (currentSymbol !is null)
290 	{
291 		symbol.type = currentSymbol;
292 		symbol.ownType = false;
293 	}
294 	else if (!remainingImports.empty)
295 	{
296 		auto deferred = Mallocator.instance.make!DeferredSymbol(symbol);
297 //		info("Deferring type resolution for ", symbol.name);
298 		// TODO: The scope has ownership of the import information
299 		deferred.imports.insert(remainingImports[]);
300 		deferred.typeLookups.insert(lookup);
301 		cache.deferredSymbols.insert(deferred);
302 	}
303 }
304 
305 private:
306 
307 void resolveInheritance(DSymbol* symbol, ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups,
308 	Scope* moduleScope, ref ModuleCache cache)
309 {
310 	import std.algorithm : filter;
311 
312 	outer: foreach (TypeLookup* lookup; typeLookups[])
313 	{
314 		if (lookup.kind != TypeLookupKind.inherit)
315 			continue;
316 		DSymbol* baseClass;
317 		assert(lookup.breadcrumbs.length > 0);
318 
319 		// TODO: Delayed type lookup
320 		auto symbolScope = moduleScope.getScopeByCursor(
321 			symbol.location + symbol.name.length);
322 		auto symbols = moduleScope.getSymbolsByNameAndCursor(lookup.breadcrumbs.front,
323 			symbol.location);
324 		if (symbols.length == 0)
325 			continue;
326 
327 		baseClass = symbols[0];
328 		lookup.breadcrumbs.popFront();
329 		foreach (part; lookup.breadcrumbs[])
330 		{
331 			symbols = baseClass.getPartsByName(part);
332 			if (symbols.length == 0)
333 				continue outer;
334 			baseClass = symbols[0];
335 		}
336 
337 		DSymbol* imp = cache.symbolAllocator.make!DSymbol(IMPORT_SYMBOL_NAME,
338 			CompletionKind.importSymbol, baseClass);
339 		symbol.addChild(imp, true);
340 		symbolScope.addSymbol(imp, false);
341 		if (baseClass.kind == CompletionKind.className)
342 		{
343 			auto s = cache.symbolAllocator.make!DSymbol(SUPER_SYMBOL_NAME,
344 				CompletionKind.variableName, baseClass);
345 			symbolScope.addSymbol(s, true);
346 		}
347 	}
348 }
349 
350 void resolveAliasThis(DSymbol* symbol,
351 	ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups, Scope* moduleScope, ref ModuleCache cache)
352 {
353 	import std.algorithm : filter;
354 
355 	foreach (aliasThis; typeLookups[].filter!(a => a.kind == TypeLookupKind.aliasThis))
356 	{
357 		assert(aliasThis.breadcrumbs.length > 0);
358 		auto parts = symbol.getPartsByName(aliasThis.breadcrumbs.front);
359 		if (parts.length == 0 || parts[0].type is null)
360 			continue;
361 		DSymbol* s = cache.symbolAllocator.make!DSymbol(IMPORT_SYMBOL_NAME,
362 			CompletionKind.importSymbol, parts[0].type);
363 		symbol.addChild(s, true);
364 		auto symbolScope = moduleScope.getScopeByCursor(s.location);
365 		if (symbolScope !is null)
366 			symbolScope.addSymbol(s, false);
367 	}
368 }
369 
370 void resolveMixinTemplates(DSymbol* symbol,
371 	ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups, Scope* moduleScope, ref ModuleCache cache)
372 {
373 	import std.algorithm : filter;
374 
375 	foreach (mix; typeLookups[].filter!(a => a.kind == TypeLookupKind.mixinTemplate))
376 	{
377 		assert(mix.breadcrumbs.length > 0);
378 		auto symbols = moduleScope.getSymbolsByNameAndCursor(mix.breadcrumbs.front,
379 			symbol.location);
380 		if (symbols.length == 0)
381 			continue;
382 		auto currentSymbol = symbols[0];
383 		mix.breadcrumbs.popFront();
384 		foreach (m; mix.breadcrumbs[])
385 		{
386 			auto s = currentSymbol.getPartsByName(m);
387 			if (s.length == 0)
388 			{
389 				currentSymbol = null;
390 				break;
391 			}
392 			else
393 				currentSymbol = s[0];
394 		}
395 		if (currentSymbol !is null)
396 		{
397 			auto i = cache.symbolAllocator.make!DSymbol(IMPORT_SYMBOL_NAME,
398 				CompletionKind.importSymbol, currentSymbol);
399 			i.ownType = false;
400 			symbol.addChild(i, true);
401 		}
402 	}
403 }
404 
405 void resolveType(DSymbol* symbol, ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups,
406 	Scope* moduleScope, ref ModuleCache cache)
407 {
408 
409 	import std.conv;
410 
411 	if (typeLookups.length == 0)
412 		return;
413 	assert(typeLookups.length == 1);
414 	auto lookup = typeLookups.front;
415 	if (lookup.kind == TypeLookupKind.varOrFunType)
416 		resolveTypeFromType(symbol, lookup, moduleScope, cache, null);
417 	else if (lookup.kind == TypeLookupKind.initializer)
418 		resolveTypeFromInitializer(symbol, lookup, moduleScope, cache);
419 	// issue 94
420 	else if (lookup.kind == TypeLookupKind.inherit)
421 		resolveInheritance(symbol, typeLookups, moduleScope, cache);
422 	else
423 		assert(false, "How did this happen?");
424 }
425 
426 
427 void resolveTypeFromInitializer(DSymbol* symbol, TypeLookup* lookup,
428 	Scope* moduleScope, ref ModuleCache cache)
429 {
430 	if (lookup.breadcrumbs.length == 0)
431 		return;
432 	DSymbol* currentSymbol = null;
433 	size_t i = 0;
434 
435 	auto crumbs = lookup.breadcrumbs[];
436 	foreach (crumb; crumbs)
437 	{
438 		if (i == 0)
439 		{
440 			currentSymbol = moduleScope.getFirstSymbolByNameAndCursor(
441 				symbolNameToTypeName(crumb), symbol.location);
442 
443 			if (currentSymbol is null)
444 				return;
445 		}
446 		else
447 		if (crumb == ARRAY_LITERAL_SYMBOL_NAME)
448 		{
449 			auto arr = cache.symbolAllocator.make!(DSymbol)(ARRAY_LITERAL_SYMBOL_NAME, CompletionKind.dummy, currentSymbol);
450 			arr.qualifier = SymbolQualifier.array;
451 			currentSymbol = arr;
452 		}
453 		else if (crumb == ARRAY_SYMBOL_NAME)
454 		{
455 			typeSwap(currentSymbol);
456 			if (currentSymbol is null)
457 				return;
458 
459 			// Index expressions can be an array index or an AA index
460 			if (currentSymbol.qualifier == SymbolQualifier.array
461 					|| currentSymbol.qualifier == SymbolQualifier.assocArray
462 					|| currentSymbol.kind == CompletionKind.aliasName)
463 			{
464 				if (currentSymbol.type !is null)
465 					currentSymbol = currentSymbol.type;
466 				else
467 					return;
468 			}
469 			else
470 			{
471 				auto opIndex = currentSymbol.getFirstPartNamed(internString("opIndex"));
472 				if (opIndex !is null)
473 					currentSymbol = opIndex.type;
474 				else
475 					return;
476 			}
477 		}
478 		else if (crumb == "foreach")
479 		{
480 			typeSwap(currentSymbol);
481 			if (currentSymbol is null)
482 				return;
483 			if (currentSymbol.qualifier == SymbolQualifier.array
484 					|| currentSymbol.qualifier == SymbolQualifier.assocArray)
485 			{
486 				currentSymbol = currentSymbol.type;
487 				break;
488 			}
489 			auto front = currentSymbol.getFirstPartNamed(internString("front"));
490 			if (front !is null)
491 			{
492 				currentSymbol = front.type;
493 				break;
494 			}
495 			auto opApply = currentSymbol.getFirstPartNamed(internString("opApply"));
496 			if (opApply !is null)
497 			{
498 				currentSymbol = opApply.type;
499 				break;
500 			}
501 		}
502 		else
503 		{
504 			typeSwap(currentSymbol);
505 			if (currentSymbol is null )
506 				return;
507 			currentSymbol = currentSymbol.getFirstPartNamed(crumb);
508 		}
509 		++i;
510 		if (currentSymbol is null)
511 			return;
512 	}
513 	typeSwap(currentSymbol);
514 	symbol.type = currentSymbol;
515 	symbol.ownType = false;
516 }
517 
518 void typeSwap(ref DSymbol* currentSymbol)
519 {
520 	while (currentSymbol !is null && currentSymbol.type !is currentSymbol
521 			&& (currentSymbol.kind == CompletionKind.variableName
522 			|| currentSymbol.kind == CompletionKind.importSymbol
523 			|| currentSymbol.kind == CompletionKind.withSymbol
524 			|| currentSymbol.kind == CompletionKind.aliasName))
525 		currentSymbol = currentSymbol.type;
526 }