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 structName:
64 	case unionName:
65 	case enumName:
66 	case keyword:
67 	case enumMember:
68 	case packageName:
69 	case moduleName:
70 	case dummy:
71 	case templateName:
72 	case mixinTemplateName:
73 		break;
74 	}
75 
76 	foreach (child; currentSymbol.children)
77 		secondPass(child, moduleScope, cache);
78 
79 	// Alias this and mixin templates are resolved after child nodes are
80 	// resolved so that the correct symbol information will be available.
81 	with (CompletionKind) switch (currentSymbol.acSymbol.kind)
82 	{
83 	case className:
84 	case interfaceName:
85 	case structName:
86 	case unionName:
87 		resolveAliasThis(currentSymbol.acSymbol, currentSymbol.typeLookups, moduleScope, cache);
88 		resolveMixinTemplates(currentSymbol.acSymbol, currentSymbol.typeLookups,
89 			moduleScope, cache);
90 		break;
91 	default:
92 		break;
93 	}
94 }
95 
96 void resolveImport(DSymbol* acSymbol, ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups,
97 	ref ModuleCache cache)
98 in
99 {
100 	assert (acSymbol.kind == CompletionKind.importSymbol);
101 }
102 body
103 {
104 	if (acSymbol.qualifier == SymbolQualifier.selectiveImport)
105 	{
106 		assert(acSymbol.symbolFile !is null);
107 		DSymbol* moduleSymbol = cache.cacheModule(acSymbol.symbolFile);
108 		if (moduleSymbol is null)
109 		{
110 		tryAgain:
111 			DeferredSymbol* deferred = Mallocator.instance.make!DeferredSymbol(acSymbol);
112 			deferred.typeLookups.insert(typeLookups[]);
113 			// Get rid of the old references to the lookups, this new deferred
114 			// symbol owns them now
115 			typeLookups.clear();
116 			cache.deferredSymbols.insert(deferred);
117 		}
118 		else
119 		{
120 			immutable size_t breadcrumbCount = typeLookups.front.breadcrumbs.length;
121 			assert(breadcrumbCount <= 2 && breadcrumbCount > 0, "Malformed selective import");
122 
123 			istring symbolName = typeLookups.front.breadcrumbs.front;
124 			DSymbol* selected = moduleSymbol.getFirstPartNamed(symbolName);
125 			if (acSymbol is null)
126 				goto tryAgain;
127 			acSymbol.type = selected;
128 			acSymbol.ownType = false;
129 
130 			// count of 1 means selective import
131 			// count of 2 means a renamed selective import
132 			if (breadcrumbCount == 2)
133 			{
134 				acSymbol.kind = CompletionKind.aliasName;
135 				acSymbol.symbolFile = acSymbol.altFile;
136 			}
137 		}
138 	}
139 	else
140 	{
141 		assert(acSymbol.symbolFile !is null);
142 		DSymbol* moduleSymbol = cache.cacheModule(acSymbol.symbolFile);
143 		if (moduleSymbol is null)
144 		{
145 			DeferredSymbol* deferred = Mallocator.instance.make!DeferredSymbol(acSymbol);
146 			cache.deferredSymbols.insert(deferred);
147 		}
148 		else
149 		{
150 			acSymbol.type = moduleSymbol;
151 			acSymbol.ownType = false;
152 		}
153 	}
154 }
155 
156 void resolveTypeFromType(DSymbol* symbol, TypeLookup* lookup, Scope* moduleScope,
157 	ref ModuleCache cache, UnrolledList!(DSymbol*, Mallocator, false)* imports)
158 in
159 {
160 	if (imports !is null)
161 		foreach (i; imports.opSlice())
162 			assert(i.kind == CompletionKind.importSymbol);
163 }
164 body
165 {
166 	// The left-most suffix
167 	DSymbol* suffix;
168 	// The right-most suffix
169 	DSymbol* lastSuffix;
170 
171 	// Create symbols for the type suffixes such as array and
172 	// associative array
173 	while (!lookup.breadcrumbs.empty)
174 	{
175 		auto back = lookup.breadcrumbs.back;
176 		immutable bool isArr = back == ARRAY_SYMBOL_NAME;
177 		immutable bool isAssoc = back == ASSOC_ARRAY_SYMBOL_NAME;
178 		immutable bool isFunction = back == FUNCTION_SYMBOL_NAME;
179 		if (back == POINTER_SYMBOL_NAME)
180 		{
181 			lastSuffix.isPointer = true;
182 			lookup.breadcrumbs.popBack();
183 			continue;
184 		}
185 		if (!isArr && !isAssoc && !isFunction)
186 			break;
187 		immutable qualifier = isAssoc ? SymbolQualifier.assocArray :
188 			(isFunction ? SymbolQualifier.func : SymbolQualifier.array);
189 		lastSuffix = cache.symbolAllocator.make!DSymbol(back, CompletionKind.dummy, lastSuffix);
190 		lastSuffix.qualifier = qualifier;
191 		lastSuffix.ownType = true;
192 		if (isFunction)
193 		{
194 			lookup.breadcrumbs.popBack();
195 			lastSuffix.callTip = lookup.breadcrumbs.back();
196 		}
197 		else
198 			lastSuffix.addChildren(isArr ? arraySymbols[] : assocArraySymbols[], false);
199 
200 		if (suffix is null)
201 			suffix = lastSuffix;
202 		lookup.breadcrumbs.popBack();
203 	}
204 
205 	UnrolledList!(DSymbol*, Mallocator, false) remainingImports;
206 
207 	DSymbol* currentSymbol;
208 
209 	void getSymbolFromImports(UnrolledList!(DSymbol*, Mallocator, false)* importList, istring name)
210 	{
211 		foreach (im; importList.opSlice())
212 		{
213 			assert(im.symbolFile !is null);
214 			// Try to find a cached version of the module
215 			DSymbol* moduleSymbol = cache.getModuleSymbol(im.symbolFile);
216 			// If the module has not been cached yet, store it in the
217 			// remaining imports list
218 			if (moduleSymbol is null)
219 			{
220 				remainingImports.insert(im);
221 				continue;
222 			}
223 			// Try to get the symbol from the imported module
224 			currentSymbol = moduleSymbol.getFirstPartNamed(name);
225 			if (currentSymbol is null)
226 				continue;
227 		}
228 	}
229 
230 	// Follow all the names and try to resolve them
231 	size_t i = 0;
232 	foreach (part; lookup.breadcrumbs[])
233 	{
234 		if (i == 0)
235 		{
236 			if (moduleScope is null)
237 				getSymbolFromImports(imports, part);
238 			else
239 			{
240 				auto symbols = moduleScope.getSymbolsByNameAndCursor(part, symbol.location);
241 				if (symbols.length > 0)
242 					currentSymbol = symbols[0];
243 				else
244 					return;
245 			}
246 		}
247 		else
248 		{
249 			if (currentSymbol.kind == CompletionKind.aliasName)
250 				currentSymbol = currentSymbol.type;
251 			if (currentSymbol is null)
252 				return;
253 			if (currentSymbol.kind == CompletionKind.moduleName && currentSymbol.type !is null)
254 				currentSymbol = currentSymbol.type;
255 			if (currentSymbol is null)
256 				return;
257 			if (currentSymbol.kind == CompletionKind.importSymbol)
258 				currentSymbol = currentSymbol.type;
259 			if (currentSymbol is null)
260 				return;
261 			currentSymbol = currentSymbol.getFirstPartNamed(part);
262 		}
263 		++i;
264 		if (currentSymbol is null)
265 			return;
266 	}
267 
268 	if (lastSuffix !is null)
269 	{
270 		assert(suffix !is null);
271 		suffix.type = currentSymbol;
272 		suffix.ownType = false;
273 		symbol.type = lastSuffix;
274 		symbol.ownType = true;
275 		if (currentSymbol is null && !remainingImports.empty)
276 		{
277 //			info("Deferring type resolution for ", symbol.name);
278 			auto deferred = Mallocator.instance.make!DeferredSymbol(suffix);
279 			// TODO: The scope has ownership of the import information
280 			deferred.imports.insert(remainingImports[]);
281 			deferred.typeLookups.insert(lookup);
282 			cache.deferredSymbols.insert(deferred);
283 		}
284 	}
285 	else if (currentSymbol !is null)
286 	{
287 		symbol.type = currentSymbol;
288 		symbol.ownType = false;
289 	}
290 	else if (!remainingImports.empty)
291 	{
292 		auto deferred = Mallocator.instance.make!DeferredSymbol(symbol);
293 //		info("Deferring type resolution for ", symbol.name);
294 		// TODO: The scope has ownership of the import information
295 		deferred.imports.insert(remainingImports[]);
296 		deferred.typeLookups.insert(lookup);
297 		cache.deferredSymbols.insert(deferred);
298 	}
299 }
300 
301 private:
302 
303 void resolveInheritance(DSymbol* symbol, ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups,
304 	Scope* moduleScope, ref ModuleCache cache)
305 {
306 	import std.algorithm : filter;
307 
308 	outer: foreach (TypeLookup* lookup; typeLookups[])
309 	{
310 		if (lookup.kind != TypeLookupKind.inherit)
311 			continue;
312 		DSymbol* baseClass;
313 		assert(lookup.breadcrumbs.length > 0);
314 
315 		// TODO: Delayed type lookup
316 		auto symbolScope = moduleScope.getScopeByCursor(symbol.location);
317 		auto symbols = moduleScope.getSymbolsByNameAndCursor(lookup.breadcrumbs.front,
318 			symbol.location);
319 		if (symbols.length == 0)
320 			continue;
321 
322 		baseClass = symbols[0];
323 		lookup.breadcrumbs.popFront();
324 		foreach (part; lookup.breadcrumbs[])
325 		{
326 			symbols = baseClass.getPartsByName(part);
327 			if (symbols.length == 0)
328 				continue outer;
329 			baseClass = symbols[0];
330 		}
331 
332 		DSymbol* imp = cache.symbolAllocator.make!DSymbol(IMPORT_SYMBOL_NAME,
333 			CompletionKind.importSymbol, baseClass);
334 		symbol.addChild(imp, true);
335 		symbolScope.addSymbol(imp, false);
336 		if (baseClass.kind == CompletionKind.className)
337 		{
338 			auto s = cache.symbolAllocator.make!DSymbol(SUPER_SYMBOL_NAME,
339 				CompletionKind.variableName, baseClass);
340 			symbolScope.addSymbol(s, true);
341 		}
342 	}
343 }
344 
345 void resolveAliasThis(DSymbol* symbol,
346 	ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups, Scope* moduleScope, ref ModuleCache cache)
347 {
348 	import std.algorithm : filter;
349 
350 	foreach (aliasThis; typeLookups[].filter!(a => a.kind == TypeLookupKind.aliasThis))
351 	{
352 		assert(aliasThis.breadcrumbs.length > 0);
353 		auto parts = symbol.getPartsByName(aliasThis.breadcrumbs.front);
354 		if (parts.length == 0 || parts[0].type is null)
355 			continue;
356 		DSymbol* s = cache.symbolAllocator.make!DSymbol(IMPORT_SYMBOL_NAME,
357 			CompletionKind.importSymbol, parts[0].type);
358 		symbol.addChild(s, true);
359 		auto symbolScope = moduleScope.getScopeByCursor(s.location);
360 		if (symbolScope !is null)
361 			symbolScope.addSymbol(s, false);
362 	}
363 }
364 
365 void resolveMixinTemplates(DSymbol* symbol,
366 	ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups, Scope* moduleScope, ref ModuleCache cache)
367 {
368 	import std.algorithm : filter;
369 
370 	foreach (mix; typeLookups[].filter!(a => a.kind == TypeLookupKind.mixinTemplate))
371 	{
372 		assert(mix.breadcrumbs.length > 0);
373 		auto symbols = moduleScope.getSymbolsByNameAndCursor(mix.breadcrumbs.front,
374 			symbol.location);
375 		if (symbols.length == 0)
376 			continue;
377 		auto currentSymbol = symbols[0];
378 		mix.breadcrumbs.popFront();
379 		foreach (m; mix.breadcrumbs[])
380 		{
381 			auto s = currentSymbol.getPartsByName(m);
382 			if (s.length == 0)
383 			{
384 				currentSymbol = null;
385 				break;
386 			}
387 			else
388 				currentSymbol = s[0];
389 		}
390 		if (currentSymbol !is null)
391 		{
392 			auto i = cache.symbolAllocator.make!DSymbol(IMPORT_SYMBOL_NAME,
393 				CompletionKind.importSymbol, currentSymbol);
394 			i.ownType = false;
395 			symbol.addChild(i, true);
396 		}
397 	}
398 }
399 
400 void resolveType(DSymbol* symbol, ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups,
401 	Scope* moduleScope, ref ModuleCache cache)
402 {
403 	if (typeLookups.length == 0)
404 		return;
405 	assert(typeLookups.length == 1);
406 	auto lookup = typeLookups.front;
407 	if (lookup.kind == TypeLookupKind.varOrFunType)
408 		resolveTypeFromType(symbol, lookup, moduleScope, cache, null);
409 	else if (lookup.kind == TypeLookupKind.initializer)
410 		resolveTypeFromInitializer(symbol, lookup, moduleScope, cache);
411 	else
412 		assert(false, "How did this happen?");
413 }
414 
415 
416 void resolveTypeFromInitializer(DSymbol* symbol, TypeLookup* lookup,
417 	Scope* moduleScope, ref ModuleCache cache)
418 {
419 	if (lookup.breadcrumbs.length == 0)
420 		return;
421 	DSymbol* currentSymbol = null;
422 	size_t i = 0;
423 
424 	auto crumbs = lookup.breadcrumbs[];
425 	foreach (crumb; crumbs)
426 	{
427 		if (i == 0)
428 		{
429 			currentSymbol = moduleScope.getFirstSymbolByNameAndCursor(
430 				symbolNameToTypeName(crumb), symbol.location);
431 
432 			// solves auto arrays
433 			if (crumb == ARRAY_SYMBOL_NAME)
434 			{
435 				auto nestedArr = crumbs.save();
436 				auto a = nestedArr.front();
437 
438 				DSymbol* suffix;
439 				DSymbol* lastSuffix;
440 
441 				// process the flags set in ArrayInitializer visit
442 				while (true)
443 				{
444 					lastSuffix = cache.symbolAllocator.make!(DSymbol)(a, CompletionKind.dummy, lastSuffix);
445 					lastSuffix.qualifier = SymbolQualifier.array;
446 
447 					if (suffix is null)
448 						suffix = lastSuffix;
449 
450 					nestedArr.popFront();
451 					if (nestedArr.empty())
452 						break;
453 					a = nestedArr.front();
454 					if (a != ARRAY_SYMBOL_NAME)
455 						break;
456 				}
457 
458 				// last crumb should be the element type
459 				DSymbol* elemType;
460 				if (!nestedArr.empty)
461 				{
462 					suffix.addChildren(arraySymbols[], false);
463 					elemType = moduleScope.getFirstSymbolByNameAndCursor(
464 						symbolNameToTypeName(a), symbol.location);
465 				}
466 
467 				// put the elem type to the back of the *arr* chain
468 				if (suffix !is null && elemType)
469 				{
470 					suffix.type = elemType;
471 					suffix.ownType = false;
472 					symbol.type = lastSuffix;
473 					symbol.ownType = true;
474 				}
475 			}
476 			if (currentSymbol is null)
477 				return;
478 		}
479 		else if (crumb == ARRAY_SYMBOL_NAME)
480 		{
481 			typeSwap(currentSymbol);
482 			if (currentSymbol is null)
483 				return;
484 
485 			// Index expressions can be an array index or an AA index
486 			if (currentSymbol.qualifier == SymbolQualifier.array
487 					|| currentSymbol.qualifier == SymbolQualifier.assocArray
488 					|| currentSymbol.kind == CompletionKind.aliasName)
489 			{
490 				if (currentSymbol.type !is null)
491 					currentSymbol = currentSymbol.type;
492 				else
493 					return;
494 			}
495 			else
496 			{
497 				auto opIndex = currentSymbol.getFirstPartNamed(internString("opIndex"));
498 				if (opIndex !is null)
499 					currentSymbol = opIndex.type;
500 				else
501 					return;
502 			}
503 		}
504 		else if (crumb == "foreach")
505 		{
506 			typeSwap(currentSymbol);
507 			if (currentSymbol is null)
508 				return;
509 			if (currentSymbol.qualifier == SymbolQualifier.array
510 					|| currentSymbol.qualifier == SymbolQualifier.assocArray)
511 			{
512 				currentSymbol = currentSymbol.type;
513 				break;
514 			}
515 			auto front = currentSymbol.getFirstPartNamed(internString("front"));
516 			if (front !is null)
517 			{
518 				currentSymbol = front.type;
519 				break;
520 			}
521 			auto opApply = currentSymbol.getFirstPartNamed(internString("opApply"));
522 			if (opApply !is null)
523 			{
524 				currentSymbol = opApply.type;
525 				break;
526 			}
527 		}
528 		else
529 		{
530 			typeSwap(currentSymbol);
531 			if (currentSymbol is null )
532 				return;
533 			currentSymbol = currentSymbol.getFirstPartNamed(crumb);
534 		}
535 		++i;
536 		if (currentSymbol is null)
537 			return;
538 	}
539 	typeSwap(currentSymbol);
540 	symbol.type = currentSymbol;
541 	symbol.ownType = false;
542 }
543 
544 void typeSwap(ref DSymbol* currentSymbol)
545 {
546 	while (currentSymbol !is null && currentSymbol.type !is currentSymbol
547 			&& (currentSymbol.kind == CompletionKind.variableName
548 			|| currentSymbol.kind == CompletionKind.importSymbol
549 			|| currentSymbol.kind == CompletionKind.withSymbol
550 			|| currentSymbol.kind == CompletionKind.aliasName))
551 		currentSymbol = currentSymbol.type;
552 }