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