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