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.length ? vd.declarators[0].name.line : vd.autoDeclaration.parts[0].identifier.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 		import dparse.ast : TypeIdentifierPart;
362 
363 		Type ret;
364 		if (type.builtinType) {
365 			ret.kind = TypeKind.Primitive;
366 			ret.typeName = dlex.str(type.builtinType);
367 		} else if (auto te = type.typeofExpression) {
368 			ret.kind = TypeKind.Primitive;
369 			ret.typeName = formatNode(te.expression);
370 		} else if (auto ti = type.typeIdentifierPart) {
371 			ret.kind = TypeKind.Primitive;
372 			for (Rebindable!(const(TypeIdentifierPart)) i = ti; i; i = i.typeIdentifierPart) {
373 				if (i.dot) ret.typeName ~= ".";
374 				auto iot = i.identifierOrTemplateInstance;
375 				if (iot.templateInstance) {
376 					ret.typeName ~= formatNode(iot.templateInstance);
377 				} else {
378 					ret.typeName ~= iot.identifier.text.idup;
379 				}
380 			}
381 			resolveTypeDecl(ret, scope_);
382 		} else if (auto tc = type.typeConstructor) {
383 			ret = parseType(type.type, scope_);
384 			ret.modifiers = CachedString(dlex.str(tc)) ~ ret.modifiers;
385 		} else if (auto tp = type.type) {
386 			return parseType(tp, scope_);
387 		} else {
388 			ret.kind = TypeKind.Primitive;
389 			ret.typeName = "(invalid)";
390 		}
391 		return ret;
392 	}
393 
394 	void insertIntoTypeMap(Declaration decl)
395 	{
396 		auto qname = decl.qualifiedName.to!string;
397 		m_typeMap[qname] = decl;
398 		auto idx = qname.indexOf('.');
399 		while (idx >= 0) {
400 			qname = qname[idx+1 .. $];
401 			m_typeMap[qname] = decl;
402 			idx = qname.indexOf('.');
403 		}
404 	}
405 
406 	bool isTypeDecl(const(Declaration) a)
407 	{
408 		switch (a.kind) {
409 			default: return false;
410 			case DeclarationKind.Struct:
411 			case DeclarationKind.Union:
412 			case DeclarationKind.Class:
413 			case DeclarationKind.Interface:
414 			case DeclarationKind.Enum:
415 				return true;
416 			case DeclarationKind.Alias:
417 				return !!(cast(AliasDeclaration)a).targetType;
418 			case DeclarationKind.TemplateParameter:
419 				return true;
420 			case DeclarationKind.Template:
421 				// support eponymous template types
422 				auto td = cast(TemplateDeclaration)a;
423 				// be optimistic for templates without content that they are in fact types
424 				if (!td.members.length) return true;
425 				// otherwise require an actual eponymous type member
426 				auto mi = td.members.countUntil!(m => m.name == a.name);
427 				return mi >= 0 && isTypeDecl(td.members[mi]);
428 		}
429 	}
430 
431 	private void resolveTypeDeferred(CachedType* dst, const(dparse.Type) tp, Entity sc)
432 	{
433 		m_deferredTypes ~= tuple(dst, cast(const(dparse.ASTNode))tp, sc);
434 	}
435 
436 	private void resolveTypeDeferred(CachedType* dst, const(dparse.Type2) tp, Entity sc)
437 	{
438 		m_deferredTypes ~= tuple(dst, cast(const(dparse.ASTNode))tp, sc);
439 	}
440 
441 	private void resolveTypeDecl(ref Type tp, const(Entity) sc)
442 	{
443 		if (tp.kind != TypeKind.Primitive) return;
444 		if (tp.typeDecl) return;
445 
446 		tp.typeDecl = sc.lookup!Declaration(tp.typeName);
447 		if (!tp.typeDecl || !isTypeDecl(tp.typeDecl)) tp.typeDecl = m_typeMap.get(tp.typeName, null);
448 	}
449 }
450 
451 
452 private string undecorateComment(string str)
453 {
454 	if (!str.length) return "";
455 
456 	auto app = appender!string();
457 	dlex.unDecorateComment(str, app);
458 	return app.data;
459 }
460 
461 
462 private string formatNode(T)(const T t)
463 {
464 	import std.array;
465 	auto writer = appender!string();
466 	auto formatter = new dformat.Formatter!(typeof(writer))(writer);
467 	formatter.format(t);
468 	return writer.data;
469 }
470 
471 private void addTemplateInfo(T)(Declaration decl, T ddecl)
472 {
473 	if (ddecl.templateParameters) {
474 		decl.isTemplate = true;
475 		if (auto tpl = ddecl.templateParameters.templateParameterList)
476 			decl.templateArgs = tpl.items.map!(tp => new TemplateParameterDeclaration(decl, formatNode(tp))).array;
477 		if (ddecl.constraint)
478 			decl.templateConstraint = formatNode(ddecl.constraint.expression);
479 	}
480 }