#!/usr/bin/env dub /+dub.sdl: dependency "dmd" path="../../.." versions "CallbackAPI" +/ /* * This file contains an example of how to retrieve the scope of a statement. * First, the callback system is used. This, however, will not work for fields * of structs or classes, which is why the visitor will cover this corner case */ import core.stdc.stdarg; import core.stdc.string; import std.conv; import std.string; import std.algorithm.sorting; import std.algorithm.mutation : SwapStrategy; import std.path : dirName; import dmd.errors; import dmd.frontend; import dmd.console; import dmd.arraytypes; import dmd.compiler; import dmd.dmodule; import dmd.dsymbol; import dmd.dsymbolsem; import dmd.location; import dmd.semantic2; import dmd.semantic3; import dmd.statement; import dmd.visitor; import dmd.dscope; import dmd.denum; import dmd.nspace; import dmd.dstruct; import dmd.dclass; import dmd.globals; import std.stdio : writeln; private bool isBefore(Loc loc1, Loc loc2) { return loc1.linnum != loc2.linnum? loc1.linnum < loc2.linnum : loc1.charnum < loc2.charnum; } private struct CallbackHelper { static Loc cursorLoc; static Scope *scp; static extern (C++) void statementSem(Statement s, Scope *sc) { if (s.loc.linnum == cursorLoc.linnum && strcmp(s.loc.filename, cursorLoc.filename) == 0) { sc.setNoFree(); scp = sc; } } }; int main() { auto dmdParentDir = dirName(dirName(dirName(dirName(__FILE_FULL_PATH__)))); global.path = Strings(); global.path.push((dirName(dmdParentDir) ~ "/phobos" ~ '\0').ptr); global.path.push((dmdParentDir ~ "/druntime/import" ~ '\0').ptr); // comment for error output in parsing & semantic diagnosticHandler = (const ref SourceLoc location, Color headerColor, const(char)* header, const(char)* messageFormat, va_list args, const(char)* prefix1, const(char)* prefix2) => true; global.gag = 1; initDMD(diagnosticHandler); Module m = parseModule("testfiles/correct.d").module_; m.importedFrom = m; // m.isRoot() == true CallbackHelper.cursorLoc = Loc(to!string(m.srcfile).ptr, 22, 10); Compiler.onStatementSemanticStart = &CallbackHelper.statementSem; m.importAll(null); // semantic m.dsymbolSemantic(null); Module.runDeferredSemantic(); m.semantic2(null); Module.runDeferredSemantic2(); m.semantic3(null); Module.runDeferredSemantic3(); Dsymbol[] symbols; // if scope could not be retrieved through the callback, then traverse AST if (!CallbackHelper.scp) { auto visitor = new DsymbolsScopeRetrievingVisitor(CallbackHelper.cursorLoc); m.accept(visitor); symbols = visitor.symbols; } while (CallbackHelper.scp) { if (CallbackHelper.scp.scopesym && CallbackHelper.scp.scopesym.symtab) foreach (x; CallbackHelper.scp.scopesym.symtab.tab.asRange()) { symbols ~= x.value; } CallbackHelper.scp = CallbackHelper.scp.enclosing; } sort!("to!string(a.ident) < to!string(b.ident)", SwapStrategy.stable)(symbols); foreach (sym; symbols) { writeln(sym.ident); } deinitializeDMD(); return 0; } private extern (C++) final class DsymbolsScopeRetrievingVisitor : Visitor { Loc loc; Dsymbol[] symbols; alias visit = Visitor.visit; public: extern (D) this(Loc loc) { this.loc = loc; } override void visit(Dsymbol s) { } override void visit(ScopeDsymbol s) { visitScopeDsymbol(s); } override void visit(EnumDeclaration d) { visitScopeDsymbol(d); } override void visit(Nspace d) { visitScopeDsymbol(d); } override void visit(StructDeclaration d) { visitScopeDsymbol(d); } override void visit(ClassDeclaration d) { visitScopeDsymbol(d); } void visitBaseClasses(ClassDeclaration d) { visitScopeDsymbol(d); } override void visit(Module m) { visitScopeDsymbol(m); } private void visitScopeDsymbol(ScopeDsymbol scopeDsym) { if (!scopeDsym.members) return; Dsymbol dsym; foreach (i, s; *scopeDsym.members) { if (s is null || s.ident is null) continue; // if the current symbol is from another module if (auto m = scopeDsym.isModule()) if (!(to!string(s.loc.filename).endsWith(m.ident.toString() ~ ".d"))) continue; if (!s.isImport()) symbols ~= s; if (!i || dsym is null) { dsym = s; continue; } // only visit a symbol which contains the cursor // choose the symbol which is before and the closest to the cursor if (isBefore(dsym.loc, loc) && isBefore(dsym.loc, s.loc) && isBefore(s.loc, loc)) { dsym = s; } } dsym.accept(this); } }