1 /**
2 	Directly parses D source code using libdparse.
3 
4 	Copyright: © 2014 RejectedSoftware e.K.
5 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
6 	Authors: Sönke Ludwig
7 */
8 module ddox.parsers.dparse;
9 
10 import ddox.ddox;
11 import ddox.entities;
12 import dparse = dparse.parser;
13 import dlex = dparse.lexer;
14 import dformat = dparse.formatter;
15 import dparse.rollback_allocator : RollbackAllocator;
16 
17 import std.algorithm;
18 import std.conv;
19 import std.exception;
20 import std.range;
21 import std.stdio;
22 import std.string;
23 import std.typecons;
24 import core.demangle;
25 import vibe.core.log;
26 import vibe.data.json;
27 
28 // random dparser notes:
29 //   Declaration.declarations[] ?
30 //   Strings should be const(char)[] instead of string if transient
31 //   how to get token name by token
32 //   convenience functions for Formatter and unDecorateComment
33 //   ambiguous representation of mutually exclusive values (maybe use subclassing? or enums for disambiguation?)
34 //   InterfaceDecl.baseClassList <-> baseInterfaceList
35 //   start/end line/column values for every AST node
36 //   doc comments for alias this
37 //   Declaration.attributeDeclaration vs. .attributes and .declarations?
38 //   AliasDeclaration direct fields vs. initializers?
39 //   Alias to non-type declarations is a type?
40 
41 
42 Package parseD(string[] d_files, Package root = null)
43 {
44 	if (!root) root = new Package(null, null);
45 	DParser p;
46 	foreach (file; d_files)
47 		p.parseModule(file, root);
48 	p.resolveTypes();
49 	return root;
50 }
51 
52 
53 private struct DParser
54 {
55 	private {
56 		Declaration[string] m_typeMap;
57 		Tuple!(CachedType*, const(dparse.ASTNode), Entity)[] m_deferredTypes;
58 		FunctionDeclaration[] m_functions;
59 	}
60 
61 	void parseModule(string filename, Package root_package)
62 	{
63 		import std.file;
64 
65 		dlex.LexerConfig config;
66 		config.fileName = filename;
67 		config.stringBehavior = dlex.StringBehavior.source;
68 		dlex.StringCache cache = dlex.StringCache(1024 * 4);
69 		auto tokens = dlex.getTokensForParser(cast(ubyte[])std.file.read(filename), config, &cache).array;
70                 RollbackAllocator rba;
71 		auto dmod = dparse.parseModule(tokens, filename, &rba);
72 
73 		Module mod;
74 		if (!dmod.moduleDeclaration) {
75 			logError("No module declaration in module %s - ignoring", filename);
76 			return;
77 		}
78 
79 		auto path = dmod.moduleDeclaration.moduleName.identifiers.map!(a => a.text.idup).array;
80 		logInfo("MOD %s", path);
81 		Package p = root_package;
82 		foreach (i, pe; path) {
83 			if (i+1 < path.length) p = p.getOrAddPackage(pe);
84 			else mod = p.createModule(pe);
85 		}
86 
87 		mod.file = filename;
88 		mod.docGroup = new DocGroup(mod, dmod.moduleDeclaration.comment.undecorateComment());
89 		mod.members = parseDeclList(dmod.declarations, mod);
90 	}
91 
92 	void resolveTypes()
93 	{
94 		foreach (d; m_deferredTypes) {
95 			if (auto t = cast(dparse.Type)d[1]) *d[0] = parseType(t, d[2]);
96 			else if (auto t2 = cast(dparse.Type2)d[1]) *d[0] = parseType(t2, d[2]);
97 		}
98 
99 		foreach (fdr; m_functions) {
100 			Type ft;
101 			ft.kind = TypeKind.Function;
102 			//ft.attributes = ...; // TODO!
103 			//ft.modifiers = ...; // TODO!
104 			ft.returnType = fdr.returnType;
105 			ft.parameterTypes = fdr.parameters.map!(p => cast(immutable)p.type).array;
106 			fdr.type = ft;
107 		}
108 	}
109 
110 	Declaration[] parseDeclList(const(dparse.Declaration)[] decls, Entity parent)
111 	{
112 		DocGroup lastdoc;
113 		Declaration[] ret;
114 		foreach (mem; decls) {
115 			foreach (decl; parseDecl(mem, parent)) {
116 				auto doc = decl.docGroup;
117 				if (lastdoc && (doc.text == lastdoc.text && doc.text.length || doc.comment.isDitto)) {
118 					lastdoc.members ~= decl;
119 					decl.docGroup = lastdoc;
120 				} else if (doc.comment.isPrivate) {
121 					decl.protection = Protection.Private;
122 					lastdoc = null;
123 				} else lastdoc = decl.docGroup;
124 				ret ~= decl;
125 			}
126 		}
127 		return ret;
128 	}
129 
130 	Declaration[] parseDecl(in dparse.Declaration decl, Entity parent, const(dparse.Attribute)[] additional_attribs = null)
131 	{
132 		if (auto ad = decl.attributeDeclaration) {
133 			additional_attribs ~= decl.attributes;
134 			additional_attribs ~= ad.attribute;
135 			return decl.declarations.map!(d => parseDecl(d, parent, additional_attribs)).join();
136 		}
137 
138 		if (decl.declarations.length) {
139 			additional_attribs ~= decl.attributes;
140 			return decl.declarations.map!(d => parseDecl(d, parent, additional_attribs)).join();
141 		}
142 
143 		Declaration[] ret;
144 		string comment;
145 		size_t line;
146 		if (auto fd = decl.functionDeclaration) {
147 			comment = fd.comment.undecorateComment();
148 			line = fd.name.line;
149 
150 			auto fdr = new FunctionDeclaration(parent, fd.name.text.idup);
151 			resolveTypeDeferred(&fdr.returnType, fd.returnType, parent);
152 			fdr.parameters = parseParameters(fd.parameters, fdr);
153 			m_functions ~= fdr,
154 			addAttributes(fdr, fd.attributes);
155 			addTemplateInfo(fdr, fd);
156 
157 			ret ~= fdr;
158 		} else if (auto vd = decl.variableDeclaration) {
159 			comment = vd.comment.undecorateComment();
160 			line = vd.declarators[0].name.line;
161 			foreach (d; vd.declarators) {
162 				auto v = new VariableDeclaration(parent, d.name.text.idup);
163 				resolveTypeDeferred(&v.type, vd.type, parent);
164 				if (d.initializer) {
165 					v.initializer = new Value(CachedType.init, formatNode(d.initializer));
166 					resolveTypeDeferred(&v.initializer.type, vd.type, parent);
167 				}
168 				ret ~= v;
169 			}
170 		} else if (auto at = decl.aliasThisDeclaration) {
171 			// TODO comment?
172 			line = at.identifier.line;
173 			auto adr = new AliasDeclaration(parent, "this");
174 			adr.targetString = at.identifier.text.idup;
175 			ret ~= adr;
176 		} else if (auto sd = decl.structDeclaration) {
177 			comment = sd.comment.undecorateComment();
178 			line = sd.name.line;
179 			auto sdr = new StructDeclaration(parent, sd.name.text.idup);
180 			sdr.members = parseDeclList(sd.structBody.declarations, sdr);
181 			addTemplateInfo(sdr, sd);
182 			ret ~= sdr;
183 			insertIntoTypeMap(sdr);
184 		} else if (auto cd = decl.classDeclaration) {
185 			comment = cd.comment.undecorateComment();
186 			line = cd.name.line;
187 			auto cdr = new ClassDeclaration(parent, cd.name.text.idup);
188 			if (cd.baseClassList) {
189 				cdr.derivedInterfaces = new CachedType[cd.baseClassList.items.length];
190 				foreach (i, bc; cd.baseClassList.items)
191 					resolveTypeDeferred(&cdr.derivedInterfaces[i], bc.type2, parent);
192 			}
193 			cdr.members = parseDeclList(cd.structBody.declarations, cdr);
194 			addTemplateInfo(cdr, cd);
195 			ret ~= cdr;
196 			insertIntoTypeMap(cdr);
197 		} else if (auto id = decl.interfaceDeclaration) {
198 			comment = id.comment.undecorateComment();
199 			line = id.name.line;
200 			auto idr = new InterfaceDeclaration(parent, id.name.text.idup);
201 			if (id.baseClassList) {
202 				idr.derivedInterfaces = new CachedType[id.baseClassList.items.length];
203 				foreach (i, bc; id.baseClassList.items)
204 					resolveTypeDeferred(&idr.derivedInterfaces[i], bc.type2, parent);
205 			}
206 			idr.members = parseDeclList(id.structBody.declarations, idr);
207 			addTemplateInfo(idr, id);
208 			ret ~= idr;
209 			insertIntoTypeMap(idr);
210 		} else if (auto ud = decl.unionDeclaration) {
211 			comment = ud.comment.undecorateComment();
212 			line = ud.name.line;
213 			auto udr = new UnionDeclaration(parent, ud.name.text.idup);
214 			udr.members = parseDeclList(ud.structBody.declarations, udr);
215 			addTemplateInfo(udr, ud);
216 			ret ~= udr;
217 			insertIntoTypeMap(udr);
218 		} else if (auto ed = decl.enumDeclaration) {
219 			logInfo("TODO: enum %s.%s", parent.qualifiedName, ed.name.text);
220 			// TODO
221 			return null;
222 		} else if (auto ad = decl.aliasDeclaration) {
223 			comment = ad.comment.undecorateComment();
224 			assert(ad.initializers.length);
225 			line = ad.initializers[0].name.line;
226 			foreach (ai; ad.initializers) {
227 				auto adr = new AliasDeclaration(parent, ai.name.text.idup);
228 				resolveTypeDeferred(&adr.targetType, ai.type, parent);
229 				adr.targetString = formatNode(ai.type);
230 				ret ~= adr;
231 			}
232 		} else if (auto td = decl.templateDeclaration) {
233 			logInfo("TODO: template %s.%s", parent.qualifiedName, td.name.text);
234 			// TODO
235 			return null;
236 		} else if (auto cd = decl.constructor) {
237 			comment = cd.comment.undecorateComment();
238 			line = cd.line;
239 
240 			auto cdr = new FunctionDeclaration(parent, "this");
241 			cdr.parameters = parseParameters(cd.parameters, cdr);
242 
243 			Type t;
244 			t.kind = TypeKind.Function;
245 			t.parameterTypes = cdr.parameters.map!(p => p.type).array.assumeUnique;
246 			cdr.type = t;
247 			//addAttributes(cdr, cd.memberFunctionAttributes); // TODO!
248 			addTemplateInfo(cdr, cd);
249 			ret ~= cdr;
250 		} else if (auto dd = decl.destructor) {
251 			// destructors don't get documented for now
252 			return null;
253 		} else if (auto scd = decl.staticConstructor) {
254 			logInfo("TODO: %s.static this()", parent.qualifiedName);
255 			// TODO
256 			return null;
257 		} else if (auto sdd = decl.staticDestructor) {
258 			logInfo("TODO: %s.static ~this()", parent.qualifiedName);
259 			// TODO
260 			return null;
261 		} else if (auto pbd = decl.postblit) {
262 			// postblit doesn't get documented for now
263 			return null;
264 		} else if (auto id = decl.importDeclaration) {
265 			// TODO: use for type resolution
266 			return null;
267 		} else if (auto id = decl.unittest_) {
268 			// TODO: use for unit test examples!
269 			logInfo("TODO: %s.unittest", parent.qualifiedName);
270 			return null;
271 		} else {
272 			logInfo("Unknown declaration in %s: %s", parent.qualifiedName, formatNode(decl));
273 			return null;
274 		}
275 
276 		if (!ret) return null;
277 
278 		foreach (d; ret) {
279 			addAttributes(d, additional_attribs);
280 			addAttributes(d, decl.attributes);
281 
282 			d.docGroup = new DocGroup(d, comment);
283 			d.line = line.to!int;
284 		}
285 
286 		return ret;
287 	}
288 
289 	void addAttributes(Declaration decl, const(dparse.Attribute)[] attrs)
290 	{
291 		return addAttributes(decl, attrs.map!(att => formatNode(att)).array);
292 	}
293 
294 	void addAttributes(Declaration decl, string[] attrs)
295 	{
296 		foreach (as; attrs) {
297 			switch (as) {
298 				default:
299 					if (!decl.attributes.canFind(as))
300 						decl.attributes ~= CachedString(as);
301 					break;
302 				case "private": decl.protection = Protection.Private; break;
303 				case "package": decl.protection = Protection.Package; break;
304 				case "protected": decl.protection = Protection.Protected; break;
305 				case "public": decl.protection = Protection.Public; break;
306 			}
307 		}
308 	}
309 
310 	VariableDeclaration[] parseParameters(in dparse.Parameters dparams, FunctionDeclaration parent)
311 	{
312 		VariableDeclaration[] ret;
313 		foreach (p; dparams.parameters) {
314 			ret ~= parseParameter(p, parent);
315 			if (p.vararg) ret[$-1].name ~= "...";
316 		}
317 		if (dparams.hasVarargs)
318 			ret ~= new VariableDeclaration(parent, "...");
319 		return ret;
320 	}
321 
322 	VariableDeclaration parseParameter(in dparse.Parameter dparam, FunctionDeclaration parent)
323 	{
324 		auto ret = new VariableDeclaration(parent, dparam.name.text.idup);
325 		resolveTypeDeferred(&ret.type, dparam.type, parent);
326 		if (dparam.default_) {
327 			ret.initializer = new Value;
328 			ret.initializer.type = ret.type;
329 			ret.initializer.valueString = formatNode(dparam.default_);
330 		}
331 		addAttributes(ret, dparam.parameterAttributes.map!(a => dlex.str(a)).array);
332 		return ret;
333 	}
334 
335 	Type parseType(in dparse.Type type, Entity scope_)
336 	{
337 		auto ret = parseType(type.type2, scope_);
338 		foreach (tc; type.typeConstructors)
339 			ret.modifiers ~= CachedString(dlex.str(tc));
340 
341 		foreach (sf; type.typeSuffixes) {
342 			if (sf.delegateOrFunction.text) {
343 				if (sf.delegateOrFunction.text == "function")
344 					ret = Type.makeFunction(CachedType(ret), parseParameters(sf.parameters, null).map!(p => p.type).array.assumeUnique);
345 				else ret = Type.makeDelegate(CachedType(ret), parseParameters(sf.parameters, null).map!(p => p.type).array.assumeUnique);
346 			}
347 			else if (sf.star.type != dlex.tok!"") ret = Type.makePointer(CachedType(ret));
348 			else if (sf.array) {
349 				if (sf.type) ret = ret.makeAssociativeArray(CachedType(ret), CachedType(parseType(sf.type, scope_)));
350 				else if (sf.low) ret = ret.makeStaticArray(CachedType(ret), formatNode(sf.low));
351 				else ret = Type.makeArray(CachedType(ret));
352 			}
353 		}
354 
355 		ret.text = formatNode(type);
356 		return ret;
357 	}
358 
359 	Type parseType(in dparse.Type2 type, Entity scope_)
360 	{
361 		Type ret;
362 		if (type.builtinType) {
363 			ret.kind = TypeKind.Primitive;
364 			ret.typeName = dlex.str(type.builtinType);
365 		} else if (type.symbol) {
366 			ret.kind = TypeKind.Primitive;
367 			ret.typeName = formatNode(type.symbol);
368 			resolveTypeDecl(ret, scope_);
369 		} else if (auto te = type.typeofExpression) {
370 			ret.kind = TypeKind.Primitive;
371 			ret.typeName = formatNode(te.expression);
372 			// te.return_?
373 		} else if (auto itc = type.identifierOrTemplateChain) {
374 			ret.kind = TypeKind.Primitive;
375 			ret.typeName = itc.identifiersOrTemplateInstances
376 				.map!(it => it.templateInstance ? formatNode(it.templateInstance) : it.identifier.text.idup)
377 				.join(".");
378 			resolveTypeDecl(ret, scope_);
379 		} else if (auto tc = type.typeConstructor) {
380 			ret = parseType(type.type, scope_);
381 			ret.modifiers = CachedString(dlex.str(tc)) ~ ret.modifiers;
382 		} else if (auto tp = type.type) {
383 			return parseType(tp, scope_);
384 		} else {
385 			ret.kind = TypeKind.Primitive;
386 			ret.typeName = "(invalid)";
387 		}
388 		return ret;
389 	}
390 
391 	void insertIntoTypeMap(Declaration decl)
392 	{
393 		auto qname = decl.qualifiedName.to!string;
394 		m_typeMap[qname] = decl;
395 		auto idx = qname.indexOf('.');
396 		while (idx >= 0) {
397 			qname = qname[idx+1 .. $];
398 			m_typeMap[qname] = decl;
399 			idx = qname.indexOf('.');
400 		}
401 	}
402 
403 	bool isTypeDecl(const(Declaration) a)
404 	{
405 		switch (a.kind) {
406 			default: return false;
407 			case DeclarationKind.Struct:
408 			case DeclarationKind.Union:
409 			case DeclarationKind.Class:
410 			case DeclarationKind.Interface:
411 			case DeclarationKind.Enum:
412 				return true;
413 			case DeclarationKind.Alias:
414 				return !!(cast(AliasDeclaration)a).targetType;
415 			case DeclarationKind.TemplateParameter:
416 				return true;
417 			case DeclarationKind.Template:
418 				// support eponymous template types
419 				auto td = cast(TemplateDeclaration)a;
420 				// be optimistic for templates without content that they are in fact types
421 				if (!td.members.length) return true;
422 				// otherwise require an actual eponymous type member
423 				auto mi = td.members.countUntil!(m => m.name == a.name);
424 				return mi >= 0 && isTypeDecl(td.members[mi]);
425 		}
426 	}
427 
428 	private void resolveTypeDeferred(CachedType* dst, const(dparse.Type) tp, Entity sc)
429 	{
430 		m_deferredTypes ~= tuple(dst, cast(const(dparse.ASTNode))tp, sc);
431 	}
432 
433 	private void resolveTypeDeferred(CachedType* dst, const(dparse.Type2) tp, Entity sc)
434 	{
435 		m_deferredTypes ~= tuple(dst, cast(const(dparse.ASTNode))tp, sc);
436 	}
437 
438 	private void resolveTypeDecl(ref Type tp, const(Entity) sc)
439 	{
440 		if (tp.kind != TypeKind.Primitive) return;
441 		if (tp.typeDecl) return;
442 
443 		tp.typeDecl = sc.lookup!Declaration(tp.typeName);
444 		if (!tp.typeDecl || !isTypeDecl(tp.typeDecl)) tp.typeDecl = m_typeMap.get(tp.typeName, null);
445 	}
446 }
447 
448 
449 private string undecorateComment(string str)
450 {
451 	if (!str.length) return "";
452 
453 	auto app = appender!string();
454 	dlex.unDecorateComment(str, app);
455 	return app.data;
456 }
457 
458 
459 private string formatNode(T)(const T t)
460 {
461 	import std.array;
462 	auto writer = appender!string();
463 	auto formatter = new dformat.Formatter!(typeof(writer))(writer);
464 	formatter.format(t);
465 	return writer.data;
466 }
467 
468 private void addTemplateInfo(T)(Declaration decl, T ddecl)
469 {
470 	if (ddecl.templateParameters) {
471 		decl.isTemplate = true;
472 		if (auto tpl = ddecl.templateParameters.templateParameterList)
473 			decl.templateArgs = tpl.items.map!(tp => new TemplateParameterDeclaration(decl, formatNode(tp))).array;
474 		if (ddecl.constraint)
475 			decl.templateConstraint = formatNode(ddecl.constraint.expression);
476 	}
477 }