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(
317 			symbol.location + symbol.name.length);
318 		auto symbols = moduleScope.getSymbolsByNameAndCursor(lookup.breadcrumbs.front,
319 			symbol.location);
320 		if (symbols.length == 0)
321 			continue;
322 
323 		baseClass = symbols[0];
324 		lookup.breadcrumbs.popFront();
325 		foreach (part; lookup.breadcrumbs[])
326 		{
327 			symbols = baseClass.getPartsByName(part);
328 			if (symbols.length == 0)
329 				continue outer;
330 			baseClass = symbols[0];
331 		}
332 
333 		DSymbol* imp = cache.symbolAllocator.make!DSymbol(IMPORT_SYMBOL_NAME,
334 			CompletionKind.importSymbol, baseClass);
335 		symbol.addChild(imp, true);
336 		symbolScope.addSymbol(imp, false);
337 		if (baseClass.kind == CompletionKind.className)
338 		{
339 			auto s = cache.symbolAllocator.make!DSymbol(SUPER_SYMBOL_NAME,
340 				CompletionKind.variableName, baseClass);
341 			symbolScope.addSymbol(s, true);
342 		}
343 	}
344 }
345 
346 void resolveAliasThis(DSymbol* symbol,
347 	ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups, Scope* moduleScope, ref ModuleCache cache)
348 {
349 	import std.algorithm : filter;
350 
351 	foreach (aliasThis; typeLookups[].filter!(a => a.kind == TypeLookupKind.aliasThis))
352 	{
353 		assert(aliasThis.breadcrumbs.length > 0);
354 		auto parts = symbol.getPartsByName(aliasThis.breadcrumbs.front);
355 		if (parts.length == 0 || parts[0].type is null)
356 			continue;
357 		DSymbol* s = cache.symbolAllocator.make!DSymbol(IMPORT_SYMBOL_NAME,
358 			CompletionKind.importSymbol, parts[0].type);
359 		symbol.addChild(s, true);
360 		auto symbolScope = moduleScope.getScopeByCursor(s.location);
361 		if (symbolScope !is null)
362 			symbolScope.addSymbol(s, false);
363 	}
364 }
365 
366 void resolveMixinTemplates(DSymbol* symbol,
367 	ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups, Scope* moduleScope, ref ModuleCache cache)
368 {
369 	import std.algorithm : filter;
370 
371 	foreach (mix; typeLookups[].filter!(a => a.kind == TypeLookupKind.mixinTemplate))
372 	{
373 		assert(mix.breadcrumbs.length > 0);
374 		auto symbols = moduleScope.getSymbolsByNameAndCursor(mix.breadcrumbs.front,
375 			symbol.location);
376 		if (symbols.length == 0)
377 			continue;
378 		auto currentSymbol = symbols[0];
379 		mix.breadcrumbs.popFront();
380 		foreach (m; mix.breadcrumbs[])
381 		{
382 			auto s = currentSymbol.getPartsByName(m);
383 			if (s.length == 0)
384 			{
385 				currentSymbol = null;
386 				break;
387 			}
388 			else
389 				currentSymbol = s[0];
390 		}
391 		if (currentSymbol !is null)
392 		{
393 			auto i = cache.symbolAllocator.make!DSymbol(IMPORT_SYMBOL_NAME,
394 				CompletionKind.importSymbol, currentSymbol);
395 			i.ownType = false;
396 			symbol.addChild(i, true);
397 		}
398 	}
399 }
400 
401 void resolveType(DSymbol* symbol, ref UnrolledList!(TypeLookup*, Mallocator, false) typeLookups,
402 	Scope* moduleScope, ref ModuleCache cache)
403 {
404 	if (typeLookups.length == 0)
405 		return;
406 	assert(typeLookups.length == 1);
407 	auto lookup = typeLookups.front;
408 	if (lookup.kind == TypeLookupKind.varOrFunType)
409 		resolveTypeFromType(symbol, lookup, moduleScope, cache, null);
410 	else if (lookup.kind == TypeLookupKind.initializer)
411 		resolveTypeFromInitializer(symbol, lookup, moduleScope, cache);
412 	else
413 		assert(false, "How did this happen?");
414 }
415 
416 
417 void resolveTypeFromInitializer(DSymbol* symbol, TypeLookup* lookup,
418 	Scope* moduleScope, ref ModuleCache cache)
419 {
420 	if (lookup.breadcrumbs.length == 0)
421 		return;
422 	DSymbol* currentSymbol = null;
423 	size_t i = 0;
424 
425 	auto crumbs = lookup.breadcrumbs[];
426 	foreach (crumb; crumbs)
427 	{
428 		if (i == 0)
429 		{
430 			currentSymbol = moduleScope.getFirstSymbolByNameAndCursor(
431 				symbolNameToTypeName(crumb), symbol.location);
432 
433 			// solves auto arrays
434 			if (crumb == ARRAY_SYMBOL_NAME)
435 			{
436 				auto nestedArr = crumbs.save();
437 				auto a = nestedArr.front();
438 
439 				DSymbol* suffix;
440 				DSymbol* lastSuffix;
441 
442 				// process the flags set in ArrayInitializer visit
443 				while (true)
444 				{
445 					lastSuffix = cache.symbolAllocator.make!(DSymbol)(a, CompletionKind.dummy, lastSuffix);
446 					lastSuffix.qualifier = SymbolQualifier.array;
447 
448 					if (suffix is null)
449 						suffix = lastSuffix;
450 
451 					nestedArr.popFront();
452 					if (nestedArr.empty())
453 						break;
454 					a = nestedArr.front();
455 					if (a != ARRAY_SYMBOL_NAME)
456 						break;
457 				}
458 
459 				// last crumb should be the element type
460 				DSymbol* elemType;
461 				if (!nestedArr.empty)
462 				{
463 					suffix.addChildren(arraySymbols[], false);
464 					elemType = moduleScope.getFirstSymbolByNameAndCursor(
465 						symbolNameToTypeName(a), symbol.location);
466 				}
467 
468 				// put the elem type to the back of the *arr* chain
469 				if (suffix !is null && elemType)
470 				{
471 					suffix.type = elemType;
472 					suffix.ownType = false;
473 					symbol.type = lastSuffix;
474 					symbol.ownType = true;
475 				}
476 			}
477 			if (currentSymbol is null)
478 				return;
479 		}
480 		else if (crumb == ARRAY_SYMBOL_NAME)
481 		{
482 			typeSwap(currentSymbol);
483 			if (currentSymbol is null)
484 				return;
485 
486 			// Index expressions can be an array index or an AA index
487 			if (currentSymbol.qualifier == SymbolQualifier.array
488 					|| currentSymbol.qualifier == SymbolQualifier.assocArray
489 					|| currentSymbol.kind == CompletionKind.aliasName)
490 			{
491 				if (currentSymbol.type !is null)
492 					currentSymbol = currentSymbol.type;
493 				else
494 					return;
495 			}
496 			else
497 			{
498 				auto opIndex = currentSymbol.getFirstPartNamed(internString("opIndex"));
499 				if (opIndex !is null)
500 					currentSymbol = opIndex.type;
501 				else
502 					return;
503 			}
504 		}
505 		else if (crumb == "foreach")
506 		{
507 			typeSwap(currentSymbol);
508 			if (currentSymbol is null)
509 				return;
510 			if (currentSymbol.qualifier == SymbolQualifier.array
511 					|| currentSymbol.qualifier == SymbolQualifier.assocArray)
512 			{
513 				currentSymbol = currentSymbol.type;
514 				break;
515 			}
516 			auto front = currentSymbol.getFirstPartNamed(internString("front"));
517 			if (front !is null)
518 			{
519 				currentSymbol = front.type;
520 				break;
521 			}
522 			auto opApply = currentSymbol.getFirstPartNamed(internString("opApply"));
523 			if (opApply !is null)
524 			{
525 				currentSymbol = opApply.type;
526 				break;
527 			}
528 		}
529 		else
530 		{
531 			typeSwap(currentSymbol);
532 			if (currentSymbol is null )
533 				return;
534 			currentSymbol = currentSymbol.getFirstPartNamed(crumb);
535 		}
536 		++i;
537 		if (currentSymbol is null)
538 			return;
539 	}
540 	typeSwap(currentSymbol);
541 	symbol.type = currentSymbol;
542 	symbol.ownType = false;
543 }
544 
545 void typeSwap(ref DSymbol* currentSymbol)
546 {
547 	while (currentSymbol !is null && currentSymbol.type !is currentSymbol
548 			&& (currentSymbol.kind == CompletionKind.variableName
549 			|| currentSymbol.kind == CompletionKind.importSymbol
550 			|| currentSymbol.kind == CompletionKind.withSymbol
551 			|| currentSymbol.kind == CompletionKind.aliasName))
552 		currentSymbol = currentSymbol.type;
553 }