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.symbol;
20 
21 import stdx.allocator;
22 import stdx.allocator.mallocator : Mallocator;
23 import std.array;
24 
25 import containers.ttree;
26 import containers.unrolledlist;
27 import containers.slist;
28 import containers.hashset;
29 import dparse.lexer;
30 import std.bitmanip;
31 
32 import dsymbol.builtin.names;
33 public import dsymbol.string_interning;
34 
35 import std.range : isOutputRange;
36 
37 /**
38  * Identifies the kind of the item in an identifier completion list
39  */
40 enum CompletionKind : char
41 {
42 	/// Invalid completion kind. This is used internally and will never
43 	/// be returned in a completion response.
44 	dummy = '?',
45 
46 	/// Import symbol. This is used internally and will never
47 	/// be returned in a completion response.
48 	importSymbol = '*',
49 
50 	/// With symbol. This is used internally and will never
51 	/// be returned in a completion response.
52 	withSymbol = 'w',
53 
54 	/// class names
55 	className = 'c',
56 
57 	/// interface names
58 	interfaceName = 'i',
59 
60 	/// structure names
61 	structName = 's',
62 
63 	/// union name
64 	unionName = 'u',
65 
66 	/// variable name
67 	variableName = 'v',
68 
69 	/// member variable
70 	memberVariableName = 'm',
71 
72 	/// keyword, built-in version, scope statement
73 	keyword = 'k',
74 
75 	/// function or method
76 	functionName = 'f',
77 
78 	/// enum name
79 	enumName = 'g',
80 
81 	/// enum member
82 	enumMember = 'e',
83 
84 	/// package name
85 	packageName = 'P',
86 
87 	/// module name
88 	moduleName = 'M',
89 
90 	/// alias name
91 	aliasName = 'l',
92 
93 	/// template name
94 	templateName = 't',
95 
96 	/// mixin template name
97 	mixinTemplateName = 'T',
98 
99 	/// variadic template parameter
100 	variadicTmpParam = 'p',
101 
102 	/// type template parameter when no constraint
103 	typeTmpParam = 'h',
104 }
105 
106 /**
107  * Returns: true if `kind` is something that can be returned to the client
108  */
109 bool isPublicCompletionKind(CompletionKind kind) pure nothrow @safe @nogc
110 {
111 	return kind != CompletionKind.dummy && kind != CompletionKind.importSymbol
112 		&& kind != CompletionKind.withSymbol;
113 }
114 
115 
116 /**
117  * Any special information about a variable declaration symbol.
118  */
119 enum SymbolQualifier : ubyte
120 {
121 	/// None
122 	none,
123 	/// The symbol is an array
124 	array,
125 	/// The symbol is a associative array
126 	assocArray,
127 	/// The symbol is a function or delegate pointer
128 	func,
129 	/// Selective import
130 	selectiveImport,
131 }
132 
133 /**
134  * Autocompletion symbol
135  */
136 struct DSymbol
137 {
138 	// Copying is disabled
139 	@disable this();
140 	@disable this(this);
141 
142 	/**
143 	 * Params:
144 	 *     name = the symbol's name
145 	 *     kind = the symbol's completion kind
146 	 *     type = the resolved type of the symbol
147 	 */
148 	this(string name, CompletionKind kind = CompletionKind.dummy, DSymbol* type = null) nothrow @nogc @safe
149 	{
150 		this.name = istring(name);
151 		this.kind = kind;
152 		this.type = type;
153 	}
154 	/// ditto
155 	this(istring name, CompletionKind kind = CompletionKind.dummy, DSymbol* type = null) nothrow @nogc @safe
156 	{
157 		this.name = name;
158 		this.kind = kind;
159 		this.type = type;
160 	}
161 
162 	~this()
163 	{
164 		foreach (ref part; parts[])
165 		{
166 			if (part.owned)
167 			{
168 				assert(part.ptr !is null);
169 				typeid(DSymbol).destroy(part.ptr);
170 			}
171 			else
172 				part.ptr = null;
173 		}
174 		if (ownType)
175 			typeid(DSymbol).destroy(type);
176 	}
177 
178 	ptrdiff_t opCmp(ref const DSymbol other) const pure nothrow @nogc @safe
179 	{
180 		return name.opCmpFast(other.name);
181 	}
182 
183 	bool opEquals(ref const DSymbol other) const pure nothrow @nogc @safe
184 	{
185 		return name == other.name;
186 	}
187 
188 	size_t toHash() const pure nothrow @nogc @safe
189 	{
190 		return name.toHash();
191 	}
192 
193 	/**
194 	 * Gets all parts whose name matches the given string.
195 	 */
196 	inout(DSymbol)*[] getPartsByName(istring name) inout
197 	{
198 		auto app = appender!(DSymbol*[])();
199 		HashSet!size_t visited;
200 		getParts(name, app, visited);
201 		return cast(typeof(return)) app.data;
202 	}
203 
204 	inout(DSymbol)* getFirstPartNamed(this This)(istring name) inout
205 	{
206 		auto app = appender!(DSymbol*[])();
207 		HashSet!size_t visited;
208 		getParts(name, app, visited);
209 		return app.data.length > 0 ? cast(typeof(return)) app.data[0] : null;
210 	}
211 
212 	/**
213 	 * Gets all parts and imported parts. Filters based on the part's name if
214 	 * the `name` argument is not null. Stores results in `app`.
215 	 */
216 	void getParts(OR)(istring name, ref OR app, ref HashSet!size_t visited,
217 			bool onlyOne = false) inout
218 		if (isOutputRange!(OR, DSymbol*))
219 	{
220 		import std.algorithm.iteration : filter;
221 
222 		if (&this is null)
223 			return;
224 		if (visited.contains(cast(size_t) &this))
225 			return;
226 		visited.insert(cast(size_t) &this);
227 
228 		if (name is null)
229 		{
230 			foreach (part; parts[].filter!(a => a.name != IMPORT_SYMBOL_NAME))
231 			{
232 				app.put(cast(DSymbol*) part);
233 				if (onlyOne)
234 					return;
235 			}
236 			DSymbol p = DSymbol(IMPORT_SYMBOL_NAME);
237 			foreach (im; parts.equalRange(SymbolOwnership(&p)))
238 			{
239 				if (im.type !is null && !im.skipOver)
240 				{
241 					if (im.qualifier == SymbolQualifier.selectiveImport)
242 					{
243 						app.put(cast(DSymbol*) im.type);
244 						if (onlyOne)
245 							return;
246 					}
247 					else
248 						im.type.getParts(name, app, visited, onlyOne);
249 				}
250 			}
251 		}
252 		else
253 		{
254 			DSymbol s = DSymbol(name);
255 			foreach (part; parts.equalRange(SymbolOwnership(&s)))
256 			{
257 				app.put(cast(DSymbol*) part);
258 				if (onlyOne)
259 					return;
260 			}
261 			if (name == CONSTRUCTOR_SYMBOL_NAME ||
262 				name == DESTRUCTOR_SYMBOL_NAME ||
263 				name == UNITTEST_SYMBOL_NAME ||
264 				name == THIS_SYMBOL_NAME)
265 				return;	// these symbols should not be imported
266 
267 			DSymbol p = DSymbol(IMPORT_SYMBOL_NAME);
268 			foreach (im; parts.equalRange(SymbolOwnership(&p)))
269 			{
270 				if (im.type !is null && !im.skipOver)
271 				{
272 					if (im.qualifier == SymbolQualifier.selectiveImport)
273 					{
274 						if (im.type.name == name)
275 						{
276 							app.put(cast(DSymbol*) im.type);
277 							if (onlyOne)
278 								return;
279 						}
280 					}
281 					else
282 						im.type.getParts(name, app, visited, onlyOne);
283 				}
284 			}
285 		}
286 	}
287 
288 	/**
289 	 * Returns: a range over this symbol's parts and publicly visible imports
290 	 */
291 	inout(DSymbol)*[] opSlice(this This)() inout
292 	{
293 		auto app = appender!(DSymbol*[])();
294 		HashSet!size_t visited;
295 		getParts!(typeof(app))(istring(null), app, visited);
296 		return cast(typeof(return)) app.data;
297 	}
298 
299 	void addChild(DSymbol* symbol, bool owns)
300 	{
301 		assert(symbol !is null);
302 		parts.insert(SymbolOwnership(symbol, owns));
303 	}
304 
305 	void addChildren(R)(R symbols, bool owns)
306 	{
307 		foreach (symbol; symbols)
308 		{
309 			assert(symbol !is null);
310 			parts.insert(SymbolOwnership(symbol, owns));
311 		}
312 	}
313 
314 	void addChildren(DSymbol*[] symbols, bool owns)
315 	{
316 		foreach (symbol; symbols)
317 		{
318 			assert(symbol !is null);
319 			parts.insert(SymbolOwnership(symbol, owns));
320 		}
321 	}
322 
323 	/**
324 	 * Updates the type field based on the mappings contained in the given
325 	 * collection.
326 	 */
327 	void updateTypes(ref UpdatePairCollection collection)
328 	{
329 		auto r = collection.equalRange(UpdatePair(type, null));
330 		if (!r.empty)
331 			type = r.front.newSymbol;
332 		foreach (part; parts[])
333 			part.updateTypes(collection);
334 	}
335 
336 	/**
337 	 * Symbols that compose this symbol, such as enum members, class variables,
338 	 * methods, parameters, etc.
339 	 */
340 	private TTree!(SymbolOwnership, Mallocator, true, "a < b", false) parts;
341 
342 	/**
343 	 * DSymbol's name
344 	 */
345 	istring name;
346 
347 	/**
348 	 * Calltip to display if this is a function
349 	 */
350 	istring callTip;
351 
352 	/**
353 	 * Used for storing information for selective renamed imports
354 	 */
355 	alias altFile = callTip;
356 
357 	/**
358 	 * Module containing the symbol.
359 	 */
360 	istring symbolFile;
361 
362 	/**
363 	 * Documentation for the symbol.
364 	 */
365 	DocString doc;
366 
367 	/**
368 	 * The symbol that represents the type.
369 	 */
370 	// TODO: assert that the type is not a function
371 	DSymbol* type;
372 
373 	/**
374 	 * Names of function arguments
375 	 */
376 	UnrolledList!(istring) argNames;
377 
378 	private uint _location;
379 
380 	/**
381 	 * DSymbol location
382 	 */
383 	size_t location() const pure nothrow @nogc @property @safe
384 	{
385 		return _location;
386 	}
387 
388 	void location(size_t location) pure nothrow @nogc @property @safe
389 	{
390 		// If the symbol was declared in a file, assert that it has a location
391 		// in that file. Built-in symbols don't need a location.
392 		assert(symbolFile is null || location < uint.max);
393 		_location = cast(uint) location;
394 	}
395 
396 	/**
397 	 * The kind of symbol
398 	 */
399 	CompletionKind kind;
400 
401 	/**
402 	 * DSymbol qualifier
403 	 */
404 	SymbolQualifier qualifier;
405 
406 	/**
407 	 * If true, this symbol owns its type and will free it on destruction
408 	 */
409 	// dfmt off
410 	mixin(bitfields!(bool, "ownType", 1,
411 		bool, "skipOver", 1,
412 		bool, "isPointer", 1,
413 		ubyte, "", 5));
414 	// dfmt on
415 
416 	/// Protection level for this symbol
417 	IdType protection;
418 
419 }
420 
421 /**
422  * istring with actual content and information if it was ditto
423  */
424 struct DocString
425 {
426 	/// Creates a non-ditto comment.
427 	this(istring content)
428 	{
429 		this.content = content;
430 	}
431 
432 	/// Creates a comment which may have been ditto, but has been resolved.
433 	this(istring content, bool ditto)
434 	{
435 		this.content = content;
436 		this.ditto = ditto;
437 	}
438 
439 	alias content this;
440 
441 	/// Contains the documentation string associated with this symbol, resolves ditto to the previous comment with correct scope.
442 	istring content;
443 	/// `true` if the documentation was just a "ditto" comment copying from the previous comment.
444 	bool ditto;
445 }
446 
447 struct UpdatePair
448 {
449 	ptrdiff_t opCmp(ref const UpdatePair other) const pure nothrow @nogc @safe
450 	{
451 		return (cast(ptrdiff_t) other.oldSymbol) - (cast(ptrdiff_t) this.oldSymbol);
452 	}
453 
454 	DSymbol* oldSymbol;
455 	DSymbol* newSymbol;
456 }
457 
458 alias UpdatePairCollection = TTree!(UpdatePair, Mallocator, false, "a < b", false);
459 
460 void generateUpdatePairs(DSymbol* oldSymbol, DSymbol* newSymbol, ref UpdatePairCollection results)
461 {
462 	results.insert(UpdatePair(oldSymbol, newSymbol));
463 	foreach (part; oldSymbol.parts[])
464 	{
465 		auto temp = DSymbol(oldSymbol.name);
466 		auto r = newSymbol.parts.equalRange(SymbolOwnership(&temp));
467 		if (r.empty)
468 			continue;
469 		generateUpdatePairs(part, r.front, results);
470 	}
471 }
472 
473 struct SymbolOwnership
474 {
475 	ptrdiff_t opCmp(ref const SymbolOwnership other) const @nogc
476 	{
477 		return this.ptr.opCmp(*other.ptr);
478 	}
479 
480 	DSymbol* ptr;
481 	bool owned;
482 	alias ptr this;
483 }