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