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 }